Home | History | Annotate | Download | only in bluetooth
      1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import json
      6 
      7 from autotest_lib.client.cros import constants
      8 from autotest_lib.server import autotest
      9 
     10 
     11 class BluetoothDevice(object):
     12     """BluetoothDevice is a thin layer of logic over a remote DUT.
     13 
     14     The Autotest host object representing the remote DUT, passed to this
     15     class on initialization, can be accessed from its host property.
     16 
     17     """
     18 
     19     XMLRPC_BRINGUP_TIMEOUT_SECONDS = 60
     20     XMLRPC_LOG_PATH = '/var/log/bluetooth_xmlrpc_device.log'
     21 
     22     def __init__(self, device_host):
     23         """Construct a BluetoothDevice.
     24 
     25         @param device_host: host object representing a remote host.
     26 
     27         """
     28         self.host = device_host
     29         # Make sure the client library is on the device so that the proxy code
     30         # is there when we try to call it.
     31         client_at = autotest.Autotest(self.host)
     32         client_at.install()
     33         # Start up the XML-RPC proxy on the client.
     34         self._proxy = self.host.rpc_server_tracker.xmlrpc_connect(
     35                 constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_COMMAND,
     36                 constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT,
     37                 command_name=
     38                   constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_CLEANUP_PATTERN,
     39                 ready_test_name=
     40                   constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_READY_METHOD,
     41                 timeout_seconds=self.XMLRPC_BRINGUP_TIMEOUT_SECONDS,
     42                 logfile=self.XMLRPC_LOG_PATH)
     43 
     44         # Get some static information about the bluetooth adapter.
     45         properties = self.get_adapter_properties()
     46         self.bluez_version = properties.get('Name')
     47         self.address = properties.get('Address')
     48         self.bluetooth_class = properties.get('Class')
     49         self.UUIDs = properties.get('UUIDs')
     50 
     51 
     52     def start_bluetoothd(self):
     53         """start bluetoothd.
     54 
     55         @returns: True if bluetoothd is started correctly.
     56                   False otherwise.
     57 
     58         """
     59         return self._proxy.start_bluetoothd()
     60 
     61 
     62     def stop_bluetoothd(self):
     63         """stop bluetoothd.
     64 
     65         @returns: True if bluetoothd is stopped correctly.
     66                   False otherwise.
     67 
     68         """
     69         return self._proxy.stop_bluetoothd()
     70 
     71 
     72     def is_bluetoothd_running(self):
     73         """Is bluetoothd running?
     74 
     75         @returns: True if bluetoothd is running
     76 
     77         """
     78         return self._proxy.is_bluetoothd_running()
     79 
     80 
     81     def reset_on(self):
     82         """Reset the adapter and settings and power up the adapter.
     83 
     84         @return True on success, False otherwise.
     85 
     86         """
     87         return self._proxy.reset_on()
     88 
     89 
     90     def reset_off(self):
     91         """Reset the adapter and settings, leave the adapter powered off.
     92 
     93         @return True on success, False otherwise.
     94 
     95         """
     96         return self._proxy.reset_off()
     97 
     98 
     99     def has_adapter(self):
    100         """@return True if an adapter is present, False if not."""
    101         return self._proxy.has_adapter()
    102 
    103 
    104     def set_powered(self, powered):
    105         """Set the adapter power state.
    106 
    107         @param powered: adapter power state to set (True or False).
    108 
    109         @return True on success, False otherwise.
    110 
    111         """
    112         return self._proxy.set_powered(powered)
    113 
    114 
    115     def is_powered_on(self):
    116         """Is the adapter powered on?
    117 
    118         @returns: True if the adapter is powered on
    119 
    120         """
    121         properties = self.get_adapter_properties()
    122         return bool(properties.get(u'Powered'))
    123 
    124 
    125     def get_hci(self):
    126         """Get hci of the adapter; normally, it is 'hci0'.
    127 
    128         @returns: the hci name of the adapter.
    129 
    130         """
    131         dev_info = self.get_dev_info()
    132         hci = (dev_info[1] if isinstance(dev_info, list) and
    133                len(dev_info) > 1 else None)
    134         return hci
    135 
    136 
    137     def get_address(self):
    138         """Get the bluetooth address of the adapter.
    139 
    140         An example of the bluetooth address of the adapter: '6C:29:95:1A:D4:6F'
    141 
    142         @returns: the bluetooth address of the adapter.
    143 
    144         """
    145         return self.address
    146 
    147 
    148     def get_bluez_version(self):
    149         """Get bluez version.
    150 
    151         An exmaple of bluez version: 'BlueZ 5.39'
    152 
    153         @returns: the bluez version
    154 
    155         """
    156         return self.bluez_version
    157 
    158 
    159     def get_bluetooth_class(self):
    160         """Get the bluetooth class of the adapter.
    161 
    162         An example of the bluetooth class of a chromebook: 4718852
    163 
    164         @returns: the bluetooth class.
    165 
    166         """
    167         return self.bluetooth_class
    168 
    169 
    170     def get_UUIDs(self):
    171         """Get the UUIDs.
    172 
    173         An example of UUIDs:
    174             [u'00001112-0000-1000-8000-00805f9b34fb',
    175              u'00001801-0000-1000-8000-00805f9b34fb',
    176              u'0000110a-0000-1000-8000-00805f9b34fb',
    177              u'0000111f-0000-1000-8000-00805f9b34fb',
    178              u'00001200-0000-1000-8000-00805f9b34fb',
    179              u'00001800-0000-1000-8000-00805f9b34fb']
    180 
    181         @returns: the list of the UUIDs.
    182 
    183         """
    184         return self.UUIDs
    185 
    186 
    187     def set_discoverable(self, discoverable):
    188         """Set the adapter discoverable state.
    189 
    190         @param discoverable: adapter discoverable state to set (True or False).
    191 
    192         @return True on success, False otherwise.
    193 
    194         """
    195         return self._proxy.set_discoverable(discoverable)
    196 
    197 
    198     def is_discoverable(self):
    199         """Is the adapter in the discoverable state?
    200 
    201         @return True if discoverable. False otherwise.
    202 
    203         """
    204         properties = self.get_adapter_properties()
    205         return properties.get('Discoverable') == 1
    206 
    207 
    208     def set_pairable(self, pairable):
    209         """Set the adapter pairable state.
    210 
    211         @param pairable: adapter pairable state to set (True or False).
    212 
    213         @return True on success, False otherwise.
    214 
    215         """
    216         return self._proxy.set_pairable(pairable)
    217 
    218 
    219     def is_pairable(self):
    220         """Is the adapter in the pairable state?
    221 
    222         @return True if pairable. False otherwise.
    223 
    224         """
    225         properties = self.get_adapter_properties()
    226         return properties.get('Pairable') == 1
    227 
    228 
    229     def get_adapter_properties(self):
    230         """Read the adapter properties from the Bluetooth Daemon.
    231 
    232         An example of the adapter properties looks like
    233         {u'Name': u'BlueZ 5.35',
    234          u'Alias': u'Chromebook',
    235          u'Modalias': u'bluetooth:v00E0p2436d0400',
    236          u'Powered': 1,
    237          u'DiscoverableTimeout': 180,
    238          u'PairableTimeout': 0,
    239          u'Discoverable': 0,
    240          u'Address': u'6C:29:95:1A:D4:6F',
    241          u'Discovering': 0,
    242          u'Pairable': 1,
    243          u'Class': 4718852,
    244          u'UUIDs': [u'00001112-0000-1000-8000-00805f9b34fb',
    245                     u'00001801-0000-1000-8000-00805f9b34fb',
    246                     u'0000110a-0000-1000-8000-00805f9b34fb',
    247                     u'0000111f-0000-1000-8000-00805f9b34fb',
    248                     u'00001200-0000-1000-8000-00805f9b34fb',
    249                     u'00001800-0000-1000-8000-00805f9b34fb']}
    250 
    251         @return the properties as a dictionary on success,
    252             the value False otherwise.
    253 
    254         """
    255         return json.loads(self._proxy.get_adapter_properties())
    256 
    257 
    258     def read_version(self):
    259         """Read the version of the management interface from the Kernel.
    260 
    261         @return the version as a tuple of:
    262           ( version, revision )
    263 
    264         """
    265         return json.loads(self._proxy.read_version())
    266 
    267 
    268     def read_supported_commands(self):
    269         """Read the set of supported commands from the Kernel.
    270 
    271         @return set of supported commands as arrays in a tuple of:
    272           ( commands, events )
    273 
    274         """
    275         return json.loads(self._proxy.read_supported_commands())
    276 
    277 
    278     def read_index_list(self):
    279         """Read the list of currently known controllers from the Kernel.
    280 
    281         @return array of controller indexes.
    282 
    283         """
    284         return json.loads(self._proxy.read_index_list())
    285 
    286 
    287     def read_info(self):
    288         """Read the adapter information from the Kernel.
    289 
    290         An example of the adapter information looks like
    291         [u'6C:29:95:1A:D4:6F', 6, 2, 65535, 2769, 4718852, u'Chromebook', u'']
    292 
    293         @return the information as a tuple of:
    294           ( address, bluetooth_version, manufacturer_id,
    295             supported_settings, current_settings, class_of_device,
    296             name, short_name )
    297 
    298         """
    299         return json.loads(self._proxy.read_info())
    300 
    301 
    302     def add_device(self, address, address_type, action):
    303         """Add a device to the Kernel action list.
    304 
    305         @param address: Address of the device to add.
    306         @param address_type: Type of device in @address.
    307         @param action: Action to take.
    308 
    309         @return tuple of ( address, address_type ) on success,
    310           None on failure.
    311 
    312         """
    313         return json.loads(self._proxy.add_device(address, address_type, action))
    314 
    315 
    316     def remove_device(self, address, address_type):
    317         """Remove a device from the Kernel action list.
    318 
    319         @param address: Address of the device to remove.
    320         @param address_type: Type of device in @address.
    321 
    322         @return tuple of ( address, address_type ) on success,
    323           None on failure.
    324 
    325         """
    326         return json.loads(self._proxy.remove_device(address, address_type))
    327 
    328 
    329     def get_devices(self):
    330         """Read information about remote devices known to the adapter.
    331 
    332         An example of the device information of RN-42 looks like
    333         [{u'Name': u'RNBT-A96F',
    334           u'Alias': u'RNBT-A96F',
    335           u'Adapter': u'/org/bluez/hci0',
    336           u'LegacyPairing': 0,
    337           u'Paired': 1,
    338           u'Connected': 0,
    339           u'UUIDs': [u'00001124-0000-1000-8000-00805f9b34fb'],
    340           u'Address': u'00:06:66:75:A9:6F',
    341           u'Icon': u'input-mouse',
    342           u'Class': 1408,
    343           u'Trusted': 1,
    344           u'Blocked': 0}]
    345 
    346         @return the properties of each device as an array of
    347             dictionaries on success, the value False otherwise.
    348 
    349         """
    350         return json.loads(self._proxy.get_devices())
    351 
    352 
    353     def get_device_properties(self, address):
    354         """Read information about remote devices known to the adapter.
    355 
    356         An example of the device information of RN-42 looks like
    357 
    358         @param address: Address of the device to pair.
    359         @param pin: The pin code of the device to pair.
    360         @param timeout: The timeout in seconds for pairing.
    361 
    362         @returns: a dictionary of device properties of the device on success;
    363                   an empty dictionary otherwise.
    364 
    365         """
    366         return json.loads(self._proxy.get_device_by_address(address))
    367 
    368         for device in self.get_devices():
    369             if device.get['Address'] == address:
    370                 return device
    371         return dict()
    372 
    373 
    374     def start_discovery(self):
    375         """Start discovery of remote devices.
    376 
    377         Obtain the discovered device information using get_devices(), called
    378         stop_discovery() when done.
    379 
    380         @return True on success, False otherwise.
    381 
    382         """
    383         return self._proxy.start_discovery()
    384 
    385 
    386     def stop_discovery(self):
    387         """Stop discovery of remote devices.
    388 
    389         @return True on success, False otherwise.
    390 
    391         """
    392         return self._proxy.stop_discovery()
    393 
    394 
    395     def is_discovering(self):
    396         """Is it discovering?
    397 
    398         @return True if it is discovering. False otherwise.
    399 
    400         """
    401         return self.get_adapter_properties().get('Discovering') == 1
    402 
    403 
    404     def get_dev_info(self):
    405         """Read raw HCI device information.
    406 
    407         An example of the device information looks like:
    408         [0, u'hci0', u'6C:29:95:1A:D4:6F', 13, 0, 1, 581900950526, 52472, 7,
    409          32768, 1021, 5, 96, 6, 0, 0, 151, 151, 0, 0, 0, 0, 1968, 12507]
    410 
    411         @return tuple of (index, name, address, flags, device_type, bus_type,
    412                        features, pkt_type, link_policy, link_mode,
    413                        acl_mtu, acl_pkts, sco_mtu, sco_pkts,
    414                        err_rx, err_tx, cmd_tx, evt_rx, acl_tx, acl_rx,
    415                        sco_tx, sco_rx, byte_rx, byte_tx) on success,
    416                 None on failure.
    417 
    418         """
    419         return json.loads(self._proxy.get_dev_info())
    420 
    421 
    422     def register_profile(self, path, uuid, options):
    423         """Register new profile (service).
    424 
    425         @param path: Path to the profile object.
    426         @param uuid: Service Class ID of the service as string.
    427         @param options: Dictionary of options for the new service, compliant
    428                         with BlueZ D-Bus Profile API standard.
    429 
    430         @return True on success, False otherwise.
    431 
    432         """
    433         return self._proxy.register_profile(path, uuid, options)
    434 
    435 
    436     def has_device(self, address):
    437         """Checks if the device with a given address exists.
    438 
    439         @param address: Address of the device.
    440 
    441         @returns: True if there is a device with that address.
    442                   False otherwise.
    443 
    444         """
    445         return self._proxy.has_device(address)
    446 
    447 
    448     def device_is_paired(self, address):
    449         """Checks if a device is paired.
    450 
    451         @param address: address of the device.
    452 
    453         @returns: True if device is paired. False otherwise.
    454 
    455         """
    456         return self._proxy.device_is_paired(address)
    457 
    458 
    459     def set_trusted(self, address, trusted=True):
    460         """Set the device trusted.
    461 
    462         @param address: The bluetooth address of the device.
    463         @param trusted: True or False indicating whether to set trusted or not.
    464 
    465         @returns: True if successful. False otherwise.
    466 
    467         """
    468         return self._proxy.set_trusted(address, trusted)
    469 
    470 
    471     def pair_legacy_device(self, address, pin, trusted, timeout):
    472         """Pairs a device with a given pin code.
    473 
    474         Registers an agent who handles pin code request and
    475         pairs a device with known pin code.
    476 
    477         @param address: Address of the device to pair.
    478         @param pin: The pin code of the device to pair.
    479         @param trusted: indicating whether to set the device trusted.
    480         @param timeout: The timeout in seconds for pairing.
    481 
    482         @returns: True on success. False otherwise.
    483 
    484         """
    485         return self._proxy.pair_legacy_device(address, pin, trusted, timeout)
    486 
    487 
    488     def remove_device_object(self, address):
    489         """Removes a device object and the pairing information.
    490 
    491         Calls RemoveDevice method to remove remote device
    492         object and the pairing information.
    493 
    494         @param address: address of the device to unpair.
    495 
    496         @returns: True on success. False otherwise.
    497 
    498         """
    499         return self._proxy.remove_device_object(address)
    500 
    501 
    502     def connect_device(self, address):
    503         """Connects a device.
    504 
    505         Connects a device if it is not connected.
    506 
    507         @param address: Address of the device to connect.
    508 
    509         @returns: True on success. False otherwise.
    510 
    511         """
    512         return self._proxy.connect_device(address)
    513 
    514 
    515     def device_is_connected(self, address):
    516         """Checks if a device is connected.
    517 
    518         @param address: Address of the device to check if it is connected.
    519 
    520         @returns: True if device is connected. False otherwise.
    521 
    522         """
    523         return self._proxy.device_is_connected(address)
    524 
    525 
    526     def disconnect_device(self, address):
    527         """Disconnects a device.
    528 
    529         Disconnects a device if it is connected.
    530 
    531         @param address: Address of the device to disconnect.
    532 
    533         @returns: True on success. False otherwise.
    534 
    535         """
    536         return self._proxy.disconnect_device(address)
    537 
    538 
    539     def btmon_start(self):
    540         """Start btmon monitoring."""
    541         self._proxy.btmon_start()
    542 
    543 
    544     def btmon_stop(self):
    545         """Stop btmon monitoring."""
    546         self._proxy.btmon_stop()
    547 
    548 
    549     def btmon_get(self, search_str='', start_str=''):
    550         """Get btmon output contents.
    551 
    552         @param search_str: only lines with search_str would be kept.
    553         @param start_str: all lines before the occurrence of start_str would be
    554                 filtered.
    555 
    556         @returns: the recorded btmon output.
    557 
    558         """
    559         return self._proxy.btmon_get(search_str, start_str)
    560 
    561 
    562     def btmon_find(self, pattern_str):
    563         """Find if a pattern string exists in btmon output.
    564 
    565         @param pattern_str: the pattern string to find.
    566 
    567         @returns: True on success. False otherwise.
    568 
    569         """
    570         return self._proxy.btmon_find(pattern_str)
    571 
    572 
    573     def register_advertisement(self, advertisement_data):
    574         """Register an advertisement.
    575 
    576         Note that rpc supports only conformable types. Hence, a
    577         dict about the advertisement is passed as a parameter such
    578         that the advertisement object could be contructed on the host.
    579 
    580         @param advertisement_data: a dict of the advertisement for
    581                                    the adapter to register.
    582 
    583         @returns: True on success. False otherwise.
    584 
    585         """
    586         return self._proxy.register_advertisement(advertisement_data)
    587 
    588 
    589     def unregister_advertisement(self, advertisement_data):
    590         """Unregister an advertisement.
    591 
    592         @param advertisement_data: a dict of the advertisement to unregister.
    593 
    594         @returns: True on success. False otherwise.
    595 
    596         """
    597         return self._proxy.unregister_advertisement(advertisement_data)
    598 
    599 
    600     def set_advertising_intervals(self, min_adv_interval_ms,
    601                                   max_adv_interval_ms):
    602         """Set advertising intervals.
    603 
    604         @param min_adv_interval_ms: the min advertising interval in ms.
    605         @param max_adv_interval_ms: the max advertising interval in ms.
    606 
    607         @returns: True on success. False otherwise.
    608 
    609         """
    610         return self._proxy.set_advertising_intervals(min_adv_interval_ms,
    611                                                      max_adv_interval_ms)
    612 
    613 
    614     def reset_advertising(self):
    615         """Reset advertising.
    616 
    617         This includes unregister all advertisements, reset advertising
    618         intervals, and disable advertising.
    619 
    620         @returns: True on success. False otherwise.
    621 
    622         """
    623         return self._proxy.reset_advertising()
    624 
    625 
    626     def copy_logs(self, destination):
    627         """Copy the logs generated by this device to a given location.
    628 
    629         @param destination: destination directory for the logs.
    630 
    631         """
    632         self.host.collect_logs(self.XMLRPC_LOG_PATH, destination)
    633 
    634 
    635     def close(self):
    636         """Tear down state associated with the client."""
    637         # Turn off the discoverable flag since it may affect future tests.
    638         self._proxy.set_discoverable(False)
    639         # Leave the adapter powered off, but don't do a full reset.
    640         self._proxy.set_powered(False)
    641         # This kills the RPC server.
    642         self.host.close()
    643