• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Lint as: python2, python3
2# Copyright 2020 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Bluetooth tests to check controller role support
7
8In preparation for Nearby features, we need to verify that our device
9controllers support the LE connection roles that we will require
10"""
11
12from __future__ import absolute_import
13
14import logging
15import threading
16import time
17
18import common
19from autotest_lib.server.cros.bluetooth import advertisements_data
20from autotest_lib.server.cros.bluetooth import bluetooth_adapter_tests
21
22DEFAULT_MIN_ADV_INTERVAL = 200
23DEFAULT_MAX_ADV_INTERVAL = 500
24
25
26class bluetooth_AdapterControllerRoleTests(
27        bluetooth_adapter_tests.BluetoothAdapterTests):
28    """Bluetooth controller role tests.
29
30    This class comprises a number of test cases to verify our controllers
31    support the minimum requirements for LE connection states.
32    """
33
34    def pair_adapter_to_device(self, device):
35        """Pairs to device, then disconnects
36
37        For our Nearby tests, we use a peer emulating a HID device to act as
38        the Nearby device. Since HID profile requires bonding for connection to
39        occur, this function exchanges the bonding information as a test
40        prerequisite so the Nearby device can later connected
41
42        @param device: handle to peripheral object
43        """
44
45        self.test_discover_device(device.address)
46        time.sleep(self.TEST_SLEEP_SECS)
47        self.test_pairing(device.address, device.pin, trusted=False)
48        self.test_disconnection_by_adapter(device.address)
49
50
51    def connect_and_test_secondary_device(self, device, secondary_test_func):
52        """Creates connection to a secondary device and tests it
53
54        @param device: handle to peripheral object
55        @param secondary_test_func: function handle to test connection
56        """
57        logging.info('Setting up secondary device')
58        if not self.test_discover_device(device.address):
59            logging.error('connect_and_test_secondary_device exits early as '
60                          'test_discover_device fails')
61            return
62        if not self.test_pairing(device.address, device.pin, trusted=False):
63            logging.error('connect_and_test_secondary_device exits early as '
64                          'test_pairing fails')
65            return
66        time.sleep(self.TEST_SLEEP_SECS)
67        if not self.test_connection_by_adapter(device.address):
68            logging.error('connect_and_test_secondary_device exits early as '
69                          'test_connection_by_adapter fails')
70            return
71        time.sleep(self.TEST_SLEEP_SECS)
72        secondary_test_func(device)
73
74
75    def _receiver_discovery_async(self, device):
76        """Asynchronously discovers and begins advertising from peer
77
78        We want to verify that the DUT is scanning and advertising at the same
79        time. This function returns a thread that waits, discovers the desired
80        device, and then starts advertising back to emulate a Nearby Receiver
81        device.
82
83        @param device: handle to peripheral object
84
85        @returns threading.Thread object with receiver discovery task
86        """
87
88        def _action_receiver_discovery():
89            time.sleep(3)
90            self.test_discover_by_device(device)
91            self.test_device_set_discoverable(device, True)
92
93        thread = threading.Thread(target=_action_receiver_discovery)
94        return thread
95
96    # ---------------------------------------------------------------
97    # Definitions of controller readiness tests
98    # ---------------------------------------------------------------
99
100    ### General test for controller in secondary role
101    def controller_secondary_role_test(self, primary_device, primary_test_func,
102                                       secondary_info=None):
103        """Advertise from DUT and verify we can handle connection as secondary
104
105        Optional secondary device arguments allows us to try test with existing
106        connection, or to establish new secondary connection during test
107
108        @param primary_device: primary device of connection test
109        @param primary_test_func: function to test connection to primary
110        @param secondary_info: Optional tuple with structure
111            (secondary_device_handle, secondary_test_func, use):
112            secondary_device_handle: peer device to test with
113            secondary_test_func: function handle to run connection test
114            device_use: 'pre' - device should be connected before test runs - or
115                        'mid' - device should be connected during test
116        """
117
118        #
119        # Due to crbug/946835, some messages does not reach btmon
120        # causing our tests to fails. This is seen on kernel 3.18 and lower.
121        # Remove this check when the issue is fixed
122        # TODO(crbug/946835)
123        #
124        self.is_supported_kernel_version(self.host.get_kernel_version(),
125                                         '3.19',
126                                         'Test cannnot proceed on this'
127                                         'kernel due to crbug/946835 ')
128
129        self.bluetooth_le_facade = self.bluetooth_facade
130
131        if secondary_info is not None:
132            (secondary_device_handle, secondary_test_func,
133                    device_use) = secondary_info
134
135        # Start fresh, remove DUT from peer's known devices
136        primary_device.RemoveDevice(self.bluetooth_facade.address)
137
138        # Pair the primary device first - necessary for later connection to
139        # secondary device
140        self.pair_adapter_to_device(primary_device)
141        self.test_device_set_discoverable(primary_device, False)
142
143        # If test requires it, connect and test secondary device
144        if secondary_info is not None and device_use == 'pre':
145            self.connect_and_test_secondary_device(
146                secondary_device_handle, secondary_test_func)
147
148        # Register and start advertising instance
149        # We ignore failure because the test isn't able to verify the min/max
150        # advertising intervals, but this is ok.
151        self.test_reset_advertising()
152        self.test_set_advertising_intervals(DEFAULT_MIN_ADV_INTERVAL,
153                                            DEFAULT_MAX_ADV_INTERVAL)
154        self.test_register_advertisement(advertisements_data.ADVERTISEMENTS[0],
155                                         1)
156
157        # Discover DUT from peer
158        self.test_discover_by_device(primary_device)
159        time.sleep(self.TEST_SLEEP_SECS)
160
161        # Connect to DUT from peer, putting DUT in secondary role
162        self.test_connection_by_device(primary_device)
163
164        # If test requires it, connect and test secondary device
165        if secondary_info is not None and device_use == 'mid':
166            self.connect_and_test_secondary_device(
167                secondary_device_handle, secondary_test_func)
168
169        # Try transferring data over connection
170        primary_test_func(primary_device)
171
172        # Handle cleanup of connected devices
173        if secondary_info is not None:
174            self.test_disconnection_by_adapter(secondary_device_handle.address)
175
176        self.test_disconnection_by_device(primary_device)
177        self.test_reset_advertising()
178
179    ### Nearby sender role test
180
181    def nearby_sender_role_test(self, nearby_device, nearby_device_test_func,
182                                secondary_info=None):
183        """Test Nearby Sender role
184
185        Optional secondary device arguments allows us to try test with existing
186        connection, or to establish new secondary connection during test
187
188        @param nearby_device: Device acting as Nearby Receiver in test
189        @param nearby_device_test_func: function to test connection to device
190        @param secondary_info: Optional tuple with structure
191            (secondary_device_handle, secondary_test_func, use):
192            secondary_device_handle: peer device to test with
193            secondary_test_func: function handle to run connection test
194            device_use: 'pre' - device should be connected before test runs - or
195                        'mid' - device should be connected during test
196        """
197
198        #
199        # Due to crbug/946835, some messages does not reach btmon
200        # causing our tests to fails. This is seen on kernel 3.18 and lower.
201        # Remove this check when the issue is fixed
202        # TODO(crbug/946835)
203        #
204        self.is_supported_kernel_version(self.host.get_kernel_version(),
205                                         '3.19',
206                                         'Test cannnot proceed on this'
207                                         'kernel due to crbug/946835 ')
208
209        self.bluetooth_le_facade = self.bluetooth_facade
210
211        if secondary_info is not None:
212            (secondary_device_handle, secondary_test_func,
213                    device_use) = secondary_info
214
215        # Start fresh, remove DUT from nearby device
216        nearby_device.RemoveDevice(self.bluetooth_facade.address)
217
218        # Pair the nearby device first - necessary for later connection to
219        # secondary device
220        self.pair_adapter_to_device(nearby_device)
221
222        # We don't want peer advertising until it hears our broadcast
223        self.test_device_set_discoverable(nearby_device, False)
224
225        # If test requires it, connect and test secondary device
226        if secondary_info is not None and device_use == 'pre':
227            self.connect_and_test_secondary_device(
228                secondary_device_handle, secondary_test_func)
229
230        # Register and start non-connectable advertising instance
231        # We ignore failure because the test isn't able to verify the min/max
232        # advertising intervals, but this is ok.
233        self.test_reset_advertising()
234        self.test_set_advertising_intervals(DEFAULT_MIN_ADV_INTERVAL,
235                                            DEFAULT_MAX_ADV_INTERVAL)
236
237        # For now, advertise connectable advertisement. If we use a broadcast
238        # advertisement, the Pi can't resolve the address and
239        # test_discover_by_device will fail
240        self.test_register_advertisement(advertisements_data.ADVERTISEMENTS[0],
241                                         1)
242
243        # Second thread runs on peer, delays, discovers DUT, and then advertises
244        # itself back
245        peer_discover = self._receiver_discovery_async(nearby_device)
246        peer_discover.start()
247
248        # Verify that we correctly receive advertisement from nearby device
249        self.test_receive_advertisement(address=nearby_device.address,
250                                        timeout=30)
251
252        # Make sure peer thread completes
253        peer_discover.join()
254
255        # Connect to peer from DUT
256        self.test_connection_by_adapter(nearby_device.address)
257
258        # TODO(b/164131633) On 4.4 kernel, sometimes the input device is not
259        # created if we connect a second device too quickly
260        time.sleep(self.TEST_SLEEP_SECS)
261
262        # If test requires it, connect and test secondary device
263        if secondary_info is not None and device_use == 'mid':
264            self.connect_and_test_secondary_device(
265                secondary_device_handle, secondary_test_func)
266
267        time.sleep(self.TEST_SLEEP_SECS)
268
269        # Try data test with nearby device
270        nearby_device_test_func(nearby_device)
271
272        # Handle cleanup of connected devices
273        if secondary_info is not None:
274            self.test_disconnection_by_adapter(secondary_device_handle.address)
275
276        self.test_disconnection_by_adapter(nearby_device.address)
277        self.test_reset_advertising()
278
279    # Nearby receiver role test
280
281    def nearby_receiver_role_test(self, nearby_device, nearby_device_test_func,
282                                  secondary_info=None):
283        """Test Nearby Receiver role
284
285        Optional secondary device arguments allows us to try test with existing
286        connection, or to establish new secondary connection during test
287
288        @param nearby_device: Device acting as Nearby Sender in test
289        @param nearby_device_test_func: function to test connection to device
290        @param secondary_info: Optional tuple with structure
291            (secondary_device_handle, secondary_test_func, use):
292            secondary_device_handle: peer device to test with
293            secondary_test_func: function handle to run connection test
294            device_use: 'pre' - device should be connected before test runs - or
295                        'mid' - device should be connected in middle of test,
296                                during advertisement
297                        'end' - device should be connected at end of test, when
298                                already connected to Nearby device
299        """
300
301        #
302        # Due to crbug/946835, some messages does not reach btmon
303        # causing our tests to fails. This is seen on kernel 3.18 and lower.
304        # Remove this check when the issue is fixed
305        # TODO(crbug/946835)
306        #
307        self.is_supported_kernel_version(self.host.get_kernel_version(),
308                                         '3.19',
309                                         'Test cannnot proceed on this'
310                                         'kernel due to crbug/946835 ')
311
312        self.bluetooth_le_facade = self.bluetooth_facade
313
314        if secondary_info is not None:
315            (secondary_device_handle, secondary_test_func,
316                    device_use) = secondary_info
317
318        # Start fresh, remove device peer
319        nearby_device.RemoveDevice(self.bluetooth_facade.address)
320
321        # If test requires it, connect and test secondary device
322        if secondary_info is not None and device_use == 'pre':
323            self.connect_and_test_secondary_device(
324                secondary_device_handle, secondary_test_func)
325
326        # Verify that we correctly receive advertisement from peer
327        # TODO ideally, peer would be broadcasting non-connectable adv with
328        # 0xFE2C data, but this is not implemented yet on peer
329        self.test_receive_advertisement(address=nearby_device.address,
330                                        timeout=30)
331
332        # Pair the nearby device first - necessary for later connection to
333        # secondary device
334        self.pair_adapter_to_device(nearby_device)
335
336        # Register and start non-connectable advertising instance
337        # We ignore failure because the test isn't able to verify the min/max
338        # advertising intervals, but this is ok.
339        self.test_reset_advertising()
340        self.test_set_advertising_intervals(DEFAULT_MIN_ADV_INTERVAL,
341                                            DEFAULT_MAX_ADV_INTERVAL)
342        self.test_register_advertisement(advertisements_data.ADVERTISEMENTS[0],
343                                         1)
344
345        # If test requires it, connect and test secondary device
346        if secondary_info is not None and device_use == 'mid':
347            self.connect_and_test_secondary_device(
348                secondary_device_handle, secondary_test_func)
349
350        # Discover DUT from peer
351        self.test_discover_by_device(nearby_device)
352
353        # Connect to DUT from peer
354        self.test_connection_by_device(nearby_device)
355
356        # TODO(b/164131633) On 4.4 kernel, sometimes the input device is not
357        # created if we connect a second device too quickly
358        time.sleep(self.TEST_SLEEP_SECS)
359
360        # If test requires it, connect and test secondary device
361        if secondary_info is not None and device_use == 'end':
362            self.connect_and_test_secondary_device(
363                secondary_device_handle, secondary_test_func)
364
365        time.sleep(self.TEST_SLEEP_SECS)
366
367        # Try data test with nearby device
368        nearby_device_test_func(nearby_device)
369
370        self.test_disconnection_by_device(nearby_device)
371        self.test_reset_advertising()
372