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 connection tests. 18""" 19 20import pprint 21from queue import Empty 22import time 23 24from acts.test_decorators import test_tracker_info 25from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest 26from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes 27from acts.test_utils.bt.bt_constants import ble_scan_settings_modes 28from acts.test_utils.bt.bt_constants import ble_scan_settings_match_nums 29from acts.test_utils.bt.bt_constants import bt_profile_constants 30from acts.test_utils.bt.bt_constants import gatt_characteristic 31from acts.test_utils.bt.bt_constants import gatt_descriptor 32from acts.test_utils.bt.bt_constants import gatt_service_types 33from acts.test_utils.bt.bt_constants import gatt_cb_err 34from acts.test_utils.bt.bt_constants import gatt_cb_strings 35from acts.test_utils.bt.bt_constants import gatt_connection_state 36from acts.test_utils.bt.bt_constants import gatt_mtu_size 37from acts.test_utils.bt.bt_constants import gatt_phy_mask 38from acts.test_utils.bt.bt_constants import gatt_transport 39from acts.test_utils.bt.bt_constants import scan_result 40from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError 41from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection 42from acts.test_utils.bt.bt_gatt_utils import wait_for_gatt_disconnect_event 43from acts.test_utils.bt.bt_gatt_utils import close_gatt_client 44from acts.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids 45from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection 46from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection 47from acts.test_utils.bt.bt_gatt_utils import setup_multiple_services 48from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement 49from acts.test_utils.bt.bt_test_utils import clear_bonded_devices 50 51PHYSICAL_DISCONNECT_TIMEOUT = 5 52 53 54class GattConnectTest(BluetoothBaseTest): 55 adv_instances = [] 56 bluetooth_gatt_list = [] 57 gatt_server_list = [] 58 default_timeout = 10 59 default_discovery_timeout = 3 60 61 def __init__(self, controllers): 62 BluetoothBaseTest.__init__(self, controllers) 63 self.cen_ad = self.android_devices[0] 64 self.per_ad = self.android_devices[1] 65 66 def setup_test(self): 67 super(BluetoothBaseTest, self).setup_test() 68 bluetooth_gatt_list = [] 69 self.gatt_server_list = [] 70 self.adv_instances = [] 71 # Ensure there is ample time for a physical disconnect in between 72 # testcases. 73 self.log.info( 74 "Waiting for {} seconds for physical GATT disconnections".format( 75 PHYSICAL_DISCONNECT_TIMEOUT)) 76 time.sleep(PHYSICAL_DISCONNECT_TIMEOUT) 77 78 def teardown_test(self): 79 for bluetooth_gatt in self.bluetooth_gatt_list: 80 self.cen_ad.droid.gattClientClose(bluetooth_gatt) 81 for gatt_server in self.gatt_server_list: 82 self.per_ad.droid.gattServerClose(gatt_server) 83 for adv in self.adv_instances: 84 self.per_ad.droid.bleStopBleAdvertising(adv) 85 return True 86 87 def _orchestrate_gatt_disconnection(self, bluetooth_gatt, gatt_callback): 88 self.log.info("Disconnecting from peripheral device.") 89 try: 90 disconnect_gatt_connection(self.cen_ad, bluetooth_gatt, 91 gatt_callback) 92 close_gatt_client(self.cen_ad, bluetooth_gatt) 93 if bluetooth_gatt in self.bluetooth_gatt_list: 94 self.bluetooth_gatt_list.remove(bluetooth_gatt) 95 except GattTestUtilsError as err: 96 self.log.error(err) 97 return False 98 return True 99 100 def _find_service_added_event(self, gatt_server_cb, uuid): 101 expected_event = gatt_cb_strings['serv_added'].format(gatt_server_cb) 102 try: 103 event = self.per_ad.ed.pop_event(expected_event, 104 self.default_timeout) 105 except Empty: 106 self.log.error( 107 gatt_cb_strings['serv_added_err'].format(expected_event)) 108 return False 109 if event['data']['serviceUuid'].lower() != uuid.lower(): 110 self.log.error("Uuid mismatch. Found: {}, Expected {}.".format( 111 event['data']['serviceUuid'], uuid)) 112 return False 113 return True 114 115 def _verify_mtu_changed_on_client_and_server( 116 self, expected_mtu, gatt_callback, gatt_server_callback): 117 expected_event = gatt_cb_strings['mtu_changed'].format(gatt_callback) 118 try: 119 mtu_event = self.cen_ad.ed.pop_event(expected_event, 120 self.default_timeout) 121 mtu_size_found = mtu_event['data']['MTU'] 122 if mtu_size_found != expected_mtu: 123 self.log.error("MTU size found: {}, expected: {}".format( 124 mtu_size_found, expected_mtu)) 125 return False 126 except Empty: 127 self.log.error( 128 gatt_cb_err['mtu_changed_err'].format(expected_event)) 129 return False 130 131 expected_event = gatt_cb_strings['mtu_serv_changed'].format( 132 gatt_server_callback) 133 try: 134 mtu_event = self.per_ad.ed.pop_event(expected_event, 135 self.default_timeout) 136 mtu_size_found = mtu_event['data']['MTU'] 137 if mtu_size_found != expected_mtu: 138 self.log.error("MTU size found: {}, expected: {}".format( 139 mtu_size_found, expected_mtu)) 140 return False 141 except Empty: 142 self.log.error( 143 gatt_cb_err['mtu_serv_changed_err'].format(expected_event)) 144 return False 145 return True 146 147 @BluetoothBaseTest.bt_test_wrap 148 @test_tracker_info(uuid='8a3530a3-c8bb-466b-9710-99e694c38618') 149 def test_gatt_connect(self): 150 """Test GATT connection over LE. 151 152 Test establishing a gatt connection between a GATT server and GATT 153 client. 154 155 Steps: 156 1. Start a generic advertisement. 157 2. Start a generic scanner. 158 3. Find the advertisement and extract the mac address. 159 4. Stop the first scanner. 160 5. Create a GATT connection between the scanner and advertiser. 161 6. Disconnect the GATT connection. 162 163 Expected Result: 164 Verify that a connection was established and then disconnected 165 successfully. 166 167 Returns: 168 Pass if True 169 Fail if False 170 171 TAGS: LE, Advertising, Filtering, Scanning, GATT 172 Priority: 0 173 """ 174 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 175 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 176 gatt_server_cb) 177 self.gatt_server_list.append(gatt_server) 178 try: 179 bluetooth_gatt, gatt_callback, adv_callback = ( 180 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 181 self.bluetooth_gatt_list.append(bluetooth_gatt) 182 except GattTestUtilsError as err: 183 self.log.error(err) 184 return False 185 self.adv_instances.append(adv_callback) 186 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 187 gatt_callback) 188 189 @BluetoothBaseTest.bt_test_wrap 190 @test_tracker_info(uuid='a839b505-03ac-4783-be7e-1d43129a1948') 191 def test_gatt_connect_stop_advertising(self): 192 """Test GATT connection over LE then stop advertising 193 194 A test case that verifies the GATT connection doesn't 195 disconnect when LE advertisement is stopped. 196 197 Steps: 198 1. Start a generic advertisement. 199 2. Start a generic scanner. 200 3. Find the advertisement and extract the mac address. 201 4. Stop the first scanner. 202 5. Create a GATT connection between the scanner and advertiser. 203 6. Stop the advertiser. 204 7. Verify no connection state changed happened. 205 8. Disconnect the GATT connection. 206 207 Expected Result: 208 Verify that a connection was established and not disconnected 209 when advertisement stops. 210 211 Returns: 212 Pass if True 213 Fail if False 214 215 TAGS: LE, Advertising, Filtering, Scanning, GATT 216 Priority: 0 217 """ 218 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 219 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 220 gatt_server_cb) 221 self.gatt_server_list.append(gatt_server) 222 try: 223 bluetooth_gatt, gatt_callback, adv_callback = ( 224 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 225 self.bluetooth_gatt_list.append(bluetooth_gatt) 226 except GattTestUtilsError as err: 227 self.log.error(err) 228 return False 229 self.per_ad.droid.bleStopBleAdvertising(adv_callback) 230 try: 231 event = self.cen_ad.ed.pop_event( 232 gatt_cb_strings['gatt_conn_change'].format( 233 gatt_callback, self.default_timeout)) 234 self.log.error( 235 "Connection event found when not expected: {}".format(event)) 236 return False 237 except Empty: 238 self.log.info("No connection state change as expected") 239 try: 240 self._orchestrate_gatt_disconnection(bluetooth_gatt, gatt_callback) 241 except Exception as err: 242 self.log.info("Failed to orchestrate disconnect: {}".format(err)) 243 return False 244 return True 245 246 @BluetoothBaseTest.bt_test_wrap 247 @test_tracker_info(uuid='b82f91a8-54bb-4779-a117-73dc7fdb28cc') 248 def test_gatt_connect_autoconnect(self): 249 """Test GATT connection over LE. 250 251 Test re-establishing a gatt connection using autoconnect 252 set to True in order to test connection whitelist. 253 254 Steps: 255 1. Start a generic advertisement. 256 2. Start a generic scanner. 257 3. Find the advertisement and extract the mac address. 258 4. Stop the first scanner. 259 5. Create a GATT connection between the scanner and advertiser. 260 6. Disconnect the GATT connection. 261 7. Create a GATT connection with autoconnect set to True 262 8. Disconnect the GATT connection. 263 264 Expected Result: 265 Verify that a connection was re-established and then disconnected 266 successfully. 267 268 Returns: 269 Pass if True 270 Fail if False 271 272 TAGS: LE, Advertising, Filtering, Scanning, GATT 273 Priority: 0 274 """ 275 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 276 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 277 gatt_server_cb) 278 self.gatt_server_list.append(gatt_server) 279 autoconnect = False 280 mac_address, adv_callback, scan_callback = ( 281 get_mac_address_of_generic_advertisement(self.cen_ad, self.per_ad)) 282 try: 283 bluetooth_gatt, gatt_callback = setup_gatt_connection( 284 self.cen_ad, mac_address, autoconnect) 285 self.cen_ad.droid.bleStopBleScan(scan_callback) 286 self.bluetooth_gatt_list.append(bluetooth_gatt) 287 except GattTestUtilsError as err: 288 self.log.error(err) 289 return False 290 try: 291 disconnect_gatt_connection(self.cen_ad, bluetooth_gatt, 292 gatt_callback) 293 close_gatt_client(self.cen_ad, bluetooth_gatt) 294 if bluetooth_gatt in self.bluetooth_gatt_list: 295 self.bluetooth_gatt_list.remove(bluetooth_gatt) 296 except GattTestUtilsError as err: 297 self.log.error(err) 298 return False 299 autoconnect = True 300 bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt( 301 gatt_callback, mac_address, autoconnect, gatt_transport['auto'], 302 False, gatt_phy_mask['1m_mask']) 303 self.bluetooth_gatt_list.append(bluetooth_gatt) 304 expected_event = gatt_cb_strings['gatt_conn_change'].format( 305 gatt_callback) 306 try: 307 event = self.cen_ad.ed.pop_event(expected_event, 308 self.default_timeout) 309 except Empty: 310 self.log.error( 311 gatt_cb_err['gatt_conn_change_err'].format(expected_event)) 312 test_result = False 313 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 314 gatt_callback) 315 316 @BluetoothBaseTest.bt_test_wrap 317 @test_tracker_info(uuid='e506fa50-7cd9-4bd8-938a-6b85dcfe6bc6') 318 def test_gatt_connect_opportunistic(self): 319 """Test opportunistic GATT connection over LE. 320 321 Test establishing a gatt connection between a GATT server and GATT 322 client in opportunistic mode. 323 324 Steps: 325 1. Start a generic advertisement. 326 2. Start a generic scanner. 327 3. Find the advertisement and extract the mac address. 328 4. Stop the first scanner. 329 5. Create GATT connection 1 between the scanner and advertiser normally 330 6. Create GATT connection 2 between the scanner and advertiser using 331 opportunistic mode 332 7. Disconnect GATT connection 1 333 334 Expected Result: 335 Verify GATT connection 2 automatically disconnects when GATT connection 336 1 disconnect 337 338 Returns: 339 Pass if True 340 Fail if False 341 342 TAGS: LE, Advertising, Filtering, Scanning, GATT 343 Priority: 0 344 """ 345 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 346 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 347 gatt_server_cb) 348 self.gatt_server_list.append(gatt_server) 349 mac_address, adv_callback, scan_callback = ( 350 get_mac_address_of_generic_advertisement(self.cen_ad, self.per_ad)) 351 # Make GATT connection 1 352 try: 353 bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection( 354 self.cen_ad, 355 mac_address, 356 False, 357 transport=gatt_transport['auto'], 358 opportunistic=False) 359 self.cen_ad.droid.bleStopBleScan(scan_callback) 360 self.bluetooth_gatt_list.append(bluetooth_gatt_1) 361 except GattTestUtilsError as err: 362 self.log.error(err) 363 return False 364 # Make GATT connection 2 365 try: 366 bluetooth_gatt_2, gatt_callback_2 = setup_gatt_connection( 367 self.cen_ad, 368 mac_address, 369 False, 370 transport=gatt_transport['auto'], 371 opportunistic=True) 372 self.bluetooth_gatt_list.append(bluetooth_gatt_2) 373 except GattTestUtilsError as err: 374 self.log.error(err) 375 return False 376 # Disconnect GATT connection 1 377 try: 378 disconnect_gatt_connection(self.cen_ad, bluetooth_gatt_1, 379 gatt_callback_1) 380 close_gatt_client(self.cen_ad, bluetooth_gatt_1) 381 if bluetooth_gatt_1 in self.bluetooth_gatt_list: 382 self.bluetooth_gatt_list.remove(bluetooth_gatt_1) 383 except GattTestUtilsError as err: 384 self.log.error(err) 385 return False 386 # Confirm that GATT connection 2 also disconnects 387 wait_for_gatt_disconnect_event(self.cen_ad, gatt_callback_2) 388 close_gatt_client(self.cen_ad, bluetooth_gatt_2) 389 if bluetooth_gatt_2 in self.bluetooth_gatt_list: 390 self.bluetooth_gatt_list.remove(bluetooth_gatt_2) 391 return True 392 393 @BluetoothBaseTest.bt_test_wrap 394 @test_tracker_info(uuid='1e01838e-c4de-4720-9adf-9e0419378226') 395 def test_gatt_request_min_mtu(self): 396 """Test GATT connection over LE and exercise MTU sizes. 397 398 Test establishing a gatt connection between a GATT server and GATT 399 client. Request an MTU size that matches the correct minimum size. 400 401 Steps: 402 1. Start a generic advertisement. 403 2. Start a generic scanner. 404 3. Find the advertisement and extract the mac address. 405 4. Stop the first scanner. 406 5. Create a GATT connection between the scanner and advertiser. 407 6. From the scanner (client) request MTU size change to the 408 minimum value. 409 7. Find the MTU changed event on the client. 410 8. Disconnect the GATT connection. 411 412 Expected Result: 413 Verify that a connection was established and the MTU value found 414 matches the expected MTU value. 415 416 Returns: 417 Pass if True 418 Fail if False 419 420 TAGS: LE, Advertising, Filtering, Scanning, GATT, MTU 421 Priority: 0 422 """ 423 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 424 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 425 gatt_server_cb) 426 self.gatt_server_list.append(gatt_server) 427 try: 428 bluetooth_gatt, gatt_callback, adv_callback = ( 429 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 430 self.bluetooth_gatt_list.append(bluetooth_gatt) 431 except GattTestUtilsError as err: 432 self.log.error(err) 433 return False 434 self.adv_instances.append(adv_callback) 435 expected_mtu = gatt_mtu_size['min'] 436 self.cen_ad.droid.gattClientRequestMtu(bluetooth_gatt, expected_mtu) 437 if not self._verify_mtu_changed_on_client_and_server( 438 expected_mtu, gatt_callback, gatt_server_cb): 439 return False 440 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 441 gatt_callback) 442 443 @BluetoothBaseTest.bt_test_wrap 444 @test_tracker_info(uuid='c1fa3a2d-fb47-47db-bdd1-458928cd6a5f') 445 def test_gatt_request_max_mtu(self): 446 """Test GATT connection over LE and exercise MTU sizes. 447 448 Test establishing a gatt connection between a GATT server and GATT 449 client. Request an MTU size that matches the correct maximum size. 450 451 Steps: 452 1. Start a generic advertisement. 453 2. Start a generic scanner. 454 3. Find the advertisement and extract the mac address. 455 4. Stop the first scanner. 456 5. Create a GATT connection between the scanner and advertiser. 457 6. From the scanner (client) request MTU size change to the 458 maximum value. 459 7. Find the MTU changed event on the client. 460 8. Disconnect the GATT connection. 461 462 Expected Result: 463 Verify that a connection was established and the MTU value found 464 matches the expected MTU value. 465 466 Returns: 467 Pass if True 468 Fail if False 469 470 TAGS: LE, Advertising, Filtering, Scanning, GATT, MTU 471 Priority: 0 472 """ 473 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 474 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 475 gatt_server_cb) 476 self.gatt_server_list.append(gatt_server) 477 try: 478 bluetooth_gatt, gatt_callback, adv_callback = ( 479 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 480 self.bluetooth_gatt_list.append(bluetooth_gatt) 481 except GattTestUtilsError as err: 482 self.log.error(err) 483 return False 484 self.adv_instances.append(adv_callback) 485 expected_mtu = gatt_mtu_size['max'] 486 self.cen_ad.droid.gattClientRequestMtu(bluetooth_gatt, expected_mtu) 487 if not self._verify_mtu_changed_on_client_and_server( 488 expected_mtu, gatt_callback, gatt_server_cb): 489 return False 490 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 491 gatt_callback) 492 493 @BluetoothBaseTest.bt_test_wrap 494 @test_tracker_info(uuid='4416d483-dec3-46cb-8038-4d82620f873a') 495 def test_gatt_request_out_of_bounds_mtu(self): 496 """Test GATT connection over LE and exercise an out of bound MTU size. 497 498 Test establishing a gatt connection between a GATT server and GATT 499 client. Request an MTU size that is the MIN value minus 1. 500 501 Steps: 502 1. Start a generic advertisement. 503 2. Start a generic scanner. 504 3. Find the advertisement and extract the mac address. 505 4. Stop the first scanner. 506 5. Create a GATT connection between the scanner and advertiser. 507 6. From the scanner (client) request MTU size change to the 508 minimum value minus one. 509 7. Find the MTU changed event on the client. 510 8. Disconnect the GATT connection. 511 512 Expected Result: 513 Verify that an MTU changed event was not discovered and that 514 it didn't cause an exception when requesting an out of bounds 515 MTU. 516 517 Returns: 518 Pass if True 519 Fail if False 520 521 TAGS: LE, Advertising, Filtering, Scanning, GATT, MTU 522 Priority: 0 523 """ 524 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 525 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 526 gatt_server_cb) 527 self.gatt_server_list.append(gatt_server) 528 try: 529 bluetooth_gatt, gatt_callback, adv_callback = ( 530 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 531 self.bluetooth_gatt_list.append(bluetooth_gatt) 532 except GattTestUtilsError as err: 533 self.log.error(err) 534 return False 535 self.adv_instances.append(adv_callback) 536 unexpected_mtu = gatt_mtu_size['min'] - 1 537 self.cen_ad.droid.gattClientRequestMtu(bluetooth_gatt, unexpected_mtu) 538 if self._verify_mtu_changed_on_client_and_server( 539 unexpected_mtu, gatt_callback, gatt_server_cb): 540 return False 541 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 542 gatt_callback) 543 544 @BluetoothBaseTest.bt_test_wrap 545 @test_tracker_info(uuid='31ffb9ca-cc75-43fb-9802-c19f1c5856b6') 546 def test_gatt_connect_trigger_on_read_rssi(self): 547 """Test GATT connection over LE read RSSI. 548 549 Test establishing a gatt connection between a GATT server and GATT 550 client then read the RSSI. 551 552 Steps: 553 1. Start a generic advertisement. 554 2. Start a generic scanner. 555 3. Find the advertisement and extract the mac address. 556 4. Stop the first scanner. 557 5. Create a GATT connection between the scanner and advertiser. 558 6. From the scanner, request to read the RSSI of the advertiser. 559 7. Disconnect the GATT connection. 560 561 Expected Result: 562 Verify that a connection was established and then disconnected 563 successfully. Verify that the RSSI was ready correctly. 564 565 Returns: 566 Pass if True 567 Fail if False 568 569 TAGS: LE, Advertising, Filtering, Scanning, GATT, RSSI 570 Priority: 1 571 """ 572 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 573 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 574 gatt_server_cb) 575 self.gatt_server_list.append(gatt_server) 576 try: 577 bluetooth_gatt, gatt_callback, adv_callback = ( 578 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 579 self.bluetooth_gatt_list.append(bluetooth_gatt) 580 except GattTestUtilsError as err: 581 self.log.error(err) 582 return False 583 self.adv_instances.append(adv_callback) 584 expected_event = gatt_cb_strings['rd_remote_rssi'].format( 585 gatt_callback) 586 if self.cen_ad.droid.gattClientReadRSSI(bluetooth_gatt): 587 try: 588 self.cen_ad.ed.pop_event(expected_event, self.default_timeout) 589 except Empty: 590 self.log.error( 591 gatt_cb_err['rd_remote_rssi_err'].format(expected_event)) 592 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 593 gatt_callback) 594 595 @BluetoothBaseTest.bt_test_wrap 596 @test_tracker_info(uuid='dee9ef28-b872-428a-821b-cc62f27ba936') 597 def test_gatt_connect_trigger_on_services_discovered(self): 598 """Test GATT connection and discover services of peripheral. 599 600 Test establishing a gatt connection between a GATT server and GATT 601 client the discover all services from the connected device. 602 603 Steps: 604 1. Start a generic advertisement. 605 2. Start a generic scanner. 606 3. Find the advertisement and extract the mac address. 607 4. Stop the first scanner. 608 5. Create a GATT connection between the scanner and advertiser. 609 6. From the scanner (central device), discover services. 610 7. Disconnect the GATT connection. 611 612 Expected Result: 613 Verify that a connection was established and then disconnected 614 successfully. Verify that the service were discovered. 615 616 Returns: 617 Pass if True 618 Fail if False 619 620 TAGS: LE, Advertising, Filtering, Scanning, GATT, Services 621 Priority: 1 622 """ 623 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 624 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 625 gatt_server_cb) 626 self.gatt_server_list.append(gatt_server) 627 try: 628 bluetooth_gatt, gatt_callback, adv_callback = ( 629 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 630 self.bluetooth_gatt_list.append(bluetooth_gatt) 631 except GattTestUtilsError as err: 632 self.log.error(err) 633 return False 634 self.adv_instances.append(adv_callback) 635 if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt): 636 expected_event = gatt_cb_strings['gatt_serv_disc'].format( 637 gatt_callback) 638 try: 639 event = self.cen_ad.ed.pop_event(expected_event, 640 self.default_timeout) 641 except Empty: 642 self.log.error( 643 gatt_cb_err['gatt_serv_disc'].format(expected_event)) 644 return False 645 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 646 gatt_callback) 647 648 @BluetoothBaseTest.bt_test_wrap 649 @test_tracker_info(uuid='01883bdd-0cf8-48fb-bf15-467bbd4f065b') 650 def test_gatt_connect_trigger_on_services_discovered_iterate_attributes( 651 self): 652 """Test GATT connection and iterate peripherals attributes. 653 654 Test establishing a gatt connection between a GATT server and GATT 655 client and iterate over all the characteristics and descriptors of the 656 discovered services. 657 658 Steps: 659 1. Start a generic advertisement. 660 2. Start a generic scanner. 661 3. Find the advertisement and extract the mac address. 662 4. Stop the first scanner. 663 5. Create a GATT connection between the scanner and advertiser. 664 6. From the scanner (central device), discover services. 665 7. Iterate over all the characteristics and descriptors of the 666 discovered features. 667 8. Disconnect the GATT connection. 668 669 Expected Result: 670 Verify that a connection was established and then disconnected 671 successfully. Verify that the services, characteristics, and descriptors 672 were discovered. 673 674 Returns: 675 Pass if True 676 Fail if False 677 678 TAGS: LE, Advertising, Filtering, Scanning, GATT, Services 679 Characteristics, Descriptors 680 Priority: 1 681 """ 682 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 683 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 684 gatt_server_cb) 685 self.gatt_server_list.append(gatt_server) 686 try: 687 bluetooth_gatt, gatt_callback, adv_callback = ( 688 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 689 self.bluetooth_gatt_list.append(bluetooth_gatt) 690 except GattTestUtilsError as err: 691 self.log.error(err) 692 return False 693 self.adv_instances.append(adv_callback) 694 if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt): 695 expected_event = gatt_cb_strings['gatt_serv_disc'].format( 696 gatt_callback) 697 try: 698 event = self.cen_ad.ed.pop_event(expected_event, 699 self.default_timeout) 700 discovered_services_index = event['data']['ServicesIndex'] 701 except Empty: 702 self.log.error( 703 gatt_cb_err['gatt_serv_disc'].format(expected_event)) 704 return False 705 log_gatt_server_uuids(self.cen_ad, discovered_services_index) 706 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 707 gatt_callback) 708 709 @BluetoothBaseTest.bt_test_wrap 710 @test_tracker_info(uuid='d4277bee-da99-4f48-8a4d-f81b5389da18') 711 def test_gatt_connect_with_service_uuid_variations(self): 712 """Test GATT connection with multiple service uuids. 713 714 Test establishing a gatt connection between a GATT server and GATT 715 client with multiple service uuid variations. 716 717 Steps: 718 1. Start a generic advertisement. 719 2. Start a generic scanner. 720 3. Find the advertisement and extract the mac address. 721 4. Stop the first scanner. 722 5. Create a GATT connection between the scanner and advertiser. 723 6. From the scanner (central device), discover services. 724 7. Verify that all the service uuid variations are found. 725 8. Disconnect the GATT connection. 726 727 Expected Result: 728 Verify that a connection was established and then disconnected 729 successfully. Verify that the service uuid variations are found. 730 731 Returns: 732 Pass if True 733 Fail if False 734 735 TAGS: LE, Advertising, Filtering, Scanning, GATT, Services 736 Priority: 2 737 """ 738 try: 739 gatt_server_cb, gatt_server = setup_multiple_services(self.per_ad) 740 self.gatt_server_list.append(gatt_server) 741 except GattTestUtilsError as err: 742 self.log.error(err) 743 return False 744 try: 745 bluetooth_gatt, gatt_callback, adv_callback = ( 746 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 747 self.bluetooth_gatt_list.append(bluetooth_gatt) 748 except GattTestUtilsError as err: 749 self.log.error(err) 750 return False 751 self.adv_instances.append(adv_callback) 752 if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt): 753 expected_event = gatt_cb_strings['gatt_serv_disc'].format( 754 gatt_callback) 755 try: 756 event = self.cen_ad.ed.pop_event(expected_event, 757 self.default_timeout) 758 except Empty: 759 self.log.error( 760 gatt_cb_err['gatt_serv_disc'].format(expected_event)) 761 return False 762 discovered_services_index = event['data']['ServicesIndex'] 763 log_gatt_server_uuids(self.cen_ad, discovered_services_index) 764 765 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 766 gatt_callback) 767 768 @BluetoothBaseTest.bt_test_wrap 769 @test_tracker_info(uuid='7d3442c5-f71f-44ae-bd35-f2569f01b3b8') 770 def test_gatt_connect_in_quick_succession(self): 771 """Test GATT connections multiple times. 772 773 Test establishing a gatt connection between a GATT server and GATT 774 client with multiple iterations. 775 776 Steps: 777 1. Start a generic advertisement. 778 2. Start a generic scanner. 779 3. Find the advertisement and extract the mac address. 780 4. Stop the first scanner. 781 5. Create a GATT connection between the scanner and advertiser. 782 6. Disconnect the GATT connection. 783 7. Repeat steps 5 and 6 twenty times. 784 785 Expected Result: 786 Verify that a connection was established and then disconnected 787 successfully twenty times. 788 789 Returns: 790 Pass if True 791 Fail if False 792 793 TAGS: LE, Advertising, Filtering, Scanning, GATT, Stress 794 Priority: 1 795 """ 796 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 797 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 798 gatt_server_cb) 799 self.gatt_server_list.append(gatt_server) 800 mac_address, adv_callback, scan_callback = get_mac_address_of_generic_advertisement( 801 self.cen_ad, self.per_ad) 802 autoconnect = False 803 for i in range(1000): 804 self.log.info("Starting connection iteration {}".format(i + 1)) 805 try: 806 bluetooth_gatt, gatt_callback = setup_gatt_connection( 807 self.cen_ad, mac_address, autoconnect) 808 self.cen_ad.droid.bleStopBleScan(scan_callback) 809 except GattTestUtilsError as err: 810 self.log.error(err) 811 return False 812 test_result = self._orchestrate_gatt_disconnection( 813 bluetooth_gatt, gatt_callback) 814 if not test_result: 815 self.log.info("Failed to disconnect from peripheral device.") 816 return False 817 self.adv_instances.append(adv_callback) 818 return True 819 820 @BluetoothBaseTest.bt_test_wrap 821 @test_tracker_info(uuid='148469d9-7ab0-4c08-b2e9-7e49e88da1fc') 822 def test_gatt_connect_mitm_attack(self): 823 """Test GATT connection with permission write encrypted mitm. 824 825 Test establishing a gatt connection between a GATT server and GATT 826 client while the GATT server's characteristic includes the property 827 write value and the permission write encrypted mitm value. This will 828 prompt LE pairing and then the devices will create a bond. 829 830 Steps: 831 1. Create a GATT server and server callback on the peripheral device. 832 2. Create a unique service and characteristic uuid on the peripheral. 833 3. Create a characteristic on the peripheral with these properties: 834 gatt_characteristic['property_write'], 835 gatt_characteristic['permission_write_encrypted_mitm'] 836 4. Create a GATT service on the peripheral. 837 5. Add the characteristic to the GATT service. 838 6. Create a GATT connection between your central and peripheral device. 839 7. From the central device, discover the peripheral's services. 840 8. Iterate the services found until you find the unique characteristic 841 created in step 3. 842 9. Once found, write a random but valid value to the characteristic. 843 10. Start pairing helpers on both devices immediately after attempting 844 to write to the characteristic. 845 11. Within 10 seconds of writing the characteristic, there should be 846 a prompt to bond the device from the peripheral. The helpers will 847 handle the UI interaction automatically. (see 848 BluetoothConnectionFacade.java bluetoothStartPairingHelper). 849 12. Verify that the two devices are bonded. 850 851 Expected Result: 852 Verify that a connection was established and the devices are bonded. 853 854 Returns: 855 Pass if True 856 Fail if False 857 858 TAGS: LE, Advertising, Filtering, Scanning, GATT, Characteristic, MITM 859 Priority: 1 860 """ 861 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 862 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 863 gatt_server_cb) 864 self.gatt_server_list.append(gatt_server) 865 service_uuid = "3846D7A0-69C8-11E4-BA00-0002A5D5C51B" 866 test_uuid = "aa7edd5a-4d1d-4f0e-883a-d145616a1630" 867 bonded = False 868 characteristic = self.per_ad.droid.gattServerCreateBluetoothGattCharacteristic( 869 test_uuid, gatt_characteristic['property_write'], 870 gatt_characteristic['permission_write_encrypted_mitm']) 871 gatt_service = self.per_ad.droid.gattServerCreateService( 872 service_uuid, gatt_service_types['primary']) 873 self.per_ad.droid.gattServerAddCharacteristicToService( 874 gatt_service, characteristic) 875 self.per_ad.droid.gattServerAddService(gatt_server, gatt_service) 876 result = self._find_service_added_event(gatt_server_cb, service_uuid) 877 if not result: 878 return False 879 bluetooth_gatt, gatt_callback, adv_callback = ( 880 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 881 self.bluetooth_gatt_list.append(bluetooth_gatt) 882 self.adv_instances.append(adv_callback) 883 if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt): 884 expected_event = gatt_cb_strings['gatt_serv_disc'].format( 885 gatt_callback) 886 try: 887 event = self.cen_ad.ed.pop_event(expected_event, 888 self.default_timeout) 889 except Empty: 890 self.log.error( 891 gatt_cb_err['gatt_serv_disc'].format(expected_event)) 892 return False 893 discovered_services_index = event['data']['ServicesIndex'] 894 else: 895 self.log.info("Failed to discover services.") 896 return False 897 test_value = [1, 2, 3, 4, 5, 6, 7] 898 services_count = self.cen_ad.droid.gattClientGetDiscoveredServicesCount( 899 discovered_services_index) 900 for i in range(services_count): 901 characteristic_uuids = ( 902 self.cen_ad.droid.gattClientGetDiscoveredCharacteristicUuids( 903 discovered_services_index, i)) 904 for characteristic_uuid in characteristic_uuids: 905 if characteristic_uuid == test_uuid: 906 self.cen_ad.droid.bluetoothStartPairingHelper() 907 self.per_ad.droid.bluetoothStartPairingHelper() 908 self.cen_ad.droid.gattClientCharacteristicSetValue( 909 bluetooth_gatt, discovered_services_index, i, 910 characteristic_uuid, test_value) 911 self.cen_ad.droid.gattClientWriteCharacteristic( 912 bluetooth_gatt, discovered_services_index, i, 913 characteristic_uuid) 914 start_time = time.time() + self.default_timeout 915 target_name = self.per_ad.droid.bluetoothGetLocalName() 916 while time.time() < start_time and bonded == False: 917 bonded_devices = \ 918 self.cen_ad.droid.bluetoothGetBondedDevices() 919 for device in bonded_devices: 920 if ('name' in device.keys() 921 and device['name'] == target_name): 922 bonded = True 923 break 924 bonded = False 925 target_name = self.cen_ad.droid.bluetoothGetLocalName() 926 while time.time() < start_time and bonded == False: 927 bonded_devices = \ 928 self.per_ad.droid.bluetoothGetBondedDevices() 929 for device in bonded_devices: 930 if ('name' in device.keys() 931 and device['name'] == target_name): 932 bonded = True 933 break 934 for ad in [self.cen_ad, self.per_ad]: 935 if not clear_bonded_devices(ad): 936 return False 937 # Necessary sleep time for entries to update unbonded state 938 time.sleep(2) 939 bonded_devices = ad.droid.bluetoothGetBondedDevices() 940 if len(bonded_devices) > 0: 941 self.log.error( 942 "Failed to unbond devices: {}".format(bonded_devices)) 943 return False 944 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 945 gatt_callback) 946 947 @BluetoothBaseTest.bt_test_wrap 948 @test_tracker_info(uuid='cc3fc361-7bf1-4ee2-9e46-4a27c88ce6a8') 949 def test_gatt_connect_get_connected_devices(self): 950 """Test GATT connections show up in getConnectedDevices 951 952 Test establishing a gatt connection between a GATT server and GATT 953 client. Verify that active connections show up using 954 BluetoothManager.getConnectedDevices API. 955 956 Steps: 957 1. Start a generic advertisement. 958 2. Start a generic scanner. 959 3. Find the advertisement and extract the mac address. 960 4. Stop the first scanner. 961 5. Create a GATT connection between the scanner and advertiser. 962 7. Verify the GATT Client has an open connection to the GATT Server. 963 8. Verify the GATT Server has an open connection to the GATT Client. 964 9. Disconnect the GATT connection. 965 966 Expected Result: 967 Verify that a connection was established, connected devices are found 968 on both the central and peripheral devices, and then disconnected 969 successfully. 970 971 Returns: 972 Pass if True 973 Fail if False 974 975 TAGS: LE, Advertising, Scanning, GATT 976 Priority: 2 977 """ 978 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 979 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 980 gatt_server_cb) 981 self.gatt_server_list.append(gatt_server) 982 try: 983 bluetooth_gatt, gatt_callback, adv_callback = ( 984 orchestrate_gatt_connection(self.cen_ad, self.per_ad)) 985 self.bluetooth_gatt_list.append(bluetooth_gatt) 986 except GattTestUtilsError as err: 987 self.log.error(err) 988 return False 989 conn_cen_devices = self.cen_ad.droid.bluetoothGetConnectedLeDevices( 990 bt_profile_constants['gatt']) 991 conn_per_devices = self.per_ad.droid.bluetoothGetConnectedLeDevices( 992 bt_profile_constants['gatt_server']) 993 target_name = self.per_ad.droid.bluetoothGetLocalName() 994 error_message = ("Connected device {} not found in list of connected " 995 "devices {}") 996 if not any(d['name'] == target_name for d in conn_cen_devices): 997 self.log.error(error_message.format(target_name, conn_cen_devices)) 998 return False 999 # For the GATT server only check the size of the list since 1000 # it may or may not include the device name. 1001 target_name = self.cen_ad.droid.bluetoothGetLocalName() 1002 if not conn_per_devices: 1003 self.log.error(error_message.format(target_name, conn_per_devices)) 1004 return False 1005 self.adv_instances.append(adv_callback) 1006 return self._orchestrate_gatt_disconnection(bluetooth_gatt, 1007 gatt_callback) 1008 1009 @BluetoothBaseTest.bt_test_wrap 1010 @test_tracker_info(uuid='a0a37ca6-9fa8-4d35-9fdb-0e25b4b8a363') 1011 def test_gatt_connect_second_adv_after_canceling_first_adv(self): 1012 """Test GATT connection to peripherals second advertising address. 1013 1014 The the ability of cancelling GATT connections and trying to reconnect 1015 to the same device via a different address. 1016 1017 Steps: 1018 1. A starts advertising 1019 2. B starts scanning and finds A's mac address 1020 3. Stop advertisement from step 1. Start a new advertisement on A and 1021 find the new new mac address, B knows of both old and new address. 1022 4. B1 sends connect request to old address of A 1023 5. B1 cancel connect attempt after 10 seconds 1024 6. B1 sends connect request to new address of A 1025 7. Verify B1 establish connection to A in less than 10 seconds 1026 1027 Expected Result: 1028 Verify that a connection was established only on the second 1029 advertisement's mac address. 1030 1031 Returns: 1032 Pass if True 1033 Fail if False 1034 1035 TAGS: LE, Advertising, Scanning, GATT 1036 Priority: 3 1037 """ 1038 autoconnect = False 1039 transport = gatt_transport['auto'] 1040 opportunistic = False 1041 # Setup a basic Gatt server on the peripheral 1042 gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback() 1043 gatt_server = self.per_ad.droid.gattServerOpenGattServer( 1044 gatt_server_cb) 1045 1046 # Set advertisement settings to include local name in advertisement 1047 # and set the advertising mode to low_latency. 1048 self.per_ad.droid.bleSetAdvertiseSettingsIsConnectable(True) 1049 self.per_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True) 1050 self.per_ad.droid.bleSetAdvertiseSettingsAdvertiseMode( 1051 ble_advertise_settings_modes['low_latency']) 1052 1053 # Setup necessary advertisement objects. 1054 advertise_data = self.per_ad.droid.bleBuildAdvertiseData() 1055 advertise_settings = self.per_ad.droid.bleBuildAdvertiseSettings() 1056 advertise_callback = self.per_ad.droid.bleGenBleAdvertiseCallback() 1057 1058 # Step 1: Start advertisement 1059 self.per_ad.droid.bleStartBleAdvertising( 1060 advertise_callback, advertise_data, advertise_settings) 1061 1062 # Setup scan settings for low_latency scanning and to include the local name 1063 # of the advertisement started in step 1. 1064 filter_list = self.cen_ad.droid.bleGenFilterList() 1065 self.cen_ad.droid.bleSetScanSettingsNumOfMatches( 1066 ble_scan_settings_match_nums['one']) 1067 self.cen_ad.droid.bleSetScanFilterDeviceName( 1068 self.per_ad.droid.bluetoothGetLocalName()) 1069 self.cen_ad.droid.bleBuildScanFilter(filter_list) 1070 self.cen_ad.droid.bleSetScanSettingsScanMode( 1071 ble_scan_settings_modes['low_latency']) 1072 1073 # Setup necessary scan objects. 1074 scan_settings = self.cen_ad.droid.bleBuildScanSetting() 1075 scan_callback = self.cen_ad.droid.bleGenScanCallback() 1076 1077 # Step 2: Start scanning on central Android device and find peripheral 1078 # address. 1079 self.cen_ad.droid.bleStartBleScan(filter_list, scan_settings, 1080 scan_callback) 1081 expected_event_name = scan_result.format(scan_callback) 1082 try: 1083 mac_address_pre_restart = self.cen_ad.ed.pop_event( 1084 expected_event_name, self.default_timeout)['data']['Result'][ 1085 'deviceInfo']['address'] 1086 self.log.info( 1087 "Peripheral advertisement found with mac address: {}".format( 1088 mac_address_pre_restart)) 1089 except Empty: 1090 self.log.info("Peripheral advertisement not found") 1091 test_result = False 1092 1093 # Step 3: Restart peripheral advertising such that a new mac address is 1094 # created. 1095 self.per_ad.droid.bleStopBleAdvertising(advertise_callback) 1096 self.per_ad.droid.bleStartBleAdvertising( 1097 advertise_callback, advertise_data, advertise_settings) 1098 1099 try: 1100 mac_address_post_restart = self.cen_ad.ed.pop_event( 1101 expected_event_name, self.default_timeout)['data']['Result'][ 1102 'deviceInfo']['address'] 1103 self.log.info( 1104 "Peripheral advertisement found with mac address: {}".format( 1105 mac_address_post_restart)) 1106 except Empty: 1107 self.log.info("Peripheral advertisement not found") 1108 test_result = False 1109 1110 # Steps 4: Try to connect to the first mac address 1111 gatt_callback = self.cen_ad.droid.gattCreateGattCallback() 1112 self.log.info( 1113 "Gatt Connect to mac address {}.".format(mac_address_pre_restart)) 1114 bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt( 1115 gatt_callback, mac_address_pre_restart, autoconnect, transport, 1116 opportunistic, gatt_phy_mask['1m_mask']) 1117 self.bluetooth_gatt_list.append(bluetooth_gatt) 1118 expected_event = gatt_cb_strings['gatt_conn_change'].format( 1119 gatt_callback) 1120 try: 1121 event = self.cen_ad.ed.pop_event(expected_event, 1122 self.default_timeout) 1123 self.log.error( 1124 "Connection callback updated unexpectedly: {}".format(event)) 1125 return False 1126 except Empty: 1127 self.log.info("No connection update as expected.") 1128 1129 # Step 5: Cancel connection request. 1130 self.cen_ad.droid.gattClientDisconnect(bluetooth_gatt) 1131 1132 # Step 6: Connect to second mac address. 1133 self.log.info( 1134 "Gatt Connect to mac address {}.".format(mac_address_post_restart)) 1135 bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt( 1136 gatt_callback, mac_address_post_restart, autoconnect, transport, 1137 opportunistic, gatt_phy_mask['1m_mask']) 1138 self.bluetooth_gatt_list.append(bluetooth_gatt) 1139 expected_event = gatt_cb_strings['gatt_conn_change'].format( 1140 gatt_callback) 1141 1142 # Step 7: Verify connection was setup successfully. 1143 try: 1144 event = self.cen_ad.ed.pop_event(expected_event, 1145 self.default_timeout) 1146 self.log.info( 1147 "Connection callback updated successfully: {}".format(event)) 1148 if event['data']['State'] != gatt_connection_state['connected']: 1149 self.log.error( 1150 "Could not establish a connection to the peripheral.") 1151 return False 1152 except Empty: 1153 self.log.error("No connection update was found.") 1154 return False 1155 return self.cen_ad.droid.gattClientDisconnect(bluetooth_gatt) 1156