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