| # Copyright 2020 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Bluetooth tests to check controller role support |
| |
| In preparation for Nearby features, we need to verify that our device |
| controllers support the LE connection roles that we will require |
| """ |
| |
| from __future__ import absolute_import |
| |
| import logging |
| import threading |
| import time |
| |
| import common |
| from autotest_lib.server.cros.bluetooth import advertisements_data |
| from autotest_lib.server.cros.bluetooth import bluetooth_adapter_tests |
| |
| DEFAULT_MIN_ADV_INTERVAL = 200 |
| DEFAULT_MAX_ADV_INTERVAL = 500 |
| |
| |
| class bluetooth_AdapterControllerRoleTests( |
| bluetooth_adapter_tests.BluetoothAdapterTests): |
| """Bluetooth controller role tests. |
| |
| This class comprises a number of test cases to verify our controllers |
| support the minimum requirements for LE connection states. |
| """ |
| |
| def pair_adapter_to_device(self, device): |
| """Pairs to device, then disconnects |
| |
| For our Nearby tests, we use a peer emulating a HID device to act as |
| the Nearby device. Since HID profile requires bonding for connection to |
| occur, this function exchanges the bonding information as a test |
| prerequisite so the Nearby device can later connected |
| |
| @param device: handle to peripheral object |
| """ |
| |
| self.test_discover_device(device.address) |
| time.sleep(self.TEST_SLEEP_SECS) |
| self.test_pairing(device.address, device.pin, trusted=True) |
| self.test_disconnection_by_adapter(device.address) |
| |
| |
| def connect_and_test_secondary_device(self, device, secondary_test_func): |
| """Creates connection to a secondary device and tests it |
| |
| @param device: handle to peripheral object |
| @param secondary_test_func: function handle to test connection |
| """ |
| logging.info('Setting up secondary device') |
| self.test_discover_device(device.address) |
| self.test_pairing(device.address, device.pin, trusted=True) |
| time.sleep(self.TEST_SLEEP_SECS) |
| self.test_connection_by_adapter(device.address) |
| time.sleep(self.TEST_SLEEP_SECS) |
| secondary_test_func(device) |
| |
| |
| def _receiver_discovery_async(self, device): |
| """Asynchronously discovers and begins advertising from peer |
| |
| We want to verify that the DUT is scanning and advertising at the same |
| time. This function returns a thread that waits, discovers the desired |
| device, and then starts advertising back to emulate a Nearby Receiver |
| device. |
| |
| @param device: handle to peripheral object |
| |
| @returns threading.Thread object with receiver discovery task |
| """ |
| |
| def _action_receiver_discovery(): |
| time.sleep(3) |
| self.test_discover_by_device(device) |
| self.test_device_set_discoverable(device, True) |
| |
| thread = threading.Thread(target=_action_receiver_discovery) |
| return thread |
| |
| # --------------------------------------------------------------- |
| # Definitions of controller readiness tests |
| # --------------------------------------------------------------- |
| |
| ### General test for controller in secondary role |
| def controller_secondary_role_test(self, primary_device, primary_test_func, |
| secondary_info=None): |
| """Advertise from DUT and verify we can handle connection as secondary |
| |
| Optional secondary device arguments allows us to try test with existing |
| connection, or to establish new secondary connection during test |
| |
| @param primary_device: primary device of connection test |
| @param primary_test_func: function to test connection to primary |
| @param secondary_info: Optional tuple with structure |
| (secondary_device_handle, secondary_test_func, use): |
| secondary_device_handle: peer device to test with |
| secondary_test_func: function handle to run connection test |
| device_use: 'pre' - device should be connected before test runs - or |
| 'mid' - device should be connected during test |
| """ |
| |
| # |
| # Due to crbug/946835, some messages does not reach btmon |
| # causing our tests to fails. This is seen on kernel 3.18 and lower. |
| # Remove this check when the issue is fixed |
| # TODO(crbug/946835) |
| # |
| self.is_supported_kernel_version(self.host.get_kernel_version(), |
| '3.19', |
| 'Test cannnot proceed on this' |
| 'kernel due to crbug/946835 ') |
| |
| self.bluetooth_le_facade = self.bluetooth_facade |
| |
| if secondary_info is not None: |
| (secondary_device_handle, secondary_test_func, |
| device_use) = secondary_info |
| |
| # Start fresh, remove DUT from peer's known devices |
| primary_device.RemoveDevice(self.bluetooth_facade.address) |
| |
| # Pair the primary device first - necessary for later connection to |
| # secondary device |
| self.pair_adapter_to_device(primary_device) |
| self.test_device_set_discoverable(primary_device, False) |
| |
| # If test requires it, connect and test secondary device |
| if secondary_info is not None and device_use == 'pre': |
| self.connect_and_test_secondary_device( |
| secondary_device_handle, secondary_test_func) |
| |
| # Register and start advertising instance |
| # We ignore failure because the test isn't able to verify the min/max |
| # advertising intervals, but this is ok. |
| self.test_reset_advertising() |
| self.test_set_advertising_intervals(DEFAULT_MIN_ADV_INTERVAL, |
| DEFAULT_MAX_ADV_INTERVAL) |
| self.test_register_advertisement(advertisements_data.ADVERTISEMENTS[0], |
| 1, DEFAULT_MIN_ADV_INTERVAL, |
| DEFAULT_MAX_ADV_INTERVAL) |
| |
| # Discover DUT from peer |
| self.test_discover_by_device(primary_device) |
| time.sleep(self.TEST_SLEEP_SECS) |
| |
| # Connect to DUT from peer, putting DUT in secondary role |
| self.test_connection_by_device(primary_device) |
| |
| # If test requires it, connect and test secondary device |
| if secondary_info is not None and device_use == 'mid': |
| self.connect_and_test_secondary_device( |
| secondary_device_handle, secondary_test_func) |
| |
| # Try transferring data over connection |
| primary_test_func(primary_device) |
| |
| # Handle cleanup of connected devices |
| if secondary_info is not None: |
| self.test_disconnection_by_adapter(secondary_device_handle.address) |
| |
| self.test_disconnection_by_device(primary_device) |
| self.test_reset_advertising() |
| |
| ### Nearby sender role test |
| |
| def nearby_sender_role_test(self, nearby_device, nearby_device_test_func, |
| secondary_info=None): |
| """Test Nearby Sender role |
| |
| Optional secondary device arguments allows us to try test with existing |
| connection, or to establish new secondary connection during test |
| |
| @param nearby_device: Device acting as Nearby Receiver in test |
| @param nearby_device_test_func: function to test connection to device |
| @param secondary_info: Optional tuple with structure |
| (secondary_device_handle, secondary_test_func, use): |
| secondary_device_handle: peer device to test with |
| secondary_test_func: function handle to run connection test |
| device_use: 'pre' - device should be connected before test runs - or |
| 'mid' - device should be connected during test |
| """ |
| |
| # |
| # Due to crbug/946835, some messages does not reach btmon |
| # causing our tests to fails. This is seen on kernel 3.18 and lower. |
| # Remove this check when the issue is fixed |
| # TODO(crbug/946835) |
| # |
| self.is_supported_kernel_version(self.host.get_kernel_version(), |
| '3.19', |
| 'Test cannnot proceed on this' |
| 'kernel due to crbug/946835 ') |
| |
| self.bluetooth_le_facade = self.bluetooth_facade |
| |
| if secondary_info is not None: |
| (secondary_device_handle, secondary_test_func, |
| device_use) = secondary_info |
| |
| # Start fresh, remove DUT from nearby device |
| nearby_device.RemoveDevice(self.bluetooth_facade.address) |
| |
| # Pair the nearby device first - necessary for later connection to |
| # secondary device |
| self.pair_adapter_to_device(nearby_device) |
| |
| # We don't want peer advertising until it hears our broadcast |
| self.test_device_set_discoverable(nearby_device, False) |
| |
| # If test requires it, connect and test secondary device |
| if secondary_info is not None and device_use == 'pre': |
| self.connect_and_test_secondary_device( |
| secondary_device_handle, secondary_test_func) |
| |
| # Register and start non-connectable advertising instance |
| # We ignore failure because the test isn't able to verify the min/max |
| # advertising intervals, but this is ok. |
| self.test_reset_advertising() |
| self.test_set_advertising_intervals(DEFAULT_MIN_ADV_INTERVAL, |
| DEFAULT_MAX_ADV_INTERVAL) |
| |
| # For now, advertise connectable advertisement. If we use a broadcast |
| # advertisement, the Pi can't resolve the address and |
| # test_discover_by_device will fail |
| self.test_register_advertisement( |
| advertisements_data.ADVERTISEMENTS[0], 1, |
| DEFAULT_MIN_ADV_INTERVAL, DEFAULT_MAX_ADV_INTERVAL) |
| |
| # Second thread runs on peer, delays, discovers DUT, and then advertises |
| # itself back |
| peer_discover = self._receiver_discovery_async(nearby_device) |
| peer_discover.start() |
| |
| # Verify that we correctly receive advertisement from nearby device |
| self.test_receive_advertisement(address=nearby_device.address, |
| timeout=30) |
| |
| # Make sure peer thread completes |
| peer_discover.join() |
| |
| # Connect to peer from DUT |
| self.test_connection_by_adapter(nearby_device.address) |
| |
| # TODO(b/164131633) On 4.4 kernel, sometimes the input device is not |
| # created if we connect a second device too quickly |
| time.sleep(self.TEST_SLEEP_SECS) |
| |
| # If test requires it, connect and test secondary device |
| if secondary_info is not None and device_use == 'mid': |
| self.connect_and_test_secondary_device( |
| secondary_device_handle, secondary_test_func) |
| |
| time.sleep(self.TEST_SLEEP_SECS) |
| |
| # Try data test with nearby device |
| nearby_device_test_func(nearby_device) |
| |
| # Handle cleanup of connected devices |
| if secondary_info is not None: |
| self.test_disconnection_by_adapter(secondary_device_handle.address) |
| |
| self.test_disconnection_by_adapter(nearby_device.address) |
| self.test_reset_advertising() |
| |
| # Nearby receiver role test |
| |
| def nearby_receiver_role_test(self, nearby_device, nearby_device_test_func, |
| secondary_info=None): |
| """Test Nearby Receiver role |
| |
| Optional secondary device arguments allows us to try test with existing |
| connection, or to establish new secondary connection during test |
| |
| @param nearby_device: Device acting as Nearby Sender in test |
| @param nearby_device_test_func: function to test connection to device |
| @param secondary_info: Optional tuple with structure |
| (secondary_device_handle, secondary_test_func, use): |
| secondary_device_handle: peer device to test with |
| secondary_test_func: function handle to run connection test |
| device_use: 'pre' - device should be connected before test runs - or |
| 'mid' - device should be connected in middle of test, |
| during advertisement |
| 'end' - device should be connected at end of test, when |
| already connected to Nearby device |
| """ |
| |
| # |
| # Due to crbug/946835, some messages does not reach btmon |
| # causing our tests to fails. This is seen on kernel 3.18 and lower. |
| # Remove this check when the issue is fixed |
| # TODO(crbug/946835) |
| # |
| self.is_supported_kernel_version(self.host.get_kernel_version(), |
| '3.19', |
| 'Test cannnot proceed on this' |
| 'kernel due to crbug/946835 ') |
| |
| self.bluetooth_le_facade = self.bluetooth_facade |
| |
| if secondary_info is not None: |
| (secondary_device_handle, secondary_test_func, |
| device_use) = secondary_info |
| |
| # Start fresh, remove device peer |
| nearby_device.RemoveDevice(self.bluetooth_facade.address) |
| |
| # If test requires it, connect and test secondary device |
| if secondary_info is not None and device_use == 'pre': |
| self.connect_and_test_secondary_device( |
| secondary_device_handle, secondary_test_func) |
| |
| # Verify that we correctly receive advertisement from peer |
| # TODO ideally, peer would be broadcasting non-connectable adv with |
| # 0xFE2C data, but this is not implemented yet on peer |
| self.test_receive_advertisement(address=nearby_device.address, |
| timeout=30) |
| |
| # Pair the nearby device first - necessary for later connection to |
| # secondary device |
| self.pair_adapter_to_device(nearby_device) |
| |
| # Register and start non-connectable advertising instance |
| # We ignore failure because the test isn't able to verify the min/max |
| # advertising intervals, but this is ok. |
| self.test_reset_advertising() |
| self.test_set_advertising_intervals(DEFAULT_MIN_ADV_INTERVAL, |
| DEFAULT_MAX_ADV_INTERVAL) |
| self.test_register_advertisement(advertisements_data.ADVERTISEMENTS[0], |
| 1, DEFAULT_MIN_ADV_INTERVAL, |
| DEFAULT_MAX_ADV_INTERVAL) |
| |
| # If test requires it, connect and test secondary device |
| if secondary_info is not None and device_use == 'mid': |
| self.connect_and_test_secondary_device( |
| secondary_device_handle, secondary_test_func) |
| |
| # Discover DUT from peer |
| self.test_discover_by_device(nearby_device) |
| |
| # Connect to DUT from peer |
| self.test_connection_by_device(nearby_device) |
| |
| # TODO(b/164131633) On 4.4 kernel, sometimes the input device is not |
| # created if we connect a second device too quickly |
| time.sleep(self.TEST_SLEEP_SECS) |
| |
| # If test requires it, connect and test secondary device |
| if secondary_info is not None and device_use == 'end': |
| self.connect_and_test_secondary_device( |
| secondary_device_handle, secondary_test_func) |
| |
| time.sleep(self.TEST_SLEEP_SECS) |
| |
| # Try data test with nearby device |
| nearby_device_test_func(nearby_device) |
| |
| self.test_disconnection_by_device(nearby_device) |
| self.test_reset_advertising() |