1#!/usr/bin/env python3 2# 3# Copyright (C) 2016 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); you may not 6# use this file except in compliance with the License. You may obtain a copy of 7# the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14# License for the specific language governing permissions and limitations under 15# the License. 16""" 17This test script exercises different GATT write procedures. 18""" 19 20from acts.test_decorators import test_tracker_info 21from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest 22from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest 23from acts.test_utils.bt.bt_constants import gatt_characteristic 24from acts.test_utils.bt.bt_constants import gatt_descriptor 25from acts.test_utils.bt.bt_constants import gatt_event 26from acts.test_utils.bt.bt_constants import gatt_cb_strings 27from acts.test_utils.bt.bt_constants import gatt_connection_priority 28from acts.test_utils.bt.bt_constants import gatt_characteristic_attr_length 29from acts.test_utils.bt.bt_constants import gatt_mtu_size 30from acts.test_utils.bt.bt_gatt_utils import setup_gatt_mtu 31 32 33class GattWriteTest(GattConnectedBaseTest): 34 @BluetoothBaseTest.bt_test_wrap 35 @test_tracker_info(uuid='513f4cef-489e-4bb6-96cc-c298c589225c') 36 def test_write_char(self): 37 """Test write characteristic value 38 39 Test write characteristic value using Write Request 40 41 1. Central: write WRITABLE_CHAR_UUID characteristic with char_value 42 using write request. 43 2. Peripheral: receive the written data. 44 3. Peripheral: send response with status 0 (success). 45 4. Central: make sure write callback is called. 46 47 Expected Result: 48 Verify that write request/response is properly delivered. 49 50 Returns: 51 Pass if True 52 Fail if False 53 54 TAGS: LE, GATT, Characteristic 55 Priority: 0 56 """ 57 char_value = [1, 2, 3, 4, 5, 6, 7] 58 self.cen_ad.droid.gattClientCharacteristicSetValue( 59 self.bluetooth_gatt, self.discovered_services_index, 60 self.test_service_index, self.WRITABLE_CHAR_UUID, char_value) 61 62 self.cen_ad.droid.gattClientCharacteristicSetWriteType( 63 self.bluetooth_gatt, self.discovered_services_index, 64 self.test_service_index, self.WRITABLE_CHAR_UUID, 65 gatt_characteristic['write_type_default']) 66 67 self.cen_ad.droid.gattClientWriteCharacteristic( 68 self.bluetooth_gatt, self.discovered_services_index, 69 self.test_service_index, self.WRITABLE_CHAR_UUID) 70 71 event = self._server_wait(gatt_event['char_write_req']) 72 73 request_id = event['data']['requestId'] 74 self.assertEqual(True, event['data']['responseNeeded'], 75 "Should need response") 76 self.assertEqual(char_value, event['data']['value']) 77 self.assertEqual(0, event['data']['offset']) 78 79 bt_device_id = 0 80 status = 0 81 #both offset and return value don't matter, just the status 82 offset = 0 83 self.per_ad.droid.gattServerGetConnectedDevices(self.gatt_server) 84 self.per_ad.droid.gattServerSendResponse( 85 self.gatt_server, bt_device_id, request_id, status, offset, []) 86 87 event = self._client_wait(gatt_event['char_write']) 88 self.assertEqual(status, event["data"]["Status"], 89 "Write status should be 0") 90 # Write response doesn't carry any data expcept status 91 return True 92 93 @BluetoothBaseTest.bt_test_wrap 94 @test_tracker_info(uuid='329dbef8-1b54-47e2-a388-b33ef9384464') 95 def test_write_descr(self): 96 """Test write descriptor value 97 98 Test write descriptor value 99 100 1. Central: write WRITABLE_DESC_UUID descriptor with desc_value. 101 2. Peripheral: receive the written data. 102 3. Peripheral: send response with status 0 (success). 103 4. Central: make sure write callback is called. 104 105 Expected Result: 106 Verify that write request/response is properly delivered. 107 108 Returns: 109 Pass if True 110 Fail if False 111 112 TAGS: LE, GATT, Descriptor 113 Priority: 0 114 """ 115 desc_value = [1, 2, 3, 4, 5, 6, 7] 116 self.cen_ad.droid.gattClientDescriptorSetValue( 117 self.bluetooth_gatt, self.discovered_services_index, 118 self.test_service_index, self.WRITABLE_CHAR_UUID, 119 self.WRITABLE_DESC_UUID, desc_value) 120 121 self.cen_ad.droid.gattClientWriteDescriptor( 122 self.bluetooth_gatt, self.discovered_services_index, 123 self.test_service_index, self.WRITABLE_CHAR_UUID, 124 self.WRITABLE_DESC_UUID) 125 126 event = self._server_wait(gatt_event['desc_write_req']) 127 128 request_id = event['data']['requestId'] 129 self.assertEqual(True, event['data']['responseNeeded'], 130 "Should need response") 131 self.assertEqual(desc_value, event['data']['value']) 132 self.assertEqual(0, event['data']['offset']) 133 134 bt_device_id = 0 135 status = 0 136 #both offset and return value don't matter, just the status 137 offset = 0 138 self.per_ad.droid.gattServerGetConnectedDevices(self.gatt_server) 139 self.per_ad.droid.gattServerSendResponse( 140 self.gatt_server, bt_device_id, request_id, status, offset, []) 141 142 event = self._client_wait(gatt_event['desc_write']) 143 self.assertEqual(status, event["data"]["Status"], 144 "Write status should be 0") 145 # Write response doesn't carry any data except status 146 return True 147 148 @BluetoothBaseTest.bt_test_wrap 149 @test_tracker_info(uuid='85757307-5bb1-43e5-9331-f1d7bdcbd6a0') 150 def test_write_char_no_resp(self): 151 """Test write characteristic value 152 153 Test write characteristic value using Write Command 154 155 1. Central: write WRITABLE_CHAR_UUID characteristic with char_value 156 using write command. 157 2. Central: make sure write callback is called. 158 3. Peripheral: receive the written data. 159 160 Expected Result: 161 Verify that write command is properly delivered. 162 163 Returns: 164 Pass if True 165 Fail if False 166 167 TAGS: LE, GATT, Characteristic 168 Priority: 0 169 """ 170 char_value = [1, 2, 3, 4, 5, 6, 7] 171 self.cen_ad.droid.gattClientCharacteristicSetValue( 172 self.bluetooth_gatt, self.discovered_services_index, 173 self.test_service_index, self.WRITABLE_CHAR_UUID, char_value) 174 175 self.cen_ad.droid.gattClientCharacteristicSetWriteType( 176 self.bluetooth_gatt, self.discovered_services_index, 177 self.test_service_index, self.WRITABLE_CHAR_UUID, 178 gatt_characteristic['write_type_no_response']) 179 180 self.cen_ad.droid.gattClientWriteCharacteristic( 181 self.bluetooth_gatt, self.discovered_services_index, 182 self.test_service_index, self.WRITABLE_CHAR_UUID) 183 184 event = self._client_wait(gatt_event['char_write']) 185 if event["data"]["Status"] != 0: 186 self.log.error("Write status should be 0") 187 return False 188 189 event = self._server_wait(gatt_event['char_write_req']) 190 191 request_id = event['data']['requestId'] 192 self.assertEqual(False, event['data']['responseNeeded'], 193 "Should not need response") 194 self.assertEqual(0, event['data']['offset']) 195 self.assertEqual(char_value, event['data']['value']) 196 197 return True 198 199 @BluetoothBaseTest.bt_test_wrap 200 @test_tracker_info(uuid='0bf0182a-c315-4160-81be-9ce09f93608b') 201 def test_write_characteristic_long_no_resp(self): 202 """Test write characteristic value 203 204 Test write characteristic value using Write Command 205 206 1. Central: write WRITABLE_CHAR_UUID characteristic with char_value 207 using write command. 208 2. Central: make sure write callback is called. 209 3. Peripheral: receive the written data. Check it was properly trimmed. 210 211 Expected Result: 212 Verify that write command is properly trimmed and delivered. 213 214 Returns: 215 Pass if True 216 Fail if False 217 218 TAGS: LE, GATT, Characteristic 219 Priority: 0 220 """ 221 char_value = [] 222 for i in range(512): 223 char_value.append(i % 256) 224 225 self.cen_ad.droid.gattClientCharacteristicSetValue( 226 self.bluetooth_gatt, self.discovered_services_index, 227 self.test_service_index, self.WRITABLE_CHAR_UUID, char_value) 228 229 self.cen_ad.droid.gattClientCharacteristicSetWriteType( 230 self.bluetooth_gatt, self.discovered_services_index, 231 self.test_service_index, self.WRITABLE_CHAR_UUID, 232 gatt_characteristic['write_type_no_response']) 233 234 self.cen_ad.droid.gattClientWriteCharacteristic( 235 self.bluetooth_gatt, self.discovered_services_index, 236 self.test_service_index, self.WRITABLE_CHAR_UUID) 237 238 event = self._server_wait(gatt_event['char_write_req']) 239 240 request_id = event['data']['requestId'] 241 self.assertEqual(False, event['data']['responseNeeded']) 242 243 # value shall be trimmed to MTU-3 244 trimmed_value = char_value[0:self.mtu - 3] 245 self.assertEqual( 246 trimmed_value, event['data']['value'], 247 "Received value should be sent value trimmed to MTU-3") 248 249 event = self._client_wait(gatt_event['char_write']) 250 if event["data"]["Status"] != 0: 251 self.log.error("Write status should be 0") 252 return False 253 return True 254 255 @BluetoothBaseTest.bt_test_wrap 256 @test_tracker_info(uuid='b80f1b5a-a223-441e-a6ed-d3c284c83cc7') 257 def test_write_characteristic_value_longer_than_mtu_request(self): 258 """Test writing characteristic value longer than what mtu limts 259 260 Test establishing a gatt connection between a GATT Peripheral and GATT 261 Client. Request mtu size equal to the max MTU. 262 The max number of bytes can be sent within a characteristic is 263 (MTU - gatt_characteristic_attr_length['attr_2']) since 264 the gatt_characteristic_attr_length['attr_2'] (3 bytes) are 265 used for its attribute of the command code and its handle. 266 Then reduce mtu by 1 and re-send the same characteristic. 267 Make sure the characteristic value received by the remote side is 268 also reduced properly. 269 270 Steps: 271 1. Create a GATT connection between the scanner(Client) and 272 advertiser(Peripheral). 273 2. Client: request new mtu size change to max MTU. 274 3. Client: write a characteristic with char_value of max MTU bytes. 275 4. Peripheral: receive the written data. Check it was properly 276 truncated to (max MTU - gatt_characteristic_attr_length['attr_2']). 277 5. Client: request mtu size change to (max MTU - 1). 278 6. Client: write the same characteristic again. 279 7. Peripheral: receive the written data. Check it was properly 280 truncated to (max MTU - 1 - gatt_characteristic_attr_length['attr_2']) 281 8. Client: return mtu size. 282 283 Expected Result: 284 Verify that data received by the Peripheral side is properly truncated 285 when mtu is set. 286 287 Returns: 288 Pass if True 289 Fail if False 290 291 TAGS: LE, GATT, Characteristic, MTU 292 Priority: 2 293 """ 294 self.mtu = gatt_mtu_size['max'] 295 self.log.info("Set mtu to max MTU: {}".format(self.mtu)) 296 # set new MTU to the middle point of min and max of MTU 297 if not setup_gatt_mtu(self.cen_ad, self.bluetooth_gatt, 298 self.gatt_callback, self.mtu): 299 return False 300 301 # create a characteristic with max MTU (217) bytes 302 char_value = [] 303 for i in range(gatt_mtu_size['max']): 304 char_value.append(i) 305 306 self.cen_ad.droid.gattClientCharacteristicSetValue( 307 self.bluetooth_gatt, self.discovered_services_index, 308 self.test_service_index, self.WRITABLE_CHAR_UUID, char_value) 309 310 self.cen_ad.droid.gattClientCharacteristicSetWriteType( 311 self.bluetooth_gatt, self.discovered_services_index, 312 self.test_service_index, self.WRITABLE_CHAR_UUID, 313 gatt_characteristic['write_type_no_response']) 314 315 # write data to the characteristic of the Peripheral 316 self.cen_ad.droid.gattClientWriteCharacteristic( 317 self.bluetooth_gatt, self.discovered_services_index, 318 self.test_service_index, self.WRITABLE_CHAR_UUID) 319 320 event = self._server_wait(gatt_event['char_write_req']) 321 self.log.info("Received value with mtu = max MTU: {}".format(event[ 322 'data']['value'])) 323 324 # check the data received by Peripheral shall be truncated to 325 # (mtu - gatt_characteristic_attr_length['attr_2']) bytes 326 data_length = self.mtu - gatt_characteristic_attr_length['attr_2'] 327 expected_value = char_value[:data_length] 328 self.assertEqual( 329 expected_value, event['data']['value'], 330 "Received value should have {} bytes".format(data_length)) 331 332 # set the mtu to max MTU-1 333 self.mtu = gatt_mtu_size['max'] - 1 334 self.log.info("Set mtu to max MTU - 1 : {}".format(self.mtu)) 335 data_length = self.mtu - gatt_characteristic_attr_length['attr_2'] 336 if not setup_gatt_mtu(self.cen_ad, self.bluetooth_gatt, 337 self.gatt_callback, self.mtu): 338 return False 339 340 # write the same characteric to Peripheral again 341 self.cen_ad.droid.gattClientWriteCharacteristic( 342 self.bluetooth_gatt, self.discovered_services_index, 343 self.test_service_index, self.WRITABLE_CHAR_UUID) 344 345 event = self._server_wait(gatt_event['char_write_req']) 346 self.log.info("Data received when mtu = max MTU - 1: {}".format(event[ 347 'data']['value'])) 348 349 # check the data received by Peripheral shall be truncated to 350 # (mtu - gatt_characteristic_attr_length['attr_2']) bytes 351 # when mtu is reduced 352 expected_value = char_value[:data_length] 353 self.assertEqual( 354 expected_value, event['data']['value'], 355 "Received value should have {} bytes".format(data_length)) 356 357 # return the mtu to default 358 self.mtu = gatt_mtu_size['min'] 359 self.log.info("Set mtu to min : {}".format(self.mtu)) 360 if not setup_gatt_mtu(self.cen_ad, self.bluetooth_gatt, 361 self.gatt_callback, self.mtu): 362 return False 363 return True 364 365 @BluetoothBaseTest.bt_test_wrap 366 @test_tracker_info(uuid='319eee6d-22d9-4498-bb15-21d0018e45e6') 367 def test_write_characteristic_stress(self): 368 """Stress test write characteristic value 369 370 Test write characteristic value using Write Request 371 372 1. Central: write WRITABLE_CHAR_UUID characteristic with char_value 373 using write request. 374 2. Peripheral: receive the written data. 375 3. Peripheral: send response with status 0 (success). 376 4. Central: make sure write callback is called. 377 5. Repeat steps 1-4 100 times. 378 379 Expected Result: 380 Verify that write request/response is properly delivered. 381 382 Returns: 383 Pass if True 384 Fail if False 385 386 TAGS: LE, GATT, Characteristic 387 Priority: 0 388 """ 389 self.cen_ad.droid.gattClientRequestConnectionPriority( 390 self.bluetooth_gatt, gatt_connection_priority['high']) 391 392 bt_device_id = 0 393 394 self.cen_ad.droid.gattClientCharacteristicSetWriteType( 395 self.bluetooth_gatt, self.discovered_services_index, 396 self.test_service_index, self.WRITABLE_CHAR_UUID, 397 gatt_characteristic['write_type_default']) 398 399 for i in range(100): 400 401 char_value = [] 402 for j in range(i, i + self.mtu - 3): 403 char_value.append(j % 256) 404 405 self.cen_ad.droid.gattClientCharacteristicSetValue( 406 self.bluetooth_gatt, self.discovered_services_index, 407 self.test_service_index, self.WRITABLE_CHAR_UUID, char_value) 408 409 self.cen_ad.droid.gattClientWriteCharacteristic( 410 self.bluetooth_gatt, self.discovered_services_index, 411 self.test_service_index, self.WRITABLE_CHAR_UUID) 412 413 event = self._server_wait(gatt_event['char_write_req']) 414 415 self.log.info("{} event found: {}".format(gatt_cb_strings[ 416 'char_write_req'].format(self.gatt_server_callback), event[ 417 'data']['value'])) 418 request_id = event['data']['requestId'] 419 found_value = event['data']['value'] 420 if found_value != char_value: 421 self.log.info("Values didn't match. Found: {}, " 422 "Expected: {}".format(found_value, char_value)) 423 return False 424 425 # only status is sent 426 status = 0 427 offset = 0 428 char_value_return = [] 429 self.per_ad.droid.gattServerSendResponse( 430 self.gatt_server, bt_device_id, request_id, status, offset, 431 char_value_return) 432 433 event = self._client_wait(gatt_event['char_write']) 434 if event["data"]["Status"] != status: 435 self.log.error("Write status should be 0") 436 return False 437 438 return True 439 440 @BluetoothBaseTest.bt_test_wrap 441 @test_tracker_info(uuid='b19d42dc-58ba-4b20-b6c1-6628e7d21de4') 442 def test_write_descriptor_stress(self): 443 """Stress test write descriptor value 444 445 Stress test write descriptor value 446 447 1. Central: write WRITABLE_DESC_UUID descriptor with desc_value. 448 2. Peripheral: receive the written data. 449 3. Peripheral: send response with status 0 (success). 450 4. Central: make sure write callback is called. 451 5. Repeat 1-4 100 times 452 453 Expected Result: 454 Verify that write request/response is properly delivered. 455 456 Returns: 457 Pass if True 458 Fail if False 459 460 TAGS: LE, GATT, Descriptor 461 Priority: 0 462 """ 463 self.cen_ad.droid.gattClientRequestConnectionPriority( 464 self.bluetooth_gatt, gatt_connection_priority['high']) 465 466 for i in range(100): 467 468 desc_value = [] 469 for j in range(i, i + self.mtu - 3): 470 desc_value.append(j % 256) 471 472 self.cen_ad.droid.gattClientDescriptorSetValue( 473 self.bluetooth_gatt, self.discovered_services_index, 474 self.test_service_index, self.WRITABLE_CHAR_UUID, 475 self.WRITABLE_DESC_UUID, desc_value) 476 477 self.cen_ad.droid.gattClientWriteDescriptor( 478 self.bluetooth_gatt, self.discovered_services_index, 479 self.test_service_index, self.WRITABLE_CHAR_UUID, 480 self.WRITABLE_DESC_UUID) 481 482 event = self._server_wait(gatt_event['desc_write_req']) 483 484 self.log.info("{} event found: {}".format(gatt_cb_strings[ 485 'char_write_req'].format(self.gatt_server_callback), event[ 486 'data']['value'])) 487 488 request_id = event['data']['requestId'] 489 self.assertEqual(True, event['data']['responseNeeded'], 490 "Should need response") 491 self.assertEqual(desc_value, event['data']['value']) 492 self.assertEqual(0, event['data']['offset']) 493 494 bt_device_id = 0 495 status = 0 496 #both offset and return value don't matter, just the status 497 offset = 0 498 self.per_ad.droid.gattServerGetConnectedDevices(self.gatt_server) 499 self.per_ad.droid.gattServerSendResponse( 500 self.gatt_server, bt_device_id, request_id, status, offset, []) 501 502 event = self._client_wait(gatt_event['desc_write']) 503 self.assertEqual(status, event["data"]["Status"], 504 "Write status should be 0") 505 # Write response doesn't carry any data except status 506 return True 507 508 @BluetoothBaseTest.bt_test_wrap 509 @test_tracker_info(uuid='74c147eb-2702-4cd8-be1f-efff3e9eaa6c') 510 def test_write_characteristic_no_resp_stress(self): 511 """Stress test write characteristic value 512 513 Stress test write characteristic value using Write Command 514 515 1. Central: write WRITABLE_CHAR_UUID characteristic with char_value 516 using write command. 517 2. Central: make sure write callback is called. 518 3. Peripheral: receive the written data. 519 4. Repeat steps 1-3 100 times. 520 521 Expected Result: 522 Verify that write command is properly delivered. 523 524 Returns: 525 Pass if True 526 Fail if False 527 528 TAGS: LE, GATT, Characteristic 529 Priority: 0 530 """ 531 self.cen_ad.droid.gattClientRequestConnectionPriority( 532 self.bluetooth_gatt, gatt_connection_priority['high']) 533 534 bt_device_id = 0 535 536 self.cen_ad.droid.gattClientCharacteristicSetWriteType( 537 self.bluetooth_gatt, self.discovered_services_index, 538 self.test_service_index, self.WRITABLE_CHAR_UUID, 539 gatt_characteristic['write_type_no_response']) 540 541 for i in range(100): 542 char_value = [] 543 for j in range(i, i + self.mtu - 3): 544 char_value.append(j % 256) 545 546 self.cen_ad.droid.gattClientCharacteristicSetValue( 547 self.bluetooth_gatt, self.discovered_services_index, 548 self.test_service_index, self.WRITABLE_CHAR_UUID, char_value) 549 550 self.cen_ad.droid.gattClientWriteCharacteristic( 551 self.bluetooth_gatt, self.discovered_services_index, 552 self.test_service_index, self.WRITABLE_CHAR_UUID) 553 554 # client shall not wait for server, get complete event right away 555 event = self._client_wait(gatt_event['char_write']) 556 if event["data"]["Status"] != 0: 557 self.log.error("Write status should be 0") 558 return False 559 560 event = self._server_wait(gatt_event['char_write_req']) 561 562 self.log.info("{} event found: {}".format(gatt_cb_strings[ 563 'char_write_req'].format(self.gatt_server_callback), event[ 564 'data']['value'])) 565 request_id = event['data']['requestId'] 566 found_value = event['data']['value'] 567 if found_value != char_value: 568 self.log.info("Values didn't match. Found: {}, " 569 "Expected: {}".format(found_value, char_value)) 570 return False 571 572 return True 573