1# Lint as: python2, python3 2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6from __future__ import absolute_import 7 8import base64 9import functools 10import json 11import logging 12import threading 13from datetime import datetime 14 15import common 16from autotest_lib.client.bin import utils 17from autotest_lib.client.cros import constants 18from autotest_lib.server import autotest 19 20def proxy_thread_safe(method): 21 """A decorator enabling thread-safe XmlRpc calls""" 22 23 @functools.wraps(method) 24 def wrapper(self, *args, **kwargs): 25 """A wrapper of the decorated method""" 26 with self._proxy_lock: 27 return method(self, *args, **kwargs) 28 29 return wrapper 30 31 32class BluetoothDevice(object): 33 """BluetoothDevice is a thin layer of logic over a remote DUT. 34 35 The Autotest host object representing the remote DUT, passed to this 36 class on initialization, can be accessed from its host property. 37 38 """ 39 40 XMLRPC_BRINGUP_TIMEOUT_SECONDS = 60 41 XMLRPC_LOG_PATH = '/var/log/bluetooth_xmlrpc_device.log' 42 XMLRPC_REQUEST_TIMEOUT_SECONDS = 180 43 44 # We currently get dates back in string format due to some inconsistencies 45 # between python2 and python3. This is the standard date format we use. 46 STANDARD_DATE_FORMAT = '%Y-%m-%d %H:%M:%S.%f' 47 48 def __init__(self, device_host, remote_facade_proxy=None, floss=False): 49 """Construct a BluetoothDevice. 50 51 @param device_host: host object representing a remote host. 52 53 """ 54 self.host = device_host 55 self.floss = floss 56 self._remote_proxy = remote_facade_proxy 57 58 # Make sure the client library is on the device so that the proxy code 59 # is there when we try to call it. 60 client_at = autotest.Autotest(self.host) 61 client_at.install() 62 self._proxy_lock = threading.Lock() 63 64 # Assign the correct _proxy based on the remote facade 65 if self._remote_proxy: 66 if self.floss: 67 self._proxy = self._remote_proxy.floss 68 else: 69 self._proxy = self._remote_proxy.bluetooth 70 else: 71 # If remote facade wasn't already created, connect directly here 72 self._proxy = self._connect_xmlrpc_directly() 73 74 def __getattr__(self, name): 75 """Override default attribute behavior to call proxy methods. 76 77 To remove duplicate code in this class, we allow methods in the proxy 78 class to be called directly from the bluetooth device class. If an 79 attribute is contained within this class, we return it. Otherwise, if 80 the proxy object contains a callable attribute of that name, we return a 81 function that calls that object when invoked. 82 83 All methods called on the proxy in this way will hold the proxy lock. 84 """ 85 try: 86 return object.__getattr__(self, name) 87 except AttributeError as ae: 88 pass 89 90 # We only return proxied methods if no such attribute exists on this 91 # class. Any attribute errors here will be raised at the end if attr 92 # is None 93 try: 94 proxy = object.__getattribute__(self, '_proxy') 95 proxy_lock = object.__getattribute__(self, '_proxy_lock') 96 attr = proxy.__getattr__(name) 97 if attr: 98 99 def wrapper(*args, **kwargs): 100 """Call target function while holding proxy lock.""" 101 with proxy_lock: 102 return attr(*args, **kwargs) 103 104 return wrapper 105 except AttributeError as ae: 106 pass 107 108 # Couldn't find the attribute in either self or self._proxy. 109 raise AttributeError('{} has no attribute: {}'.format( 110 type(self).__name__, name)) 111 112 def is_floss(self): 113 """Is the current facade running Floss?""" 114 return self.floss 115 116 def _connect_xmlrpc_directly(self): 117 """Connects to the bluetooth facade directly via xmlrpc.""" 118 # When the xmlrpc server is already created (using the 119 # RemoteFacadeFactory), we will use the BluezFacadeLocal inside the 120 # remote proxy. Otherwise, we will use the xmlrpc server started from 121 # this class. Currently, there are a few users outside of the Bluetooth 122 # autotests that use this and this can be removed once those users 123 # migrate to using the RemoteFacadeFactory to generate the xmlrpc 124 # connection. 125 proxy = self.host.rpc_server_tracker.xmlrpc_connect( 126 constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_COMMAND, 127 constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT, 128 command_name=constants. 129 BLUETOOTH_DEVICE_XMLRPC_SERVER_CLEANUP_PATTERN, 130 ready_test_name=constants. 131 BLUETOOTH_DEVICE_XMLRPC_SERVER_READY_METHOD, 132 timeout_seconds=self.XMLRPC_BRINGUP_TIMEOUT_SECONDS, 133 logfile=self.XMLRPC_LOG_PATH, 134 request_timeout_seconds=self.XMLRPC_REQUEST_TIMEOUT_SECONDS) 135 136 self._bt_direct_proxy = proxy 137 return proxy 138 139 @property 140 @proxy_thread_safe 141 def address(self): 142 """Get the adapter address.""" 143 return self._proxy.get_address() 144 145 @property 146 @proxy_thread_safe 147 def bluez_version(self): 148 """Get the bluez version.""" 149 return self._proxy.get_bluez_version() 150 151 @property 152 @proxy_thread_safe 153 def bluetooth_class(self): 154 """Get the bluetooth class.""" 155 return self._proxy.get_bluetooth_class() 156 157 @proxy_thread_safe 158 def set_debug_log_levels(self, bluez_vb, kernel_vb): 159 """Enable or disable the debug logs of bluetooth 160 161 @param bluez_vb: verbosity of bluez debug log, either 0 or 1 162 @param kernel_vb: verbosity of kernel debug log, either 0 or 1 163 164 """ 165 return self._proxy.set_debug_log_levels(bluez_vb, kernel_vb) 166 167 @proxy_thread_safe 168 def set_quality_debug_log(self, enable): 169 """Enable or disable bluez quality debug log in the DUT 170 @param enable: True to enable all of the debug log, 171 False to disable all of the debug log. 172 """ 173 return self._proxy.set_quality_debug_log(enable) 174 175 @proxy_thread_safe 176 def log_message(self, msg, dut=True, peer=True): 177 """ Log a message in DUT log and peer logs with timestamp. 178 179 @param msg: message to be logged. 180 @param dut: log message on DUT 181 @param peer: log message on peer devices 182 """ 183 try: 184 # TODO(b/146671469) Implement logging to tester 185 186 date = datetime.strftime(datetime.now(),"%Y:%m:%d %H:%M:%S:%f") 187 msg = "bluetooth autotest --- %s : %s ---" % (date, msg) 188 logging.debug("Broadcasting '%s'",msg) 189 190 if dut: 191 self._proxy.log_message(msg) 192 193 if peer: 194 for btpeer in self.host.btpeer_list: 195 btpeer.log_message(msg) 196 except Exception as e: 197 logging.error("Exception '%s' in log_message '%s'", str(e), msg) 198 199 200 @proxy_thread_safe 201 def is_wrt_supported(self): 202 """ Check if Bluetooth adapter support WRT logs. 203 204 Intel adapter support WRT (except of WP2 and StP2) 205 206 @returns: True if adapter support WRT logs 207 """ 208 return self._proxy.is_wrt_supported() 209 210 211 @proxy_thread_safe 212 def enable_wrt_logs(self): 213 """Enable wrt logs on Intel adapters.""" 214 return self._proxy.enable_wrt_logs() 215 216 217 @proxy_thread_safe 218 def collect_wrt_logs(self): 219 """Collect wrt logs on Intel adapters.""" 220 return self._proxy.collect_wrt_logs() 221 222 223 @proxy_thread_safe 224 def start_bluetoothd(self): 225 """start bluetoothd. 226 227 @returns: True if bluetoothd is started correctly. 228 False otherwise. 229 230 """ 231 return self._proxy.start_bluetoothd() 232 233 234 @proxy_thread_safe 235 def stop_bluetoothd(self): 236 """stop bluetoothd. 237 238 @returns: True if bluetoothd is stopped correctly. 239 False otherwise. 240 241 """ 242 return self._proxy.stop_bluetoothd() 243 244 245 @proxy_thread_safe 246 def is_bluetoothd_running(self): 247 """Is bluetoothd running? 248 249 @returns: True if bluetoothd is running 250 251 """ 252 return self._proxy.is_bluetoothd_running() 253 254 255 @proxy_thread_safe 256 def is_bluetoothd_valid(self): 257 """Checks whether the current bluetoothd session is ok. 258 259 Returns: 260 True if the current bluetoothd session is ok. False if bluetoothd is 261 not running or it is a new session. 262 """ 263 return self._proxy.is_bluetoothd_proxy_valid() 264 265 @proxy_thread_safe 266 def has_adapter(self): 267 """@return True if an adapter is present, False if not.""" 268 return self._proxy.has_adapter() 269 270 271 @proxy_thread_safe 272 def is_wake_enabled(self): 273 """@return True if adapter is wake enabled, False if not.""" 274 return self._proxy.is_wake_enabled() 275 276 277 @proxy_thread_safe 278 def set_wake_enabled(self, value): 279 """ Sets the power/wakeup value for the adapter. 280 281 Args: 282 value: Whether the adapter can wake from suspend 283 284 @return True if able to set it to value, False if not.""" 285 return self._proxy.set_wake_enabled(value) 286 287 288 def get_hci(self): 289 """Get hci of the adapter; normally, it is 'hci0'. 290 291 @returns: the hci name of the adapter. 292 293 """ 294 dev_info = self.get_dev_info() 295 hci = (dev_info[1] if isinstance(dev_info, list) and 296 len(dev_info) > 1 else None) 297 return hci 298 299 300 def get_UUIDs(self): 301 """Get the UUIDs. 302 303 The UUIDs can be dynamically changed at run time due to adapter/CRAS 304 services availability. Therefore, always query them from the adapter, 305 not from the cache. 306 307 An example of UUIDs: 308 [u'00001112-0000-1000-8000-00805f9b34fb', 309 u'00001801-0000-1000-8000-00805f9b34fb', 310 u'0000110a-0000-1000-8000-00805f9b34fb', 311 u'0000111f-0000-1000-8000-00805f9b34fb', 312 u'00001200-0000-1000-8000-00805f9b34fb', 313 u'00001800-0000-1000-8000-00805f9b34fb'] 314 315 @returns: the list of the UUIDs. 316 317 """ 318 properties = self.get_adapter_properties() 319 return properties.get('UUIDs') 320 321 322 @proxy_thread_safe 323 def set_pairable_timeout(self, pairable_timeout): 324 """Set the adapter PairableTimeout. 325 326 @param pairable_timeout: adapter PairableTimeout 327 value to set in seconds (Integer). 328 329 @return True on success, False otherwise. 330 331 """ 332 return self._proxy.set_pairable_timeout(pairable_timeout) 333 334 335 @proxy_thread_safe 336 def get_pairable_timeout(self): 337 """Get the adapter PairableTimeout. 338 339 @return Value of property PairableTimeout in seconds (Integer). 340 341 """ 342 return self._proxy.get_pairable_timeout() 343 344 345 @proxy_thread_safe 346 def set_pairable(self, pairable): 347 """Set the adapter pairable state. 348 349 @param pairable: adapter pairable state to set (True or False). 350 351 @return True on success, False otherwise. 352 353 """ 354 return self._proxy.set_pairable(pairable) 355 356 357 @proxy_thread_safe 358 def is_pairable(self): 359 """Is the adapter in the pairable state? 360 361 @return True if pairable. False otherwise. 362 363 """ 364 return self._proxy.get_pairable() 365 366 @proxy_thread_safe 367 def set_adapter_alias(self, alias): 368 """Set the adapter alias. 369 370 A note on Alias property - providing an empty string ('') will reset the 371 Alias property to the system default 372 373 @param alias: adapter alias to set with type String 374 375 @return True on success, False otherwise. 376 """ 377 378 return self._proxy.set_adapter_alias(alias) 379 380 @proxy_thread_safe 381 def get_adapter_properties(self): 382 """Read the adapter properties from the Bluetooth Daemon. 383 384 An example of the adapter properties looks like 385 {u'Name': u'BlueZ 5.35', 386 u'Alias': u'Chromebook', 387 u'Modalias': u'bluetooth:v00E0p2436d0400', 388 u'Powered': 1, 389 u'DiscoverableTimeout': 180, 390 u'PairableTimeout': 0, 391 u'Discoverable': 0, 392 u'Address': u'6C:29:95:1A:D4:6F', 393 u'Discovering': 0, 394 u'Pairable': 1, 395 u'Class': 4718852, 396 u'UUIDs': [u'00001112-0000-1000-8000-00805f9b34fb', 397 u'00001801-0000-1000-8000-00805f9b34fb', 398 u'0000110a-0000-1000-8000-00805f9b34fb', 399 u'0000111f-0000-1000-8000-00805f9b34fb', 400 u'00001200-0000-1000-8000-00805f9b34fb', 401 u'00001800-0000-1000-8000-00805f9b34fb']} 402 403 @return the properties as a dictionary on success, 404 the value False otherwise. 405 406 """ 407 return json.loads(self._proxy.get_adapter_properties()) 408 409 410 @proxy_thread_safe 411 def read_version(self): 412 """Read the version of the management interface from the Kernel. 413 414 @return the version as a tuple of: 415 ( version, revision ) 416 417 """ 418 return json.loads(self._proxy.read_version()) 419 420 421 @proxy_thread_safe 422 def read_supported_commands(self): 423 """Read the set of supported commands from the Kernel. 424 425 @return set of supported commands as arrays in a tuple of: 426 ( commands, events ) 427 428 """ 429 return json.loads(self._proxy.read_supported_commands()) 430 431 432 @proxy_thread_safe 433 def read_index_list(self): 434 """Read the list of currently known controllers from the Kernel. 435 436 @return array of controller indexes. 437 438 """ 439 return json.loads(self._proxy.read_index_list()) 440 441 442 @proxy_thread_safe 443 def read_info(self): 444 """Read the adapter information from the Kernel. 445 446 An example of the adapter information looks like 447 [u'6C:29:95:1A:D4:6F', 6, 2, 65535, 2769, 4718852, u'Chromebook', u''] 448 449 @return the information as a tuple of: 450 ( address, bluetooth_version, manufacturer_id, 451 supported_settings, current_settings, class_of_device, 452 name, short_name ) 453 454 """ 455 return json.loads(self._proxy.read_info()) 456 457 458 @proxy_thread_safe 459 def add_device(self, address, address_type, action): 460 """Add a device to the Kernel action list. 461 462 @param address: Address of the device to add. 463 @param address_type: Type of device in @address. 464 @param action: Action to take. 465 466 @return tuple of ( address, address_type ) on success, 467 None on failure. 468 469 """ 470 return json.loads(self._proxy.add_device(address, address_type, action)) 471 472 473 @proxy_thread_safe 474 def remove_device(self, address, address_type): 475 """Remove a device from the Kernel action list. 476 477 @param address: Address of the device to remove. 478 @param address_type: Type of device in @address. 479 480 @return tuple of ( address, address_type ) on success, 481 None on failure. 482 483 """ 484 return json.loads(self._proxy.remove_device(address, address_type)) 485 486 def _decode_json_base64(self, data): 487 """Load serialized JSON and then base64 decode it 488 489 Required to handle non-ascii data 490 @param data: data to be JSON and base64 decode 491 492 @return : JSON and base64 decoded data 493 494 495 """ 496 logging.debug("_decode_json_base64 raw data is %s", data) 497 json_encoded = json.loads(data) 498 logging.debug("JSON encoded data is %s", json_encoded) 499 base64_decoded = utils.base64_recursive_decode(json_encoded) 500 logging.debug("base64 decoded data is %s", base64_decoded) 501 return base64_decoded 502 503 504 @proxy_thread_safe 505 def get_devices(self): 506 """Read information about remote devices known to the adapter. 507 508 An example of the device information of RN-42 looks like 509 [{u'Name': u'RNBT-A96F', 510 u'Alias': u'RNBT-A96F', 511 u'Adapter': u'/org/bluez/hci0', 512 u'LegacyPairing': 0, 513 u'Paired': 1, 514 u'Connected': 0, 515 u'UUIDs': [u'00001124-0000-1000-8000-00805f9b34fb'], 516 u'Address': u'00:06:66:75:A9:6F', 517 u'Icon': u'input-mouse', 518 u'Class': 1408, 519 u'Trusted': 1, 520 u'Blocked': 0}] 521 522 @return the properties of each device as an array of 523 dictionaries on success, the value False otherwise. 524 525 """ 526 return json.loads(self._proxy.get_devices()) 527 528 529 @proxy_thread_safe 530 def get_device_property(self, address, prop_name): 531 """Read a property of BT device by directly querying device dbus object 532 533 @param address: Address of the device to query 534 @param prop_name: Property to be queried 535 536 @return The property if device is found and has property, None otherwise 537 """ 538 539 prop_val = self._proxy.get_device_property(address, prop_name) 540 541 # Handle dbus error case returned by dbus_safe decorator 542 if prop_val is None: 543 return None 544 545 # Decode and return property value 546 return json.loads(prop_val) 547 548 549 @proxy_thread_safe 550 def get_battery_property(self, address, prop_name): 551 """Read a property of battery by directly querying the dbus object 552 553 @param address: Address of the device to query 554 @param prop_name: Property to be queried 555 556 @return The property if battery is found and has property, 557 None otherwise 558 """ 559 560 return self._proxy.get_battery_property(address, prop_name) 561 562 @proxy_thread_safe 563 def get_dev_info(self): 564 """Read raw HCI device information. 565 566 An example of the device information looks like: 567 [0, u'hci0', u'6C:29:95:1A:D4:6F', 13, 0, 1, 581900950526, 52472, 7, 568 32768, 1021, 5, 96, 6, 0, 0, 151, 151, 0, 0, 0, 0, 1968, 12507] 569 570 @return tuple of (index, name, address, flags, device_type, bus_type, 571 features, pkt_type, link_policy, link_mode, 572 acl_mtu, acl_pkts, sco_mtu, sco_pkts, 573 err_rx, err_tx, cmd_tx, evt_rx, acl_tx, acl_rx, 574 sco_tx, sco_rx, byte_rx, byte_tx) on success, 575 None on failure. 576 577 """ 578 return json.loads(self._proxy.get_dev_info()) 579 580 581 @proxy_thread_safe 582 def get_supported_capabilities(self): 583 """ Get the supported_capabilities of the adapter 584 @returns (capabilities,None) on success (None, <error>) on failure 585 """ 586 capabilities, error = self._proxy.get_supported_capabilities() 587 return (json.loads(capabilities), error) 588 589 590 @proxy_thread_safe 591 def register_profile(self, path, uuid, options): 592 """Register new profile (service). 593 594 @param path: Path to the profile object. 595 @param uuid: Service Class ID of the service as string. 596 @param options: Dictionary of options for the new service, compliant 597 with BlueZ D-Bus Profile API standard. 598 599 @return True on success, False otherwise. 600 601 """ 602 return self._proxy.register_profile(path, uuid, options) 603 604 605 @proxy_thread_safe 606 def has_device(self, address): 607 """Checks if the device with a given address exists. 608 609 @param address: Address of the device. 610 611 @returns: True if there is a device with that address. 612 False otherwise. 613 614 """ 615 return self._proxy.has_device(address) 616 617 618 @proxy_thread_safe 619 def device_is_paired(self, address): 620 """Checks if a device is paired. 621 622 @param address: address of the device. 623 624 @returns: True if device is paired. False otherwise. 625 626 """ 627 return self._proxy.device_is_paired(address) 628 629 630 @proxy_thread_safe 631 def device_services_resolved(self, address): 632 """Checks if services are resolved for a device. 633 634 @param address: address of the device. 635 636 @returns: True if services are resolved. False otherwise. 637 638 """ 639 return self._proxy.device_services_resolved(address) 640 641 642 @proxy_thread_safe 643 def set_trusted(self, address, trusted=True): 644 """Set the device trusted. 645 646 @param address: The bluetooth address of the device. 647 @param trusted: True or False indicating whether to set trusted or not. 648 649 @returns: True if successful. False otherwise. 650 651 """ 652 return self._proxy.set_trusted(address, trusted) 653 654 655 @proxy_thread_safe 656 def pair_legacy_device(self, address, pin, trusted, timeout): 657 """Pairs a device with a given pin code. 658 659 Registers an agent who handles pin code request and 660 pairs a device with known pin code. 661 662 @param address: Address of the device to pair. 663 @param pin: The pin code of the device to pair. 664 @param trusted: indicating whether to set the device trusted. 665 @param timeout: The timeout in seconds for pairing. 666 667 @returns: True on success. False otherwise. 668 669 """ 670 return self._proxy.pair_legacy_device(address, pin, trusted, timeout) 671 672 673 @proxy_thread_safe 674 def remove_device_object(self, address): 675 """Removes a device object and the pairing information. 676 677 Calls RemoveDevice method to remove remote device 678 object and the pairing information. 679 680 @param address: address of the device to unpair. 681 682 @returns: True on success. False otherwise. 683 684 """ 685 return self._proxy.remove_device_object(address) 686 687 688 @proxy_thread_safe 689 def connect_device(self, address): 690 """Connects a device. 691 692 Connects a device if it is not connected. 693 694 @param address: Address of the device to connect. 695 696 @returns: True on success. False otherwise. 697 698 """ 699 return self._proxy.connect_device(address) 700 701 702 @proxy_thread_safe 703 def device_is_connected(self, address): 704 """Checks if a device is connected. 705 706 @param address: Address of the device to check if it is connected. 707 708 @returns: True if device is connected. False otherwise. 709 710 """ 711 return self._proxy.device_is_connected(address) 712 713 714 @proxy_thread_safe 715 def disconnect_device(self, address): 716 """Disconnects a device. 717 718 Disconnects a device if it is connected. 719 720 @param address: Address of the device to disconnect. 721 722 @returns: True on success. False otherwise. 723 724 """ 725 return self._proxy.disconnect_device(address) 726 727 728 @proxy_thread_safe 729 def btmon_start(self): 730 """Start btmon monitoring.""" 731 self._proxy.btmon_start() 732 733 734 @proxy_thread_safe 735 def btmon_stop(self): 736 """Stop btmon monitoring.""" 737 self._proxy.btmon_stop() 738 739 740 @proxy_thread_safe 741 def btmon_get(self, search_str='', start_str=''): 742 """Get btmon output contents. 743 744 @param search_str: only lines with search_str would be kept. 745 @param start_str: all lines before the occurrence of start_str would be 746 filtered. 747 748 @returns: the recorded btmon output. 749 750 """ 751 return self._proxy.btmon_get(search_str, start_str) 752 753 754 @proxy_thread_safe 755 def btmon_find(self, pattern_str): 756 """Find if a pattern string exists in btmon output. 757 758 @param pattern_str: the pattern string to find. 759 760 @returns: True on success. False otherwise. 761 762 """ 763 return self._proxy.btmon_find(pattern_str) 764 765 766 @proxy_thread_safe 767 def advmon_check_manager_interface_exist(self): 768 """Check if AdvertisementMonitorManager1 interface is available. 769 770 @returns: True if Manager interface is available, False otherwise. 771 772 """ 773 return self._proxy.advmon_check_manager_interface_exist() 774 775 776 @proxy_thread_safe 777 def advmon_read_supported_types(self): 778 """Read the Advertisement Monitor supported monitor types. 779 780 @returns: List of supported advertisement monitor types. 781 782 """ 783 return self._proxy.advmon_read_supported_types() 784 785 786 @proxy_thread_safe 787 def advmon_read_supported_features(self): 788 """Read the Advertisement Monitor supported features. 789 790 @returns: List of supported advertisement monitor features. 791 792 """ 793 return self._proxy.advmon_read_supported_features() 794 795 796 @proxy_thread_safe 797 def advmon_create_app(self): 798 """Create an advertisement monitor app. 799 800 @returns: app id, once the app is created. 801 802 """ 803 return self._proxy.advmon_create_app() 804 805 806 @proxy_thread_safe 807 def advmon_exit_app(self, app_id): 808 """Exit an advertisement monitor app. 809 810 @param app_id: the app id. 811 812 @returns: True on success, False otherwise. 813 814 """ 815 return self._proxy.advmon_exit_app(app_id) 816 817 818 @proxy_thread_safe 819 def advmon_kill_app(self, app_id): 820 """Kill an advertisement monitor app by sending SIGKILL. 821 822 @param app_id: the app id. 823 824 @returns: True on success, False otherwise. 825 826 """ 827 return self._proxy.advmon_kill_app(app_id) 828 829 830 @proxy_thread_safe 831 def advmon_register_app(self, app_id): 832 """Register an advertisement monitor app. 833 834 @param app_id: the app id. 835 836 @returns: True on success, False otherwise. 837 838 """ 839 return self._proxy.advmon_register_app(app_id) 840 841 842 @proxy_thread_safe 843 def advmon_unregister_app(self, app_id): 844 """Unregister an advertisement monitor app. 845 846 @param app_id: the app id. 847 848 @returns: True on success, False otherwise. 849 850 """ 851 return self._proxy.advmon_unregister_app(app_id) 852 853 854 @proxy_thread_safe 855 def advmon_add_monitor(self, app_id, monitor_data): 856 """Create an Advertisement Monitor object. 857 858 @param app_id: the app id. 859 @param monitor_data: the list containing monitor type, RSSI filter 860 values and patterns. 861 862 @returns: monitor id, once the monitor is created, None otherwise. 863 864 """ 865 return self._proxy.advmon_add_monitor(app_id, monitor_data) 866 867 868 @proxy_thread_safe 869 def advmon_remove_monitor(self, app_id, monitor_id): 870 """Remove the Advertisement Monitor object. 871 872 @param app_id: the app id. 873 @param monitor_id: the monitor id. 874 875 @returns: True on success, False otherwise. 876 877 """ 878 return self._proxy.advmon_remove_monitor(app_id, monitor_id) 879 880 881 @proxy_thread_safe 882 def advmon_get_event_count(self, app_id, monitor_id, event): 883 """Read the count of a particular event on the given monitor. 884 885 @param app_id: the app id. 886 @param monitor_id: the monitor id. 887 @param event: name of the specific event or 'All' for all events. 888 889 @returns: count of the specific event or dict of counts of all events. 890 891 """ 892 return self._proxy.advmon_get_event_count(app_id, monitor_id, event) 893 894 895 @proxy_thread_safe 896 def advmon_reset_event_count(self, app_id, monitor_id, event): 897 """Reset the count of a particular event on the given monitor. 898 899 @param app_id: the app id. 900 @param monitor_id: the monitor id. 901 @param event: name of the specific event or 'All' for all events. 902 903 @returns: True on success, False otherwise. 904 905 """ 906 return self._proxy.advmon_reset_event_count(app_id, monitor_id, event) 907 908 @proxy_thread_safe 909 def advmon_set_target_devices(self, app_id, monitor_id, devices): 910 """Set the target devices to the given monitor. 911 912 DeviceFound and DeviceLost will only be counted if it is triggered by a 913 target device. 914 915 @param app_id: the app id. 916 @param monitor_id: the monitor id. 917 @param devices: a list of devices in MAC address 918 919 @returns: True on success, False otherwise. 920 921 """ 922 return self._proxy.advmon_set_target_devices(app_id, monitor_id, 923 devices) 924 925 @proxy_thread_safe 926 def advmon_interleave_scan_logger_start(self): 927 """ Start interleave logger recording 928 """ 929 self._proxy.advmon_interleave_scan_logger_start() 930 931 @proxy_thread_safe 932 def advmon_interleave_scan_logger_stop(self): 933 """ Stop interleave logger recording 934 935 @returns: True if logs were successfully collected, 936 False otherwise. 937 938 """ 939 return self._proxy.advmon_interleave_scan_logger_stop() 940 941 @proxy_thread_safe 942 def advmon_interleave_scan_logger_get_records(self): 943 """ Get records in previous log collections 944 945 @returns: a list of records, where each item is a record of 946 interleave |state| and the |time| the state starts. 947 |state| could be {'no filter', 'allowlist'} 948 |time| is system time in sec 949 950 """ 951 return self._proxy.advmon_interleave_scan_logger_get_records() 952 953 @proxy_thread_safe 954 def advmon_interleave_scan_logger_get_cancel_events(self): 955 """ Get cancel events in previous log collections 956 957 @returns: a list of cancel |time| when a interleave cancel event log 958 was found. 959 |time| is system time in sec 960 961 """ 962 return self._proxy.advmon_interleave_scan_logger_get_cancel_events() 963 964 @proxy_thread_safe 965 def advmon_interleave_scan_get_durations(self): 966 """Get durations of allowlist scan and no filter scan 967 968 @returns: a dict of {'allowlist': allowlist_duration, 969 'no filter': no_filter_duration}, 970 or None if something went wrong 971 """ 972 return self._proxy.get_advmon_interleave_durations() 973 974 @proxy_thread_safe 975 def messages_start(self): 976 """Start messages monitoring.""" 977 self._proxy.messages_start() 978 979 @proxy_thread_safe 980 def messages_stop(self): 981 """Stop messages monitoring. 982 983 @returns: True if logs were successfully gathered since logging started, 984 else False 985 """ 986 return self._proxy.messages_stop() 987 988 @proxy_thread_safe 989 def messages_find(self, pattern_str): 990 """Find if a pattern string exists in messages output. 991 992 @param pattern_str: the pattern string to find. 993 994 @returns: True on success. False otherwise. 995 996 """ 997 return self._proxy.messages_find(pattern_str) 998 999 @proxy_thread_safe 1000 def clean_bluetooth_kernel_log(self, log_level=7): 1001 """Remove Bluetooth kernel logs in /var/log/messages with loglevel 1002 equal to or greater than |log_level| 1003 1004 @param log_level: int in range [0..7] 1005 """ 1006 self._proxy.clean_bluetooth_kernel_log(log_level) 1007 1008 @proxy_thread_safe 1009 def register_advertisement(self, advertisement_data): 1010 """Register an advertisement. 1011 1012 Note that rpc supports only conformable types. Hence, a 1013 dict about the advertisement is passed as a parameter such 1014 that the advertisement object could be contructed on the host. 1015 1016 @param advertisement_data: a dict of the advertisement for 1017 the adapter to register. 1018 1019 @returns: True on success. False otherwise. 1020 1021 """ 1022 return self._proxy.register_advertisement(advertisement_data) 1023 1024 1025 @proxy_thread_safe 1026 def unregister_advertisement(self, advertisement_data): 1027 """Unregister an advertisement. 1028 1029 @param advertisement_data: a dict of the advertisement to unregister. 1030 1031 @returns: True on success. False otherwise. 1032 1033 """ 1034 return self._proxy.unregister_advertisement(advertisement_data) 1035 1036 1037 @proxy_thread_safe 1038 def set_advertising_intervals(self, min_adv_interval_ms, 1039 max_adv_interval_ms): 1040 """Set advertising intervals. 1041 1042 @param min_adv_interval_ms: the min advertising interval in ms. 1043 @param max_adv_interval_ms: the max advertising interval in ms. 1044 1045 @returns: True on success. False otherwise. 1046 1047 """ 1048 return self._proxy.set_advertising_intervals(min_adv_interval_ms, 1049 max_adv_interval_ms) 1050 1051 1052 @proxy_thread_safe 1053 def get_advertisement_property(self, adv_path, prop_name): 1054 """Grab property of an advertisement registered on the DUT 1055 1056 The service on the DUT registers a dbus object and holds it. During the 1057 test, some properties on the object may change, so this allows the test 1058 access to the properties at run-time. 1059 1060 @param adv_path: string path of the dbus object 1061 @param prop_name: string name of the property required 1062 1063 @returns: the value of the property in standard (non-dbus) type if the 1064 property exists, else None 1065 """ 1066 1067 return self._proxy.get_advertisement_property(adv_path, prop_name) 1068 1069 @proxy_thread_safe 1070 def get_advertising_manager_property(self, prop_name): 1071 """Grab property of the bluez advertising manager 1072 1073 This allows us to understand the DUT's advertising capabilities, for 1074 instance the maximum number of advertising instances supported, so that 1075 we can test these capabilities. 1076 1077 @param adv_path: string path of the dbus object 1078 @param prop_name: string name of the property required 1079 1080 @returns: the value of the property in standard (non-dbus) type if the 1081 property exists, else None 1082 """ 1083 1084 return self._proxy.get_advertising_manager_property(prop_name) 1085 1086 @proxy_thread_safe 1087 def reset_advertising(self): 1088 """Reset advertising. 1089 1090 This includes unregister all advertisements, reset advertising 1091 intervals, and disable advertising. 1092 1093 @returns: True on success. False otherwise. 1094 1095 """ 1096 return self._proxy.reset_advertising() 1097 1098 1099 @proxy_thread_safe 1100 def create_audio_record_directory(self, audio_record_dir): 1101 """Create the audio recording directory. 1102 1103 @param audio_record_dir: the audio recording directory 1104 1105 @returns: True on success. False otherwise. 1106 """ 1107 return self._proxy.create_audio_record_directory(audio_record_dir) 1108 1109 @proxy_thread_safe 1110 def get_audio_thread_summary(self): 1111 """Dumps audio thread info. 1112 1113 @returns: a list of cras audio information. 1114 """ 1115 return self._proxy.get_audio_thread_summary() 1116 1117 @proxy_thread_safe 1118 def get_device_id_from_node_type(self, node_type, is_input): 1119 """Gets device id from node type. 1120 1121 @param node_type: a node type defined in CRAS_NODE_TYPES. 1122 @param is_input: True if the node is input. False otherwise. 1123 1124 @returns: a string for device id. 1125 """ 1126 return self._proxy.get_device_id_from_node_type(node_type, is_input) 1127 1128 @proxy_thread_safe 1129 def start_capturing_audio_subprocess(self, audio_data, recording_device): 1130 """Start capturing audio in a subprocess. 1131 1132 @param audio_data: the audio test data 1133 @param recording_device: which device recorded the audio, 1134 possible values are 'recorded_by_dut' or 'recorded_by_peer' 1135 1136 @returns: True on success. False otherwise. 1137 """ 1138 return self._proxy.start_capturing_audio_subprocess( 1139 json.dumps(audio_data), recording_device) 1140 1141 1142 @proxy_thread_safe 1143 def stop_capturing_audio_subprocess(self): 1144 """Stop capturing audio. 1145 1146 @returns: True on success. False otherwise. 1147 """ 1148 return self._proxy.stop_capturing_audio_subprocess() 1149 1150 1151 @proxy_thread_safe 1152 def start_playing_audio_subprocess(self, audio_data, pin_device=None): 1153 """Start playing audio in a subprocess. 1154 1155 @param audio_data: the audio test data. 1156 @param pin_device: the device id to play audio. 1157 1158 @returns: True on success. False otherwise. 1159 """ 1160 audio_data = json.dumps(audio_data) 1161 return self._proxy.start_playing_audio_subprocess( 1162 audio_data, pin_device) 1163 1164 1165 @proxy_thread_safe 1166 def stop_playing_audio_subprocess(self): 1167 """Stop playing audio in the subprocess. 1168 1169 @returns: True on success. False otherwise. 1170 """ 1171 return self._proxy.stop_playing_audio_subprocess() 1172 1173 1174 @proxy_thread_safe 1175 def play_audio(self, audio_data): 1176 """Play audio. 1177 1178 It blocks until it has completed playing back the audio. 1179 1180 @param audio_data: the audio test data 1181 1182 @returns: True on success. False otherwise. 1183 """ 1184 return self._proxy.play_audio(json.dumps(audio_data)) 1185 1186 1187 @proxy_thread_safe 1188 def check_audio_frames_legitimacy(self, audio_test_data, recording_device, 1189 recorded_file): 1190 """Get the number of frames in the recorded audio file. 1191 @param audio_test_data: the audio test data 1192 @param recording_device: which device recorded the audio, 1193 possible values are 'recorded_by_dut' or 'recorded_by_peer' 1194 @param recorded_file: the recorded file name 1195 1196 @returns: True if audio frames are legitimate. 1197 """ 1198 return self._proxy.check_audio_frames_legitimacy( 1199 json.dumps(audio_test_data), recording_device, recorded_file) 1200 1201 1202 @proxy_thread_safe 1203 def convert_audio_sample_rate(self, input_file, out_file, test_data, 1204 new_rate): 1205 """Convert audio file to new sample rate. 1206 1207 @param input_file: Path to file to upsample. 1208 @param out_file: Path to create upsampled file. 1209 @param test_data: Dictionary with information about file. 1210 @param new_rate: New rate to upsample file to. 1211 1212 @returns: True if upsampling succeeded, False otherwise. 1213 """ 1214 return self._proxy.convert_audio_sample_rate(input_file, out_file, 1215 json.dumps(test_data), 1216 new_rate) 1217 1218 1219 @proxy_thread_safe 1220 def trim_wav_file(self, in_file, out_file, new_duration, test_data, 1221 tolerance=0.1): 1222 """Trim long file to desired length. 1223 1224 Trims audio file to length by cutting out silence from beginning and 1225 end. 1226 1227 @param in_file: Path to audio file to be trimmed. 1228 @param out_file: Path to trimmed audio file to create. 1229 @param new_duration: A float representing the desired duration of 1230 the resulting trimmed file. 1231 @param test_data: Dictionary containing information about the test file. 1232 @param tolerance: (optional) A float representing the allowable 1233 difference between trimmed file length and desired duration 1234 1235 @returns: True if file was trimmed successfully, False otherwise. 1236 """ 1237 return self._proxy.trim_wav_file(in_file, out_file, new_duration, 1238 json.dumps(test_data), tolerance) 1239 1240 1241 @proxy_thread_safe 1242 def unzip_audio_test_data(self, tar_path, data_dir): 1243 """Unzip audio test data files. 1244 1245 @param tar_path: Path to audio test data tarball on DUT. 1246 @oaram data_dir: Path to directory where to extract test data directory. 1247 1248 @returns: True if audio test data folder exists, False otherwise. 1249 """ 1250 return self._proxy.unzip_audio_test_data(tar_path, data_dir) 1251 1252 1253 @proxy_thread_safe 1254 def convert_raw_to_wav(self, input_file, output_file, test_data): 1255 """Convert raw audio file to wav file. 1256 1257 @oaram input_file: The location of the raw file. 1258 @param output_file: The location to place the resulting wav file. 1259 @param test_data: The data for the file being converted. 1260 1261 @returns: True if conversion was successful, otherwise false. 1262 """ 1263 return self._proxy.convert_raw_to_wav(input_file, output_file, 1264 json.dumps(test_data)) 1265 1266 1267 @proxy_thread_safe 1268 def get_primary_frequencies(self, audio_test_data, recording_device, 1269 recorded_file): 1270 """Get primary frequencies of the audio test file. 1271 1272 @param audio_test_data: the audio test data 1273 @param recording_device: which device recorded the audio, 1274 possible values are 'recorded_by_dut' or 'recorded_by_peer' 1275 @param recorded_file: the recorded file name 1276 1277 @returns: a list of primary frequencies of channels in the audio file 1278 """ 1279 return self._proxy.get_primary_frequencies( 1280 json.dumps(audio_test_data), recording_device, recorded_file) 1281 1282 1283 @proxy_thread_safe 1284 def enable_wbs(self, value): 1285 """Enable or disable wideband speech (wbs) per the value. 1286 1287 @param value: True to enable wbs. 1288 1289 @returns: True if the operation succeeds. 1290 """ 1291 logging.debug('%s wbs', 'enable' if value else 'disable') 1292 return self._proxy.enable_wbs(value) 1293 1294 1295 @proxy_thread_safe 1296 def set_player_playback_status(self, status): 1297 """Set playback status for the registered media player. 1298 1299 @param status: playback status in string. 1300 1301 """ 1302 logging.debug('Set media player playback status to %s', status) 1303 return self._proxy.set_player_playback_status(status) 1304 1305 1306 @proxy_thread_safe 1307 def set_player_position(self, position): 1308 """Set media position for the registered media player. 1309 1310 @param position: position in micro seconds. 1311 1312 """ 1313 logging.debug('Set media player position to %d', position) 1314 return self._proxy.set_player_position(position) 1315 1316 1317 @proxy_thread_safe 1318 def set_player_metadata(self, metadata): 1319 """Set metadata for the registered media player. 1320 1321 @param metadata: dictionary of media metadata. 1322 1323 """ 1324 logging.debug('Set media player album:%s artist:%s title:%s', 1325 metadata.get("album"), metadata.get("artist"), 1326 metadata.get("title")) 1327 return self._proxy.set_player_metadata(metadata) 1328 1329 1330 @proxy_thread_safe 1331 def set_player_length(self, length): 1332 """Set media length for the registered media player. 1333 1334 @param length: length in micro seconds. 1335 1336 """ 1337 logging.debug('Set media player length to %d', length) 1338 return self._proxy.set_player_length(length) 1339 1340 1341 @proxy_thread_safe 1342 def select_input_device(self, device_name): 1343 """Select the audio input device. 1344 1345 @param device_name: the name of the Bluetooth peer device 1346 1347 @returns: True if the operation succeeds. 1348 """ 1349 return self._proxy.select_input_device(device_name) 1350 1351 1352 @proxy_thread_safe 1353 def select_output_node(self, node_type): 1354 """Select the audio output node. 1355 1356 @param node_type: the node type of the Bluetooth peer device 1357 1358 @returns: True if the operation succeeds. 1359 """ 1360 return self._proxy.select_output_node(node_type) 1361 1362 1363 @proxy_thread_safe 1364 def get_selected_output_device_type(self): 1365 """Get the selected audio output node type. 1366 1367 @returns: the node type of the selected output device. 1368 """ 1369 return self._proxy.get_selected_output_device_type() 1370 1371 1372 @proxy_thread_safe 1373 def read_characteristic(self, uuid, address): 1374 """Reads the value of a gatt characteristic. 1375 1376 Reads the current value of a gatt characteristic. 1377 1378 @param uuid: The uuid of the characteristic to read, as a string. 1379 @param address: The MAC address of the remote device. 1380 1381 @returns: A byte array containing the value of the if the uuid/address 1382 was found in the object tree. 1383 None if the uuid/address was not found in the object tree, or 1384 if a DBus exception was raised by the read operation. 1385 1386 """ 1387 value = self._proxy.read_characteristic(uuid, address) 1388 if value is None: 1389 return None 1390 return bytearray(base64.standard_b64decode(value)) 1391 1392 1393 @proxy_thread_safe 1394 def write_characteristic(self, uuid, address, bytes_to_write): 1395 """Performs a write operation on a gatt characteristic. 1396 1397 Writes to a GATT characteristic on a remote device. 1398 1399 @param uuid: The uuid of the characteristic to write to, as a string. 1400 @param address: The MAC address of the remote device, as a string. 1401 @param bytes_to_write: A byte array containing the data to write. 1402 1403 @returns: True if the write operation does not raise an exception. 1404 None if the uuid/address was not found in the object tree, or 1405 if a DBus exception was raised by the write operation. 1406 1407 """ 1408 return self._proxy.write_characteristic( 1409 uuid, address, base64.standard_b64encode(bytes_to_write)) 1410 1411 1412 @proxy_thread_safe 1413 def exchange_messages(self, tx_object_path, rx_object_path, bytes_to_write): 1414 """Performs a write operation on a gatt characteristic and wait for 1415 the response on another characteristic. 1416 1417 @param tx_object_path: the object path of the characteristic to write. 1418 @param rx_object_path: the object path of the characteristic to read. 1419 @param value: A byte array containing the data to write. 1420 1421 @returns: The value of the characteristic to read from. 1422 None if the uuid/address was not found in the object tree, or 1423 if a DBus exception was raised by the write operation. 1424 1425 """ 1426 return self._proxy.exchange_messages( 1427 tx_object_path, rx_object_path, 1428 base64.standard_b64encode(bytes_to_write)) 1429 1430 1431 @proxy_thread_safe 1432 def start_notify(self, object_path, cccd_value): 1433 """Starts the notification session on the gatt characteristic. 1434 1435 @param object_path: the object path of the characteristic. 1436 @param cccd_value: Possible CCCD values include 1437 0x00 - inferred from the remote characteristic's properties 1438 0x01 - notification 1439 0x02 - indication 1440 1441 @returns: True if the operation succeeds. 1442 False if the characteristic is not found, or 1443 if a DBus exception was raised by the operation. 1444 1445 """ 1446 return self._proxy.start_notify(object_path, cccd_value) 1447 1448 1449 @proxy_thread_safe 1450 def stop_notify(self, object_path): 1451 """Stops the notification session on the gatt characteristic. 1452 1453 @param object_path: the object path of the characteristic. 1454 1455 @returns: True if the operation succeeds. 1456 False if the characteristic is not found, or 1457 if a DBus exception was raised by the operation. 1458 1459 """ 1460 return self._proxy.stop_notify(object_path) 1461 1462 1463 @proxy_thread_safe 1464 def is_notifying(self, object_path): 1465 """Is the GATT characteristic in a notifying session? 1466 1467 @param object_path: the object path of the characteristic. 1468 1469 @return True if it is in a notification session. False otherwise. 1470 1471 """ 1472 return self._proxy.is_notifying(object_path) 1473 1474 1475 @proxy_thread_safe 1476 def is_characteristic_path_resolved(self, uuid, address): 1477 """Checks whether a characteristic is in the object tree. 1478 1479 Checks whether a characteristic is curently found in the object tree. 1480 1481 @param uuid: The uuid of the characteristic to search for. 1482 @param address: The MAC address of the device on which to search for 1483 the characteristic. 1484 1485 @returns: True if the characteristic is found, False otherwise. 1486 1487 """ 1488 return self._proxy.is_characteristic_path_resolved(uuid, address) 1489 1490 1491 @proxy_thread_safe 1492 def get_gatt_attributes_map(self, address): 1493 """Return a JSON formated string of the GATT attributes of a device, 1494 keyed by UUID 1495 @param address: a string of the MAC address of the device 1496 1497 @return: JSON formated string, stored the nested structure of the 1498 attributes. Each attribute has 'path' and ['chrcs' | 'descs'], which 1499 store their object path and children respectively. 1500 """ 1501 return self._proxy.get_gatt_attributes_map(address) 1502 1503 1504 @proxy_thread_safe 1505 def get_gatt_service_property(self, object_path, property_name): 1506 """Get property from a service attribute 1507 @param object_path: a string of the object path of the service 1508 @param property_name: a string of a property, ex: 'Value', 'UUID' 1509 1510 @return: the property if success, 1511 None otherwise 1512 """ 1513 return self._proxy.get_gatt_service_property(object_path, property_name) 1514 1515 1516 @proxy_thread_safe 1517 def get_gatt_characteristic_property(self, object_path, property_name): 1518 """Get property from a characteristic attribute 1519 @param object_path: a string of the object path of the characteristic 1520 @param property_name: a string of a property, ex: 'Value', 'UUID' 1521 1522 @return: the property if success, 1523 None otherwise 1524 """ 1525 return self._proxy.get_gatt_characteristic_property(object_path, 1526 property_name) 1527 1528 1529 @proxy_thread_safe 1530 def get_gatt_descriptor_property(self, object_path, property_name): 1531 """Get property from a descriptor attribute 1532 @param object_path: a string of the object path of the descriptor 1533 @param property_name: a string of a property, ex: 'Value', 'UUID' 1534 1535 @return: the property if success, 1536 None otherwise 1537 """ 1538 return self._proxy.get_gatt_descriptor_property(object_path, 1539 property_name) 1540 1541 1542 @proxy_thread_safe 1543 def gatt_characteristic_read_value(self, uuid, object_path): 1544 """Perform method ReadValue on a characteristic attribute 1545 @param uuid: a string of uuid 1546 @param object_path: a string of the object path of the characteristic 1547 1548 @return: base64 string of dbus bytearray 1549 """ 1550 return self._proxy.gatt_characteristic_read_value(uuid, object_path) 1551 1552 1553 @proxy_thread_safe 1554 def gatt_descriptor_read_value(self, uuid, object_path): 1555 """Perform method ReadValue on a descriptor attribute 1556 @param uuid: a string of uuid 1557 @param object_path: a string of the object path of the descriptor 1558 1559 @return: base64 string of dbus bytearray 1560 """ 1561 return self._proxy.gatt_descriptor_read_value(uuid, object_path) 1562 1563 1564 @proxy_thread_safe 1565 def get_gatt_object_path(self, address, uuid): 1566 """Get property from a characteristic attribute 1567 1568 @param address: The MAC address of the remote device. 1569 @param uuid: The uuid of the attribute. 1570 1571 @return: the object path of the attribute if success, 1572 none otherwise 1573 1574 """ 1575 return self._proxy.get_gatt_object_path(address, uuid) 1576 1577 1578 def copy_logs(self, destination): 1579 """Copy the logs generated by this device to a given location. 1580 1581 @param destination: destination directory for the logs. 1582 1583 """ 1584 self.host.collect_logs(self.XMLRPC_LOG_PATH, destination) 1585 1586 1587 @proxy_thread_safe 1588 def get_connection_info(self, address): 1589 """Get device connection info. 1590 1591 @param address: The MAC address of the device. 1592 1593 @returns: On success, a tuple of: 1594 ( RSSI, transmit_power, max_transmit_power ) 1595 None otherwise. 1596 1597 """ 1598 return self._proxy.get_connection_info(address) 1599 1600 1601 @proxy_thread_safe 1602 def set_discovery_filter(self, filter): 1603 """Set the discovery filter. 1604 1605 @param filter: The discovery filter to set. 1606 1607 @return True on success, False otherwise. 1608 1609 """ 1610 return self._proxy.set_discovery_filter(filter) 1611 1612 1613 @proxy_thread_safe 1614 def set_le_connection_parameters(self, address, parameters): 1615 """Set the LE connection parameters. 1616 1617 @param address: The MAC address of the device. 1618 @param parameters: The LE connection parameters to set. 1619 1620 @return: True on success. False otherwise. 1621 1622 """ 1623 return self._proxy.set_le_connection_parameters(address, parameters) 1624 1625 1626 @proxy_thread_safe 1627 def wait_for_hid_device(self, 1628 device_address, 1629 timeout=None, 1630 sleep_interval=None): 1631 """Wait for hid device with given device address. 1632 1633 Args: 1634 device_address: Peripheral Address 1635 timeout: maximum number of seconds to wait 1636 sleep_interval: time to sleep between polls 1637 1638 Returns: 1639 True if hid device is found. 1640 """ 1641 return self._proxy.wait_for_hid_device(device_address, timeout, 1642 sleep_interval) 1643 1644 1645 @proxy_thread_safe 1646 def bt_caused_last_resume(self): 1647 """Checks if last resume from suspend was caused by bluetooth 1648 1649 @return: True if BT wake path was cause of resume, False otherwise 1650 """ 1651 1652 return self._proxy.bt_caused_last_resume() 1653 1654 @proxy_thread_safe 1655 def find_last_suspend_via_powerd_logs(self): 1656 """Finds the last suspend attempt via powerd logs. 1657 1658 @return: Tuple (suspend start time, suspend end time, suspend result) or 1659 None 1660 """ 1661 info = self._proxy.find_last_suspend_via_powerd_logs() 1662 1663 # Currently, we get the date back in string format due to python2/3 1664 # inconsistencies. We can get rid of this once everything is running 1665 # python3 (hopefully) 1666 # TODO - Revisit converting date to string and back in this method 1667 if info: 1668 start_date = datetime.strptime(info[0], self.STANDARD_DATE_FORMAT) 1669 end_date = datetime.strptime(info[1], self.STANDARD_DATE_FORMAT) 1670 ret = info[2] 1671 1672 return (start_date, end_date, ret) 1673 1674 return None 1675 1676 @proxy_thread_safe 1677 def do_suspend(self, seconds, expect_bt_wake): 1678 """Suspend DUT using the power manager. 1679 1680 @param seconds: The number of seconds to suspend the device. 1681 @param expect_bt_wake: Whether we expect bluetooth to wake us from 1682 suspend. If true, we expect this resume will occur early 1683 """ 1684 1685 # Do not retry this RPC if it fails or times out 1686 return self._proxy.do_suspend(seconds, expect_bt_wake, __no_retry=True) 1687 1688 1689 @proxy_thread_safe 1690 def get_wlan_vid_pid(self): 1691 """ Return vendor id and product id of the wlan chip on BT/WiFi module 1692 1693 @returns: (vid,pid) on success; (None,None) on failure 1694 """ 1695 return self._proxy.get_wlan_vid_pid() 1696 1697 @proxy_thread_safe 1698 def get_bt_transport(self): 1699 """ Return the transport used by Bluetooth module 1700 1701 @returns: USB/UART/SDIO on success; None on failure 1702 """ 1703 return self._proxy.get_bt_transport() 1704 1705 @proxy_thread_safe 1706 def get_bt_module_name(self): 1707 """ Return bluetooth module name for non-USB devices 1708 1709 @returns: Name of the Bluetooth module (or string read from device on 1710 success); '' on failure 1711 """ 1712 return self._proxy.get_bt_module_name() 1713 1714 @proxy_thread_safe 1715 def get_chipset_name(self): 1716 """ Get the name of BT/WiFi chipset on this host 1717 1718 @returns chipset name if successful else '' 1719 """ 1720 return self._proxy.get_chipset_name() 1721 1722 @proxy_thread_safe 1723 def get_device_utc_time(self): 1724 """ Get the current device time in UTC. """ 1725 return datetime.strptime(self._proxy.get_device_utc_time(), 1726 self.STANDARD_DATE_FORMAT) 1727 1728 @proxy_thread_safe 1729 def get_bt_usb_disconnect_str(self): 1730 """ Return the expected log error on USB disconnect 1731 1732 Locate the descriptor that will be used from the list of all usb 1733 descriptors associated with our bluetooth chip, and format into the 1734 expected string error for USB disconnect 1735 1736 @returns: string representing expected usb disconnect log entry if usb 1737 device could be identified, None otherwise 1738 """ 1739 return self._proxy.get_bt_usb_disconnect_str() 1740 1741 @proxy_thread_safe 1742 def close(self, close_host=True): 1743 """Tear down state associated with the client. 1744 1745 @param close_host: If True, shut down the xml rpc server by closing the 1746 underlying host object (which also shuts down all other xml rpc 1747 servers running on the DUT). Otherwise, only shut down the 1748 bluetooth device xml rpc server, which can be desirable if the host 1749 object and/or other xml rpc servers need to be used afterwards. 1750 """ 1751 # Turn off the discoverable flag since it may affect future tests. 1752 self._proxy.set_discoverable(False) 1753 # Reset the adapter and leave it on. 1754 self._proxy.reset_on() 1755 # This kills the RPC server. 1756 if close_host: 1757 self.host.close() 1758 elif hasattr(self, '_bt_direct_proxy'): 1759 self.host.rpc_server_tracker.disconnect( 1760 constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT) 1761 1762 1763 @proxy_thread_safe 1764 def policy_get_service_allow_list(self): 1765 """Get the service allow list for enterprise policy. 1766 1767 @returns: array of strings representing the allowed service UUIDs. 1768 """ 1769 return self._proxy.policy_get_service_allow_list() 1770 1771 1772 @proxy_thread_safe 1773 def policy_set_service_allow_list(self, uuids): 1774 """Get the service allow list for enterprise policy. 1775 1776 @param uuids: a string representing the uuids 1777 e.g., "0x1234,0xabcd" or "" 1778 1779 @returns: (True, '') on success, (False, '<error>') on failure 1780 """ 1781 return self._proxy.policy_set_service_allow_list(uuids) 1782 1783 @proxy_thread_safe 1784 def policy_get_device_affected(self, device_address): 1785 """Get if the device is affected by enterprise policy. 1786 1787 @param device_address: address of the device 1788 e.g. '6C:29:95:1A:D4:6F' 1789 1790 @returns: True if the device is affected by the enterprise policy. 1791 False if not. None if the device is not found. 1792 """ 1793 return self._proxy.policy_get_device_affected(device_address) 1794