14dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant#!/usr/bin/env python 24dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 34dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 44dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant# Use of this source code is governed by a BSD-style license that can be 54dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant# found in the LICENSE file. 64dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 7a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnantimport dbus 875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiangimport dbus.mainloop.glib 975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiangimport dbus.service 1075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiangimport gobject 111ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnantimport json 124dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnantimport logging 134dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnantimport logging.handlers 14a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnantimport os 15a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnantimport shutil 164dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 174dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnantimport common 181c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnantfrom autotest_lib.client.bin import utils 191ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnantfrom autotest_lib.client.common_lib.cros.bluetooth import bluetooth_socket 20e0b08e6170b57f90262726eb7f04e059cb47419cHsinyu Chaofrom autotest_lib.client.cros import constants 21ec0b71daa4d20f466724523821e4f4908d7e7d5aChristopher Wileyfrom autotest_lib.client.cros import xmlrpc_server 224dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 234dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 2475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiangclass _PinAgent(dbus.service.Object): 2575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """The agent handling bluetooth device with a known pin code. 2675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 2775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang _PinAgent overrides RequestPinCode method to return a given pin code. 2875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang User can use this agent to pair bluetooth device which has a known pin code. 2975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 3075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 3175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def __init__(self, pin, *args, **kwargs): 3275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang super(_PinAgent, self).__init__(*args, **kwargs) 3375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang self._pin = pin 3475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 3575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 3675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @dbus.service.method('org.bluez.Agent1', in_signature="o", out_signature="s") 3775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def RequestPinCode(self, device_path): 3875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Requests pin code for a device. 3975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 4075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang Returns the known pin code for the request. 4175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 4275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param device_path: The object path of the device. 4375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 4475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @returns: The known pin code. 4575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 4675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 4775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.info('RequestPinCode for %s, return %s', device_path, self._pin) 4875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return self._pin 4975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 5075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 518d2cbf363de4aace06c65ddd20ec862431ce928cScott James Remnantclass BluetoothDeviceXmlRpcDelegate(xmlrpc_server.XmlRpcDelegate): 521c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant """Exposes DUT methods called remotely during Bluetooth autotests. 534dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 544dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant All instance methods of this object without a preceding '_' are exposed via 554dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant an XML-RPC server. This is not a stateless handler object, which means that 561c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant if you store state inside the delegate, that state will remain around for 574dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant future calls. 584dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant """ 594dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 60a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant UPSTART_PATH = 'unix:abstract=/com/ubuntu/upstart' 61a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant UPSTART_MANAGER_PATH = '/com/ubuntu/Upstart' 62a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant UPSTART_MANAGER_IFACE = 'com.ubuntu.Upstart0_6' 63a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant UPSTART_JOB_IFACE = 'com.ubuntu.Upstart0_6.Job' 64a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 65a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant UPSTART_ERROR_UNKNOWNINSTANCE = \ 66a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 'com.ubuntu.Upstart0_6.Error.UnknownInstance' 67a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 68a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant BLUETOOTHD_JOB = 'bluetoothd' 69a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 70a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant DBUS_ERROR_SERVICEUNKNOWN = 'org.freedesktop.DBus.Error.ServiceUnknown' 71a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 72a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant BLUEZ_SERVICE_NAME = 'org.bluez' 73a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant BLUEZ_MANAGER_PATH = '/' 74a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant BLUEZ_MANAGER_IFACE = 'org.freedesktop.DBus.ObjectManager' 75a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant BLUEZ_ADAPTER_IFACE = 'org.bluez.Adapter1' 76aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant BLUEZ_DEVICE_IFACE = 'org.bluez.Device1' 7775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang BLUEZ_AGENT_MANAGER_PATH = '/org/bluez' 7875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang BLUEZ_AGENT_MANAGER_IFACE = 'org.bluez.AgentManager1' 79b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov BLUEZ_PROFILE_MANAGER_PATH = '/org/bluez' 80b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov BLUEZ_PROFILE_MANAGER_IFACE = 'org.bluez.ProfileManager1' 8175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang BLUEZ_ERROR_ALREADY_EXISTS = 'org.bluez.Error.AlreadyExists' 82a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 83a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant BLUETOOTH_LIBDIR = '/var/lib/bluetooth' 84a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 85a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant # Timeout for how long we'll wait for BlueZ and the Adapter to show up 86a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant # after reset. 87a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant ADAPTER_TIMEOUT = 30 88a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 894dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant def __init__(self): 908d2cbf363de4aace06c65ddd20ec862431ce928cScott James Remnant super(BluetoothDeviceXmlRpcDelegate, self).__init__() 91a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 92c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant # Open the Bluetooth Raw socket to the kernel which provides us direct, 93c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant # raw, access to the HCI controller. 94c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant self._raw = bluetooth_socket.BluetoothRawSocket() 95c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant 961ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant # Open the Bluetooth Control socket to the kernel which provides us 971ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant # raw management access to the Bluetooth Host Subsystem. Read the list 981ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant # of adapter indexes to determine whether or not this device has a 991ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant # Bluetooth Adapter or not. 1001ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant self._control = bluetooth_socket.BluetoothControlSocket() 1011ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant self._has_adapter = len(self._control.read_index_list()) > 0 1021ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 103a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant # Set up the connection to Upstart so we can start and stop services 104a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant # and fetch the bluetoothd job. 105a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._upstart_conn = dbus.connection.Connection(self.UPSTART_PATH) 106a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._upstart = self._upstart_conn.get_object( 107a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant None, 108a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self.UPSTART_MANAGER_PATH) 109a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 110a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant bluetoothd_path = self._upstart.GetJobByName( 111a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self.BLUETOOTHD_JOB, 112a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant dbus_interface=self.UPSTART_MANAGER_IFACE) 113a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._bluetoothd = self._upstart_conn.get_object( 114a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant None, 115a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant bluetoothd_path) 116a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 11775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang # Arrange for the GLib main loop to be the default. 11875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 11975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 1201ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant # Set up the connection to the D-Bus System Bus, get the object for 1211ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant # the Bluetooth Userspace Daemon (BlueZ) and that daemon's object for 1221ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant # the Bluetooth Adapter. 123a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._system_bus = dbus.SystemBus() 124a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._update_bluez() 125a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._update_adapter() 126a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 12775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang # The agent to handle pin code request, which will be 12875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang # created when user calls pair_legacy_device method. 12975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang self._pin_agent = None 13075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 1311c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 132a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant def _update_bluez(self): 1331c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant """Store a D-Bus proxy for the Bluetooth daemon in self._bluez. 1341c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 1351ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant This may be called in a loop until it returns True to wait for the 1361ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant daemon to be ready after it has been started. 1371ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 1381c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant @return True on success, False otherwise. 1391c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 1401c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant """ 141a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._bluez = None 142a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant try: 143a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._bluez = self._system_bus.get_object( 144a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self.BLUEZ_SERVICE_NAME, 145a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self.BLUEZ_MANAGER_PATH) 146a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant logging.debug('bluetoothd is running') 1471c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant return True 148a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant except dbus.exceptions.DBusException, e: 149a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant if e.get_dbus_name() == self.DBUS_ERROR_SERVICEUNKNOWN: 150a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant logging.debug('bluetoothd is not running') 151a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._bluez = None 1521c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant return False 153a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant else: 154c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld logging.error('Error updating Bluez!') 155a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant raise 156a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 1571c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 158a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant def _update_adapter(self): 1591c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant """Store a D-Bus proxy for the local adapter in self._adapter. 1601c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 1611ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant This may be called in a loop until it returns True to wait for the 1621ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant daemon to be ready, and have obtained the adapter information itself, 1631ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant after it has been started. 1641ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 1651ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant Since not all devices will have adapters, this will also return True 1661ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant in the case where we have obtained an empty adapter index list from the 1671ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant kernel. 1681ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 1691c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant @return True on success, including if there is no local adapter, 1701c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant False otherwise. 1711c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 1721c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant """ 173a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._adapter = None 174a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant if self._bluez is None: 175c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld logging.warning('Bluez not found!') 1761c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant return False 1771ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant if not self._has_adapter: 178c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld logging.debug('Device has no adapter; returning') 1791ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant return True 180a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 181a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant objects = self._bluez.GetManagedObjects( 182a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant dbus_interface=self.BLUEZ_MANAGER_IFACE) 183a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant for path, ifaces in objects.iteritems(): 184a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant logging.debug('%s -> %r', path, ifaces.keys()) 185a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant if self.BLUEZ_ADAPTER_IFACE in ifaces: 186a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant logging.debug('using adapter %s', path) 187a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._adapter = self._system_bus.get_object( 188a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self.BLUEZ_SERVICE_NAME, 189a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant path) 1901c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant return True 1911c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant else: 192c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld logging.warning('No adapter found in interface!') 1931c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant return False 1941c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 195a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 196a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @xmlrpc_server.dbus_safe(False) 197a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant def reset_on(self): 198a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """Reset the adapter and settings and power up the adapter. 199a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 200a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @return True on success, False otherwise. 201a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 202a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """ 203a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._reset() 2042fd063e087b815be157bfc64edfbd110d40bdd0bCheng-Yi Chiang if not self._adapter: 2052fd063e087b815be157bfc64edfbd110d40bdd0bCheng-Yi Chiang return False 206a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._set_powered(True) 207a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant return True 208a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 2091c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 210a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @xmlrpc_server.dbus_safe(False) 211a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant def reset_off(self): 212a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """Reset the adapter and settings, leave the adapter powered off. 213a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 214a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @return True on success, False otherwise. 215a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 216a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """ 217a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._reset() 218a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant return True 219a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 2201c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 2211ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant def has_adapter(self): 2221ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant """Return if an adapter is present. 2231ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 2241ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant This will only return True if we have determined both that there is 2251ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant a Bluetooth adapter on this device (kernel adapter index list is not 2261ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant empty) and that the Bluetooth daemon has exported an object for it. 2271ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 2281ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant @return True if an adapter is present, False if not. 2291ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 2301ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant """ 2311ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant return self._has_adapter and self._adapter is not None 2321ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 2331ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 234a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant def _reset(self): 235a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """Reset the Bluetooth adapter and settings.""" 236a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant logging.debug('_reset') 237a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant if self._adapter: 238a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._set_powered(False) 239a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 240a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant try: 241a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._bluetoothd.Stop(dbus.Array(signature='s'), True, 242a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant dbus_interface=self.UPSTART_JOB_IFACE) 243a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant except dbus.exceptions.DBusException, e: 244a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant if e.get_dbus_name() != self.UPSTART_ERROR_UNKNOWNINSTANCE: 245c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld logging.error('Error resetting adapter!') 246a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant raise 247a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 2486b47f360718a7ac4131f0ef1967af693cc4b311eScott James Remnant def bluez_stopped(): 249c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang """Checks the bluetooth daemon status. 250c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang 251c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang @returns: True if bluez is stopped. False otherwise. 252c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang 253c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang """ 2546b47f360718a7ac4131f0ef1967af693cc4b311eScott James Remnant return not self._update_bluez() 2556b47f360718a7ac4131f0ef1967af693cc4b311eScott James Remnant 2566b47f360718a7ac4131f0ef1967af693cc4b311eScott James Remnant logging.debug('waiting for bluez stop') 2576b47f360718a7ac4131f0ef1967af693cc4b311eScott James Remnant utils.poll_for_condition( 2586b47f360718a7ac4131f0ef1967af693cc4b311eScott James Remnant condition=bluez_stopped, 2596b47f360718a7ac4131f0ef1967af693cc4b311eScott James Remnant desc='Bluetooth Daemon has stopped.', 2606b47f360718a7ac4131f0ef1967af693cc4b311eScott James Remnant timeout=self.ADAPTER_TIMEOUT) 2616b47f360718a7ac4131f0ef1967af693cc4b311eScott James Remnant 262a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant for subdir in os.listdir(self.BLUETOOTH_LIBDIR): 263a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant shutil.rmtree(os.path.join(self.BLUETOOTH_LIBDIR, subdir)) 264a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 265a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._bluetoothd.Start(dbus.Array(signature='s'), True, 266a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant dbus_interface=self.UPSTART_JOB_IFACE) 267a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 268a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant logging.debug('waiting for bluez start') 2691c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant utils.poll_for_condition( 2701c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant condition=self._update_bluez, 2711c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant desc='Bluetooth Daemon has started.', 2721c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant timeout=self.ADAPTER_TIMEOUT) 2731c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 2741c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant logging.debug('waiting for bluez to obtain adapter information') 2751c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant utils.poll_for_condition( 2761c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant condition=self._update_adapter, 2771c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant desc='Bluetooth Daemon has adapter information.', 2781c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant timeout=self.ADAPTER_TIMEOUT) 2791c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 280a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 281a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @xmlrpc_server.dbus_safe(False) 282a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant def set_powered(self, powered): 283a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """Set the adapter power state. 284a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 2851c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant @param powered: adapter power state to set (True or False). 286a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 287a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @return True on success, False otherwise. 288a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 289a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """ 290c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld if not self._adapter: 291c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld if not powered: 292c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld # Return success if we are trying to power off an adapter that's 293c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld # missing or gone away, since the expected result has happened. 294c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld return True 295c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld else: 296c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld logging.warning('Adapter not found!') 297c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld return False 298a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._set_powered(powered) 299a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant return True 300a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 3011c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 302a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant def _set_powered(self, powered): 303a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """Set the adapter power state. 304a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 3051c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant @param powered: adapter power state to set (True or False). 306a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 307a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """ 308a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant logging.debug('_set_powered %r', powered) 309a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._adapter.Set(self.BLUEZ_ADAPTER_IFACE, 'Powered', powered, 310a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant dbus_interface=dbus.PROPERTIES_IFACE) 311a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 3121c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 313a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @xmlrpc_server.dbus_safe(False) 314a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant def set_discoverable(self, discoverable): 315a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """Set the adapter discoverable state. 316a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 3171c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant @param discoverable: adapter discoverable state to set (True or False). 318a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 319a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @return True on success, False otherwise. 320a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 321a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """ 322da9f43cc5c84691d82b1dd9222fea37234cb9a0aScott James Remnant if not discoverable and not self._adapter: 323da9f43cc5c84691d82b1dd9222fea37234cb9a0aScott James Remnant # Return success if we are trying to make an adapter that's 324da9f43cc5c84691d82b1dd9222fea37234cb9a0aScott James Remnant # missing or gone away, undiscoverable, since the expected result 325da9f43cc5c84691d82b1dd9222fea37234cb9a0aScott James Remnant # has happened. 326da9f43cc5c84691d82b1dd9222fea37234cb9a0aScott James Remnant return True 327a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._adapter.Set(self.BLUEZ_ADAPTER_IFACE, 328a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 'Discoverable', discoverable, 329a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant dbus_interface=dbus.PROPERTIES_IFACE) 330a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant return True 331a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 3321c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant 333a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @xmlrpc_server.dbus_safe(False) 334a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant def set_pairable(self, pairable): 335a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """Set the adapter pairable state. 336a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 3371c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant @param pairable: adapter pairable state to set (True or False). 338a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 339a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant @return True on success, False otherwise. 340a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant 341a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant """ 342a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant self._adapter.Set(self.BLUEZ_ADAPTER_IFACE, 'Pairable', pairable, 343a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant dbus_interface=dbus.PROPERTIES_IFACE) 344a6442f513846b0fc002ca4bbfa453e7603442f02Scott James Remnant return True 3454dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 3464dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 3471ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant @xmlrpc_server.dbus_safe(False) 3481ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant def get_adapter_properties(self): 3491ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant """Read the adapter properties from the Bluetooth Daemon. 3501ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 3511ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant @return the properties as a JSON-encoded dictionary on success, 3521ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant the value False otherwise. 3531ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 3541ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant """ 3551ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant objects = self._bluez.GetManagedObjects( 3561ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant dbus_interface=self.BLUEZ_MANAGER_IFACE) 3571ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant adapter = objects[self._adapter.object_path][self.BLUEZ_ADAPTER_IFACE] 3581ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant return json.dumps(adapter) 3591ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 3601ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 36150915ad3a231755ce430e032272346adf7a3e472Scott James Remnant def read_version(self): 36250915ad3a231755ce430e032272346adf7a3e472Scott James Remnant """Read the version of the management interface from the Kernel. 36350915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 36450915ad3a231755ce430e032272346adf7a3e472Scott James Remnant @return the information as a JSON-encoded tuple of: 36550915ad3a231755ce430e032272346adf7a3e472Scott James Remnant ( version, revision ) 36650915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 36750915ad3a231755ce430e032272346adf7a3e472Scott James Remnant """ 36850915ad3a231755ce430e032272346adf7a3e472Scott James Remnant return json.dumps(self._control.read_version()) 36950915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 37050915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 37150915ad3a231755ce430e032272346adf7a3e472Scott James Remnant def read_supported_commands(self): 37250915ad3a231755ce430e032272346adf7a3e472Scott James Remnant """Read the set of supported commands from the Kernel. 37350915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 37450915ad3a231755ce430e032272346adf7a3e472Scott James Remnant @return the information as a JSON-encoded tuple of: 37550915ad3a231755ce430e032272346adf7a3e472Scott James Remnant ( commands, events ) 37650915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 37750915ad3a231755ce430e032272346adf7a3e472Scott James Remnant """ 37850915ad3a231755ce430e032272346adf7a3e472Scott James Remnant return json.dumps(self._control.read_supported_commands()) 37950915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 38050915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 38150915ad3a231755ce430e032272346adf7a3e472Scott James Remnant def read_index_list(self): 38250915ad3a231755ce430e032272346adf7a3e472Scott James Remnant """Read the list of currently known controllers from the Kernel. 38350915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 38450915ad3a231755ce430e032272346adf7a3e472Scott James Remnant @return the information as a JSON-encoded array of controller indexes. 38550915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 38650915ad3a231755ce430e032272346adf7a3e472Scott James Remnant """ 38750915ad3a231755ce430e032272346adf7a3e472Scott James Remnant return json.dumps(self._control.read_index_list()) 38850915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 38950915ad3a231755ce430e032272346adf7a3e472Scott James Remnant 3901ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant def read_info(self): 3911ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant """Read the adapter information from the Kernel. 3921ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 3931ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant @return the information as a JSON-encoded tuple of: 3941ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant ( address, bluetooth_version, manufacturer_id, 3951ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant supported_settings, current_settings, class_of_device, 3961ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant name, short_name ) 3971ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 3981ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant """ 3991ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant return json.dumps(self._control.read_info(0)) 4001ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 4011ca2e0e1b6cd0b6f5c2b972e44f3f63539d1dda6Scott James Remnant 402abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant def add_device(self, address, address_type, action): 403abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant """Add a device to the Kernel action list. 404abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 405abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant @param address: Address of the device to add. 406abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant @param address_type: Type of device in @address. 407abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant @param action: Action to take. 408abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 409abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant @return on success, a JSON-encoded typle of: 410abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant ( address, address_type ), None on failure. 411abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 412abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant """ 413abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant return json.dumps(self._control.add_device( 414abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 0, address, address_type, action)) 415abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 416abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 417abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant def remove_device(self, address, address_type): 418abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant """Remove a device from the Kernel action list. 419abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 420abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant @param address: Address of the device to remove. 421abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant @param address_type: Type of device in @address. 422abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 423abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant @return on success, a JSON-encoded typle of: 424abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant ( address, address_type ), None on failure. 425abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 426abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant """ 427abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant return json.dumps(self._control.remove_device( 428abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 0, address, address_type)) 429abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 430abea37c34ea732b1c8c14cf3d4ecf5cf595c9f24Scott James Remnant 431aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant @xmlrpc_server.dbus_safe(False) 432aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant def get_devices(self): 433aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant """Read information about remote devices known to the adapter. 434aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 435aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant @return the properties of each device as a JSON-encoded array of 436aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant dictionaries on success, the value False otherwise. 437aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 438aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant """ 439aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant objects = self._bluez.GetManagedObjects( 440cdea4046128d757ad78a581a4dc65c881ffc0119Cheng-Yi Chiang dbus_interface=self.BLUEZ_MANAGER_IFACE, byte_arrays=True) 441aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant devices = [] 442aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant for path, ifaces in objects.iteritems(): 443aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant if self.BLUEZ_DEVICE_IFACE in ifaces: 444aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant devices.append(objects[path][self.BLUEZ_DEVICE_IFACE]) 445aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant return json.dumps(devices) 446aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 447aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 448aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant @xmlrpc_server.dbus_safe(False) 449aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant def start_discovery(self): 450aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant """Start discovery of remote devices. 451aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 452aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant Obtain the discovered device information using get_devices(), called 453aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant stop_discovery() when done. 454aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 455aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant @return True on success, False otherwise. 456aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 457aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant """ 4582fd063e087b815be157bfc64edfbd110d40bdd0bCheng-Yi Chiang if not self._adapter: 4592fd063e087b815be157bfc64edfbd110d40bdd0bCheng-Yi Chiang return False 460aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant self._adapter.StartDiscovery(dbus_interface=self.BLUEZ_ADAPTER_IFACE) 461aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant return True 462aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 463aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 464aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant @xmlrpc_server.dbus_safe(False) 465aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant def stop_discovery(self): 466aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant """Stop discovery of remote devices. 467aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 468aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant @return True on success, False otherwise. 469aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 470aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant """ 4712fd063e087b815be157bfc64edfbd110d40bdd0bCheng-Yi Chiang if not self._adapter: 4722fd063e087b815be157bfc64edfbd110d40bdd0bCheng-Yi Chiang return False 473aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant self._adapter.StopDiscovery(dbus_interface=self.BLUEZ_ADAPTER_IFACE) 474aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant return True 475aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 476aec4edd340e914885cb1fe26e9b8766788009f2fScott James Remnant 477c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant def get_dev_info(self): 478c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant """Read raw HCI device information. 479c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant 480c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant @return JSON-encoded tuple of: 481c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant (index, name, address, flags, device_type, bus_type, 482c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant features, pkt_type, link_policy, link_mode, 483c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant acl_mtu, acl_pkts, sco_mtu, sco_pkts, 484c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant err_rx, err_tx, cmd_tx, evt_rx, acl_tx, acl_rx, 485c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant sco_tx, sco_rx, byte_rx, byte_tx) on success, 486c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant None on failure. 487c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant 488c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant """ 489c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant return json.dumps(self._raw.get_dev_info(0)) 490c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant 491c7fd7a47c0ccb7da2bea83ba070e7350bf591c80Scott James Remnant 492b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov @xmlrpc_server.dbus_safe(False) 493b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov def register_profile(self, path, uuid, options): 494b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov """Register new profile (service). 495b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov 496b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov @param path: Path to the profile object. 497b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov @param uuid: Service Class ID of the service as string. 498b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov @param options: Dictionary of options for the new service, compliant 499b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov with BlueZ D-Bus Profile API standard. 500b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov 501b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov @return True on success, False otherwise. 502b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov 503b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov """ 504b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov profile_manager = dbus.Interface( 505b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov self._system_bus.get_object( 506b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov self.BLUEZ_SERVICE_NAME, 507b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov self.BLUEZ_PROFILE_MANAGER_PATH), 508b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov self.BLUEZ_PROFILE_MANAGER_IFACE) 509b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov profile_manager.RegisterProfile(path, uuid, options) 510b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov return True 511b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov 512b144dcec000ff483bb6d287e2de36be81392a637Artem Rakhov 513dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang @xmlrpc_server.dbus_safe(False) 514dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang def has_device(self, address): 515dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang """Checks if the device with a given address exists. 516dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang 517dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang @param address: Address of the device. 518dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang 519dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang @returns: True if there is a device with that address. 520dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang False otherwise. 521dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang 522dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang """ 523dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang return self._find_device(address) != None 524dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang 525dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang 52675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def _find_device(self, address): 52775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Finds the device with a given address. 52875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 52975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang Find the device with a given address and returns the 53075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device interface. 53175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 532dc6442b70574048ad5cc3958f049cfe985961a20Cheng-Yi Chiang @param address: Address of the device. 53375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 53475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @returns: An 'org.bluez.Device1' interface to the device. 53575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang None if device can not be found. 53675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 53775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 53875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang objects = self._bluez.GetManagedObjects( 53975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang dbus_interface=self.BLUEZ_MANAGER_IFACE) 54075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang for path, ifaces in objects.iteritems(): 54175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device = ifaces.get(self.BLUEZ_DEVICE_IFACE) 54275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if device is None: 54375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang continue 54475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if (device['Address'] == address and 54575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang path.startswith(self._adapter.object_path)): 54675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang obj = self._system_bus.get_object( 54775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang self.BLUEZ_SERVICE_NAME, path) 54875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return dbus.Interface(obj, self.BLUEZ_DEVICE_IFACE) 54975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.error('Device not found') 55075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return None 55175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 55275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 55375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def _setup_pin_agent(self, pin): 55475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Initializes a _PinAgent and registers it to handle pin code request. 55575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 55675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param pin: The pin code this agent will answer. 55775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 55875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 55975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang agent_path = '/test/agent' 56075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if self._pin_agent: 56175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.info('Removing the old agent before initializing a new one') 56275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang self._pin_agent.remove_from_connection() 56375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang self._pin_agent = None 56475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang self._pin_agent = _PinAgent(pin, self._system_bus, agent_path) 56575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang agent_manager = dbus.Interface( 566c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld self._system_bus.get_object(self.BLUEZ_SERVICE_NAME, 567c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld self.BLUEZ_AGENT_MANAGER_PATH), 568c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld self.BLUEZ_AGENT_MANAGER_IFACE) 56975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang try: 57075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang agent_manager.RegisterAgent(agent_path, 'NoInputNoOutput') 57175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang except dbus.exceptions.DBusException, e: 57275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if e.get_dbus_name() == self.BLUEZ_ERROR_ALREADY_EXISTS: 573c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld logging.info('Unregistering old agent and registering the new') 57475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang agent_manager.UnregisterAgent(agent_path) 57575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang agent_manager.RegisterAgent(agent_path, 'NoInputNoOutput') 57675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang else: 577c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld logging.error('Error setting up pin agent: %s', e) 57875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang raise 57975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.info('Agent registered') 58075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 58175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 58275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def _is_paired(self, device): 58375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Checks if a device is paired. 58475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 58575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param device: An 'org.bluez.Device1' interface to the device. 58675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 58775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @returns: True if device is paired. False otherwise. 58875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 58975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 59075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang props = dbus.Interface(device, dbus.PROPERTIES_IFACE) 59175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang paired = props.Get(self.BLUEZ_DEVICE_IFACE, 'Paired') 59275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return bool(paired) 59375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 59475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 59575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def _is_connected(self, device): 59675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Checks if a device is connected. 59775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 59875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param device: An 'org.bluez.Device1' interface to the device. 59975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 60075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @returns: True if device is connected. False otherwise. 60175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 60275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 60375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang props = dbus.Interface(device, dbus.PROPERTIES_IFACE) 60475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang connected = props.Get(self.BLUEZ_DEVICE_IFACE, 'Connected') 605c026987fe3ef3f5d16f956176d4f52c464b46856Katherine Threlkeld logging.info('Got connected = %r', connected) 60675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return bool(connected) 60775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 60875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 60975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @xmlrpc_server.dbus_safe(False) 61075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def pair_legacy_device(self, address, pin, timeout): 61175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Pairs a device with a given pin code. 61275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 61375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang Registers a agent who handles pin code request and 61475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang pairs a device with known pin code. 61575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 61675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param address: Address of the device to pair. 61775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param pin: The pin code of the device to pair. 61875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param timeout: The timeout in seconds for pairing. 61975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 62075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @returns: True on success. False otherwise. 62175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 62275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 62375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device = self._find_device(address) 62475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if not device: 62575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.error('Device not found') 62675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return False 62775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if self._is_paired(device): 62875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.info('Device is already paired') 62975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return True 63075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 63175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang self._setup_pin_agent(pin) 63275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang mainloop = gobject.MainLoop() 63375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 63475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 63575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def pair_reply(): 63675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Handler when pairing succeeded.""" 63775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.info('Device paired') 63875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang mainloop.quit() 63975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 64075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 64175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def pair_error(error): 642c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang """Handler when pairing failed. 643c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang 644c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang @param error: one of errors defined in org.bluez.Error representing 645c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang the error in pairing. 646c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang 647c5b6d2c5b7a0f18a9db61db816c0b8df2ab644adCheng-Yi Chiang """ 64875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang try: 64975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang error_name = error.get_dbus_name() 65075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if error_name == 'org.freedesktop.DBus.Error.NoReply': 65175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.error('Timed out. Cancelling pairing') 65275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device.CancelPairing() 65375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang else: 65475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.error('Pairing device failed: %s', error) 65575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang finally: 65675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang mainloop.quit() 65775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 65875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 65975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device.Pair(reply_handler=pair_reply, error_handler=pair_error, 66075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang timeout=timeout * 1000) 66175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang mainloop.run() 66275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return self._is_paired(device) 66375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 66475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 66575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @xmlrpc_server.dbus_safe(False) 66675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def remove_device_object(self, address): 66775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Removes a device object and the pairing information. 66875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 66975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang Calls RemoveDevice method to remove remote device 67075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang object and the pairing information. 67175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 67275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param address: Address of the device to unpair. 67375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 67475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @returns: True on success. False otherwise. 67575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 67675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 67775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device = self._find_device(address) 67875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if not device: 67975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.error('Device not found') 68075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return False 68175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang self._adapter.RemoveDevice( 68275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device.object_path, dbus_interface=self.BLUEZ_ADAPTER_IFACE) 68375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return True 68475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 68575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 68675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @xmlrpc_server.dbus_safe(False) 68775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def connect_device(self, address): 68875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Connects a device. 68975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 69075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang Connects a device if it is not connected. 69175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 69275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param address: Address of the device to connect. 69375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 69475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @returns: True on success. False otherwise. 69575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 69675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 69775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device = self._find_device(address) 69875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if not device: 69975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.error('Device not found') 70075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return False 70175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if self._is_connected(device): 70275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.info('Device is already connected') 70375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return True 70475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device.Connect() 705bd1b6261cd756adf2b1328f93bb6fbdaed8fa6a3Cheng-Yi Chiang return self._is_connected(device) 706bd1b6261cd756adf2b1328f93bb6fbdaed8fa6a3Cheng-Yi Chiang 70775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 708a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang @xmlrpc_server.dbus_safe(False) 709a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang def device_is_connected(self, address): 710a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang """Checks if a device is connected. 711a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang 712a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang @param address: Address of the device to connect. 713a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang 714a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang @returns: True if device is connected. False otherwise. 715a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang 716a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang """ 717a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang device = self._find_device(address) 718a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang if not device: 719a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang logging.error('Device not found') 720a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang return False 721a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang return self._is_connected(device) 722a532852d21ce53b84c8d00a2d59a63b02d293146Cheng-Yi Chiang 72375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 72475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @xmlrpc_server.dbus_safe(False) 72575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang def disconnect_device(self, address): 72675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """Disconnects a device. 72775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 72875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang Disconnects a device if it is connected. 72975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 73075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @param address: Address of the device to disconnect. 73175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 73275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang @returns: True on success. False otherwise. 73375e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 73475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang """ 73575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device = self._find_device(address) 73675e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if not device: 73775e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.error('Device not found') 73875e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return False 73975e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang if not self._is_connected(device): 74075e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang logging.info('Device is not connected') 74175e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang return True 74275e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang device.Disconnect() 743bd1b6261cd756adf2b1328f93bb6fbdaed8fa6a3Cheng-Yi Chiang return not self._is_connected(device) 74475e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 74575e7094c75b140e33c3b639b1519439d41a6b9bbCheng-Yi Chiang 7464dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnantif __name__ == '__main__': 7474dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant logging.basicConfig(level=logging.DEBUG) 7481c72d7adf4148029baa8846eebe9f7e1a287e10fScott James Remnant handler = logging.handlers.SysLogHandler(address='/dev/log') 7499fd7f46552205dc17e1d888632658bdf1a97d603Christopher Wiley formatter = logging.Formatter( 7508d2cbf363de4aace06c65ddd20ec862431ce928cScott James Remnant 'bluetooth_device_xmlrpc_server: [%(levelname)s] %(message)s') 7519fd7f46552205dc17e1d888632658bdf1a97d603Christopher Wiley handler.setFormatter(formatter) 7524dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant logging.getLogger().addHandler(handler) 7538d2cbf363de4aace06c65ddd20ec862431ce928cScott James Remnant logging.debug('bluetooth_device_xmlrpc_server main...') 7544dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant server = xmlrpc_server.XmlRpcServer( 7554dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant 'localhost', 7568d2cbf363de4aace06c65ddd20ec862431ce928cScott James Remnant constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT) 7578d2cbf363de4aace06c65ddd20ec862431ce928cScott James Remnant server.register_delegate(BluetoothDeviceXmlRpcDelegate()) 7584dcd73f7d38d89063911c74796db9aa68ff8b064Scott James Remnant server.run() 759