1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6
7from autotest_lib.client.common_lib import error
8from autotest_lib.client.common_lib.cros.bluetooth import bluetooth_socket
9from autotest_lib.server.cros.bluetooth import bluetooth_test
10
11
12class bluetooth_Sanity_DefaultState(bluetooth_test.BluetoothTest):
13    """
14    Verify that the Bluetooth adapter has correct state.
15    """
16    version = 1
17
18    def _log_settings(self, msg, settings):
19        strs = []
20        if settings & bluetooth_socket.MGMT_SETTING_POWERED:
21            strs.append("POWERED")
22        if settings & bluetooth_socket.MGMT_SETTING_CONNECTABLE:
23            strs.append("CONNECTABLE")
24        if settings & bluetooth_socket.MGMT_SETTING_FAST_CONNECTABLE:
25            strs.append("FAST-CONNECTABLE")
26        if settings & bluetooth_socket.MGMT_SETTING_DISCOVERABLE:
27            strs.append("DISCOVERABLE")
28        if settings & bluetooth_socket.MGMT_SETTING_PAIRABLE:
29            strs.append("PAIRABLE")
30        if settings & bluetooth_socket.MGMT_SETTING_LINK_SECURITY:
31            strs.append("LINK-SECURITY")
32        if settings & bluetooth_socket.MGMT_SETTING_SSP:
33            strs.append("SSP")
34        if settings & bluetooth_socket.MGMT_SETTING_BREDR:
35            strs.append("BR/EDR")
36        if settings & bluetooth_socket.MGMT_SETTING_HS:
37            strs.append("HS")
38        if settings & bluetooth_socket.MGMT_SETTING_LE:
39            strs.append("LE")
40        logging.debug(msg + ': %s', " ".join(strs))
41
42    def _log_flags(self, msg, flags):
43        strs = []
44        if flags & bluetooth_socket.HCI_UP:
45            strs.append("UP")
46        else:
47            strs.append("DOWN")
48        if flags & bluetooth_socket.HCI_INIT:
49            strs.append("INIT")
50        if flags & bluetooth_socket.HCI_RUNNING:
51            strs.append("RUNNING")
52        if flags & bluetooth_socket.HCI_PSCAN:
53            strs.append("PSCAN")
54        if flags & bluetooth_socket.HCI_ISCAN:
55            strs.append("ISCAN")
56        if flags & bluetooth_socket.HCI_AUTH:
57            strs.append("AUTH")
58        if flags & bluetooth_socket.HCI_ENCRYPT:
59            strs.append("ENCRYPT")
60        if flags & bluetooth_socket.HCI_INQUIRY:
61            strs.append("INQUIRY")
62        if flags & bluetooth_socket.HCI_RAW:
63            strs.append("RAW")
64        logging.debug(msg + ' [HCI]: %s', " ".join(strs))
65
66    def run_once(self):
67        # Reset the adapter to the powered off state.
68        if not self.device.reset_off():
69            raise error.TestFail('DUT could not be reset to initial state')
70
71        # Kernel default state depends on whether the kernel supports the
72        # BR/EDR Whitelist. When this is supported the 'connectable' setting
73        # remains unset and instead page scan is managed by the kernel based
74        # on whether or not a BR/EDR device is in the whitelist.
75        ( commands, events ) = self.device.read_supported_commands()
76        supports_add_device = bluetooth_socket.MGMT_OP_ADD_DEVICE in commands
77
78        # Read the initial state of the adapter. Verify that it is powered down.
79        ( address, bluetooth_version, manufacturer_id,
80                supported_settings, current_settings, class_of_device,
81                name, short_name ) = self.device.read_info()
82        self._log_settings('Initial state', current_settings)
83
84        if current_settings & bluetooth_socket.MGMT_SETTING_POWERED:
85            raise error.TestFail('Bluetooth adapter is powered')
86
87        # The other kernel settings (connectable, pairable, etc.) reflect the
88        # initial state before the bluetooth daemon adjusts them - we're ok
89        # with them being on or off during that brief period.
90        #
91        # Except for discoverable - that one should be off.
92        if current_settings & bluetooth_socket.MGMT_SETTING_DISCOVERABLE:
93            raise error.TestFail('Bluetooth adapter would be discoverable '
94                                 'during power on')
95
96        # Verify that the Bluetooth Daemon sees that it is also powered down,
97        # non-discoverable and not discovering devices.
98        bluez_properties = self.device.get_adapter_properties()
99
100        if bluez_properties['Powered']:
101            raise error.TestFail('Bluetooth daemon Powered property does not '
102                                 'match kernel while powered off')
103        if bluez_properties['Discoverable']:
104            raise error.TestFail('Bluetooth daemon Discoverable property '
105                                 'does not match kernel while powered off')
106        if bluez_properties['Discovering']:
107            raise error.TestFail('Bluetooth daemon believes adapter is '
108                                 'discovering while powered off')
109
110        # Compare with the raw HCI state of the adapter as well, this should
111        # be just not "UP", otherwise something deeply screwy is happening.
112        flags = self.device.get_dev_info()[3]
113        self._log_flags('Initial state', flags)
114
115        if flags & bluetooth_socket.HCI_UP:
116            raise error.TestFail('HCI UP flag does not match kernel while '
117                                 'powered off')
118
119        # Power on the adapter, then read the state again. Verify that it is
120        # powered up, pairable, but not discoverable.
121        self.device.set_powered(True)
122        current_settings = self.device.read_info()[4]
123        self._log_settings("Powered up", current_settings)
124
125        if not current_settings & bluetooth_socket.MGMT_SETTING_POWERED:
126            raise error.TestFail('Bluetooth adapter is not powered')
127        if not current_settings & bluetooth_socket.MGMT_SETTING_PAIRABLE:
128            raise error.TestFail('Bluetooth adapter is not pairable')
129
130        if current_settings & bluetooth_socket.MGMT_SETTING_DISCOVERABLE:
131            raise error.TestFail('Bluetooth adapter is discoverable')
132
133        # If the kernel supports the BR/EDR whitelist, the adapter should _not_
134        # be generically connectable; if it doesn't, it should be.
135        if supports_add_device:
136            if current_settings & bluetooth_socket.MGMT_SETTING_CONNECTABLE:
137                raise error.TestFail('Bluetooth adapter is connectable')
138        elif not current_settings & bluetooth_socket.MGMT_SETTING_CONNECTABLE:
139            raise error.TestFail('Bluetooth adapter is not connectable')
140
141        # Verify that the Bluetooth Daemon sees the same state as the kernel
142        # and that it's not discovering.
143        bluez_properties = self.device.get_adapter_properties()
144
145        if not bluez_properties['Powered']:
146            raise error.TestFail('Bluetooth daemon Powered property does not '
147                                 'match kernel while powered on')
148        if not bluez_properties['Pairable']:
149            raise error.TestFail('Bluetooth daemon Pairable property does not '
150                                 'match kernel while powered on')
151
152        if bluez_properties['Discoverable']:
153            raise error.TestFail('Bluetooth daemon Discoverable property '
154                                 'does not match kernel while powered on')
155        if bluez_properties['Discovering']:
156            raise error.TestFail('Bluetooth daemon believes adapter is '
157                                 'discovering while powered on')
158
159        # Compare with the raw HCI state of the adapter while powered up as
160        # well.
161        flags = self.device.get_dev_info()[3]
162        self._log_flags('Powered up', flags)
163
164        if not flags & bluetooth_socket.HCI_UP:
165            raise error.TestFail('HCI UP flag does not match kernel while '
166                                 'powered on')
167        if not flags & bluetooth_socket.HCI_RUNNING:
168            raise error.TestFail('HCI RUNNING flag does not match kernel while '
169                                 'powered on')
170        if flags & bluetooth_socket.HCI_ISCAN:
171            raise error.TestFail('HCI ISCAN flag does not match kernel while '
172                                 'powered on')
173        if flags & bluetooth_socket.HCI_INQUIRY:
174            raise error.TestFail('HCI INQUIRY flag does not match kernel while '
175                                 'powered on')
176
177        # If the kernel supports the BR/EDR whitelist, the adapter isn't
178        # supposed to be generically connectable, so should _not_ be in PSCAN
179        # mode yet. If it doesn't, it should be. This matches the management
180        # API "connectable" setting so far.
181        if supports_add_device:
182            if flags & bluetooth_socket.HCI_PSCAN:
183                raise error.TestFail('HCI PSCAN flag does not match kernel '
184                                     'while powered on')
185        elif not flags & bluetooth_socket.HCI_PSCAN:
186                raise error.TestFail('HCI PSCAN flag does not match kernel '
187                                     'while powered on')
188
189        # Now we can examine the differences. Try adding and removing a device
190        # from the kernel BR/EDR whitelist. The management API "connectable"
191        # setting should remain off, but we should be able to see the PSCAN
192        # flag come and go.
193        if supports_add_device:
194            previous_settings = current_settings
195            previous_flags = flags
196
197            self.device.add_device('01:02:03:04:05:06', 0, 1)
198
199            current_settings = self.device.read_info()[4]
200            self._log_settings("After add device", current_settings)
201
202            flags = self.device.get_dev_info()[3]
203            self._log_flags('After add device', flags)
204
205            if current_settings != previous_settings:
206                raise error.TestFail(
207                    'Bluetooth adapter settings changed after add device')
208            if not flags & bluetooth_socket.HCI_PSCAN:
209                raise error.TestFail('HCI PSCAN flag not set after add device')
210
211            # Remove the device again, and make sure the PSCAN flag goes away.
212            self.device.remove_device('01:02:03:04:05:06', 0)
213
214            current_settings = self.device.read_info()[4]
215            self._log_settings("After remove device", current_settings)
216
217            flags = self.device.get_dev_info()[3]
218            self._log_flags('After remove device', flags)
219
220            if current_settings != previous_settings:
221                raise error.TestFail(
222                    'Bluetooth adapter settings changed after remove device')
223            if flags & bluetooth_socket.HCI_PSCAN:
224                raise error.TestFail('HCI PSCAN flag set after add device')
225
226        # Finally power off the adapter again, and verify that the adapter has
227        # returned to powered down.
228        self.device.set_powered(False)
229        current_settings = self.device.read_info()[4]
230        self._log_settings("After power down", current_settings)
231
232        if current_settings & bluetooth_socket.MGMT_SETTING_POWERED:
233            raise error.TestFail('Bluetooth adapter is powered after power off')
234
235        if current_settings & bluetooth_socket.MGMT_SETTING_DISCOVERABLE:
236            raise error.TestFail('Bluetooth adapter would be discoverable '
237                                 'during next power on')
238
239        # Verify that the Bluetooth Daemon sees the same state as the kernel.
240        bluez_properties = self.device.get_adapter_properties()
241
242        if bluez_properties['Powered']:
243            raise error.TestFail('Bluetooth daemon Powered property does not '
244                                 'match kernel after power off')
245        if bluez_properties['Discoverable']:
246            raise error.TestFail('Bluetooth daemon Discoverable property '
247                                 'does not match kernel after off')
248        if bluez_properties['Discovering']:
249            raise error.TestFail('Bluetooth daemon believes adapter is '
250                                 'discovering after power off')
251
252        # And one last comparison with the raw HCI state of the adapter.
253        flags = self.device.get_dev_info()[3]
254        self._log_flags('After power down', flags)
255
256        if flags & bluetooth_socket.HCI_UP:
257            raise error.TestFail('HCI UP flag does not match kernel after '
258                                 'power off')
259