1# Lint as: python2, python3 2# Copyright 2019 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""" 7Server side bluetooth tests on adapter pairing and connecting to a bluetooth 8HID device. 9""" 10 11from __future__ import absolute_import 12from __future__ import division 13from __future__ import print_function 14 15import logging 16import time 17 18import common 19from autotest_lib.server.cros.bluetooth import bluetooth_adapter_tests 20from six.moves import range 21 22 23class BluetoothAdapterPairingTests( 24 bluetooth_adapter_tests.BluetoothAdapterTests): 25 """Server side bluetooth adapter pairing and connecting to bluetooth device 26 27 This test tries to verify that the adapter of the DUT could 28 pair and connect to a bluetooth HID device correctly. 29 30 In particular, the following subtests are performed. Look at the 31 docstrings of the subtests for more details. 32 - 33 34 Refer to BluetoothAdapterTests for all subtests performed in this test. 35 36 """ 37 38 # TODO(josephsih): Reduce the sleep intervals to speed up the tests. 39 PAIR_TEST_SLEEP_SECS = 5 40 41 def pairing_test(self, device, check_connected_method=lambda device: True, 42 pairing_twice=False, suspend_resume=False, reboot=False): 43 """Running Bluetooth adapter tests about pairing to a device.""" 44 45 # Reset the adapter to forget previously paired devices if any. 46 if not self.test_reset_on_adapter(): 47 return 48 49 # The adapter must be set to the pairable state. 50 if not self.test_pairable(): 51 return 52 53 # Test if the adapter could discover the target device. 54 time.sleep(self.PAIR_TEST_SLEEP_SECS) 55 if not self.test_discover_device(device.address): 56 return 57 58 # Test if the discovered device class of service is correct. 59 if not self.test_device_class_of_service(device.address, 60 device.class_of_service): 61 return 62 63 # Test if the discovered device class of device is correct. 64 if not self.test_device_class_of_device(device.address, 65 device.class_of_device): 66 return 67 68 # Verify that the adapter could pair with the device. 69 # Also set the device trusted when pairing is done. 70 # Device will be connected at the end of pairing. 71 if not self.test_pairing(device.address, device.pin, trusted=True): 72 return 73 74 # Test if the discovered device name is correct. 75 # Sometimes, it takes quite a long time after discovering 76 # the device (more than 60 seconds) to resolve the device name. 77 # Hence, it is safer to test the device name after pairing and 78 # connection is done. 79 if not self.test_device_name(device.address, device.name): 80 return 81 82 # Run hid test to make sure profile is connected 83 if not check_connected_method(device): 84 return 85 86 # Test if the device is still connected after suspend/resume. 87 if suspend_resume: 88 self.suspend_resume() 89 90 time.sleep(self.PAIR_TEST_SLEEP_SECS) 91 if not self.test_device_is_paired(device.address): 92 return 93 94 95 # check if peripheral is connected after suspend resume 96 if not self.ignore_failure(check_connected_method, device): 97 logging.info("device not connected after suspend_resume") 98 self.test_connection_by_device(device) 99 time.sleep(self.PAIR_TEST_SLEEP_SECS) 100 if not check_connected_method(device): 101 return 102 else: 103 logging.info("device remains connected after suspend_resume") 104 105 time.sleep(self.PAIR_TEST_SLEEP_SECS) 106 if not self.test_device_name(device.address, device.name): 107 return 108 109 # Test if the device is still connected after reboot. 110 # if reboot: 111 # self.host.reboot() 112 113 # time.sleep(self.PAIR_TEST_SLEEP_SECS) 114 # self.test_device_is_paired(device.address) 115 116 # # After a reboot, we need to wake the peripheral 117 # # as it is not connected. 118 # time.sleep(self.PAIR_TEST_SLEEP_SECS) 119 # self.test_connection_by_adapter(device.address) 120 121 # time.sleep(self.PAIR_TEST_SLEEP_SECS) 122 # self.test_device_is_connected(device.address) 123 124 # time.sleep(self.PAIR_TEST_SLEEP_SECS) 125 # self.test_device_name(device.address, device.name) 126 127 # Verify that the adapter could disconnect the device. 128 if not self.test_disconnection_by_adapter(device.address): 129 return 130 131 time.sleep(self.PAIR_TEST_SLEEP_SECS) 132 133 def test_connection(): 134 """Tests connection inited by either the device or the adapter""" 135 if device.can_init_connection: 136 # Verify that the device could initiate the connection. 137 if not self.test_connection_by_device(device): 138 return False 139 140 # With raspberry pi peer, it takes a moment before the device is 141 # registered as an input device. Without delay, the input recorder 142 # doesn't find the device 143 time.sleep(1) 144 return check_connected_method(device) 145 # Adapter inited connection. 146 # Reconnect so that we can test disconnection from the kit. 147 return self.test_connection_by_adapter(device.address) 148 149 if not test_connection(): 150 return 151 152 def test_disconnection(): 153 """Tests disconnection inited by either the device or the adapter""" 154 # TODO(alent): Needs a new capability, but this is a good proxy 155 if device.can_init_connection: 156 # Verify that the device could initiate the disconnection. 157 return self.test_disconnection_by_device(device) 158 # Adapter inited connection. 159 return self.test_disconnection_by_adapter(device.address) 160 161 if not test_disconnection(): 162 return 163 164 # Verify that the adapter could remove the paired device. 165 if not self.test_remove_pairing(device.address): 166 return 167 168 # Check if the device could be re-paired after being forgotten. 169 if pairing_twice: 170 # Test if the adapter could discover the target device again. 171 time.sleep(self.PAIR_TEST_SLEEP_SECS) 172 if not self.test_discover_device(device.address): 173 return 174 175 # Verify that the adapter could pair with the device again. 176 # Also set the device trusted when pairing is done. 177 time.sleep(self.PAIR_TEST_SLEEP_SECS) 178 if not self.test_pairing(device.address, device.pin, trusted=True): 179 return 180 181 # Verify that the adapter could remove the paired device again. 182 time.sleep(self.PAIR_TEST_SLEEP_SECS) 183 self.test_remove_pairing(device.address) 184 185 186 def connect_disconnect_loop(self, device, loops): 187 """Perform a connect disconnect loop test""" 188 189 # First pair and disconnect, to emulate real life scenario 190 self.test_discover_device(device.address) 191 # self.bluetooth_facade.is_discovering() doesn't work as expected: 192 # crbug:905374 193 # self.test_stop_discovery() 194 time.sleep(self.PAIR_TEST_SLEEP_SECS) 195 self.test_pairing(device.address, device.pin, trusted=False) 196 197 # Verify device is now connected 198 self.test_device_is_connected(device.address) 199 self.test_hid_device_created(device.address) 200 201 # Disconnect the device 202 self.test_disconnection_by_adapter(device.address) 203 total_duration_by_adapter = 0 204 loop_cnt = 0 205 for i in range(0, loops): 206 207 # Verify device didn't connect automatically 208 time.sleep(2) 209 self.test_device_is_not_connected(device.address) 210 211 start_time = time.time() 212 self.test_connection_by_adapter(device.address) 213 end_time = time.time() 214 time_diff = end_time - start_time 215 216 # Verify device is now connected 217 self.test_device_is_connected(device.address) 218 self.test_hid_device_created(device.address) 219 220 self.test_disconnection_by_adapter(device.address) 221 222 if not bool(self.fails): 223 loop_cnt += 1 224 total_duration_by_adapter += time_diff 225 logging.info('%d: Connection establishment duration %f sec', 226 i, time_diff) 227 else: 228 break 229 230 if not bool(self.fails): 231 logging.info('Average duration (by adapter) %f sec', 232 total_duration_by_adapter/loop_cnt) 233 234 235 def connect_disconnect_by_device_loop( 236 self, 237 device, 238 loops, 239 device_type, 240 check_connected_method=lambda device: True): 241 """Perform a connect disconnect loop test""" 242 243 # Reset the adapter to forget previously paired devices if any. 244 self.test_reset_on_adapter() 245 self.test_pairable() 246 # First pair and disconnect, to emulate real life scenario 247 self.test_discover_device(device.address) 248 time.sleep(self.PAIR_TEST_SLEEP_SECS) 249 self.test_pairing(device.address, device.pin, trusted=True) 250 251 # Verify device is now connected 252 self.test_device_is_connected(device.address) 253 self.test_hid_device_created(device.address) 254 255 # Make device not discoverable and disconnect 256 self.test_device_set_discoverable(device, False) 257 self.test_disconnection_by_device(device) 258 259 total_reconnection_duration = 0 260 loop_cnt = 0 261 for i in range(0, loops): 262 263 # Verify device didn't connect automatically 264 time.sleep(2) 265 self.test_device_is_not_connected(device.address) 266 267 start_time = time.time() 268 if 'BLE' in device_type: 269 self.test_device_set_discoverable(device, True) 270 self.test_device_is_connected(device.address, 271 sleep_interval=0.1) 272 else: 273 self.test_connection_by_device(device, post_connection_delay=0) 274 275 check_connected_method(device) 276 end_time = time.time() 277 time_diff = end_time - start_time 278 279 if 'BLE' in device_type: 280 self.test_device_set_discoverable(device, False) 281 282 self.test_disconnection_by_device(device) 283 284 if not bool(self.fails): 285 loop_cnt += 1 286 total_reconnection_duration += time_diff 287 logging.info('%d: Connection establishment duration %f sec', i, 288 time_diff) 289 else: 290 break 291 292 self.test_remove_pairing(device.address) 293 if not bool(self.fails): 294 average_reconnection_duration = total_reconnection_duration / loop_cnt 295 logging.info('Average duration (by device) %f sec', 296 average_reconnection_duration) 297 return average_reconnection_duration 298 299 300 def auto_reconnect_loop(self, 301 device, 302 loops, 303 check_connected_method=lambda device: True, 304 restart_adapter=False): 305 """Running a loop to verify the paired peer can auto reconnect""" 306 307 # Let the adapter pair, and connect to the target device first 308 self.test_discover_device(device.address) 309 self.test_pairing(device.address, device.pin, trusted=True) 310 311 # Verify device is now connected 312 self.test_connection_by_adapter(device.address) 313 self.test_hid_device_created(device.address) 314 315 total_reconnection_duration = 0 316 loop_cnt = 0 317 for i in range(loops): 318 # Restart either the adapter or the peer 319 if restart_adapter: 320 self.test_power_off_adapter() 321 self.test_power_on_adapter() 322 start_time = time.time() 323 else: 324 # Restart and clear peer HID device 325 self.restart_peers() 326 start_time = time.time() 327 328 # Verify that the device is reconnected. Wait for the input device 329 # to become available before checking the profile connection. 330 self.test_device_is_connected(device.address) 331 self.test_hid_device_created(device.address) 332 333 check_connected_method(device) 334 end_time = time.time() 335 time_diff = end_time - start_time 336 337 if not bool(self.fails): 338 total_reconnection_duration += time_diff 339 loop_cnt += 1 340 logging.info('%d: Reconnection duration %f sec', i, time_diff) 341 else: 342 break 343 344 if not bool(self.fails): 345 logging.info('Average Reconnection duration %f sec', 346 total_reconnection_duration/loop_cnt) 347 348 349 def hid_reconnect_speed(self, device, device_type): 350 """Test the HID device reconnect speed 351 352 @param device: the meta data with the peer device 353 @param device_type: The device type (used to check if it's LE) 354 """ 355 356 duration = self.connect_disconnect_by_device_loop( 357 device=device, 358 loops=3, 359 device_type=device_type, 360 check_connected_method=self.test_hid_device_created_speed) 361 if duration is not None: 362 self.test_hid_device_reconnect_time(duration, device_type) 363