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