1#!/usr/bin/env python3 2# 3# Copyright 2019 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of 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, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import inspect 18import logging 19 20from queue import Empty 21 22from acts.controllers.android_device import AndroidDevice 23from acts.controllers.fuchsia_device import FuchsiaDevice 24from acts.test_utils.bt.bt_constants import ble_scan_settings_modes 25from acts.test_utils.bt.bt_constants import gatt_cb_strings 26from acts.test_utils.bt.bt_constants import gatt_event 27from acts.test_utils.bt.bt_constants import scan_result 28from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError 29from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection 30from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection 31from acts.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name 32 33import acts.test_utils.bt.bt_test_utils as bt_test_utils 34 35 36def create_bluetooth_device(hardware_device): 37 """Creates a generic Bluetooth device based on type of device that is sent 38 to the functions. 39 40 Args: 41 hardware_device: A Bluetooth hardware device that is supported by ACTS. 42 """ 43 if isinstance(hardware_device, FuchsiaDevice): 44 return FuchsiaBluetoothDevice(hardware_device) 45 elif isinstance(hardware_device, AndroidDevice): 46 return AndroidBluetoothDevice(hardware_device) 47 else: 48 raise ValueError('Unable to create BluetoothDevice for type %s' % 49 type(hardware_device)) 50 51 52class BluetoothDevice(object): 53 """Class representing a generic Bluetooth device. 54 55 Each object of this class represents a generic Bluetooth device. 56 Android device and Fuchsia devices are the currently supported devices. 57 58 Attributes: 59 device: A generic Bluetooth device. 60 """ 61 def __init__(self, device): 62 self.device = device 63 self.log = logging 64 65 def a2dp_initiate_open_stream(self): 66 """Base generic Bluetooth interface. Only called if not overridden by 67 another supported device. 68 """ 69 raise NotImplementedError("{} must be defined.".format( 70 inspect.currentframe().f_code.co_name)) 71 72 def start_profile_a2dp_sink(self): 73 """Base generic Bluetooth interface. Only called if not overridden by 74 another supported device. 75 """ 76 raise NotImplementedError("{} must be defined.".format( 77 inspect.currentframe().f_code.co_name)) 78 79 def stop_profile_a2dp_sink(self): 80 """Base generic Bluetooth interface. Only called if not overridden by 81 another supported device. 82 """ 83 raise NotImplementedError("{} must be defined.".format( 84 inspect.currentframe().f_code.co_name)) 85 86 def start_pairing_helper(self): 87 """Base generic Bluetooth interface. Only called if not overridden by 88 another supported device. 89 """ 90 raise NotImplementedError("{} must be defined.".format( 91 inspect.currentframe().f_code.co_name)) 92 93 def set_discoverable(self, is_discoverable): 94 """Base generic Bluetooth interface. Only called if not overridden by 95 another supported device. 96 """ 97 raise NotImplementedError("{} must be defined.".format( 98 inspect.currentframe().f_code.co_name)) 99 100 def bluetooth_toggle_state(self, state): 101 """Base generic Bluetooth interface. Only called if not overridden by 102 another supported device. 103 """ 104 raise NotImplementedError("{} must be defined.".format( 105 inspect.currentframe().f_code.co_name)) 106 107 def gatt_client_discover_characteristic_by_uuid(self, peer_identifier, 108 uuid): 109 """Base generic Bluetooth interface. Only called if not overridden by 110 another supported device. 111 """ 112 raise NotImplementedError("{} must be defined.".format( 113 inspect.currentframe().f_code.co_name)) 114 115 def initialize_bluetooth_controller(self): 116 """Base generic Bluetooth interface. Only called if not overridden by 117 another supported device. 118 """ 119 raise NotImplementedError("{} must be defined.".format( 120 inspect.currentframe().f_code.co_name)) 121 122 def get_pairing_pin(self): 123 """Base generic Bluetooth interface. Only called if not overridden by 124 another supported device. 125 """ 126 raise NotImplementedError("{} must be defined.".format( 127 inspect.currentframe().f_code.co_name)) 128 129 def input_pairing_pin(self, pin): 130 """Base generic Bluetooth interface. Only called if not overridden by 131 another supported device. 132 """ 133 raise NotImplementedError("{} must be defined.".format( 134 inspect.currentframe().f_code.co_name)) 135 136 def get_bluetooth_local_address(self): 137 """Base generic Bluetooth interface. Only called if not overridden by 138 another supported device. 139 """ 140 raise NotImplementedError("{} must be defined.".format( 141 inspect.currentframe().f_code.co_name)) 142 143 def gatt_connect(self, peer_identifier, transport, autoconnect): 144 """Base generic Bluetooth interface. Only called if not overridden by 145 another supported device. 146 """ 147 raise NotImplementedError("{} must be defined.".format( 148 inspect.currentframe().f_code.co_name)) 149 150 def gatt_client_write_characteristic_without_response_by_handle( 151 self, peer_identifier, handle, value): 152 """Base generic Bluetooth interface. Only called if not overridden by 153 another supported device. 154 """ 155 raise NotImplementedError("{} must be defined.".format( 156 inspect.currentframe().f_code.co_name)) 157 158 def gatt_client_write_characteristic_by_handle(self, peer_identifier, 159 handle, offset, value): 160 """Base generic Bluetooth interface. Only called if not overridden by 161 another supported device. 162 """ 163 raise NotImplementedError("{} must be defined.".format( 164 inspect.currentframe().f_code.co_name)) 165 166 def gatt_client_read_characteristic_by_handle(self, peer_identifier, 167 handle): 168 """Base generic Bluetooth interface. Only called if not overridden by 169 another supported device. 170 """ 171 raise NotImplementedError("{} must be defined.".format( 172 inspect.currentframe().f_code.co_name)) 173 174 def gatt_client_read_long_characteristic_by_handle(self, peer_identifier, 175 handle, offset, 176 max_bytes): 177 """Base generic Bluetooth interface. Only called if not overridden by 178 another supported device. 179 """ 180 raise NotImplementedError("{} must be defined.".format( 181 inspect.currentframe().f_code.co_name)) 182 183 def gatt_client_enable_notifiy_characteristic_by_handle( 184 self, peer_identifier, handle): 185 """Base generic Bluetooth interface. Only called if not overridden by 186 another supported device. 187 """ 188 raise NotImplementedError("{} must be defined.".format( 189 inspect.currentframe().f_code.co_name)) 190 191 def gatt_client_disable_notifiy_characteristic_by_handle( 192 self, peer_identifier, handle): 193 """Base generic Bluetooth interface. Only called if not overridden by 194 another supported device. 195 """ 196 raise NotImplementedError("{} must be defined.".format( 197 inspect.currentframe().f_code.co_name)) 198 199 def gatt_client_read_descriptor_by_handle(self, peer_identifier, handle): 200 """Base generic Bluetooth interface. Only called if not overridden by 201 another supported device. 202 """ 203 raise NotImplementedError("{} must be defined.".format( 204 inspect.currentframe().f_code.co_name)) 205 206 def gatt_client_write_descriptor_by_handle(self, peer_identifier, handle, 207 offset, value): 208 """Base generic Bluetooth interface. Only called if not overridden by 209 another supported device. 210 """ 211 raise NotImplementedError("{} must be defined.".format( 212 inspect.currentframe().f_code.co_name)) 213 214 def gatt_client_long_read_descriptor_by_handle(self, peer_identifier, 215 handle, offset, max_bytes): 216 """Base generic Bluetooth interface. Only called if not overridden by 217 another supported device. 218 """ 219 raise NotImplementedError("{} must be defined.".format( 220 inspect.currentframe().f_code.co_name)) 221 222 def gatt_disconnect(self, peer_identifier): 223 """Base generic Bluetooth interface. Only called if not overridden by 224 another supported device. 225 """ 226 raise NotImplementedError("{} must be defined.".format( 227 inspect.currentframe().f_code.co_name)) 228 229 def gatt_client_refresh(self, peer_identifier): 230 """Base generic Bluetooth interface. Only called if not overridden by 231 another supported device. 232 """ 233 raise NotImplementedError("{} must be defined.".format( 234 inspect.currentframe().f_code.co_name)) 235 236 def le_scan_with_name_filter(self, name, timeout): 237 """Base generic Bluetooth interface. Only called if not overridden by 238 another supported device. 239 """ 240 raise NotImplementedError("{} must be defined.".format( 241 inspect.currentframe().f_code.co_name)) 242 243 def log_info(self, log): 244 """Base generic Bluetooth interface. Only called if not overridden by 245 another supported device. 246 """ 247 raise NotImplementedError("{} must be defined.".format( 248 inspect.currentframe().f_code.co_name)) 249 250 def reset_bluetooth(self): 251 """Base generic Bluetooth interface. Only called if not overridden by 252 another supported device. 253 """ 254 raise NotImplementedError("{} must be defined.".format( 255 inspect.currentframe().f_code.co_name)) 256 257 def sdp_add_search(self, attribute_list, profile_id): 258 """Base generic Bluetooth interface. Only called if not overridden by 259 another supported device. 260 """ 261 raise NotImplementedError("{} must be defined.".format( 262 inspect.currentframe().f_code.co_name)) 263 264 def sdp_add_service(self, sdp_record): 265 """Base generic Bluetooth interface. Only called if not overridden by 266 another supported device. 267 """ 268 raise NotImplementedError("{} must be defined.".format( 269 inspect.currentframe().f_code.co_name)) 270 271 def sdp_clean_up(self): 272 """Base generic Bluetooth interface. Only called if not overridden by 273 another supported device. 274 """ 275 raise NotImplementedError("{} must be defined.".format( 276 inspect.currentframe().f_code.co_name)) 277 278 def sdp_init(self): 279 """Base generic Bluetooth interface. Only called if not overridden by 280 another supported device. 281 """ 282 raise NotImplementedError("{} must be defined.".format( 283 inspect.currentframe().f_code.co_name)) 284 285 def sdp_remove_service(self, service_id): 286 """Base generic Bluetooth interface. Only called if not overridden by 287 another supported device. 288 """ 289 raise NotImplementedError("{} must be defined.".format( 290 inspect.currentframe().f_code.co_name)) 291 292 def start_le_advertisement(self, adv_data, adv_interval): 293 """Base generic Bluetooth interface. Only called if not overridden by 294 another supported device. 295 """ 296 raise NotImplementedError("{} must be defined.".format( 297 inspect.currentframe().f_code.co_name)) 298 299 def stop_le_advertisement(self): 300 """Base generic Bluetooth interface. Only called if not overridden by 301 another supported device. 302 """ 303 raise NotImplementedError("{} must be defined.".format( 304 inspect.currentframe().f_code.co_name)) 305 306 def set_bluetooth_local_name(self, name): 307 """Base generic Bluetooth interface. Only called if not overridden by 308 another supported device. 309 """ 310 raise NotImplementedError("{} must be defined.".format( 311 inspect.currentframe().f_code.co_name)) 312 313 def setup_gatt_server(self, database): 314 """Base generic Bluetooth interface. Only called if not overridden by 315 another supported device. 316 """ 317 raise NotImplementedError("{} must be defined.".format( 318 inspect.currentframe().f_code.co_name)) 319 320 def close_gatt_server(self): 321 """Base generic Bluetooth interface. Only called if not overridden by 322 another supported device. 323 """ 324 raise NotImplementedError("{} must be defined.".format( 325 inspect.currentframe().f_code.co_name)) 326 327 def unbond_device(self, peer_identifier): 328 """Base generic Bluetooth interface. Only called if not overridden by 329 another supported device. 330 """ 331 raise NotImplementedError("{} must be defined.".format( 332 inspect.currentframe().f_code.co_name)) 333 334 def unbond_all_known_devices(self): 335 """Base generic Bluetooth interface. Only called if not overridden by 336 another supported device. 337 """ 338 raise NotImplementedError("{} must be defined.".format( 339 inspect.currentframe().f_code.co_name)) 340 341 def init_pair(self, peer_identifier, security_level, non_bondable, 342 transport): 343 """Base generic Bluetooth interface. Only called if not overridden by 344 another supported device. 345 """ 346 raise NotImplementedError("{} must be defined.".format( 347 inspect.currentframe().f_code.co_name)) 348 349 350class AndroidBluetoothDevice(BluetoothDevice): 351 """Class wrapper for an Android Bluetooth device. 352 353 Each object of this class represents a generic Bluetooth device. 354 Android device and Fuchsia devices are the currently supported devices/ 355 356 Attributes: 357 android_device: An Android Bluetooth device. 358 """ 359 def __init__(self, android_device): 360 super().__init__(android_device) 361 self.gatt_timeout = 10 362 self.peer_mapping = {} 363 self.discovered_services_index = None 364 365 def _client_wait(self, gatt_event, gatt_callback): 366 return self._timed_pop(gatt_event, gatt_callback) 367 368 def _timed_pop(self, gatt_event, gatt_callback): 369 expected_event = gatt_event["evt"].format(gatt_callback) 370 try: 371 return self.device.ed.pop_event(expected_event, self.gatt_timeout) 372 except Empty as emp: 373 raise AssertionError(gatt_event["err"].format(expected_event)) 374 375 def _setup_discovered_services_index(self, bluetooth_gatt): 376 """ Sets the discovered services index for the gatt connection 377 related to the Bluetooth GATT callback object. 378 379 Args: 380 bluetooth_gatt: The BluetoothGatt callback id 381 """ 382 if not self.discovered_services_index: 383 self.device.droid.gattClientDiscoverServices(bluetooth_gatt) 384 expected_event = gatt_cb_strings['gatt_serv_disc'].format( 385 self.gatt_callback) 386 event = self.dut.ed.pop_event(expected_event, self.gatt_timeout) 387 self.discovered_services_index = event['data']['ServicesIndex'] 388 389 def a2dp_initiate_open_stream(self): 390 raise NotImplementedError("{} not yet implemented.".format( 391 inspect.currentframe().f_code.co_name)) 392 393 def start_profile_a2dp_sink(self): 394 raise NotImplementedError("{} not yet implemented.".format( 395 inspect.currentframe().f_code.co_name)) 396 397 def stop_profile_a2dp_sink(self): 398 raise NotImplementedError("{} not yet implemented.".format( 399 inspect.currentframe().f_code.co_name)) 400 401 def bluetooth_toggle_state(self, state): 402 self.device.droid.bluetoothToggleState(state) 403 404 def set_discoverable(self, is_discoverable): 405 """ Sets the device's discoverability. 406 407 Args: 408 is_discoverable: True if discoverable, false if not discoverable 409 """ 410 if is_discoverable: 411 self.device.droid.bluetoothMakeDiscoverable() 412 else: 413 self.device.droid.bluetoothMakeUndiscoverable() 414 415 def initialize_bluetooth_controller(self): 416 """ Just pass for Android as there is no concept of initializing 417 a Bluetooth controller. 418 """ 419 pass 420 421 def start_pairing_helper(self): 422 """ Starts the Android pairing helper. 423 """ 424 self.device.droid.bluetoothStartPairingHelper(True) 425 426 def gatt_client_write_characteristic_without_response_by_handle( 427 self, peer_identifier, handle, value): 428 """ Perform a GATT Client write Characteristic without response to 429 remote peer GATT server database. 430 431 Args: 432 peer_identifier: The mac address associated with the GATT connection 433 handle: The characteristic handle (or instance id). 434 value: The list of bytes to write. 435 Returns: 436 True if success, False if failure. 437 """ 438 peer_info = self.peer_mapping.get(peer_identifier) 439 if not peer_info: 440 self.log.error( 441 "Peer idenifier {} not currently connected or unknown.".format( 442 peer_identifier)) 443 return False 444 self._setup_discovered_services_index() 445 self.device.droid.gattClientWriteCharacteristicByInstanceId( 446 peer_info.get('bluetooth_gatt'), self.discovered_services_index, 447 handle, value) 448 try: 449 event = self._client_wait(gatt_event['char_write'], 450 peer_info.get('gatt_callback')) 451 except AssertionError as err: 452 self.log.error("Failed to write Characteristic: {}".format(err)) 453 return True 454 455 def gatt_client_write_characteristic_by_handle(self, peer_identifier, 456 handle, offset, value): 457 """ Perform a GATT Client write Characteristic without response to 458 remote peer GATT server database. 459 460 Args: 461 peer_identifier: The mac address associated with the GATT connection 462 handle: The characteristic handle (or instance id). 463 offset: Not used yet. 464 value: The list of bytes to write. 465 Returns: 466 True if success, False if failure. 467 """ 468 peer_info = self.peer_mapping.get(peer_identifier) 469 if not peer_info: 470 self.log.error( 471 "Peer idenifier {} not currently connected or unknown.".format( 472 peer_identifier)) 473 return False 474 self._setup_discovered_services_index() 475 self.device.droid.gattClientWriteCharacteristicByInstanceId( 476 peer_info.get('bluetooth_gatt'), self.discovered_services_index, 477 handle, value) 478 try: 479 event = self._client_wait(gatt_event['char_write'], 480 peer_info.get('gatt_callback')) 481 except AssertionError as err: 482 self.log.error("Failed to write Characteristic: {}".format(err)) 483 return True 484 485 def gatt_client_read_characteristic_by_handle(self, peer_identifier, 486 handle): 487 """ Perform a GATT Client read Characteristic to remote peer GATT 488 server database. 489 490 Args: 491 peer_identifier: The mac address associated with the GATT connection 492 handle: The characteristic handle (or instance id). 493 Returns: 494 Value of Characteristic if success, None if failure. 495 """ 496 peer_info = self.peer_mapping.get(peer_identifier) 497 if not peer_info: 498 self.log.error( 499 "Peer idenifier {} not currently connected or unknown.".format( 500 peer_identifier)) 501 return False 502 self._setup_discovered_services_index() 503 self.dut.droid.gattClientReadCharacteristicByInstanceId( 504 peer_info.get('bluetooth_gatt'), self.discovered_services_index, 505 handle) 506 try: 507 event = self._client_wait(gatt_event['char_read'], 508 peer_info.get('gatt_callback')) 509 except AssertionError as err: 510 self.log.error("Failed to read Characteristic: {}".format(err)) 511 512 return event['data']['CharacteristicValue'] 513 514 def gatt_client_read_long_characteristic_by_handle(self, peer_identifier, 515 handle, offset, 516 max_bytes): 517 """ Perform a GATT Client read Characteristic to remote peer GATT 518 server database. 519 520 Args: 521 peer_identifier: The mac address associated with the GATT connection 522 offset: Not used yet. 523 handle: The characteristic handle (or instance id). 524 max_bytes: Not used yet. 525 Returns: 526 Value of Characteristic if success, None if failure. 527 """ 528 peer_info = self.peer_mapping.get(peer_identifier) 529 if not peer_info: 530 self.log.error( 531 "Peer idenifier {} not currently connected or unknown.".format( 532 peer_identifier)) 533 return False 534 self._setup_discovered_services_index() 535 self.dut.droid.gattClientReadCharacteristicByInstanceId( 536 peer_info.get('bluetooth_gatt'), self.discovered_services_index, 537 handle) 538 try: 539 event = self._client_wait(gatt_event['char_read'], 540 peer_info.get('gatt_callback')) 541 except AssertionError as err: 542 self.log.error("Failed to read Characteristic: {}".format(err)) 543 544 return event['data']['CharacteristicValue'] 545 546 def gatt_client_enable_notifiy_characteristic_by_handle( 547 self, peer_identifier, handle): 548 """ Perform a GATT Client enable Characteristic notification to remote 549 peer GATT server database. 550 551 Args: 552 peer_identifier: The mac address associated with the GATT connection 553 handle: The characteristic handle. 554 Returns: 555 True is success, False if failure. 556 """ 557 raise NotImplementedError("{} not yet implemented.".format( 558 inspect.currentframe().f_code.co_name)) 559 560 def gatt_client_disable_notifiy_characteristic_by_handle( 561 self, peer_identifier, handle): 562 """ Perform a GATT Client disable Characteristic notification to remote 563 peer GATT server database. 564 565 Args: 566 peer_identifier: The mac address associated with the GATT connection 567 handle: The characteristic handle. 568 Returns: 569 True is success, False if failure. 570 """ 571 raise NotImplementedError("{} not yet implemented.".format( 572 inspect.currentframe().f_code.co_name)) 573 574 def gatt_client_read_descriptor_by_handle(self, peer_identifier, handle): 575 """ Perform a GATT Client read Descriptor to remote peer GATT 576 server database. 577 578 Args: 579 peer_identifier: The mac address associated with the GATT connection 580 handle: The Descriptor handle (or instance id). 581 Returns: 582 Value of Descriptor if success, None if failure. 583 """ 584 peer_info = self.peer_mapping.get(peer_identifier) 585 if not peer_info: 586 self.log.error( 587 "Peer idenifier {} not currently connected or unknown.".format( 588 peer_identifier)) 589 return False 590 self._setup_discovered_services_index() 591 self.dut.droid.gattClientReadDescriptorByInstanceId( 592 peer_info.get('bluetooth_gatt'), self.discovered_services_index, 593 handle) 594 try: 595 event = self._client_wait(gatt_event['desc_read'], 596 peer_info.get('gatt_callback')) 597 except AssertionError as err: 598 self.log.error("Failed to read Descriptor: {}".format(err)) 599 # TODO: Implement sending Descriptor value in SL4A such that the data 600 # can be represented by: event['data']['DescriptorValue'] 601 return "" 602 603 def gatt_client_write_descriptor_by_handle(self, peer_identifier, handle, 604 offset, value): 605 """ Perform a GATT Client write Descriptor to the remote peer GATT 606 server database. 607 608 Args: 609 peer_identifier: The mac address associated with the GATT connection 610 handle: The Descriptor handle (or instance id). 611 offset: Not used yet 612 value: The list of bytes to write. 613 Returns: 614 True if success, False if failure. 615 """ 616 peer_info = self.peer_mapping.get(peer_identifier) 617 if not peer_info: 618 self.log.error( 619 "Peer idenifier {} not currently connected or unknown.".format( 620 peer_identifier)) 621 return False 622 self._setup_discovered_services_index() 623 self.device.droid.gattClientWriteDescriptorByInstanceId( 624 peer_info.get('bluetooth_gatt'), self.discovered_services_index, 625 handle, value) 626 try: 627 event = self._client_wait(gatt_event['desc_write'], 628 peer_info.get('gatt_callback')) 629 except AssertionError as err: 630 self.log.error("Failed to write Characteristic: {}".format(err)) 631 return True 632 633 def gatt_connect(self, peer_identifier, transport, autoconnect=False): 634 """ Perform a GATT connection to a perihperal. 635 636 Args: 637 peer_identifier: The mac address to connect to. 638 transport: Which transport to use. 639 autoconnect: Set autocnnect to True or False. 640 Returns: 641 True if success, False if failure. 642 """ 643 try: 644 bluetooth_gatt, gatt_callback = setup_gatt_connection( 645 self.device, peer_identifier, autoconnect, transport) 646 self.peer_mapping[peer_identifier] = { 647 "bluetooth_gatt": bluetooth_gatt, 648 "gatt_callback": gatt_callback 649 } 650 except GattTestUtilsError as err: 651 self.log.error(err) 652 return False 653 return True 654 655 def gatt_disconnect(self, peer_identifier): 656 """ Perform a GATT disconnect from a perihperal. 657 658 Args: 659 peer_identifier: The peer to disconnect from. 660 Returns: 661 True if success, False if failure. 662 """ 663 peer_info = self.peer_mapping.get(peer_identifier) 664 if not peer_info: 665 self.log.error( 666 "No previous connections made to {}".format(peer_identifier)) 667 return False 668 669 try: 670 disconnect_gatt_connection(self.device, 671 peer_info.get("bluetooth_gatt"), 672 peer_info.get("gatt_callback")) 673 self.device.droid.gattClientClose(peer_info.get("bluetooth_gatt")) 674 except GattTestUtilsError as err: 675 self.log.error(err) 676 return False 677 self.device.droid.gattClientClose(peer_info.get("bluetooth_gatt")) 678 679 def gatt_client_refresh(self, peer_identifier): 680 """ Perform a GATT Client Refresh of a perihperal. 681 682 Clears the internal cache and forces a refresh of the services from the 683 remote device. 684 685 Args: 686 peer_identifier: The peer to refresh. 687 """ 688 peer_info = self.peer_mapping.get(peer_identifier) 689 if not peer_info: 690 self.log.error( 691 "No previous connections made to {}".format(peer_identifier)) 692 return False 693 self.device.droid.gattClientRefresh(peer_info["bluetooth_gatt"]) 694 695 def le_scan_with_name_filter(self, name, timeout): 696 """ Scan over LE for a specific device name. 697 698 Args: 699 name: The name filter to set. 700 timeout: The timeout to wait to find the advertisement. 701 Returns: 702 Discovered mac address or None 703 """ 704 self.device.droid.bleSetScanSettingsScanMode( 705 ble_scan_settings_modes['low_latency']) 706 filter_list = self.device.droid.bleGenFilterList() 707 scan_settings = self.device.droid.bleBuildScanSetting() 708 scan_callback = self.device.droid.bleGenScanCallback() 709 self.device.droid.bleSetScanFilterDeviceName(name) 710 self.device.droid.bleBuildScanFilter(filter_list) 711 self.device.droid.bleSetScanFilterDeviceName(self.name) 712 self.device.droid.bleStartBleScan(filter_list, scan_settings, 713 scan_callback) 714 try: 715 event = self.device.ed.pop_event(scan_result.format(scan_callback), 716 timeout) 717 return event['data']['Result']['deviceInfo']['address'] 718 except Empty as err: 719 self.log.info("Scanner did not find advertisement {}".format(err)) 720 return None 721 722 def log_info(self, log): 723 """ Log directly onto the device. 724 725 Args: 726 log: The informative log. 727 """ 728 self.device.droid.log.logI(log) 729 730 def set_bluetooth_local_name(self, name): 731 """ Sets the Bluetooth controller's local name 732 Args: 733 name: The name to set. 734 """ 735 self.device.droid.bluetoothSetLocalName(name) 736 737 def get_local_bluetooth_address(self): 738 """ Returns the Bluetooth local address. 739 """ 740 return self.device.droid.bluetoothGetLocalAddress() 741 742 def reset_bluetooth(self): 743 """ Resets Bluetooth on the Android Device. 744 """ 745 bt_test_utils.reset_bluetooth([self.device]) 746 747 def sdp_add_search(self, attribute_list, profile_id): 748 """Adds an SDP search record. 749 Args: 750 attribute_list: The list of attributes to set 751 profile_id: The profile ID to set. 752 """ 753 # Android devices currently have no hooks to modify the SDP record. 754 pass 755 756 def sdp_add_service(self, sdp_record): 757 """Adds an SDP service record. 758 Args: 759 sdp_record: The dictionary representing the search record to add. 760 Returns: 761 service_id: The service id to track the service record published. 762 None if failed. 763 """ 764 # Android devices currently have no hooks to modify the SDP record. 765 pass 766 767 def sdp_clean_up(self): 768 """Cleans up all objects related to SDP. 769 """ 770 self.device.sdp_lib.cleanUp() 771 772 def sdp_init(self): 773 """Initializes SDP on the device. 774 """ 775 # Android devices currently have no hooks to modify the SDP record. 776 pass 777 778 def sdp_remove_service(self, service_id): 779 """Removes a service based on an input id. 780 Args: 781 service_id: The service ID to remove. 782 """ 783 # Android devices currently have no hooks to modify the SDP record. 784 pass 785 786 def unbond_all_known_devices(self): 787 """ Unbond all known remote devices. 788 """ 789 self.device.droid.bluetoothFactoryReset() 790 791 def unbond_device(self, peer_identifier): 792 """ Unbond peer identifier. 793 794 Args: 795 peer_identifier: The mac address for the peer to unbond. 796 797 """ 798 self.device.droid.bluetoothUnbond(peer_identifier) 799 800 def init_pair(self, peer_identifier, security_level, non_bondable, 801 transport): 802 """ Send an outgoing pairing request the input peer_identifier. 803 804 Android currently does not support setting various security levels or 805 bondable modes. Making them available for other bluetooth_device 806 variants. Depending on the Address type, Android will figure out the 807 transport to pair automatically. 808 809 Args: 810 peer_identifier: A string representing the device id. 811 security_level: Not yet implemented. See Fuchsia device impl. 812 non_bondable: Not yet implemented. See Fuchsia device impl. 813 transport: Not yet implemented. See Fuchsia device impl. 814 815 """ 816 self.dut.droid.bluetoothBond(self.peer_identifier) 817 818 819class FuchsiaBluetoothDevice(BluetoothDevice): 820 """Class wrapper for an Fuchsia Bluetooth device. 821 822 Each object of this class represents a generic luetooth device. 823 Android device and Fuchsia devices are the currently supported devices/ 824 825 Attributes: 826 fuchsia_device: A Fuchsia Bluetooth device. 827 """ 828 def __init__(self, fuchsia_device): 829 super().__init__(fuchsia_device) 830 831 def a2dp_initiate_open_stream(self): 832 raise NotImplementedError("{} not yet implemented.".format( 833 inspect.currentframe().f_code.co_name)) 834 835 def start_profile_a2dp_sink(self): 836 """ Starts the A2DP sink profile. 837 """ 838 self.device.control_daemon("bt-a2dp-sink.cmx", "start") 839 840 def stop_profile_a2dp_sink(self): 841 """ Stops the A2DP sink profile. 842 """ 843 self.device.control_daemon("bt-a2dp-sink.cmx", "stop") 844 845 def start_pairing_helper(self): 846 self.device.btc_lib.acceptPairing() 847 848 def bluetooth_toggle_state(self, state): 849 """Stub for Fuchsia implementation.""" 850 pass 851 852 def set_discoverable(self, is_discoverable): 853 """ Sets the device's discoverability. 854 855 Args: 856 is_discoverable: True if discoverable, false if not discoverable 857 """ 858 self.device.btc_lib.setDiscoverable(is_discoverable) 859 860 def get_pairing_pin(self): 861 """ Get the pairing pin from the active pairing delegate. 862 """ 863 return self.device.btc_lib.getPairingPin()['result'] 864 865 def input_pairing_pin(self, pin): 866 """ Input pairing pin to active pairing delegate. 867 868 Args: 869 pin: The pin to input. 870 """ 871 self.device.btc_lib.inputPairingPin(pin) 872 873 def initialize_bluetooth_controller(self): 874 """ Initialize Bluetooth controller for first time use. 875 """ 876 self.device.btc_lib.initBluetoothControl() 877 878 def get_local_bluetooth_address(self): 879 """ Returns the Bluetooth local address. 880 """ 881 return self.device.btc_lib.getActiveAdapterAddress().get("result") 882 883 def set_bluetooth_local_name(self, name): 884 """ Sets the Bluetooth controller's local name 885 Args: 886 name: The name to set. 887 """ 888 self.device.btc_lib.setName(name) 889 890 def gatt_client_write_characteristic_without_response_by_handle( 891 self, peer_identifier, handle, value): 892 """ Perform a GATT Client write Characteristic without response to 893 remote peer GATT server database. 894 895 Args: 896 peer_identifier: The peer to connect to. 897 handle: The characteristic handle. 898 value: The list of bytes to write. 899 Returns: 900 True if success, False if failure. 901 """ 902 if (not self._find_service_id_and_connect_to_service_for_handle( 903 peer_identifier, handle)): 904 self.log.error( 905 "Unable to find handle {} in GATT server db.".format(handle)) 906 return False 907 result = self.device.gattc_lib.writeCharByIdWithoutResponse( 908 handle, value) 909 if result.get("error") is not None: 910 self.log.error( 911 "Failed to write characteristic handle {} with err: {}".format( 912 handle, result.get("error"))) 913 return False 914 return True 915 916 def gatt_client_write_characteristic_by_handle(self, peer_identifier, 917 handle, offset, value): 918 """ Perform a GATT Client write Characteristic to remote peer GATT 919 server database. 920 921 Args: 922 peer_identifier: The peer to connect to. 923 handle: The characteristic handle. 924 offset: The offset to start writing to. 925 value: The list of bytes to write. 926 Returns: 927 True if success, False if failure. 928 """ 929 if (not self._find_service_id_and_connect_to_service_for_handle( 930 peer_identifier, handle)): 931 self.log.error( 932 "Unable to find handle {} in GATT server db.".format(handle)) 933 return False 934 result = self.device.gattc_lib.writeCharById(handle, offset, value) 935 if result.get("error") is not None: 936 self.log.error( 937 "Failed to write characteristic handle {} with err: {}".format( 938 handle, result.get("error"))) 939 return False 940 return True 941 942 def gatt_client_read_characteristic_by_handle(self, peer_identifier, 943 handle): 944 """ Perform a GATT Client read Characteristic to remote peer GATT 945 server database. 946 947 Args: 948 peer_identifier: The peer to connect to. 949 handle: The characteristic handle. 950 Returns: 951 Value of Characteristic if success, None if failure. 952 """ 953 if (not self._find_service_id_and_connect_to_service_for_handle( 954 peer_identifier, handle)): 955 self.log.error( 956 "Unable to find handle {} in GATT server db.".format(handle)) 957 return False 958 result = self.device.gattc_lib.readCharacteristicById(handle) 959 if result.get("error") is not None: 960 self.log.error( 961 "Failed to read characteristic handle {} with err: {}".format( 962 handle, result.get("error"))) 963 return None 964 return result.get("result") 965 966 def gatt_client_read_long_characteristic_by_handle(self, peer_identifier, 967 handle, offset, 968 max_bytes): 969 """ Perform a GATT Client read Characteristic to remote peer GATT 970 server database. 971 972 Args: 973 peer_identifier: The peer to connect to. 974 handle: The characteristic handle. 975 offset: The offset to start reading. 976 max_bytes: The max bytes to return for each read. 977 Returns: 978 Value of Characteristic if success, None if failure. 979 """ 980 if (not self._find_service_id_and_connect_to_service_for_handle( 981 peer_identifier, handle)): 982 self.log.error( 983 "Unable to find handle {} in GATT server db.".format(handle)) 984 return False 985 result = self.device.gattc_lib.readLongCharacteristicById( 986 handle, offset, max_bytes) 987 if result.get("error") is not None: 988 self.log.error( 989 "Failed to read characteristic handle {} with err: {}".format( 990 handle, result.get("error"))) 991 return None 992 return result.get("result") 993 994 def gatt_client_enable_notifiy_characteristic_by_handle( 995 self, peer_identifier, handle): 996 """ Perform a GATT Client enable Characteristic notification to remote 997 peer GATT server database. 998 999 Args: 1000 peer_identifier: The peer to connect to. 1001 handle: The characteristic handle. 1002 Returns: 1003 True is success, False if failure. 1004 """ 1005 if (not self._find_service_id_and_connect_to_service_for_handle( 1006 peer_identifier, handle)): 1007 self.log.error( 1008 "Unable to find handle {} in GATT server db.".format(handle)) 1009 return False 1010 result = self.device.gattc_lib.enableNotifyCharacteristic(handle) 1011 if result.get("error") is not None: 1012 self.log.error( 1013 "Failed to enable characteristic notifications for handle {} " 1014 "with err: {}".format(handle, result.get("error"))) 1015 return None 1016 return result.get("result") 1017 1018 def gatt_client_disable_notifiy_characteristic_by_handle( 1019 self, peer_identifier, handle): 1020 """ Perform a GATT Client disable Characteristic notification to remote 1021 peer GATT server database. 1022 1023 Args: 1024 peer_identifier: The peer to connect to. 1025 handle: The characteristic handle. 1026 Returns: 1027 True is success, False if failure. 1028 """ 1029 if (not self._find_service_id_and_connect_to_service_for_handle( 1030 peer_identifier, handle)): 1031 self.log.error( 1032 "Unable to find handle {} in GATT server db.".format(handle)) 1033 return False 1034 result = self.device.gattc_lib.disableNotifyCharacteristic(handle) 1035 if result.get("error") is not None: 1036 self.log.error( 1037 "Failed to disable characteristic notifications for handle {} " 1038 "with err: {}".format(peer_identifier, result.get("error"))) 1039 return None 1040 return result.get("result") 1041 1042 def gatt_client_read_descriptor_by_handle(self, peer_identifier, handle): 1043 """ Perform a GATT Client read Descriptor to remote peer GATT server 1044 database. 1045 1046 Args: 1047 peer_identifier: The peer to connect to. 1048 handle: The Descriptor handle. 1049 Returns: 1050 Value of Descriptor if success, None if failure. 1051 """ 1052 if (not self._find_service_id_and_connect_to_service_for_handle( 1053 peer_identifier, handle)): 1054 self.log.error( 1055 "Unable to find handle {} in GATT server db.".format(handle)) 1056 return False 1057 result = self.device.gattc_lib.readDescriptorById(handle) 1058 if result.get("error") is not None: 1059 self.log.error( 1060 "Failed to read descriptor for handle {} with err: {}".format( 1061 peer_identifier, result.get("error"))) 1062 return None 1063 return result.get("result") 1064 1065 def gatt_client_write_descriptor_by_handle(self, peer_identifier, handle, 1066 offset, value): 1067 """ Perform a GATT Client write Descriptor to remote peer GATT server 1068 database. 1069 1070 Args: 1071 peer_identifier: The peer to connect to. 1072 handle: The Descriptor handle. 1073 offset: The offset to start writing at. 1074 value: The list of bytes to write. 1075 Returns: 1076 True if success, False if failure. 1077 """ 1078 if (not self._find_service_id_and_connect_to_service_for_handle( 1079 peer_identifier, handle)): 1080 self.log.error( 1081 "Unable to find handle {} in GATT server db.".format(handle)) 1082 return False 1083 result = self.device.gattc_lib.writeDescriptorById( 1084 handle, offset, value) 1085 if result.get("error") is not None: 1086 self.log.error( 1087 "Failed to write descriptor for handle {} with err: {}".format( 1088 peer_identifier, result.get("error"))) 1089 return None 1090 return True 1091 1092 def gatt_connect(self, peer_identifier, transport, autoconnect): 1093 """ Perform a GATT connection to a perihperal. 1094 1095 Args: 1096 peer_identifier: The peer to connect to. 1097 transport: Not implemented. 1098 autoconnect: Not implemented. 1099 Returns: 1100 True if success, False if failure. 1101 """ 1102 connection_result = self.device.gattc_lib.bleConnectToPeripheral( 1103 peer_identifier) 1104 if connection_result.get("error") is not None: 1105 self.log.error("Failed to connect to peer id {}: {}".format( 1106 peer_identifier, connection_result.get("error"))) 1107 return False 1108 return True 1109 1110 def gatt_client_refresh(self, peer_identifier): 1111 """ Perform a GATT Client Refresh of a perihperal. 1112 1113 Clears the internal cache and forces a refresh of the services from the 1114 remote device. In Fuchsia there is no FIDL api to automatically do this 1115 yet. Therefore just read all Characteristics which satisfies the same 1116 requirements. 1117 1118 Args: 1119 peer_identifier: The peer to refresh. 1120 """ 1121 self._read_all_characteristics(peer_identifier) 1122 1123 def gatt_client_discover_characteristic_by_uuid(self, peer_identifier, 1124 uuid): 1125 """ Perform a GATT Client Refresh of a perihperal. 1126 1127 Clears the internal cache and forces a refresh of the services from the 1128 remote device. In Fuchsia there is no FIDL api to automatically do this 1129 yet. Therefore just read all Characteristics which satisfies the same 1130 requirements. 1131 1132 Args: 1133 peer_identifier: The peer to refresh. 1134 """ 1135 self._read_all_characteristics(peer_identifier, uuid) 1136 1137 def gatt_disconnect(self, peer_identifier): 1138 """ Perform a GATT disconnect from a perihperal. 1139 1140 Args: 1141 peer_identifier: The peer to disconnect from. 1142 Returns: 1143 True if success, False if failure. 1144 """ 1145 disconnect_result = self.device.gattc_lib.bleDisconnectPeripheral( 1146 peer_identifier) 1147 if disconnect_result.get("error") is None: 1148 self.log.error("Failed to disconnect from peer id {}: {}".format( 1149 peer_identifier, disconnect_result.get("error"))) 1150 return False 1151 return True 1152 1153 def reset_bluetooth(self): 1154 """Stub for Fuchsia implementation.""" 1155 pass 1156 1157 def sdp_add_search(self, attribute_list, profile_id): 1158 """Adds an SDP search record. 1159 Args: 1160 attribute_list: The list of attributes to set 1161 profile_id: The profile ID to set. 1162 """ 1163 return self.device.sdp_lib.addSearch(attribute_list, profile_id) 1164 1165 def sdp_add_service(self, sdp_record): 1166 """Adds an SDP service record. 1167 Args: 1168 sdp_record: The dictionary representing the search record to add. 1169 """ 1170 return self.device.sdp_lib.addService(sdp_record) 1171 1172 def sdp_clean_up(self): 1173 """Cleans up all objects related to SDP. 1174 """ 1175 return self.device.sdp_lib.cleanUp() 1176 1177 def sdp_init(self): 1178 """Initializes SDP on the device. 1179 """ 1180 return self.device.sdp_lib.init() 1181 1182 def sdp_remove_service(self, service_id): 1183 """Removes a service based on an input id. 1184 Args: 1185 service_id: The service ID to remove. 1186 """ 1187 return self.device.sdp_lib.init() 1188 1189 def start_le_advertisement(self, adv_data, adv_interval): 1190 """ Starts an LE advertisement 1191 1192 Args: 1193 adv_data: Advertisement data. 1194 adv_interval: Advertisement interval. 1195 """ 1196 self.device.ble_lib.bleStartBleAdvertising(adv_data, adv_interval) 1197 1198 def stop_le_advertisement(self): 1199 """ Stop active LE advertisement. 1200 """ 1201 self.device.ble_lib.bleStopBleAdvertising() 1202 1203 def setup_gatt_server(self, database): 1204 """ Sets up an input GATT server. 1205 1206 Args: 1207 database: A dictionary representing the GATT database to setup. 1208 """ 1209 self.device.gatts_lib.publishServer(database) 1210 1211 def close_gatt_server(self): 1212 """ Closes an existing GATT server. 1213 """ 1214 self.device.gatts_lib.closeServer() 1215 1216 def le_scan_with_name_filter(self, name, timeout): 1217 """ Scan over LE for a specific device name. 1218 1219 Args: 1220 name: The name filter to set. 1221 timeout: The timeout to wait to find the advertisement. 1222 Returns: 1223 Discovered device id or None 1224 """ 1225 partial_match = True 1226 return le_scan_for_device_by_name(self.device, self.device.log, name, 1227 timeout, partial_match) 1228 1229 def log_info(self, log): 1230 """ Log directly onto the device. 1231 1232 Args: 1233 log: The informative log. 1234 """ 1235 self.device.logging_lib.logI(log) 1236 pass 1237 1238 def unbond_all_known_devices(self): 1239 """ Unbond all known remote devices. 1240 """ 1241 try: 1242 device_list = self.device.btc_lib.getKnownRemoteDevices()['result'] 1243 for device_info in device_list: 1244 device = device_list[device_info] 1245 if device['bonded']: 1246 self.device.btc_lib.forgetDevice(device['id']) 1247 except Exception as err: 1248 self.log.err("Unable to unbond all devices: {}".format(err)) 1249 1250 def unbond_device(self, peer_identifier): 1251 """ Unbond peer identifier. 1252 1253 Args: 1254 peer_identifier: The peer identifier for the peer to unbond. 1255 1256 """ 1257 self.device.btc_lib.forgetDevice(peer_identifier) 1258 1259 def _find_service_id_and_connect_to_service_for_handle( 1260 self, peer_identifier, handle): 1261 fail_err = "Failed to find handle {} in Peer database." 1262 try: 1263 services = self.device.gattc_lib.listServices(peer_identifier) 1264 for service in services['result']: 1265 service_id = service['id'] 1266 self.device.gattc_lib.connectToService(peer_identifier, 1267 service_id) 1268 chars = self.device.gattc_lib.discoverCharacteristics() 1269 1270 for char in chars['result']: 1271 char_id = char['id'] 1272 if handle == char_id: 1273 return True 1274 descriptors = char['descriptors'] 1275 for desc in descriptors: 1276 desc_id = desc["id"] 1277 if handle == desc_id: 1278 return True 1279 except Exception as err: 1280 self.log.error(fail_err.format(err)) 1281 return False 1282 1283 def _read_all_characteristics(self, peer_identifier, uuid=None): 1284 fail_err = "Failed to read all characteristics with: {}" 1285 try: 1286 services = self.device.gattc_lib.listServices(peer_identifier) 1287 for service in services['result']: 1288 service_id = service['id'] 1289 service_uuid = service['uuid_type'] 1290 self.device.gattc_lib.connectToService(peer_identifier, 1291 service_id) 1292 chars = self.device.gattc_lib.discoverCharacteristics() 1293 self.log.info( 1294 "Reading chars in service uuid: {}".format(service_uuid)) 1295 1296 for char in chars['result']: 1297 char_id = char['id'] 1298 char_uuid = char['uuid_type'] 1299 if uuid and uuid.lower() not in char_uuid.lower(): 1300 continue 1301 try: 1302 read_val = \ 1303 self.device.gattc_lib.readCharacteristicById( 1304 char_id) 1305 self.log.info( 1306 "\tCharacteristic uuid / Value: {} / {}".format( 1307 char_uuid, read_val['result'])) 1308 str_value = "" 1309 for val in read_val['result']: 1310 str_value += chr(val) 1311 self.log.info("\t\tstr val: {}".format(str_value)) 1312 except Exception as err: 1313 self.log.error(err) 1314 pass 1315 except Exception as err: 1316 self.log.error(fail_err.forma(err)) 1317 1318 def _perform_read_all_descriptors(self, peer_identifier): 1319 fail_err = "Failed to read all characteristics with: {}" 1320 try: 1321 services = self.device.gattc_lib.listServices(peer_identifier) 1322 for service in services['result']: 1323 service_id = service['id'] 1324 service_uuid = service['uuid_type'] 1325 self.device.gattc_lib.connectToService(peer_identifier, 1326 service_id) 1327 chars = self.device.gattc_lib.discoverCharacteristics() 1328 self.log.info( 1329 "Reading descs in service uuid: {}".format(service_uuid)) 1330 1331 for char in chars['result']: 1332 char_id = char['id'] 1333 char_uuid = char['uuid_type'] 1334 descriptors = char['descriptors'] 1335 self.log.info( 1336 "\tReading descs in char uuid: {}".format(char_uuid)) 1337 for desc in descriptors: 1338 desc_id = desc["id"] 1339 desc_uuid = desc["uuid_type"] 1340 try: 1341 read_val = self.device.gattc_lib.readDescriptorById( 1342 desc_id) 1343 self.log.info( 1344 "\t\tDescriptor uuid / Value: {} / {}".format( 1345 desc_uuid, read_val['result'])) 1346 except Exception as err: 1347 pass 1348 except Exception as err: 1349 self.log.error(fail_err.format(err)) 1350 1351 def init_pair(self, peer_identifier, security_level, non_bondable, 1352 transport): 1353 """ Send an outgoing pairing request the input peer_identifier. 1354 1355 Android currently does not support setting various security levels or 1356 bondable modes. Making them available for other bluetooth_device 1357 variants. Depending on the Address type, Android will figure out the 1358 transport to pair automatically. 1359 1360 Args: 1361 peer_identifier: A string representing the device id. 1362 security_level: The security level required for this pairing request 1363 represented as a u64. (Only for LE pairing) 1364 Available Values 1365 1 - ENCRYPTED: Encrypted without MITM protection 1366 (unauthenticated) 1367 2 - AUTHENTICATED: Encrypted with MITM protection 1368 (authenticated) 1369 None: No pairing security level. 1370 non_bondable: A bool representing whether the pairing mode is 1371 bondable or not. None is also accepted. False if bondable, True 1372 if non-bondable 1373 transport: A u64 representing the transport type. 1374 Available Values 1375 1 - BREDR: Classic BR/EDR transport 1376 2 - LE: Bluetooth Low Energy Transport 1377 Returns: 1378 True if successful, False if failed. 1379 """ 1380 try: 1381 self.device.btc_lib.pair(peer_identifier, security_level, 1382 non_bondable, transport) 1383 return True 1384 except Exception as err: 1385 fail_err = "Failed to pair to peer_identifier {} with: {}".format( 1386 peer_identifier) 1387 self.log.error(fail_err.format(err)) 1388