| # Lint as: python2, python3 |
| # Copyright 2022 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """This class implements a Bluetooth link layer privacy health package""" |
| |
| from __future__ import absolute_import |
| from __future__ import division |
| from __future__ import print_function |
| |
| import logging |
| import time |
| |
| from autotest_lib.client.common_lib import error |
| |
| from autotest_lib.server.cros.bluetooth.bluetooth_adapter_quick_tests \ |
| import (BluetoothAdapterQuickTests, PROFILE_CONNECT_WAIT, SUSPEND_SEC) |
| from autotest_lib.server.cros.bluetooth.bluetooth_adapter_adv_monitor_tests \ |
| import (BluetoothAdapterAdvMonitorTests) |
| from autotest_lib.server.cros.bluetooth.bluetooth_adapter_tests import ( |
| LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, SUSPEND_POWER_DOWN_CHIPSETS, |
| SUSPEND_POWER_DOWN_MODELS, TABLET_MODELS) |
| from autotest_lib.server.cros.bluetooth.bluetooth_adapter_llprivacy_tests \ |
| import (BluetoothAdapterLLPrivacyTests, DEFAULT_RPA_TIMEOUT_SEC, |
| MIN_RPA_TIMEOUT_SEC) |
| from autotest_lib.server.cros.bluetooth.bluetooth_adapter_qr_tests import ( |
| BluetoothAdapterQRTests) |
| from autotest_lib.server.cros.bluetooth.bluetooth_adapter_controller_role_tests\ |
| import bluetooth_AdapterControllerRoleTests |
| from autotest_lib.server.cros.bluetooth.bluetooth_adapter_pairing_tests import ( |
| BluetoothAdapterPairingTests) |
| from autotest_lib.server.cros.bluetooth.bluetooth_adapter_hidreports_tests \ |
| import BluetoothAdapterHIDReportTests |
| |
| # TODO: Remove the list after b/330556485 is resolved. |
| LL_PRIVACY_RESUME_FAIL_CHIPSETS = ['Intel-AC9260', 'Intel-AC9560'] |
| |
| class bluetooth_AdapterLLPrivacyHealth( |
| BluetoothAdapterLLPrivacyTests, BluetoothAdapterQuickTests, |
| BluetoothAdapterAdvMonitorTests, BluetoothAdapterQRTests, |
| BluetoothAdapterPairingTests, BluetoothAdapterHIDReportTests, |
| bluetooth_AdapterControllerRoleTests): |
| """This class implements a Bluetooth ll privacy health package, using |
| methods provided in BluetoothAdapterQuickTests, |
| The package is running several sub batches of tests. |
| A batch is defined as a set of tests, preferably with a common subject, e.g |
| 'LE Health' batch, or the 'Stand Alone Health' batch. |
| The quick health test package is improving test time by doing the minimal |
| cleanups between each test and test batches, saving the auto-test ramp up |
| time of about 90-120 second per test. |
| """ |
| |
| test_wrapper = BluetoothAdapterQuickTests.quick_test_test_decorator |
| batch_wrapper = BluetoothAdapterQuickTests.quick_test_batch_decorator |
| |
| def _test_mouse(self, device): |
| """Function to test the mouse is working. |
| |
| When the peer is using a RPA, the peer device will have two addresses. |
| One is the RPA address which the peer is used in initial pairing. The |
| other one is the public address. |
| |
| In BlueZ, the public address is used in the dbus path. In Floss, the |
| initial RPA is used to create the dbus path for the device. |
| |
| The init_paired_addr in autotest is updated in Floss when pairing is done |
| with RPA. So if the self.floss is true and the init_paired_addr is not |
| None, the init_paired_addr should be used to find the device. |
| """ |
| # b/328691072: WCN3991 suspend fails with device tests |
| # TODO: Remove after the root cause is fixed. |
| if self.bluetooth_facade.get_chipset_name() == 'QCA-WCN3991': |
| return self.test_hid_device_created( |
| device.init_paired_addr if self.floss and isinstance( |
| device.init_paired_addr, str) else device.address) |
| return (self.test_hid_device_created( |
| device.init_paired_addr if self.floss and isinstance( |
| device.init_paired_addr, str) else device.address) |
| and self.test_mouse_left_click(device) |
| and self.test_mouse_move_in_xy(device, -60, 100) |
| and self.test_mouse_scroll_down(device, 70) |
| and self.test_mouse_click_and_drag(device, 90, 30)) |
| |
| def _test_keyboard_with_string(self, device): |
| # b/328691072: WCN3991 suspend fails with device tests |
| # TODO: Remove after the root cause is fixed. |
| if self.bluetooth_facade.get_chipset_name() == 'QCA-WCN3991': |
| return self.test_hid_device_created(device.address) |
| return (self.test_hid_device_created(device.address) |
| and self.test_keyboard_input_from_trace(device, "simple_text")) |
| |
| # --------------------------------------------------------------- |
| # Reconnect after suspend tests |
| # --------------------------------------------------------------- |
| |
| def run_reconnect_device_with_rpa(self, |
| devtuples, |
| iterations=1, |
| auto_reconnect=False, |
| rpa_timeout=None): |
| """ Reconnects a device in privacy mode after suspend/resume with |
| device RPA rotation. |
| |
| @param devtuples: array of tuples consisting of the following |
| * device_type: MOUSE, BLE_MOUSE, etc. |
| * device: meta object for peer device |
| * device_test: Optional; test function to run w/ |
| device (for example, mouse click) |
| @param iterations: number of suspend/resume + reconnect iterations |
| @param auto_reconnect: Expect host to automatically reconnect to peer |
| @param rpa_timeout: RPA address rotation timeout in second |
| """ |
| boot_id = self.host.get_boot_id() |
| try: |
| # Set up the device; any failures should assert |
| for device_type, device, device_test in devtuples: |
| if 'BLE' not in device_type: |
| raise error.TestFail("Only BLE device has RPA.") |
| |
| if rpa_timeout is not None: |
| logging.info('Set RPA timeout to %d', rpa_timeout) |
| self.test_update_rpa_timeout(device, rpa_timeout) |
| |
| self.test_set_device_privacy(device, True) |
| self.test_start_device_advertise_with_rpa(device) |
| logging.info('Device use RPA: %s', device.rpa) |
| previous_rpa = device.rpa |
| self.test_discover_device(device.rpa) |
| self.test_pairing_with_rpa(device) |
| self.test_stop_device_advertise_with_rpa(device) |
| self.test_connection_by_adapter(device.init_paired_addr, |
| device.address) |
| |
| if device_test is not None: |
| self.assert_on_fail(device_test(device)) |
| else: |
| time.sleep(PROFILE_CONNECT_WAIT) |
| |
| for it in range(iterations): |
| logging.info('Running iteration {}/{} of suspend reconnection'. |
| format(it + 1, iterations)) |
| |
| # Start the suspend process |
| suspend = self.suspend_async(suspend_time=SUSPEND_SEC) |
| start_time = self.bluetooth_facade.get_device_utc_time() |
| |
| # Trigger suspend, wait for regular resume, verify we can reconnect |
| # and run device specific test |
| self.test_suspend_and_wait_for_sleep(suspend, |
| sleep_timeout=SUSPEND_SEC) |
| self.test_wait_for_resume(boot_id, |
| suspend, |
| resume_timeout=SUSPEND_SEC, |
| test_start_time=start_time) |
| |
| if not auto_reconnect: |
| for device_type, device, _ in devtuples: |
| if rpa_timeout is not None: |
| logging.info( |
| "Sleep %d s to wait for RPA rotation.", |
| rpa_timeout - SUSPEND_SEC) |
| time.sleep(rpa_timeout - SUSPEND_SEC) |
| self.bluetooth_facade.btmon_start() |
| # LE can't reconnect without advertising/discoverable |
| self.test_start_device_advertise_with_rpa(device) |
| logging.info('Device current RPA: %s', device.rpa) |
| |
| # expect RPA rotation if rpa_timeout is set |
| if rpa_timeout is not None and previous_rpa == device.rpa: |
| logging.warning("RPA does not rotate.") |
| previous_rpa = device.rpa |
| # Make sure we're actually connected |
| connect_status = self.test_device_is_connected( |
| device.init_paired_addr, |
| timeout=45, |
| identity_address=device.address) |
| self.bluetooth_facade.btmon_stop() |
| # Set test as NA if the controller received a public |
| # address advertisement. |
| if connect_status: |
| self.resolved_address_type_check() |
| |
| self.test_stop_device_advertise_with_rpa(device) |
| |
| for _, device, device_test in devtuples: |
| if device_test is not None: |
| self.assert_on_fail(device_test(device)) |
| |
| finally: |
| for _, device, _ in devtuples: |
| self.test_remove_pairing(device.init_paired_addr, |
| identity_address=device.address) |
| |
| self.test_set_device_privacy(device, False) |
| if rpa_timeout is not None: |
| self.test_update_rpa_timeout(device, |
| DEFAULT_RPA_TIMEOUT_SEC) |
| logging.info('Restore RPA timeout to %d', |
| DEFAULT_RPA_TIMEOUT_SEC) |
| |
| def run_reconnect_device(self, |
| devtuples, |
| iterations=1, |
| auto_reconnect=False): |
| """ Reconnects a device after suspend/resume. |
| |
| @param devtuples: array of tuples consisting of the following |
| * device_type: MOUSE, BLE_MOUSE, etc. |
| * device: meta object for peer device |
| * device_test: Optional; test function to run w/ |
| device (for example, mouse click) |
| @params iterations: number of suspend/resume + reconnect iterations |
| @params auto_reconnect: Expect host to automatically reconnect to peer |
| """ |
| boot_id = self.host.get_boot_id() |
| |
| try: |
| # Set up the device; any failures should assert |
| for _, device, device_test in devtuples: |
| self.assert_discover_and_pair(device) |
| self.assert_on_fail( |
| self.test_device_set_discoverable(device, False)) |
| self.assert_on_fail( |
| self.test_connection_by_adapter(device.address)) |
| |
| # Profile connection may not have completed yet and this will |
| # race with a subsequent disconnection (due to suspend). Use the |
| # device test to force profile connect or wait if no test was |
| # given. |
| if device_test is not None: |
| self.assert_on_fail(device_test(device)) |
| else: |
| time.sleep(PROFILE_CONNECT_WAIT) |
| |
| for it in range(iterations): |
| logging.info('Running iteration {}/{} of suspend reconnection'. |
| format(it + 1, iterations)) |
| |
| # Start the suspend process |
| suspend = self.suspend_async(suspend_time=SUSPEND_SEC) |
| start_time = self.bluetooth_facade.get_device_utc_time() |
| |
| # Trigger suspend, wait for regular resume, verify we can reconnect |
| # and run device specific test |
| self.test_suspend_and_wait_for_sleep(suspend, |
| sleep_timeout=SUSPEND_SEC) |
| self.test_wait_for_resume(boot_id, |
| suspend, |
| resume_timeout=SUSPEND_SEC, |
| test_start_time=start_time) |
| |
| # Only reconnect if we don't expect automatic reconnect. |
| # Let the devices initiate connections before the DUT initiates |
| # auto reconnections. |
| # Complete reconnecting all peers before running device tests. |
| # Otherwise, we may have a race between auto reconnection |
| # from the dut and peer initiated connection. See b/177870286 |
| if not auto_reconnect: |
| for device_type, device, _ in devtuples: |
| if 'BLE' in device_type: |
| # LE can't reconnect without |
| # advertising/discoverable |
| self.test_device_set_discoverable(device, True) |
| # Make sure we're actually connected |
| self.test_device_is_connected(device.address) |
| else: |
| # Classic requires peer to initiate a connection to |
| # wake up the dut |
| self.test_connection_by_device(device) |
| |
| for _, device, device_test in devtuples: |
| if device_test is not None: |
| device_test(device) |
| |
| finally: |
| for _, device, __ in devtuples: |
| self.test_remove_pairing(device.address) |
| |
| @test_wrapper('Monitor Object Health Tests', |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def advmon_monitor_health_tests(self): |
| """Tests advertisement monitor object health.""" |
| self.advmon_test_monitor_creation() |
| self.advmon_test_monitor_validity() |
| |
| # TODO(b/150897528) - Dru loses firmware around suspend, which causes bluez |
| # removes all the monitors. |
| @test_wrapper('Interleave Scan Tests', |
| devices={'BLE_MOUSE': 1}, |
| skip_models=SUSPEND_POWER_DOWN_MODELS, |
| skip_chipsets=SUSPEND_POWER_DOWN_CHIPSETS + |
| LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def advmon_interleaved_scan_tests(self): |
| """Tests interleave scan.""" |
| self.advmon_test_interleaved_scan() |
| |
| @test_wrapper('Reconnect Classic HID', |
| devices={'MOUSE': 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def sr_reconnect_classic_hid(self): |
| """ Reconnects a classic HID device after suspend/resume. """ |
| device_type = 'MOUSE' |
| device = self.devices[device_type][0] |
| self.run_reconnect_device([(device_type, device, self._test_mouse)]) |
| |
| @test_wrapper('Reconnect LE HID', |
| devices={'BLE_MOUSE': 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def sr_reconnect_le_hid(self): |
| """ Reconnects a LE HID device after suspend/resume. """ |
| device_type = 'BLE_MOUSE' |
| device = self.devices[device_type][0] |
| self.run_reconnect_device([(device_type, device, self._test_mouse)]) |
| |
| @test_wrapper('Reconnect LE HID', |
| devices={'BLE_MOUSE': 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS + |
| LL_PRIVACY_RESUME_FAIL_CHIPSETS, |
| supports_floss=True) |
| def sr_reconnect_le_hid_with_rpa(self): |
| """ Reconnects a LE HID device in privacy mode after suspend/resume. """ |
| device_type = 'BLE_MOUSE' |
| device = self.devices[device_type][0] |
| self.run_reconnect_device_with_rpa( |
| [(device_type, device, self._test_mouse)], rpa_timeout=30) |
| |
| @test_wrapper('Peer wakeup Classic HID', |
| devices={'MOUSE': 1}, |
| skip_models=TABLET_MODELS + SUSPEND_POWER_DOWN_MODELS, |
| skip_chipsets=SUSPEND_POWER_DOWN_CHIPSETS + |
| LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def sr_peer_wake_classic_hid(self): |
| """ Use classic HID device to wake from suspend. """ |
| device = self.devices['MOUSE'][0] |
| self.run_peer_wakeup_device('MOUSE', |
| device, |
| device_test=self._test_mouse) |
| |
| @test_wrapper('Peer wakeup LE HID', |
| devices={'BLE_MOUSE': 1}, |
| skip_models=TABLET_MODELS + SUSPEND_POWER_DOWN_MODELS, |
| skip_chipsets=SUSPEND_POWER_DOWN_CHIPSETS + |
| LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def sr_peer_wake_le_hid(self): |
| """ Use LE HID device to wake from suspend. """ |
| device = self.devices['BLE_MOUSE'][0] |
| self.run_peer_wakeup_device('BLE_MOUSE', |
| device, |
| device_test=self._test_mouse) |
| |
| # TODO(b/163143005) - Hana can't handle two concurrent HID connections |
| @test_wrapper('Reconnect Multiple Classic HID', |
| devices={ |
| 'MOUSE': 1, |
| 'KEYBOARD': 1 |
| }, |
| skip_models=['hana'], |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def sr_reconnect_multiple_classic_hid(self): |
| """ Reconnects multiple classic HID devices after suspend/resume. """ |
| devices = [('MOUSE', self.devices['MOUSE'][0], self._test_mouse), |
| ('KEYBOARD', self.devices['KEYBOARD'][0], |
| self._test_keyboard_with_string)] |
| self.run_reconnect_device(devices) |
| |
| @test_wrapper('Reconnect one of each classic+LE HID', |
| devices={ |
| 'BLE_MOUSE': 1, |
| 'KEYBOARD': 1 |
| }, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def sr_reconnect_multiple_classic_le_hid(self): |
| """ Reconnects one of each classic and LE HID devices after |
| suspend/resume. |
| """ |
| devices = [('BLE_MOUSE', self.devices['BLE_MOUSE'][0], |
| self._test_mouse), |
| ('KEYBOARD', self.devices['KEYBOARD'][0], |
| self._test_keyboard_with_string)] |
| self.run_reconnect_device(devices) |
| |
| @test_wrapper('Connect Disconnect by Device Loop', |
| devices={'BLE_MOUSE': 1}, |
| flags=['Quick Health'], |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_connect_disconnect_by_device_loop(self): |
| """Run connect/disconnect loop initiated by device. |
| The test also checks that there are no undesired |
| reconnections. |
| """ |
| |
| device = self.devices['BLE_MOUSE'][0] |
| self.connect_disconnect_by_device_loop( |
| device=device, |
| loops=3, |
| device_type='BLE_MOUSE', |
| check_connected_method=self.test_mouse_move_in_xy) |
| |
| @test_wrapper('Connect Disconnect Loop', |
| devices={'BLE_MOUSE': 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_connect_disconnect_loop(self): |
| """Run connect/disconnect loop initiated by DUT. |
| The test also checks that there are no undesired |
| reconnections. |
| TODO(ysahvit) - add connection creation attempts |
| initiated by HID device |
| """ |
| |
| device = self.devices['BLE_MOUSE'][0] |
| self.connect_disconnect_loop(device=device, loops=3) |
| |
| @test_wrapper('HID Reconnect Speed', |
| devices={'BLE_MOUSE': 1}, |
| flags=['Quick Health'], |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_hid_reconnect_speed(self): |
| """Test the speed of a LE HID device reconnect to DUT""" |
| |
| device = self.devices['BLE_MOUSE'][0] |
| self.hid_reconnect_speed(device=device, device_type='BLE_MOUSE') |
| |
| @test_wrapper('Auto Reconnect', |
| devices={'BLE_MOUSE': 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_auto_reconnect(self): |
| """LE reconnection loop by resetting HID and check reconnection""" |
| |
| device = self.devices['BLE_MOUSE'][0] |
| self.auto_reconnect_loop( |
| device=device, |
| loops=3, |
| check_connected_method=self.test_mouse_left_click) |
| |
| @test_wrapper('LE Receiver Role Test', |
| devices={'BLE_KEYBOARD': 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_role_receiver(self): |
| """Tests basic Nearby Receiver role""" |
| |
| self.verify_controller_capability(required_roles=['peripheral'], |
| test_type=self.flag) |
| |
| kbd = self.devices['BLE_KEYBOARD'][0] |
| kbd_test_func = lambda device: self.test_keyboard_input_from_trace( |
| device, 'simple_text') |
| |
| self.nearby_receiver_role_test(kbd, |
| kbd_test_func, |
| use_privacy=self.floss) |
| |
| @test_wrapper('LE Sender Role Test', |
| devices={'BLE_KEYBOARD': 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_role_sender(self): |
| """Tests basic Nearby Sender role""" |
| |
| self.verify_controller_capability(required_roles=['central'], |
| test_type=self.flag) |
| |
| kbd = self.devices['BLE_KEYBOARD'][0] |
| kbd_test_func = lambda device: self.test_keyboard_input_from_trace( |
| device, 'simple_text') |
| |
| self.nearby_sender_role_test(kbd, kbd_test_func) |
| |
| @test_wrapper('LE Sender Role Test During HID', |
| devices={ |
| 'BLE_KEYBOARD': 1, |
| 'BLE_MOUSE': 1 |
| }, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_role_sender_during_hid(self): |
| """Tests Nearby Sender role while already connected to HID device""" |
| |
| self.verify_controller_capability( |
| required_roles=['central-peripheral'], test_type=self.flag) |
| |
| kbd = self.devices['BLE_KEYBOARD'][0] |
| mouse = self.devices['BLE_MOUSE'][0] |
| |
| kbd_test_func = lambda device: self.test_keyboard_input_from_trace( |
| device, 'simple_text') |
| mouse_test_func = self.test_mouse_left_click |
| |
| hid_test_device = (mouse, mouse_test_func, 'pre') |
| self.nearby_sender_role_test(kbd, |
| kbd_test_func, |
| secondary_info=hid_test_device) |
| |
| @test_wrapper('Use Resolving List for RPA Test', |
| devices={"BLE_MOUSE": 1}, |
| minimum_kernel_version='4.19', |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_address_resolution_power_cycle(self): |
| """Test RPA is used when pairing and address resolution is enabled with |
| LL privacy enabled. |
| """ |
| device = self.devices['BLE_MOUSE'][0] |
| self.test_set_device_privacy(device, True) |
| |
| # start advertising and set RPA |
| self.test_start_device_advertise_with_rpa(device) |
| self.test_discover_device(device.rpa) |
| |
| self.test_pairing_with_rpa(device) |
| self.test_stop_device_advertise_with_rpa(device) |
| |
| self.test_power_cycle_with_address_resolution() |
| |
| self.test_remove_pairing(device.init_paired_addr, |
| identity_address=device.address) |
| self.test_set_device_privacy(device, False) |
| |
| @test_wrapper('Pair Remove Use RPA with Privacy Mode Test', |
| devices={"BLE_MOUSE": 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_pair_remove_privacy(self): |
| """Performs discovery test with mouse peripheral and pairing with |
| RPA. |
| """ |
| device = self.devices['BLE_MOUSE'][0] |
| self.test_set_device_privacy(device, True) |
| |
| # start advertising and set RPA |
| self.test_start_device_advertise_with_rpa(device) |
| self.test_discover_device(device.rpa) |
| |
| self.test_pairing_with_rpa(device) |
| self.test_stop_device_advertise_with_rpa(device) |
| self.run_mouse_tests(device=device) |
| |
| self.test_remove_pairing(device.init_paired_addr, |
| identity_address=device.address) |
| # Restore privacy setting |
| self.test_set_device_privacy(device, False) |
| |
| @test_wrapper('Pair Remove Use IRK with Privacy Mode Test', |
| devices={"BLE_MOUSE": 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_pair_remove_with_irk(self): |
| """Performs discovery test with mouse peripheral which is in privacy |
| mode, but not using LE advertising. |
| """ |
| device = self.devices['BLE_MOUSE'][0] |
| self.test_set_device_privacy(device, True) |
| |
| self.test_discover_device(device.address) |
| |
| self._get_btmon_log(lambda: self.test_pairing( |
| device.address, device.pin, trusted=True)) |
| irk_pattern = [ |
| "> ACL Data RX: Handle", "SMP: Identity Information", |
| "Identity resolving key" |
| ] |
| has_irk = self.bluetooth_facade.btmon_find_consecutive(irk_pattern) |
| if not has_irk: |
| raise error.TestNAError("No IRK received.") |
| |
| self.run_mouse_tests(device=device) |
| self.test_remove_pairing(device.address) |
| # Restore privacy setting |
| self.test_set_device_privacy(device, False) |
| |
| @test_wrapper('RPA Timeout Test', |
| devices={"BLE_MOUSE": 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def privacy_rpa_timeout(self): |
| """Change RPA timeout""" |
| device = self.devices['BLE_MOUSE'][0] |
| test_timeout = 40 # timeout should be at least 30s |
| old_timeout = device.GetRPATimeout() |
| if test_timeout == old_timeout: |
| test_timeout = 41 |
| try: |
| logging.info('set timeout to {}'.format(test_timeout)) |
| self.test_update_rpa_timeout(device, test_timeout) |
| wait_time = test_timeout + 2 |
| |
| self.test_set_device_privacy(device, True) |
| self.test_start_device_advertise_with_rpa(device) |
| |
| timeout_half = test_timeout / 2 |
| logging.info('wait {} seconds'.format(timeout_half)) |
| time.sleep(timeout_half) |
| |
| self.test_random_address_updated(device, False) |
| |
| logging.info('wait {} seconds'.format(wait_time - timeout_half)) |
| time.sleep(wait_time - timeout_half) |
| self.test_random_address_updated(device, True) |
| |
| self.test_stop_device_advertise_with_rpa(device) |
| self.test_set_device_privacy(device, False) |
| finally: |
| # restore old value |
| self.test_update_rpa_timeout(device, old_timeout) |
| |
| @test_wrapper('Reconnect Test', |
| devices={"BLE_MOUSE": 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_auto_reconnect_with_privacy(self): |
| """Test auto reconnect after adapter reboot with device RPA rotation.""" |
| device = self.devices['BLE_MOUSE'][0] |
| self.auto_reconnect_loop_with_device_privacy( |
| device, |
| 3, |
| check_connected_method=self.test_mouse_left_click, |
| rpa_timeout=MIN_RPA_TIMEOUT_SEC) |
| |
| @test_wrapper('Reconnect Test', |
| devices={"BLE_MOUSE": 1}, |
| skip_chipsets=LL_PRIVACY_NOT_SUPPORTED_CHIPSETS, |
| supports_floss=True) |
| def le_auto_reconnect_with_privacy_by_device(self): |
| """Test auto reconnect after device disconnect with device RPA rotation.""" |
| device = self.devices['BLE_MOUSE'][0] |
| self.auto_reconnect_loop_with_device_privacy( |
| device, |
| 3, |
| check_connected_method=self.test_mouse_left_click, |
| disconnect_by_device=True, |
| rpa_timeout=MIN_RPA_TIMEOUT_SEC) |
| |
| @test_wrapper('HID Wakeup from Suspend Test', |
| devices={"BLE_MOUSE": 1}, |
| skip_models=TABLET_MODELS + SUSPEND_POWER_DOWN_MODELS, |
| skip_chipsets=SUSPEND_POWER_DOWN_CHIPSETS + |
| LL_PRIVACY_NOT_SUPPORTED_CHIPSETS + |
| LL_PRIVACY_RESUME_FAIL_CHIPSETS, |
| supports_floss=True) |
| def sr_peer_wake_le_hid_with_rpa(self): |
| """Use LE HID to wake from suspend.""" |
| # b/324975178 LL privacy is not planned to launch in BlueZ. Skip |
| # until the bug is fixed. |
| if not self.floss: |
| raise error.TestNAError( |
| "Test not supported in BlueZ for known reason.") |
| device = self.devices['BLE_MOUSE'][0] |
| self.run_hid_wakeup_with_rpa(device, device_test=self._test_mouse) |
| |
| @batch_wrapper("LL Privacy Health") |
| def ll_privacy_batch_run(self, num_iterations=1, test_name=None): |
| """A batch of tests with LL privacy enabled.""" |
| # adv monitor test |
| self.advmon_monitor_health_tests() |
| self.advmon_interleaved_scan_tests() |
| # suspend resume test |
| # b/234975037 we may remove some of the SR tests if they are stabilized |
| self.sr_reconnect_classic_hid() |
| self.sr_reconnect_le_hid() |
| self.sr_peer_wake_classic_hid() |
| self.sr_peer_wake_le_hid() |
| self.sr_reconnect_multiple_classic_hid() |
| self.sr_reconnect_multiple_classic_le_hid() |
| # LE health test |
| self.le_connect_disconnect_by_device_loop() |
| self.le_connect_disconnect_loop() |
| self.le_hid_reconnect_speed() |
| self.le_auto_reconnect() |
| # LE role test |
| self.le_role_receiver() |
| self.le_role_sender() |
| self.le_role_sender_during_hid() |
| # LE privacy mode test |
| self.le_address_resolution_power_cycle() |
| self.le_pair_remove_privacy() |
| self.le_pair_remove_with_irk() |
| self.privacy_rpa_timeout() |
| self.le_auto_reconnect_with_privacy_by_device() |
| self.le_auto_reconnect_with_privacy() |
| self.sr_peer_wake_le_hid_with_rpa() |
| self.sr_reconnect_le_hid_with_rpa() |
| |
| def run_once(self, |
| host, |
| num_iterations=1, |
| args_dict=None, |
| peer_required=True, |
| test_name=None, |
| flag='Quick Health', |
| llprivacy=True, |
| floss=False): |
| """Run the package of Bluetooth LL privacy health tests. Currently, |
| the tests are directly copied from other test packages, but with |
| the LL privacy enabled. |
| The reason to copy tests directly instead of deriving from other test |
| classes is a limitation of using the "import pattern" as described in |
| crbug/992796, b/138597710, b/144429218. Also we do not schedule batch |
| job in the lab. Creating separate job gives us more control in terms |
| of test pre-conditions and we can implement LL privacy specific tests |
| if necessary. |
| |
| @param host: the DUT, usually a chromebook |
| @param num_iterations: the number of rounds to execute the test |
| @param test_name: the test to run or None for all tests |
| @param flag: run tests with this flag (default: Quick Health) |
| """ |
| |
| # Init the quick test and start the package |
| self.quick_test_init(host, |
| use_btpeer=peer_required, |
| flag=flag, |
| args_dict=args_dict, |
| llprivacy=llprivacy, |
| floss=floss) |
| self.ll_privacy_batch_run(num_iterations, test_name) |
| # End and cleanup test package |
| self.quick_test_cleanup() |