1#/usr/bin/env python3.4 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 282 Expected Result: 283 Verify that data received by the Peripheral side is properly truncated 284 when mtu is set. 285 286 Returns: 287 Pass if True 288 Fail if False 289 290 TAGS: LE, GATT, Characteristic, MTU 291 Priority: 2 292 """ 293 self.mtu = gatt_mtu_size['max'] 294 self.log.info("Set mtu to max MTU: {}".format(self.mtu)) 295 # set new MTU to the middle point of min and max of MTU 296 if not setup_gatt_mtu(self.cen_ad, self.bluetooth_gatt, 297 self.gatt_callback, self.mtu): 298 return False 299 300 # create a characteristic with max MTU (217) bytes 301 char_value = [] 302 for i in range(gatt_mtu_size['max']): 303 char_value.append(i) 304 305 self.cen_ad.droid.gattClientCharacteristicSetValue( 306 self.bluetooth_gatt, self.discovered_services_index, 307 self.test_service_index, self.WRITABLE_CHAR_UUID, char_value) 308 309 self.cen_ad.droid.gattClientCharacteristicSetWriteType( 310 self.bluetooth_gatt, self.discovered_services_index, 311 self.test_service_index, self.WRITABLE_CHAR_UUID, 312 gatt_characteristic['write_type_no_response']) 313 314 # write data to the characteristic of the Peripheral 315 self.cen_ad.droid.gattClientWriteCharacteristic( 316 self.bluetooth_gatt, self.discovered_services_index, 317 self.test_service_index, self.WRITABLE_CHAR_UUID) 318 319 event = self._server_wait(gatt_event['char_write_req']) 320 self.log.info("Received value with mtu = max MTU: {}".format(event[ 321 'data']['value'])) 322 323 # check the data received by Peripheral shall be truncated to 324 # (mtu - gatt_characteristic_attr_length['attr_2']) bytes 325 data_length = self.mtu - gatt_characteristic_attr_length['attr_2'] 326 expected_value = char_value[:data_length] 327 self.assertEqual( 328 expected_value, event['data']['value'], 329 "Received value should have {} bytes".format(data_length)) 330 331 # set the mtu to max MTU-1 332 self.mtu = gatt_mtu_size['max'] - 1 333 self.log.info("Set mtu to max MTU - 1 : {}".format(self.mtu)) 334 data_length = self.mtu - gatt_characteristic_attr_length['attr_2'] 335 if not setup_gatt_mtu(self.cen_ad, self.bluetooth_gatt, 336 self.gatt_callback, self.mtu): 337 return False 338 339 # write the same characteric to Peripheral again 340 self.cen_ad.droid.gattClientWriteCharacteristic( 341 self.bluetooth_gatt, self.discovered_services_index, 342 self.test_service_index, self.WRITABLE_CHAR_UUID) 343 344 event = self._server_wait(gatt_event['char_write_req']) 345 self.log.info("Data received when mtu = max MTU - 1: {}".format(event[ 346 'data']['value'])) 347 348 # check the data received by Peripheral shall be truncated to 349 # (mtu - gatt_characteristic_attr_length['attr_2']) bytes 350 # when mtu is reduced 351 expected_value = char_value[:data_length] 352 self.assertEqual( 353 expected_value, event['data']['value'], 354 "Received value should have {} bytes".format(data_length)) 355 return True 356 357 @BluetoothBaseTest.bt_test_wrap 358 @test_tracker_info(uuid='319eee6d-22d9-4498-bb15-21d0018e45e6') 359 def test_write_characteristic_stress(self): 360 """Stress test write characteristic value 361 362 Test write characteristic value using Write Request 363 364 1. Central: write WRITABLE_CHAR_UUID characteristic with char_value 365 using write request. 366 2. Peripheral: receive the written data. 367 3. Peripheral: send response with status 0 (success). 368 4. Central: make sure write callback is called. 369 5. Repeat steps 1-4 100 times. 370 371 Expected Result: 372 Verify that write request/response is properly delivered. 373 374 Returns: 375 Pass if True 376 Fail if False 377 378 TAGS: LE, GATT, Characteristic 379 Priority: 0 380 """ 381 self.cen_ad.droid.gattClientRequestConnectionPriority( 382 self.bluetooth_gatt, gatt_connection_priority['high']) 383 384 bt_device_id = 0 385 386 self.cen_ad.droid.gattClientCharacteristicSetWriteType( 387 self.bluetooth_gatt, self.discovered_services_index, 388 self.test_service_index, self.WRITABLE_CHAR_UUID, 389 gatt_characteristic['write_type_default']) 390 391 for i in range(100): 392 393 char_value = [] 394 for j in range(i, i + self.mtu - 3): 395 char_value.append(j % 256) 396 397 self.cen_ad.droid.gattClientCharacteristicSetValue( 398 self.bluetooth_gatt, self.discovered_services_index, 399 self.test_service_index, self.WRITABLE_CHAR_UUID, char_value) 400 401 self.cen_ad.droid.gattClientWriteCharacteristic( 402 self.bluetooth_gatt, self.discovered_services_index, 403 self.test_service_index, self.WRITABLE_CHAR_UUID) 404 405 event = self._server_wait(gatt_event['char_write_req']) 406 407 self.log.info("{} event found: {}".format(gatt_cb_strings[ 408 'char_write_req'].format(self.gatt_server_callback), event[ 409 'data']['value'])) 410 request_id = event['data']['requestId'] 411 found_value = event['data']['value'] 412 if found_value != char_value: 413 self.log.info("Values didn't match. Found: {}, " 414 "Expected: {}".format(found_value, char_value)) 415 return False 416 417 # only status is sent 418 status = 0 419 offset = 0 420 char_value_return = [] 421 self.per_ad.droid.gattServerSendResponse( 422 self.gatt_server, bt_device_id, request_id, status, offset, 423 char_value_return) 424 425 event = self._client_wait(gatt_event['char_write']) 426 if event["data"]["Status"] != status: 427 self.log.error("Write status should be 0") 428 return False 429 430 return True 431 432 @BluetoothBaseTest.bt_test_wrap 433 @test_tracker_info(uuid='b19d42dc-58ba-4b20-b6c1-6628e7d21de4') 434 def test_write_descriptor_stress(self): 435 """Stress test write descriptor value 436 437 Stress test write descriptor value 438 439 1. Central: write WRITABLE_DESC_UUID descriptor with desc_value. 440 2. Peripheral: receive the written data. 441 3. Peripheral: send response with status 0 (success). 442 4. Central: make sure write callback is called. 443 5. Repeat 1-4 100 times 444 445 Expected Result: 446 Verify that write request/response is properly delivered. 447 448 Returns: 449 Pass if True 450 Fail if False 451 452 TAGS: LE, GATT, Descriptor 453 Priority: 0 454 """ 455 self.cen_ad.droid.gattClientRequestConnectionPriority( 456 self.bluetooth_gatt, gatt_connection_priority['high']) 457 458 for i in range(100): 459 460 desc_value = [] 461 for j in range(i, i + self.mtu - 3): 462 desc_value.append(j % 256) 463 464 self.cen_ad.droid.gattClientDescriptorSetValue( 465 self.bluetooth_gatt, self.discovered_services_index, 466 self.test_service_index, self.WRITABLE_CHAR_UUID, 467 self.WRITABLE_DESC_UUID, desc_value) 468 469 self.cen_ad.droid.gattClientWriteDescriptor( 470 self.bluetooth_gatt, self.discovered_services_index, 471 self.test_service_index, self.WRITABLE_CHAR_UUID, 472 self.WRITABLE_DESC_UUID) 473 474 event = self._server_wait(gatt_event['desc_write_req']) 475 476 self.log.info("{} event found: {}".format(gatt_cb_strings[ 477 'char_write_req'].format(self.gatt_server_callback), event[ 478 'data']['value'])) 479 480 request_id = event['data']['requestId'] 481 self.assertEqual(True, event['data']['responseNeeded'], 482 "Should need response") 483 self.assertEqual(desc_value, event['data']['value']) 484 self.assertEqual(0, event['data']['offset']) 485 486 bt_device_id = 0 487 status = 0 488 #both offset and return value don't matter, just the status 489 offset = 0 490 self.per_ad.droid.gattServerGetConnectedDevices(self.gatt_server) 491 self.per_ad.droid.gattServerSendResponse( 492 self.gatt_server, bt_device_id, request_id, status, offset, []) 493 494 event = self._client_wait(gatt_event['desc_write']) 495 self.assertEqual(status, event["data"]["Status"], 496 "Write status should be 0") 497 # Write response doesn't carry any data except status 498 return True 499 500 @BluetoothBaseTest.bt_test_wrap 501 @test_tracker_info(uuid='74c147eb-2702-4cd8-be1f-efff3e9eaa6c') 502 def test_write_characteristic_no_resp_stress(self): 503 """Stress test write characteristic value 504 505 Stress test write characteristic value using Write Command 506 507 1. Central: write WRITABLE_CHAR_UUID characteristic with char_value 508 using write command. 509 2. Central: make sure write callback is called. 510 3. Peripheral: receive the written data. 511 4. Repeat steps 1-3 100 times. 512 513 Expected Result: 514 Verify that write command is properly delivered. 515 516 Returns: 517 Pass if True 518 Fail if False 519 520 TAGS: LE, GATT, Characteristic 521 Priority: 0 522 """ 523 self.cen_ad.droid.gattClientRequestConnectionPriority( 524 self.bluetooth_gatt, gatt_connection_priority['high']) 525 526 bt_device_id = 0 527 528 self.cen_ad.droid.gattClientCharacteristicSetWriteType( 529 self.bluetooth_gatt, self.discovered_services_index, 530 self.test_service_index, self.WRITABLE_CHAR_UUID, 531 gatt_characteristic['write_type_no_response']) 532 533 for i in range(100): 534 char_value = [] 535 for j in range(i, i + self.mtu - 3): 536 char_value.append(j % 256) 537 538 self.cen_ad.droid.gattClientCharacteristicSetValue( 539 self.bluetooth_gatt, self.discovered_services_index, 540 self.test_service_index, self.WRITABLE_CHAR_UUID, char_value) 541 542 self.cen_ad.droid.gattClientWriteCharacteristic( 543 self.bluetooth_gatt, self.discovered_services_index, 544 self.test_service_index, self.WRITABLE_CHAR_UUID) 545 546 # client shall not wait for server, get complete event right away 547 event = self._client_wait(gatt_event['char_write']) 548 if event["data"]["Status"] != 0: 549 self.log.error("Write status should be 0") 550 return False 551 552 event = self._server_wait(gatt_event['char_write_req']) 553 554 self.log.info("{} event found: {}".format(gatt_cb_strings[ 555 'char_write_req'].format(self.gatt_server_callback), event[ 556 'data']['value'])) 557 request_id = event['data']['requestId'] 558 found_value = event['data']['value'] 559 if found_value != char_value: 560 self.log.info("Values didn't match. Found: {}, " 561 "Expected: {}".format(found_value, char_value)) 562 return False 563 564 return True 565