# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import json from autotest_lib.client.cros import constants from autotest_lib.server import autotest class BluetoothDevice(object): """BluetoothDevice is a thin layer of logic over a remote DUT. The Autotest host object representing the remote DUT, passed to this class on initialization, can be accessed from its host property. """ XMLRPC_BRINGUP_TIMEOUT_SECONDS = 60 XMLRPC_LOG_PATH = '/var/log/bluetooth_xmlrpc_device.log' def __init__(self, device_host): """Construct a BluetoothDevice. @param device_host: host object representing a remote host. """ self.host = device_host # Make sure the client library is on the device so that the proxy code # is there when we try to call it. client_at = autotest.Autotest(self.host) client_at.install() # Start up the XML-RPC proxy on the client. self._proxy = self.host.rpc_server_tracker.xmlrpc_connect( constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_COMMAND, constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT, command_name= constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_CLEANUP_PATTERN, ready_test_name= constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_READY_METHOD, timeout_seconds=self.XMLRPC_BRINGUP_TIMEOUT_SECONDS, logfile=self.XMLRPC_LOG_PATH) # Get some static information about the bluetooth adapter. properties = self.get_adapter_properties() self.bluez_version = properties.get('Name') self.address = properties.get('Address') self.bluetooth_class = properties.get('Class') self.UUIDs = properties.get('UUIDs') def start_bluetoothd(self): """start bluetoothd. @returns: True if bluetoothd is started correctly. False otherwise. """ return self._proxy.start_bluetoothd() def stop_bluetoothd(self): """stop bluetoothd. @returns: True if bluetoothd is stopped correctly. False otherwise. """ return self._proxy.stop_bluetoothd() def is_bluetoothd_running(self): """Is bluetoothd running? @returns: True if bluetoothd is running """ return self._proxy.is_bluetoothd_running() def reset_on(self): """Reset the adapter and settings and power up the adapter. @return True on success, False otherwise. """ return self._proxy.reset_on() def reset_off(self): """Reset the adapter and settings, leave the adapter powered off. @return True on success, False otherwise. """ return self._proxy.reset_off() def has_adapter(self): """@return True if an adapter is present, False if not.""" return self._proxy.has_adapter() def set_powered(self, powered): """Set the adapter power state. @param powered: adapter power state to set (True or False). @return True on success, False otherwise. """ return self._proxy.set_powered(powered) def is_powered_on(self): """Is the adapter powered on? @returns: True if the adapter is powered on """ properties = self.get_adapter_properties() return bool(properties.get(u'Powered')) def get_hci(self): """Get hci of the adapter; normally, it is 'hci0'. @returns: the hci name of the adapter. """ dev_info = self.get_dev_info() hci = (dev_info[1] if isinstance(dev_info, list) and len(dev_info) > 1 else None) return hci def get_address(self): """Get the bluetooth address of the adapter. An example of the bluetooth address of the adapter: '6C:29:95:1A:D4:6F' @returns: the bluetooth address of the adapter. """ return self.address def get_bluez_version(self): """Get bluez version. An exmaple of bluez version: 'BlueZ 5.39' @returns: the bluez version """ return self.bluez_version def get_bluetooth_class(self): """Get the bluetooth class of the adapter. An example of the bluetooth class of a chromebook: 4718852 @returns: the bluetooth class. """ return self.bluetooth_class def get_UUIDs(self): """Get the UUIDs. An example of UUIDs: [u'00001112-0000-1000-8000-00805f9b34fb', u'00001801-0000-1000-8000-00805f9b34fb', u'0000110a-0000-1000-8000-00805f9b34fb', u'0000111f-0000-1000-8000-00805f9b34fb', u'00001200-0000-1000-8000-00805f9b34fb', u'00001800-0000-1000-8000-00805f9b34fb'] @returns: the list of the UUIDs. """ return self.UUIDs def set_discoverable(self, discoverable): """Set the adapter discoverable state. @param discoverable: adapter discoverable state to set (True or False). @return True on success, False otherwise. """ return self._proxy.set_discoverable(discoverable) def is_discoverable(self): """Is the adapter in the discoverable state? @return True if discoverable. False otherwise. """ properties = self.get_adapter_properties() return properties.get('Discoverable') == 1 def set_pairable(self, pairable): """Set the adapter pairable state. @param pairable: adapter pairable state to set (True or False). @return True on success, False otherwise. """ return self._proxy.set_pairable(pairable) def is_pairable(self): """Is the adapter in the pairable state? @return True if pairable. False otherwise. """ properties = self.get_adapter_properties() return properties.get('Pairable') == 1 def get_adapter_properties(self): """Read the adapter properties from the Bluetooth Daemon. An example of the adapter properties looks like {u'Name': u'BlueZ 5.35', u'Alias': u'Chromebook', u'Modalias': u'bluetooth:v00E0p2436d0400', u'Powered': 1, u'DiscoverableTimeout': 180, u'PairableTimeout': 0, u'Discoverable': 0, u'Address': u'6C:29:95:1A:D4:6F', u'Discovering': 0, u'Pairable': 1, u'Class': 4718852, u'UUIDs': [u'00001112-0000-1000-8000-00805f9b34fb', u'00001801-0000-1000-8000-00805f9b34fb', u'0000110a-0000-1000-8000-00805f9b34fb', u'0000111f-0000-1000-8000-00805f9b34fb', u'00001200-0000-1000-8000-00805f9b34fb', u'00001800-0000-1000-8000-00805f9b34fb']} @return the properties as a dictionary on success, the value False otherwise. """ return json.loads(self._proxy.get_adapter_properties()) def read_version(self): """Read the version of the management interface from the Kernel. @return the version as a tuple of: ( version, revision ) """ return json.loads(self._proxy.read_version()) def read_supported_commands(self): """Read the set of supported commands from the Kernel. @return set of supported commands as arrays in a tuple of: ( commands, events ) """ return json.loads(self._proxy.read_supported_commands()) def read_index_list(self): """Read the list of currently known controllers from the Kernel. @return array of controller indexes. """ return json.loads(self._proxy.read_index_list()) def read_info(self): """Read the adapter information from the Kernel. An example of the adapter information looks like [u'6C:29:95:1A:D4:6F', 6, 2, 65535, 2769, 4718852, u'Chromebook', u''] @return the information as a tuple of: ( address, bluetooth_version, manufacturer_id, supported_settings, current_settings, class_of_device, name, short_name ) """ return json.loads(self._proxy.read_info()) def add_device(self, address, address_type, action): """Add a device to the Kernel action list. @param address: Address of the device to add. @param address_type: Type of device in @address. @param action: Action to take. @return tuple of ( address, address_type ) on success, None on failure. """ return json.loads(self._proxy.add_device(address, address_type, action)) def remove_device(self, address, address_type): """Remove a device from the Kernel action list. @param address: Address of the device to remove. @param address_type: Type of device in @address. @return tuple of ( address, address_type ) on success, None on failure. """ return json.loads(self._proxy.remove_device(address, address_type)) def get_devices(self): """Read information about remote devices known to the adapter. An example of the device information of RN-42 looks like [{u'Name': u'RNBT-A96F', u'Alias': u'RNBT-A96F', u'Adapter': u'/org/bluez/hci0', u'LegacyPairing': 0, u'Paired': 1, u'Connected': 0, u'UUIDs': [u'00001124-0000-1000-8000-00805f9b34fb'], u'Address': u'00:06:66:75:A9:6F', u'Icon': u'input-mouse', u'Class': 1408, u'Trusted': 1, u'Blocked': 0}] @return the properties of each device as an array of dictionaries on success, the value False otherwise. """ return json.loads(self._proxy.get_devices()) def get_device_properties(self, address): """Read information about remote devices known to the adapter. An example of the device information of RN-42 looks like @param address: Address of the device to pair. @param pin: The pin code of the device to pair. @param timeout: The timeout in seconds for pairing. @returns: a dictionary of device properties of the device on success; an empty dictionary otherwise. """ return json.loads(self._proxy.get_device_by_address(address)) for device in self.get_devices(): if device.get['Address'] == address: return device return dict() def start_discovery(self): """Start discovery of remote devices. Obtain the discovered device information using get_devices(), called stop_discovery() when done. @return True on success, False otherwise. """ return self._proxy.start_discovery() def stop_discovery(self): """Stop discovery of remote devices. @return True on success, False otherwise. """ return self._proxy.stop_discovery() def is_discovering(self): """Is it discovering? @return True if it is discovering. False otherwise. """ return self.get_adapter_properties().get('Discovering') == 1 def get_dev_info(self): """Read raw HCI device information. An example of the device information looks like: [0, u'hci0', u'6C:29:95:1A:D4:6F', 13, 0, 1, 581900950526, 52472, 7, 32768, 1021, 5, 96, 6, 0, 0, 151, 151, 0, 0, 0, 0, 1968, 12507] @return tuple of (index, name, address, flags, device_type, bus_type, features, pkt_type, link_policy, link_mode, acl_mtu, acl_pkts, sco_mtu, sco_pkts, err_rx, err_tx, cmd_tx, evt_rx, acl_tx, acl_rx, sco_tx, sco_rx, byte_rx, byte_tx) on success, None on failure. """ return json.loads(self._proxy.get_dev_info()) def register_profile(self, path, uuid, options): """Register new profile (service). @param path: Path to the profile object. @param uuid: Service Class ID of the service as string. @param options: Dictionary of options for the new service, compliant with BlueZ D-Bus Profile API standard. @return True on success, False otherwise. """ return self._proxy.register_profile(path, uuid, options) def has_device(self, address): """Checks if the device with a given address exists. @param address: Address of the device. @returns: True if there is a device with that address. False otherwise. """ return self._proxy.has_device(address) def device_is_paired(self, address): """Checks if a device is paired. @param address: address of the device. @returns: True if device is paired. False otherwise. """ return self._proxy.device_is_paired(address) def set_trusted(self, address, trusted=True): """Set the device trusted. @param address: The bluetooth address of the device. @param trusted: True or False indicating whether to set trusted or not. @returns: True if successful. False otherwise. """ return self._proxy.set_trusted(address, trusted) def pair_legacy_device(self, address, pin, trusted, timeout): """Pairs a device with a given pin code. Registers an agent who handles pin code request and pairs a device with known pin code. @param address: Address of the device to pair. @param pin: The pin code of the device to pair. @param trusted: indicating whether to set the device trusted. @param timeout: The timeout in seconds for pairing. @returns: True on success. False otherwise. """ return self._proxy.pair_legacy_device(address, pin, trusted, timeout) def remove_device_object(self, address): """Removes a device object and the pairing information. Calls RemoveDevice method to remove remote device object and the pairing information. @param address: address of the device to unpair. @returns: True on success. False otherwise. """ return self._proxy.remove_device_object(address) def connect_device(self, address): """Connects a device. Connects a device if it is not connected. @param address: Address of the device to connect. @returns: True on success. False otherwise. """ return self._proxy.connect_device(address) def device_is_connected(self, address): """Checks if a device is connected. @param address: Address of the device to check if it is connected. @returns: True if device is connected. False otherwise. """ return self._proxy.device_is_connected(address) def disconnect_device(self, address): """Disconnects a device. Disconnects a device if it is connected. @param address: Address of the device to disconnect. @returns: True on success. False otherwise. """ return self._proxy.disconnect_device(address) def btmon_start(self): """Start btmon monitoring.""" self._proxy.btmon_start() def btmon_stop(self): """Stop btmon monitoring.""" self._proxy.btmon_stop() def btmon_get(self, search_str='', start_str=''): """Get btmon output contents. @param search_str: only lines with search_str would be kept. @param start_str: all lines before the occurrence of start_str would be filtered. @returns: the recorded btmon output. """ return self._proxy.btmon_get(search_str, start_str) def btmon_find(self, pattern_str): """Find if a pattern string exists in btmon output. @param pattern_str: the pattern string to find. @returns: True on success. False otherwise. """ return self._proxy.btmon_find(pattern_str) def register_advertisement(self, advertisement_data): """Register an advertisement. Note that rpc supports only conformable types. Hence, a dict about the advertisement is passed as a parameter such that the advertisement object could be contructed on the host. @param advertisement_data: a dict of the advertisement for the adapter to register. @returns: True on success. False otherwise. """ return self._proxy.register_advertisement(advertisement_data) def unregister_advertisement(self, advertisement_data): """Unregister an advertisement. @param advertisement_data: a dict of the advertisement to unregister. @returns: True on success. False otherwise. """ return self._proxy.unregister_advertisement(advertisement_data) def set_advertising_intervals(self, min_adv_interval_ms, max_adv_interval_ms): """Set advertising intervals. @param min_adv_interval_ms: the min advertising interval in ms. @param max_adv_interval_ms: the max advertising interval in ms. @returns: True on success. False otherwise. """ return self._proxy.set_advertising_intervals(min_adv_interval_ms, max_adv_interval_ms) def reset_advertising(self): """Reset advertising. This includes unregister all advertisements, reset advertising intervals, and disable advertising. @returns: True on success. False otherwise. """ return self._proxy.reset_advertising() def copy_logs(self, destination): """Copy the logs generated by this device to a given location. @param destination: destination directory for the logs. """ self.host.collect_logs(self.XMLRPC_LOG_PATH, destination) def close(self): """Tear down state associated with the client.""" # Turn off the discoverable flag since it may affect future tests. self._proxy.set_discoverable(False) # Leave the adapter powered off, but don't do a full reset. self._proxy.set_powered(False) # This kills the RPC server. self.host.close()