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