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
5from __future__ import absolute_import
6
7import logging
8import time
9
10import common
11from autotest_lib.client.common_lib import error
12from autotest_lib.client.common_lib.cros.bluetooth import bluetooth_socket
13from autotest_lib.server.cros.bluetooth import bluetooth_adapter_tests
14
15DEVICE_ADDRESS = '01:02:03:04:05:06'
16ADDRESS_TYPE = 0
17
18class bluetooth_Health_DefaultStateTest(
19        bluetooth_adapter_tests.BluetoothAdapterTests):
20    """
21    This class implements the default state test
22    and all the helper functions it needs.
23    """
24    version = 1
25
26
27
28    def compare_property(self, bluez_property, mgmt_setting, current_settings):
29        """ Compare bluez property value and Kernel property
30
31            @param bluez_property : Bluez property to be compared
32            @param mgmt_setting   : Bit mask of management setting
33            @param current_settings : Current kernel settings
34            @return : True if bluez property and the current settings agree """
35
36        cur_kernel_value = 1 if mgmt_setting & current_settings else 0
37        return bluez_property == cur_kernel_value
38
39    def default_state_test(self):
40        """Test Default state of Bluetooth adapter after power cycling."""
41
42        # Reset the adapter to the powered off state.
43        self.test_reset_off_adapter()
44
45        # Kernel default state depends on whether the kernel supports the
46        # BR/EDR Allowlist. When this is supported the 'connectable' setting
47        # remains unset and instead page scan is managed by the kernel based
48        # on whether or not a BR/EDR device is in the allowlist.
49        ( commands, events ) = self.read_supported_commands()
50        supports_add_device = bluetooth_socket.MGMT_OP_ADD_DEVICE in commands
51
52        # Read the initial state of the adapter. Verify that it is powered down.
53        ( address, bluetooth_version, manufacturer_id,
54                supported_settings, current_settings, class_of_device,
55                name, short_name ) = self.read_info()
56        self.log_settings('Initial state', current_settings)
57
58        if current_settings & bluetooth_socket.MGMT_SETTING_POWERED:
59            raise error.TestFail('Bluetooth adapter is powered')
60
61        # The other kernel settings (connectable, pairable, etc.) reflect the
62        # initial state before the bluetooth daemon adjusts them - we're ok
63        # with them being on or off during that brief period.
64        #
65
66        # Verify that the Bluetooth Daemon sees that it is also powered down,
67        # non-discoverable and not discovering devices.
68        bluez_properties = self.get_adapter_properties()
69
70        if bluez_properties['Powered']:
71            raise error.TestFail('Bluetooth daemon Powered property does not '
72                                 'match kernel while powered off')
73        if not self.compare_property(bluez_properties['Discoverable'],
74                                     bluetooth_socket.MGMT_SETTING_DISCOVERABLE,
75                                     current_settings):
76            raise error.TestFail('Bluetooth daemon Discoverable property '
77                                 'does not match kernel while powered off')
78        if bluez_properties['Discovering']:
79            raise error.TestFail('Bluetooth daemon believes adapter is '
80                                 'discovering while powered off')
81
82        # Compare with the raw HCI state of the adapter as well, this should
83        # be just not "UP", otherwise something deeply screwy is happening.
84        flags = self.get_dev_info()[3]
85        self.log_flags('Initial state', flags)
86
87        if flags & bluetooth_socket.HCI_UP:
88            raise error.TestFail('HCI UP flag does not match kernel while '
89                                 'powered off')
90
91        # Power on the adapter, then read the state again. Verify that it is
92        # powered up, pairable, but not discoverable.
93        self.test_power_on_adapter()
94        current_settings = self.read_info()[4]
95        self.log_settings("Powered up", current_settings)
96
97        if not current_settings & bluetooth_socket.MGMT_SETTING_POWERED:
98            raise error.TestFail('Bluetooth adapter is not powered')
99
100        # If the kernel does not supports the BR/EDR allowlist, the adapter
101        # should be generically connectable;
102        # if it doesn't, then it depends on previous settings.
103        if not supports_add_device:
104            if not current_settings & bluetooth_socket.MGMT_SETTING_CONNECTABLE:
105                raise error.TestFail('Bluetooth adapter is not connectable '
106                                     'though kernel does not support '
107                                     'BR/EDR allowlist')
108
109        # Verify that the Bluetooth Daemon sees the same state as the kernel
110        # and that it's not discovering.
111        bluez_properties = self.get_adapter_properties()
112
113        if not bluez_properties['Powered']:
114            raise error.TestFail('Bluetooth daemon Powered property does not '
115                                 'match kernel while powered on')
116        if not self.compare_property(bluez_properties['Pairable'],
117                                     bluetooth_socket.MGMT_SETTING_PAIRABLE,
118                                     current_settings):
119            raise error.TestFail('Bluetooth daemon Pairable property does not '
120                                 'match kernel while powered on')
121        if not self.compare_property(bluez_properties['Discoverable'],
122                                     bluetooth_socket.MGMT_SETTING_DISCOVERABLE,
123                                     current_settings):
124            raise error.TestFail('Bluetooth daemon Discoverable property '
125                                 'does not match kernel while powered on')
126        if bluez_properties['Discovering']:
127            raise error.TestFail('Bluetooth daemon believes adapter is '
128                                 'discovering while powered on')
129
130        # Compare with the raw HCI state of the adapter while powered up as
131        # well.
132        flags = self.get_dev_info()[3]
133        self.log_flags('Powered up', flags)
134
135        if not flags & bluetooth_socket.HCI_UP:
136            raise error.TestFail('HCI UP flag does not match kernel while '
137                                 'powered on')
138        if not flags & bluetooth_socket.HCI_RUNNING:
139            raise error.TestFail('HCI RUNNING flag does not match kernel while '
140                                 'powered on')
141        if bool(flags & bluetooth_socket.HCI_ISCAN) != \
142            bool(bluez_properties['Discoverable']):
143            raise error.TestFail('HCI ISCAN flag does not match kernel while '
144                                 'powered on')
145        if flags & bluetooth_socket.HCI_INQUIRY:
146            raise error.TestFail('HCI INQUIRY flag does not match kernel while '
147                                 'powered on')
148
149        # If the kernel does not supports the BR/EDR allowlist, the adapter
150        # should generically connectable, so should it should be in PSCAN
151        # mode. This matches the management API "connectable" setting so far.
152        if not supports_add_device:
153            if not flags & bluetooth_socket.HCI_PSCAN:
154                raise error.TestFail('HCI PSCAN flag not set though kernel'
155                                     'does not supports BR/EDR allowlist')
156
157        # Now we can examine the differences. Try adding and removing a device
158        # from the kernel BR/EDR allowlist. The management API "connectable"
159        # setting should remain off, but we should be able to see the PSCAN
160        # flag come and go.
161        if supports_add_device:
162            # If PSCAN is currently on then device is CONNECTABLE
163            # or a previous add device which was not removed.
164            # Turn on and off DISCOVERABLE to turn off CONNECTABLE and
165            # PSCAN
166            if flags & bluetooth_socket.HCI_PSCAN:
167                if not (current_settings &
168                        bluetooth_socket.MGMT_SETTING_CONNECTABLE):
169                    raise error.TestFail('PSCAN on but device not CONNECTABLE')
170                logging.debug('Toggle Discoverable to turn off CONNECTABLE')
171                self.test_discoverable()
172                self.test_nondiscoverable()
173                current_settings = self.read_info()[4]
174                flags = self.get_dev_info()[3]
175                self.log_flags('Discoverability Toggled', flags)
176                if flags & bluetooth_socket.HCI_PSCAN:
177                    raise error.TestFail('PSCAN on after toggling DISCOVERABLE')
178
179            previous_settings = current_settings
180            previous_flags = flags
181
182            self.add_device(DEVICE_ADDRESS, ADDRESS_TYPE, 1)
183
184            # Wait for a few seconds before reading the settings
185            time.sleep(3)
186            current_settings = self.read_info()[4]
187            self.log_settings("After add device",
188                                              current_settings)
189
190            flags = self.get_dev_info()[3]
191            self.log_flags('After add device', flags)
192
193            if current_settings != previous_settings:
194                self.log_settings("previous settings", previous_settings)
195                self.log_settings("current settings", current_settings)
196                raise error.TestFail(
197                    'Bluetooth adapter settings changed after add device')
198            if not flags & bluetooth_socket.HCI_PSCAN:
199                raise error.TestFail('HCI PSCAN flag not set after add device')
200
201            # Remove the device again, and make sure the PSCAN flag goes away.
202            self.remove_device(DEVICE_ADDRESS, ADDRESS_TYPE)
203
204            # PSCAN is still enabled for a few seconds after remove device
205            # on older devices. Wait for few second before reading the settigs
206            time.sleep(3)
207            current_settings = self.read_info()[4]
208            self.log_settings("After remove device", current_settings)
209
210            flags = self.get_dev_info()[3]
211            self.log_flags('After remove device', flags)
212
213            if current_settings != previous_settings:
214                raise error.TestFail(
215                    'Bluetooth adapter settings changed after remove device')
216            if flags & bluetooth_socket.HCI_PSCAN:
217                raise error.TestFail('HCI PSCAN flag set after remove device')
218
219        # Finally power off the adapter again, and verify that the adapter has
220        # returned to powered down.
221        self.test_power_off_adapter()
222        current_settings = self.read_info()[4]
223        self.log_settings("After power down", current_settings)
224
225        if current_settings & bluetooth_socket.MGMT_SETTING_POWERED:
226            raise error.TestFail('Bluetooth adapter is powered after power off')
227
228        # Verify that the Bluetooth Daemon sees the same state as the kernel.
229        bluez_properties = self.get_adapter_properties()
230
231        if bluez_properties['Powered']:
232            raise error.TestFail('Bluetooth daemon Powered property does not '
233                                 'match kernel after power off')
234        if not self.compare_property(bluez_properties['Discoverable'],
235                                     bluetooth_socket.MGMT_SETTING_DISCOVERABLE,
236                                     current_settings):
237            raise error.TestFail('Bluetooth daemon Discoverable property '
238                                 'does not match kernel after off')
239        if bluez_properties['Discovering']:
240            raise error.TestFail('Bluetooth daemon believes adapter is '
241                                 'discovering after power off')
242
243        # And one last comparison with the raw HCI state of the adapter.
244        flags = self.get_dev_info()[3]
245        self.log_flags('After power down', flags)
246
247        if flags & bluetooth_socket.HCI_UP:
248            raise error.TestFail('HCI UP flag does not match kernel after '
249                                 'power off')
250