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