blob: 197caa1465b919ed76830d6706f0d6a5f1c09b97 [file] [log] [blame]
Mark De Ruyterd9c540a2018-05-04 11:21:55 -07001#!/usr/bin/env python3
tturney1bdf77d2015-12-28 17:46:13 -08002#
3# Copyright (C) 2016 The Android Open Source Project
Ang Li73697b32015-12-03 00:41:53 +00004#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16
Ang Li5bd83f32016-05-23 14:39:38 -070017import logging
Ang Li73697b32015-12-03 00:41:53 +000018import random
19import pprint
20import string
tturney50f851d2016-07-07 11:07:37 -070021from queue import Empty
Phillip Walker827112a2016-09-08 16:27:19 -070022import queue
Ang Li73697b32015-12-03 00:41:53 +000023import threading
24import time
tturney03a45ae2016-05-24 15:36:05 -070025from acts import utils
tprotopopov2e18bbb2018-10-02 10:29:01 -070026import re
tturneye3170f02016-05-19 14:37:00 -070027from subprocess import call
Ang Li73697b32015-12-03 00:41:53 +000028
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -080029from acts.test_utils.bt.bt_constants import bits_per_samples
30from acts.test_utils.bt.bt_constants import channel_modes
31from acts.test_utils.bt.bt_constants import codec_types
32from acts.test_utils.bt.bt_constants import codec_priorities
33from acts.test_utils.bt.bt_constants import sample_rates
tturneyec1b8f52017-07-26 07:35:06 -070034from acts.test_utils.bt.bt_constants import adv_fail
35from acts.test_utils.bt.bt_constants import adv_succ
36from acts.test_utils.bt.bt_constants import advertising_set_started
37from acts.test_utils.bt.bt_constants import advertising_set_stopped
38from acts.test_utils.bt.bt_constants import advertising_set_on_own_address_read
39from acts.test_utils.bt.bt_constants import advertising_set_stopped
40from acts.test_utils.bt.bt_constants import advertising_set_enabled
41from acts.test_utils.bt.bt_constants import advertising_set_data_set
42from acts.test_utils.bt.bt_constants import advertising_set_scan_response_set
43from acts.test_utils.bt.bt_constants import advertising_set_parameters_update
44from acts.test_utils.bt.bt_constants import \
45 advertising_set_periodic_parameters_updated
46from acts.test_utils.bt.bt_constants import advertising_set_periodic_data_set
47from acts.test_utils.bt.bt_constants import advertising_set_periodic_enable
48from acts.test_utils.bt.bt_constants import batch_scan_not_supported_list
49from acts.test_utils.bt.bt_constants import batch_scan_result
50from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
51from acts.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -080052from acts.test_utils.bt.bt_constants import bluetooth_a2dp_codec_config_changed
tturneyec1b8f52017-07-26 07:35:06 -070053from acts.test_utils.bt.bt_constants import bluetooth_off
54from acts.test_utils.bt.bt_constants import bluetooth_on
55from acts.test_utils.bt.bt_constants import \
56 bluetooth_profile_connection_state_changed
57from acts.test_utils.bt.bt_constants import bt_default_timeout
58from acts.test_utils.bt.bt_constants import bt_discovery_timeout
59from acts.test_utils.bt.bt_constants import bt_profile_states
tturneyb72742f2017-08-29 17:46:50 -070060from acts.test_utils.bt.bt_constants import bt_profile_constants
tturneyec1b8f52017-07-26 07:35:06 -070061from acts.test_utils.bt.bt_constants import bt_rfcomm_uuids
Timofey Protopopova47c8812018-03-28 11:37:31 -070062from acts.test_utils.bt.bt_constants import bluetooth_socket_conn_test_uuid
tturneyec1b8f52017-07-26 07:35:06 -070063from acts.test_utils.bt.bt_constants import bt_scan_mode_types
64from acts.test_utils.bt.bt_constants import btsnoop_last_log_path_on_device
65from acts.test_utils.bt.bt_constants import btsnoop_log_path_on_device
66from acts.test_utils.bt.bt_constants import default_rfcomm_timeout_ms
Stanley Tng180a8be2017-11-29 10:53:33 -080067from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
tturneyec1b8f52017-07-26 07:35:06 -070068from acts.test_utils.bt.bt_constants import mtu_changed
69from acts.test_utils.bt.bt_constants import pairing_variant_passkey_confirmation
70from acts.test_utils.bt.bt_constants import pan_connect_timeout
71from acts.test_utils.bt.bt_constants import small_timeout
72from acts.test_utils.bt.bt_constants import scan_result
73from acts.test_utils.bt.bt_constants import scan_failed
Tom Turney7b0baa52018-06-26 11:31:31 -070074from acts.test_utils.bt.bt_constants import sig_uuid_constants
Hansong Zhangd78b3b82017-10-03 10:36:12 -070075from acts.test_utils.bt.bt_constants import hid_id_keyboard
tturney83c28a02018-03-06 14:30:54 -080076
tturney1e174fd2017-06-01 14:42:20 -070077from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
tturneyb92368a2016-09-13 10:43:15 -070078from acts.test_utils.tel.tel_test_utils import verify_http_connection
Ang Li73697b32015-12-03 00:41:53 +000079from acts.utils import exe_cmd
Phillip Walker827112a2016-09-08 16:27:19 -070080from acts.utils import create_dir
Ang Li73697b32015-12-03 00:41:53 +000081
Ang Li5bd83f32016-05-23 14:39:38 -070082log = logging
Ang Li73697b32015-12-03 00:41:53 +000083
tturney57eb93a2016-06-21 13:58:06 -070084advertisements_to_devices = {}
Ang Li73697b32015-12-03 00:41:53 +000085
Ang Li73697b32015-12-03 00:41:53 +000086
tturney951533f2016-06-23 11:24:25 -070087class BtTestUtilsError(Exception):
88 pass
89
tturneyb92368a2016-09-13 10:43:15 -070090
tturneyc8e17bf2016-07-12 10:32:42 -070091def scan_and_verify_n_advertisements(scn_ad, max_advertisements):
tturneyed249972016-09-13 11:07:49 -070092 """Verify that input number of advertisements can be found from the scanning
93 Android device.
94
95 Args:
96 scn_ad: The Android device to start LE scanning on.
97 max_advertisements: The number of advertisements the scanner expects to
98 find.
99
100 Returns:
101 True if successful, false if unsuccessful.
102 """
tturneyc8e17bf2016-07-12 10:32:42 -0700103 test_result = False
104 address_list = []
105 filter_list = scn_ad.droid.bleGenFilterList()
106 scn_ad.droid.bleBuildScanFilter(filter_list)
107 scan_settings = scn_ad.droid.bleBuildScanSetting()
108 scan_callback = scn_ad.droid.bleGenScanCallback()
109 scn_ad.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
110 start_time = time.time()
tturneyec1b8f52017-07-26 07:35:06 -0700111 while (start_time + bt_default_timeout) > time.time():
tturneyc8e17bf2016-07-12 10:32:42 -0700112 event = None
113 try:
114 event = scn_ad.ed.pop_event(
tturneyec1b8f52017-07-26 07:35:06 -0700115 scan_result.format(scan_callback), bt_default_timeout)
tturneyc8e17bf2016-07-12 10:32:42 -0700116 except Empty as error:
Stanley Tngc9a123d2017-12-05 17:37:49 -0800117 raise BtTestUtilsError(
118 "Failed to find scan event: {}".format(error))
tturneyc8e17bf2016-07-12 10:32:42 -0700119 address = event['data']['Result']['deviceInfo']['address']
120 if address not in address_list:
121 address_list.append(address)
122 if len(address_list) == max_advertisements:
123 test_result = True
124 break
125 scn_ad.droid.bleStopBleScan(scan_callback)
126 return test_result
127
128
129def setup_n_advertisements(adv_ad, num_advertisements):
tturneyed249972016-09-13 11:07:49 -0700130 """Setup input number of advertisements on input Android device.
131
132 Args:
133 adv_ad: The Android device to start LE advertisements on.
134 num_advertisements: The number of advertisements to start.
135
136 Returns:
137 advertise_callback_list: List of advertisement callback ids.
138 """
tturneyc8e17bf2016-07-12 10:32:42 -0700139 adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
tturneyec1b8f52017-07-26 07:35:06 -0700140 ble_advertise_settings_modes['low_latency'])
tturneyc8e17bf2016-07-12 10:32:42 -0700141 advertise_data = adv_ad.droid.bleBuildAdvertiseData()
142 advertise_settings = adv_ad.droid.bleBuildAdvertiseSettings()
143 advertise_callback_list = []
144 for i in range(num_advertisements):
145 advertise_callback = adv_ad.droid.bleGenBleAdvertiseCallback()
146 advertise_callback_list.append(advertise_callback)
147 adv_ad.droid.bleStartBleAdvertising(advertise_callback, advertise_data,
148 advertise_settings)
149 try:
150 adv_ad.ed.pop_event(
tturneyec1b8f52017-07-26 07:35:06 -0700151 adv_succ.format(advertise_callback), bt_default_timeout)
tturney34ee54d2016-11-16 15:29:02 -0800152 adv_ad.log.info("Advertisement {} started.".format(i + 1))
tturneyc8e17bf2016-07-12 10:32:42 -0700153 except Empty as error:
tturney34ee54d2016-11-16 15:29:02 -0800154 adv_ad.log.error("Advertisement {} failed to start.".format(i + 1))
Stanley Tngc9a123d2017-12-05 17:37:49 -0800155 raise BtTestUtilsError(
156 "Test failed with Empty error: {}".format(error))
tturneyc8e17bf2016-07-12 10:32:42 -0700157 return advertise_callback_list
158
159
160def teardown_n_advertisements(adv_ad, num_advertisements,
161 advertise_callback_list):
tturneyed249972016-09-13 11:07:49 -0700162 """Stop input number of advertisements on input Android device.
163
164 Args:
165 adv_ad: The Android device to stop LE advertisements on.
166 num_advertisements: The number of advertisements to stop.
167 advertise_callback_list: The list of advertisement callbacks to stop.
168
169 Returns:
170 True if successful, false if unsuccessful.
171 """
tturneyc8e17bf2016-07-12 10:32:42 -0700172 for n in range(num_advertisements):
173 adv_ad.droid.bleStopBleAdvertising(advertise_callback_list[n])
174 return True
175
tturney951533f2016-06-23 11:24:25 -0700176
Ang Li73697b32015-12-03 00:41:53 +0000177def generate_ble_scan_objects(droid):
tturneyed249972016-09-13 11:07:49 -0700178 """Generate generic LE scan objects.
179
180 Args:
181 droid: The droid object to generate LE scan objects from.
182
183 Returns:
184 filter_list: The generated scan filter list id.
185 scan_settings: The generated scan settings id.
186 scan_callback: The generated scan callback id.
187 """
Ang Li73697b32015-12-03 00:41:53 +0000188 filter_list = droid.bleGenFilterList()
189 scan_settings = droid.bleBuildScanSetting()
190 scan_callback = droid.bleGenScanCallback()
191 return filter_list, scan_settings, scan_callback
192
193
194def generate_ble_advertise_objects(droid):
tturneyed249972016-09-13 11:07:49 -0700195 """Generate generic LE advertise objects.
196
197 Args:
198 droid: The droid object to generate advertise LE objects from.
199
200 Returns:
201 advertise_callback: The generated advertise callback id.
202 advertise_data: The generated advertise data id.
203 advertise_settings: The generated advertise settings id.
204 """
Ang Li73697b32015-12-03 00:41:53 +0000205 advertise_callback = droid.bleGenBleAdvertiseCallback()
206 advertise_data = droid.bleBuildAdvertiseData()
207 advertise_settings = droid.bleBuildAdvertiseSettings()
208 return advertise_callback, advertise_data, advertise_settings
209
210
tturney1ce8dc62016-02-18 09:55:53 -0800211def setup_multiple_devices_for_bt_test(android_devices):
tturneyed249972016-09-13 11:07:49 -0700212 """A common setup routine for Bluetooth on input Android device list.
213
214 Things this function sets up:
215 1. Resets Bluetooth
216 2. Set Bluetooth local name to random string of size 4
217 3. Disable BLE background scanning.
218 4. Enable Bluetooth snoop logging.
219
220 Args:
221 android_devices: Android device list to setup Bluetooth on.
222
223 Returns:
224 True if successful, false if unsuccessful.
225 """
Ang Li73697b32015-12-03 00:41:53 +0000226 log.info("Setting up Android Devices")
tturney03a45ae2016-05-24 15:36:05 -0700227 # TODO: Temp fix for an selinux error.
228 for ad in android_devices:
229 ad.adb.shell("setenforce 0")
Ang Li73697b32015-12-03 00:41:53 +0000230 threads = []
tturney1ce8dc62016-02-18 09:55:53 -0800231 try:
232 for a in android_devices:
Tom Turney9bce15d2018-06-21 13:00:42 -0700233 thread = threading.Thread(
234 target=factory_reset_bluetooth, args=([[a]]))
tturney1ce8dc62016-02-18 09:55:53 -0800235 threads.append(thread)
236 thread.start()
237 for t in threads:
238 t.join()
Ang Li73697b32015-12-03 00:41:53 +0000239
tturney1ce8dc62016-02-18 09:55:53 -0800240 for a in android_devices:
241 d = a.droid
tturney59d50d72017-11-15 15:10:16 -0800242 # TODO: Create specific RPC command to instantiate
243 # BluetoothConnectionFacade. This is just a workaround.
Stanley Tng15e4b922017-12-06 16:32:41 -0800244 d.bluetoothStartConnectionStateChangeMonitor("")
tturney1ce8dc62016-02-18 09:55:53 -0800245 setup_result = d.bluetoothSetLocalName(generate_id_by_size(4))
246 if not setup_result:
tturney34ee54d2016-11-16 15:29:02 -0800247 a.log.error("Failed to set device name.")
tturney1ce8dc62016-02-18 09:55:53 -0800248 return setup_result
249 d.bluetoothDisableBLE()
250 bonded_devices = d.bluetoothGetBondedDevices()
251 for b in bonded_devices:
tturney34ee54d2016-11-16 15:29:02 -0800252 a.log.info("Removing bond for device {}".format(b['address']))
tturney1ce8dc62016-02-18 09:55:53 -0800253 d.bluetoothUnbond(b['address'])
254 for a in android_devices:
tturney5da68262017-05-02 08:57:52 -0700255 a.adb.shell("setprop persist.bluetooth.btsnoopenable true")
256 getprop_result = bool(
257 a.adb.shell("getprop persist.bluetooth.btsnoopenable"))
258 if not getprop_result:
tturney77001ba2017-01-04 11:39:54 -0800259 a.log.warning("Failed to enable Bluetooth Hci Snoop Logging.")
tturneye3170f02016-05-19 14:37:00 -0700260 except Exception as err:
261 log.error("Something went wrong in multi device setup: {}".format(err))
tturney1ce8dc62016-02-18 09:55:53 -0800262 return False
Ang Li73697b32015-12-03 00:41:53 +0000263 return setup_result
264
265
tturney50f851d2016-07-07 11:07:37 -0700266def bluetooth_enabled_check(ad):
tturneyed249972016-09-13 11:07:49 -0700267 """Checks if the Bluetooth state is enabled, if not it will attempt to
268 enable it.
269
270 Args:
271 ad: The Android device list to enable Bluetooth on.
272
273 Returns:
274 True if successful, false if unsuccessful.
275 """
tturney50f851d2016-07-07 11:07:37 -0700276 if not ad.droid.bluetoothCheckState():
277 ad.droid.bluetoothToggleState(True)
278 expected_bluetooth_on_event_name = bluetooth_on
279 try:
tturneyec1b8f52017-07-26 07:35:06 -0700280 ad.ed.pop_event(expected_bluetooth_on_event_name,
281 bt_default_timeout)
tturney50f851d2016-07-07 11:07:37 -0700282 except Empty:
tturney5da68262017-05-02 08:57:52 -0700283 ad.log.info(
284 "Failed to toggle Bluetooth on(no broadcast received).")
tturney50f851d2016-07-07 11:07:37 -0700285 # Try one more time to poke at the actual state.
286 if ad.droid.bluetoothCheckState():
tturney34ee54d2016-11-16 15:29:02 -0800287 ad.log.info(".. actual state is ON")
tturney50f851d2016-07-07 11:07:37 -0700288 return True
tturney34ee54d2016-11-16 15:29:02 -0800289 ad.log.error(".. actual state is OFF")
tturney50f851d2016-07-07 11:07:37 -0700290 return False
291 return True
292
Tom Turney9bce15d2018-06-21 13:00:42 -0700293
294def wait_for_bluetooth_manager_state(droid,
295 state=None,
296 timeout=10,
297 threshold=5):
Timofey Protopopova66b3402018-05-02 14:09:18 -0700298 """ Waits for BlueTooth normalized state or normalized explicit state
299 args:
300 droid: droid device object
301 state: expected BlueTooth state
302 timeout: max timeout threshold
303 threshold: list len of bt state
304 Returns:
305 True if successful, false if unsuccessful.
306 """
307 all_states = []
308 get_state = lambda: droid.bluetoothGetLeState()
309 start_time = time.time()
310 while time.time() < start_time + timeout:
311 all_states.append(get_state())
312 if len(all_states) >= threshold:
313 # for any normalized state
314 if state is None:
315 if len(set(all_states[-threshold:])) == 1:
Tom Turney9bce15d2018-06-21 13:00:42 -0700316 log.info("State normalized {}".format(
317 set(all_states[-threshold:])))
Timofey Protopopova66b3402018-05-02 14:09:18 -0700318 return True
319 else:
320 # explicit check against normalized state
321 if set([state]).issubset(all_states[-threshold:]):
322 return True
323 time.sleep(0.5)
324 log.error(
325 "Bluetooth state fails to normalize" if state is None else
Tom Turney9bce15d2018-06-21 13:00:42 -0700326 "Failed to match bluetooth state, current state {} expected state {}".
327 format(get_state(), state))
Timofey Protopopova66b3402018-05-02 14:09:18 -0700328 return False
329
Tom Turney9bce15d2018-06-21 13:00:42 -0700330
Timofey Protopopova66b3402018-05-02 14:09:18 -0700331def factory_reset_bluetooth(android_devices):
332 """Clears Bluetooth stack of input Android device list.
333
334 Args:
335 android_devices: The Android device list to reset Bluetooth
336
337 Returns:
338 True if successful, false if unsuccessful.
339 """
340 for a in android_devices:
341 droid, ed = a.droid, a.ed
342 a.log.info("Reset state of bluetooth on device.")
343 if not bluetooth_enabled_check(a):
344 return False
345 # TODO: remove device unbond b/79418045
346 # Temporary solution to ensure all devices are unbonded
347 bonded_devices = droid.bluetoothGetBondedDevices()
348 for b in bonded_devices:
349 a.log.info("Removing bond for device {}".format(b['address']))
350 droid.bluetoothUnbond(b['address'])
351
352 droid.bluetoothFactoryReset()
353 wait_for_bluetooth_manager_state(droid)
354 if not enable_bluetooth(droid, ed):
355 return False
356 return True
tturney50f851d2016-07-07 11:07:37 -0700357
Tom Turney9bce15d2018-06-21 13:00:42 -0700358
tturney1ce8dc62016-02-18 09:55:53 -0800359def reset_bluetooth(android_devices):
tturneyed249972016-09-13 11:07:49 -0700360 """Resets Bluetooth state of input Android device list.
361
362 Args:
363 android_devices: The Android device list to reset Bluetooth state on.
364
365 Returns:
366 True if successful, false if unsuccessful.
Ang Li73697b32015-12-03 00:41:53 +0000367 """
tturney1ce8dc62016-02-18 09:55:53 -0800368 for a in android_devices:
369 droid, ed = a.droid, a.ed
tturney34ee54d2016-11-16 15:29:02 -0800370 a.log.info("Reset state of bluetooth on device.")
Ang Li73697b32015-12-03 00:41:53 +0000371 if droid.bluetoothCheckState() is True:
372 droid.bluetoothToggleState(False)
373 expected_bluetooth_off_event_name = bluetooth_off
374 try:
tturney5da68262017-05-02 08:57:52 -0700375 ed.pop_event(expected_bluetooth_off_event_name,
tturneyec1b8f52017-07-26 07:35:06 -0700376 bt_default_timeout)
Ang Li73697b32015-12-03 00:41:53 +0000377 except Exception:
tturney34ee54d2016-11-16 15:29:02 -0800378 a.log.error("Failed to toggle Bluetooth off.")
Ang Li73697b32015-12-03 00:41:53 +0000379 return False
380 # temp sleep for b/17723234
381 time.sleep(3)
tturney50f851d2016-07-07 11:07:37 -0700382 if not bluetooth_enabled_check(a):
Ang Li73697b32015-12-03 00:41:53 +0000383 return False
384 return True
385
386
tturney57eb93a2016-06-21 13:58:06 -0700387def determine_max_advertisements(android_device):
tturneyed249972016-09-13 11:07:49 -0700388 """Determines programatically how many advertisements the Android device
389 supports.
390
391 Args:
392 android_device: The Android device to determine max advertisements of.
393
394 Returns:
395 The maximum advertisement count.
396 """
tturney34ee54d2016-11-16 15:29:02 -0800397 android_device.log.info(
398 "Determining number of maximum concurrent advertisements...")
tturney57eb93a2016-06-21 13:58:06 -0700399 advertisement_count = 0
tturneyb9d94242016-07-06 17:28:59 -0700400 bt_enabled = False
Timofey Protopopova47c8812018-03-28 11:37:31 -0700401 expected_bluetooth_on_event_name = bluetooth_on
tturney57eb93a2016-06-21 13:58:06 -0700402 if not android_device.droid.bluetoothCheckState():
403 android_device.droid.bluetoothToggleState(True)
404 try:
405 android_device.ed.pop_event(expected_bluetooth_on_event_name,
tturneyec1b8f52017-07-26 07:35:06 -0700406 bt_default_timeout)
tturney57eb93a2016-06-21 13:58:06 -0700407 except Exception:
tturney34ee54d2016-11-16 15:29:02 -0800408 android_device.log.info(
409 "Failed to toggle Bluetooth on(no broadcast received).")
tturney57eb93a2016-06-21 13:58:06 -0700410 # Try one more time to poke at the actual state.
411 if android_device.droid.bluetoothCheckState() is True:
tturney34ee54d2016-11-16 15:29:02 -0800412 android_device.log.info(".. actual state is ON")
tturney57eb93a2016-06-21 13:58:06 -0700413 else:
tturney34ee54d2016-11-16 15:29:02 -0800414 android_device.log.error(
tturney77001ba2017-01-04 11:39:54 -0800415 "Failed to turn Bluetooth on. Setting default advertisements to 1"
416 )
tturneyb9d94242016-07-06 17:28:59 -0700417 advertisement_count = -1
tturney57eb93a2016-06-21 13:58:06 -0700418 return advertisement_count
419 advertise_callback_list = []
420 advertise_data = android_device.droid.bleBuildAdvertiseData()
421 advertise_settings = android_device.droid.bleBuildAdvertiseSettings()
422 while (True):
423 advertise_callback = android_device.droid.bleGenBleAdvertiseCallback()
424 advertise_callback_list.append(advertise_callback)
425
426 android_device.droid.bleStartBleAdvertising(
427 advertise_callback, advertise_data, advertise_settings)
Jakub Pawlowski86acec42016-11-22 12:59:12 -0800428
429 regex = "(" + adv_succ.format(
430 advertise_callback) + "|" + adv_fail.format(
Timofey Protopopova47c8812018-03-28 11:37:31 -0700431 advertise_callback) + ")"
Jakub Pawlowski86acec42016-11-22 12:59:12 -0800432 # wait for either success or failure event
tturneyec1b8f52017-07-26 07:35:06 -0700433 evt = android_device.ed.pop_events(regex, bt_default_timeout,
434 small_timeout)
Jakub Pawlowski86acec42016-11-22 12:59:12 -0800435 if evt[0]["name"] == adv_succ.format(advertise_callback):
tturney57eb93a2016-06-21 13:58:06 -0700436 advertisement_count += 1
Stanley Tngc9a123d2017-12-05 17:37:49 -0800437 android_device.log.info(
438 "Advertisement {} started.".format(advertisement_count))
Jakub Pawlowski86acec42016-11-22 12:59:12 -0800439 else:
440 error = evt[0]["data"]["Error"]
441 if error == "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS":
tturney34ee54d2016-11-16 15:29:02 -0800442 android_device.log.info(
443 "Advertisement failed to start. Reached max " +
444 "advertisements at {}".format(advertisement_count))
Jakub Pawlowski86acec42016-11-22 12:59:12 -0800445 break
446 else:
tturney34ee54d2016-11-16 15:29:02 -0800447 raise BtTestUtilsError(
448 "Expected ADVERTISE_FAILED_TOO_MANY_ADVERTISERS," +
449 " but received bad error code {}".format(error))
tturneyf02ac392016-09-21 10:37:35 -0700450 try:
tturney57eb93a2016-06-21 13:58:06 -0700451 for adv in advertise_callback_list:
452 android_device.droid.bleStopBleAdvertising(adv)
tturneyf02ac392016-09-21 10:37:35 -0700453 except Exception:
tturney34ee54d2016-11-16 15:29:02 -0800454 android_device.log.error(
455 "Failed to stop advertisingment, resetting Bluetooth.")
tturneyf02ac392016-09-21 10:37:35 -0700456 reset_bluetooth([android_device])
tturney57eb93a2016-06-21 13:58:06 -0700457 return advertisement_count
458
459
tturney1ce8dc62016-02-18 09:55:53 -0800460def get_advanced_droid_list(android_devices):
tturneyed249972016-09-13 11:07:49 -0700461 """Add max_advertisement and batch_scan_supported attributes to input
462 Android devices
463
464 This will programatically determine maximum LE advertisements of each
465 input Android device.
466
467 Args:
468 android_devices: The Android devices to setup.
469
470 Returns:
471 List of Android devices with new attribtues.
472 """
Ang Li73697b32015-12-03 00:41:53 +0000473 droid_list = []
tturney1ce8dc62016-02-18 09:55:53 -0800474 for a in android_devices:
475 d, e = a.droid, a.ed
Ang Li73697b32015-12-03 00:41:53 +0000476 model = d.getBuildModel()
tturney0ab6f012016-05-09 13:59:27 -0700477 max_advertisements = 1
Ang Li73697b32015-12-03 00:41:53 +0000478 batch_scan_supported = True
479 if model in advertisements_to_devices.keys():
480 max_advertisements = advertisements_to_devices[model]
tturney57eb93a2016-06-21 13:58:06 -0700481 else:
482 max_advertisements = determine_max_advertisements(a)
tturneyb9d94242016-07-06 17:28:59 -0700483 max_tries = 3
Timofey Protopopova47c8812018-03-28 11:37:31 -0700484 # Retry to calculate max advertisements
tturneyb9d94242016-07-06 17:28:59 -0700485 while max_advertisements == -1 and max_tries > 0:
Stanley Tngc9a123d2017-12-05 17:37:49 -0800486 a.log.info(
487 "Attempts left to determine max advertisements: {}".format(
488 max_tries))
tturneyb9d94242016-07-06 17:28:59 -0700489 max_advertisements = determine_max_advertisements(a)
490 max_tries -= 1
tturney57eb93a2016-06-21 13:58:06 -0700491 advertisements_to_devices[model] = max_advertisements
492 if model in batch_scan_not_supported_list:
493 batch_scan_supported = False
Ang Li73697b32015-12-03 00:41:53 +0000494 role = {
495 'droid': d,
496 'ed': e,
497 'max_advertisements': max_advertisements,
498 'batch_scan_supported': batch_scan_supported
499 }
500 droid_list.append(role)
501 return droid_list
502
503
tturneyc12b6ec2016-03-30 13:39:40 -0700504def generate_id_by_size(
505 size,
506 chars=(
Timofey Protopopova47c8812018-03-28 11:37:31 -0700507 string.ascii_lowercase + string.ascii_uppercase + string.digits)):
tturneyed249972016-09-13 11:07:49 -0700508 """Generate random ascii characters of input size and input char types
509
510 Args:
511 size: Input size of string.
512 chars: (Optional) Chars to use in generating a random string.
513
514 Returns:
515 String of random input chars at the input size.
516 """
Ang Li73697b32015-12-03 00:41:53 +0000517 return ''.join(random.choice(chars) for _ in range(size))
518
519
tturneyed249972016-09-13 11:07:49 -0700520def cleanup_scanners_and_advertisers(scn_android_device, scn_callback_list,
tturney1ce8dc62016-02-18 09:55:53 -0800521 adv_android_device, adv_callback_list):
tturneyed249972016-09-13 11:07:49 -0700522 """Try to gracefully stop all scanning and advertising instances.
523
524 Args:
525 scn_android_device: The Android device that is actively scanning.
526 scn_callback_list: The scan callback id list that needs to be stopped.
527 adv_android_device: The Android device that is actively advertising.
528 adv_callback_list: The advertise callback id list that needs to be
529 stopped.
Ang Li73697b32015-12-03 00:41:53 +0000530 """
tturney1ce8dc62016-02-18 09:55:53 -0800531 scan_droid, scan_ed = scn_android_device.droid, scn_android_device.ed
532 adv_droid = adv_android_device.droid
Ang Li73697b32015-12-03 00:41:53 +0000533 try:
tturneyed249972016-09-13 11:07:49 -0700534 for scan_callback in scn_callback_list:
Ang Li73697b32015-12-03 00:41:53 +0000535 scan_droid.bleStopBleScan(scan_callback)
tturneye3170f02016-05-19 14:37:00 -0700536 except Exception as err:
tturney34ee54d2016-11-16 15:29:02 -0800537 scn_android_device.log.debug(
538 "Failed to stop LE scan... reseting Bluetooth. Error {}".format(
539 err))
tturney1ce8dc62016-02-18 09:55:53 -0800540 reset_bluetooth([scn_android_device])
Ang Li73697b32015-12-03 00:41:53 +0000541 try:
542 for adv_callback in adv_callback_list:
543 adv_droid.bleStopBleAdvertising(adv_callback)
tturneye3170f02016-05-19 14:37:00 -0700544 except Exception as err:
tturney34ee54d2016-11-16 15:29:02 -0800545 adv_android_device.log.debug(
tturneyb92368a2016-09-13 10:43:15 -0700546 "Failed to stop LE advertisement... reseting Bluetooth. Error {}".
Timofey Protopopova47c8812018-03-28 11:37:31 -0700547 format(err))
tturney1ce8dc62016-02-18 09:55:53 -0800548 reset_bluetooth([adv_android_device])
Ang Li73697b32015-12-03 00:41:53 +0000549
550
tturney1ce8dc62016-02-18 09:55:53 -0800551def get_mac_address_of_generic_advertisement(scan_ad, adv_ad):
tturneyed249972016-09-13 11:07:49 -0700552 """Start generic advertisement and get it's mac address by LE scanning.
553
554 Args:
555 scan_ad: The Android device to use as the scanner.
556 adv_ad: The Android device to use as the advertiser.
557
558 Returns:
559 mac_address: The mac address of the advertisement.
560 advertise_callback: The advertise callback id of the active
561 advertisement.
562 """
tturney1ce8dc62016-02-18 09:55:53 -0800563 adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
564 adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
tturneyec1b8f52017-07-26 07:35:06 -0700565 ble_advertise_settings_modes['low_latency'])
tturney1ce8dc62016-02-18 09:55:53 -0800566 adv_ad.droid.bleSetAdvertiseSettingsIsConnectable(True)
567 adv_ad.droid.bleSetAdvertiseSettingsTxPowerLevel(
tturneyec1b8f52017-07-26 07:35:06 -0700568 ble_advertise_settings_tx_powers['high'])
Ang Li73697b32015-12-03 00:41:53 +0000569 advertise_callback, advertise_data, advertise_settings = (
tturney1ce8dc62016-02-18 09:55:53 -0800570 generate_ble_advertise_objects(adv_ad.droid))
tturneyc12b6ec2016-03-30 13:39:40 -0700571 adv_ad.droid.bleStartBleAdvertising(advertise_callback, advertise_data,
572 advertise_settings)
tturney951533f2016-06-23 11:24:25 -0700573 try:
tturneyb9d94242016-07-06 17:28:59 -0700574 adv_ad.ed.pop_event(
tturneyec1b8f52017-07-26 07:35:06 -0700575 adv_succ.format(advertise_callback), bt_default_timeout)
tturney50f851d2016-07-07 11:07:37 -0700576 except Empty as err:
tturneyb9d94242016-07-06 17:28:59 -0700577 raise BtTestUtilsError(
578 "Advertiser did not start successfully {}".format(err))
tturney1ce8dc62016-02-18 09:55:53 -0800579 filter_list = scan_ad.droid.bleGenFilterList()
580 scan_settings = scan_ad.droid.bleBuildScanSetting()
581 scan_callback = scan_ad.droid.bleGenScanCallback()
tturney5da68262017-05-02 08:57:52 -0700582 scan_ad.droid.bleSetScanFilterDeviceName(
583 adv_ad.droid.bluetoothGetLocalName())
tturney1ce8dc62016-02-18 09:55:53 -0800584 scan_ad.droid.bleBuildScanFilter(filter_list)
585 scan_ad.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
tturney951533f2016-06-23 11:24:25 -0700586 try:
587 event = scan_ad.ed.pop_event(
tturneyec1b8f52017-07-26 07:35:06 -0700588 "BleScan{}onScanResults".format(scan_callback), bt_default_timeout)
tturney50f851d2016-07-07 11:07:37 -0700589 except Empty as err:
Stanley Tngc9a123d2017-12-05 17:37:49 -0800590 raise BtTestUtilsError(
591 "Scanner did not find advertisement {}".format(err))
Ang Li73697b32015-12-03 00:41:53 +0000592 mac_address = event['data']['Result']['deviceInfo']['address']
tturney83c28a02018-03-06 14:30:54 -0800593 return mac_address, advertise_callback, scan_callback
Ang Li73697b32015-12-03 00:41:53 +0000594
Tom Turney9bce15d2018-06-21 13:00:42 -0700595
Sanket Agarwalc8fdc1a2016-07-06 16:39:34 -0700596def enable_bluetooth(droid, ed):
597 if droid.bluetoothCheckState() is True:
598 return True
599
600 droid.bluetoothToggleState(True)
601 expected_bluetooth_on_event_name = bluetooth_on
602 try:
tturneyec1b8f52017-07-26 07:35:06 -0700603 ed.pop_event(expected_bluetooth_on_event_name, bt_default_timeout)
Sanket Agarwalc8fdc1a2016-07-06 16:39:34 -0700604 except Exception:
605 log.info("Failed to toggle Bluetooth on (no broadcast received)")
606 if droid.bluetoothCheckState() is True:
607 log.info(".. actual state is ON")
608 return True
609 log.info(".. actual state is OFF")
610 return False
611
612 return True
613
Tom Turney9bce15d2018-06-21 13:00:42 -0700614
tturneyed249972016-09-13 11:07:49 -0700615def disable_bluetooth(droid):
616 """Disable Bluetooth on input Droid object.
Ang Li73697b32015-12-03 00:41:53 +0000617
tturneyed249972016-09-13 11:07:49 -0700618 Args:
619 droid: The droid object to disable Bluetooth on.
Ang Li73697b32015-12-03 00:41:53 +0000620
tturneyed249972016-09-13 11:07:49 -0700621 Returns:
622 True if successful, false if unsuccessful.
623 """
Ang Li73697b32015-12-03 00:41:53 +0000624 if droid.bluetoothCheckState() is True:
625 droid.bluetoothToggleState(False)
626 if droid.bluetoothCheckState() is True:
tturney69e3bb12017-10-26 14:15:05 -0700627 log.error("Failed to toggle Bluetooth off.")
Ang Li73697b32015-12-03 00:41:53 +0000628 return False
629 return True
630
631
tturney1ce8dc62016-02-18 09:55:53 -0800632def set_bt_scan_mode(ad, scan_mode_value):
tturneyed249972016-09-13 11:07:49 -0700633 """Set Android device's Bluetooth scan mode.
634
635 Args:
636 ad: The Android device to set the scan mode on.
637 scan_mode_value: The value to set the scan mode to.
638
639 Returns:
640 True if successful, false if unsuccessful.
641 """
tturney1ce8dc62016-02-18 09:55:53 -0800642 droid, ed = ad.droid, ad.ed
tturneyec1b8f52017-07-26 07:35:06 -0700643 if scan_mode_value == bt_scan_mode_types['state_off']:
tturneyed249972016-09-13 11:07:49 -0700644 disable_bluetooth(droid)
Ang Li73697b32015-12-03 00:41:53 +0000645 scan_mode = droid.bluetoothGetScanMode()
tturney1ce8dc62016-02-18 09:55:53 -0800646 reset_bluetooth([ad])
Ang Li73697b32015-12-03 00:41:53 +0000647 if scan_mode != scan_mode_value:
648 return False
tturneyec1b8f52017-07-26 07:35:06 -0700649 elif scan_mode_value == bt_scan_mode_types['none']:
Ang Li73697b32015-12-03 00:41:53 +0000650 droid.bluetoothMakeUndiscoverable()
651 scan_mode = droid.bluetoothGetScanMode()
652 if scan_mode != scan_mode_value:
653 return False
tturneyec1b8f52017-07-26 07:35:06 -0700654 elif scan_mode_value == bt_scan_mode_types['connectable']:
Ang Li73697b32015-12-03 00:41:53 +0000655 droid.bluetoothMakeUndiscoverable()
656 droid.bluetoothMakeConnectable()
657 scan_mode = droid.bluetoothGetScanMode()
658 if scan_mode != scan_mode_value:
659 return False
tturneyec1b8f52017-07-26 07:35:06 -0700660 elif (scan_mode_value == bt_scan_mode_types['connectable_discoverable']):
Ang Li73697b32015-12-03 00:41:53 +0000661 droid.bluetoothMakeDiscoverable()
662 scan_mode = droid.bluetoothGetScanMode()
663 if scan_mode != scan_mode_value:
664 return False
665 else:
666 # invalid scan mode
667 return False
668 return True
669
670
671def set_device_name(droid, name):
tturneyed249972016-09-13 11:07:49 -0700672 """Set and check Bluetooth local name on input droid object.
673
674 Args:
675 droid: Droid object to set local name on.
676 name: the Bluetooth local name to set.
677
678 Returns:
679 True if successful, false if unsuccessful.
680 """
Ang Li73697b32015-12-03 00:41:53 +0000681 droid.bluetoothSetLocalName(name)
Ang Li73697b32015-12-03 00:41:53 +0000682 time.sleep(2)
683 droid_name = droid.bluetoothGetLocalName()
684 if droid_name != name:
685 return False
686 return True
687
688
689def check_device_supported_profiles(droid):
tturneyed249972016-09-13 11:07:49 -0700690 """Checks for Android device supported profiles.
691
692 Args:
693 droid: The droid object to query.
694
695 Returns:
696 A dictionary of supported profiles.
697 """
Ang Li73697b32015-12-03 00:41:53 +0000698 profile_dict = {}
699 profile_dict['hid'] = droid.bluetoothHidIsReady()
700 profile_dict['hsp'] = droid.bluetoothHspIsReady()
701 profile_dict['a2dp'] = droid.bluetoothA2dpIsReady()
702 profile_dict['avrcp'] = droid.bluetoothAvrcpIsReady()
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700703 profile_dict['a2dp_sink'] = droid.bluetoothA2dpSinkIsReady()
704 profile_dict['hfp_client'] = droid.bluetoothHfpClientIsReady()
705 profile_dict['pbap_client'] = droid.bluetoothPbapClientIsReady()
Ang Li73697b32015-12-03 00:41:53 +0000706 return profile_dict
707
tturney46060782016-11-14 16:44:38 -0800708
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700709def log_energy_info(android_devices, state):
710 """Logs energy info of input Android devices.
711
712 Args:
713 android_devices: input Android device list to log energy info from.
714 state: the input state to log. Usually 'Start' or 'Stop' for logging.
715
716 Returns:
717 A logging string of the Bluetooth energy info reported.
718 """
719 return_string = "{} Energy info collection:\n".format(state)
720 # Bug: b/31966929
721 return return_string
722
723
Sanket Agarwalbd9689b2016-08-24 11:14:53 -0700724def set_profile_priority(host_ad, client_ad, profiles, priority):
725 """Sets the priority of said profile(s) on host_ad for client_ad"""
726 for profile in profiles:
tturney34ee54d2016-11-16 15:29:02 -0800727 host_ad.log.info("Profile {} on {} for {} set to priority {}".format(
Stanley Tngc9a123d2017-12-05 17:37:49 -0800728 profile, host_ad.droid.bluetoothGetLocalName(),
tturney77001ba2017-01-04 11:39:54 -0800729 client_ad.droid.bluetoothGetLocalAddress(), priority.value))
tturneyec1b8f52017-07-26 07:35:06 -0700730 if bt_profile_constants['a2dp_sink'] == profile:
Sanket Agarwalbd9689b2016-08-24 11:14:53 -0700731 host_ad.droid.bluetoothA2dpSinkSetPriority(
Phillip Walker827112a2016-09-08 16:27:19 -0700732 client_ad.droid.bluetoothGetLocalAddress(), priority.value)
tturneyec1b8f52017-07-26 07:35:06 -0700733 elif bt_profile_constants['headset_client'] == profile:
Sanket Agarwalbd9689b2016-08-24 11:14:53 -0700734 host_ad.droid.bluetoothHfpClientSetPriority(
Phillip Walker827112a2016-09-08 16:27:19 -0700735 client_ad.droid.bluetoothGetLocalAddress(), priority.value)
tturneyec1b8f52017-07-26 07:35:06 -0700736 elif bt_profile_constants['pbap_client'] == profile:
Sanket Agarwalbd9689b2016-08-24 11:14:53 -0700737 host_ad.droid.bluetoothPbapClientSetPriority(
Phillip Walker827112a2016-09-08 16:27:19 -0700738 client_ad.droid.bluetoothGetLocalAddress(), priority.value)
Sanket Agarwalbd9689b2016-08-24 11:14:53 -0700739 else:
tturney34ee54d2016-11-16 15:29:02 -0800740 host_ad.log.error(
741 "Profile {} not yet supported for priority settings".format(
742 profile))
Phillip Walker827112a2016-09-08 16:27:19 -0700743
tturney46060782016-11-14 16:44:38 -0800744
Jack Hea518bdb2016-11-01 18:11:06 -0700745def pair_pri_to_sec(pri_ad, sec_ad, attempts=2, auto_confirm=True):
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700746 """Pairs pri droid to secondary droid.
tturneyed249972016-09-13 11:07:49 -0700747
748 Args:
Jack Hea518bdb2016-11-01 18:11:06 -0700749 pri_ad: Android device initiating connection
750 sec_ad: Android device accepting connection
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700751 attempts: Number of attempts to try until failure.
Jack Hea518bdb2016-11-01 18:11:06 -0700752 auto_confirm: Auto confirm passkey match for both devices
tturneyed249972016-09-13 11:07:49 -0700753
754 Returns:
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700755 Pass if True
756 Fail if False
tturneyed249972016-09-13 11:07:49 -0700757 """
tturney46060782016-11-14 16:44:38 -0800758 pri_ad.droid.bluetoothStartConnectionStateChangeMonitor(
759 sec_ad.droid.bluetoothGetLocalAddress())
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700760 curr_attempts = 0
761 while curr_attempts < attempts:
Jack Hea518bdb2016-11-01 18:11:06 -0700762 if _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm):
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700763 return True
Jack Heff07eb62016-12-01 13:52:40 -0800764 # Wait 2 seconds before unbound
765 time.sleep(2)
766 if not clear_bonded_devices(pri_ad):
767 log.error("Failed to clear bond for primary device at attempt {}"
tturney77001ba2017-01-04 11:39:54 -0800768 .format(str(curr_attempts)))
Jack Heff07eb62016-12-01 13:52:40 -0800769 return False
770 if not clear_bonded_devices(sec_ad):
771 log.error("Failed to clear bond for secondary device at attempt {}"
tturney77001ba2017-01-04 11:39:54 -0800772 .format(str(curr_attempts)))
Jack Heff07eb62016-12-01 13:52:40 -0800773 return False
774 # Wait 2 seconds after unbound
775 time.sleep(2)
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700776 curr_attempts += 1
Phillip Walker827112a2016-09-08 16:27:19 -0700777 log.error("pair_pri_to_sec failed to connect after {} attempts".format(
778 str(attempts)))
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700779 return False
780
tturney46060782016-11-14 16:44:38 -0800781
Jack Hea518bdb2016-11-01 18:11:06 -0700782def _wait_for_passkey_match(pri_ad, sec_ad):
783 pri_pin, sec_pin = -1, 1
784 pri_variant, sec_variant = -1, 1
785 pri_pairing_req, sec_pairing_req = None, None
786 try:
787 pri_pairing_req = pri_ad.ed.pop_event(
tturney5da68262017-05-02 08:57:52 -0700788 event_name="BluetoothActionPairingRequest",
tturneyec1b8f52017-07-26 07:35:06 -0700789 timeout=bt_default_timeout)
Jack Hea518bdb2016-11-01 18:11:06 -0700790 pri_variant = pri_pairing_req["data"]["PairingVariant"]
791 pri_pin = pri_pairing_req["data"]["Pin"]
Stanley Tngc9a123d2017-12-05 17:37:49 -0800792 pri_ad.log.info("Primary device received Pin: {}, Variant: {}".format(
793 pri_pin, pri_variant))
Jack Hea518bdb2016-11-01 18:11:06 -0700794 sec_pairing_req = sec_ad.ed.pop_event(
tturney5da68262017-05-02 08:57:52 -0700795 event_name="BluetoothActionPairingRequest",
tturneyec1b8f52017-07-26 07:35:06 -0700796 timeout=bt_default_timeout)
Jack Hea518bdb2016-11-01 18:11:06 -0700797 sec_variant = sec_pairing_req["data"]["PairingVariant"]
798 sec_pin = sec_pairing_req["data"]["Pin"]
tturney34ee54d2016-11-16 15:29:02 -0800799 sec_ad.log.info("Secondary device received Pin: {}, Variant: {}"
800 .format(sec_pin, sec_variant))
Jack Hea518bdb2016-11-01 18:11:06 -0700801 except Empty as err:
802 log.error("Wait for pin error: {}".format(err))
Stanley Tngc9a123d2017-12-05 17:37:49 -0800803 log.error("Pairing request state, Primary: {}, Secondary: {}".format(
804 pri_pairing_req, sec_pairing_req))
Jack Hea518bdb2016-11-01 18:11:06 -0700805 return False
tturneyec1b8f52017-07-26 07:35:06 -0700806 if pri_variant == sec_variant == pairing_variant_passkey_confirmation:
Jack Hea518bdb2016-11-01 18:11:06 -0700807 confirmation = pri_pin == sec_pin
808 if confirmation:
809 log.info("Pairing code matched, accepting connection")
810 else:
811 log.info("Pairing code mismatched, rejecting connection")
tturney46060782016-11-14 16:44:38 -0800812 pri_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
813 str(confirmation))
814 sec_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
815 str(confirmation))
Jack Hea518bdb2016-11-01 18:11:06 -0700816 if not confirmation:
817 return False
818 elif pri_variant != sec_variant:
Jack Heff07eb62016-12-01 13:52:40 -0800819 log.error("Pairing variant mismatched, abort connection")
Jack Hea518bdb2016-11-01 18:11:06 -0700820 return False
821 return True
Phillip Walker827112a2016-09-08 16:27:19 -0700822
tturney46060782016-11-14 16:44:38 -0800823
Jack Hea518bdb2016-11-01 18:11:06 -0700824def _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm):
825 # Enable discovery on sec_ad so that pri_ad can find it.
Sanket Agarwalc4637db2016-04-06 10:43:14 -0700826 # The timeout here is based on how much time it would take for two devices
Jack Hea518bdb2016-11-01 18:11:06 -0700827 # to pair with each other once pri_ad starts seeing devices.
828 pri_droid = pri_ad.droid
829 sec_droid = sec_ad.droid
830 pri_ad.ed.clear_all_events()
831 sec_ad.ed.clear_all_events()
tturney77001ba2017-01-04 11:39:54 -0800832 log.info("Bonding device {} to {}".format(
833 pri_droid.bluetoothGetLocalAddress(),
834 sec_droid.bluetoothGetLocalAddress()))
tturneyec1b8f52017-07-26 07:35:06 -0700835 sec_droid.bluetoothMakeDiscoverable(bt_default_timeout)
tturney182241b2016-05-17 16:53:44 -0700836 target_address = sec_droid.bluetoothGetLocalAddress()
tturneye3170f02016-05-19 14:37:00 -0700837 log.debug("Starting paring helper on each device")
Jack Hea518bdb2016-11-01 18:11:06 -0700838 pri_droid.bluetoothStartPairingHelper(auto_confirm)
839 sec_droid.bluetoothStartPairingHelper(auto_confirm)
tturney34ee54d2016-11-16 15:29:02 -0800840 pri_ad.log.info("Primary device starting discovery and executing bond")
tturney182241b2016-05-17 16:53:44 -0700841 result = pri_droid.bluetoothDiscoverAndBond(target_address)
Jack Hea518bdb2016-11-01 18:11:06 -0700842 if not auto_confirm:
843 if not _wait_for_passkey_match(pri_ad, sec_ad):
844 return False
Sanket Agarwalc4637db2016-04-06 10:43:14 -0700845 # Loop until we have bonded successfully or timeout.
tturneyec1b8f52017-07-26 07:35:06 -0700846 end_time = time.time() + bt_default_timeout
tturney34ee54d2016-11-16 15:29:02 -0800847 pri_ad.log.info("Verifying devices are bonded")
Sanket Agarwalc4637db2016-04-06 10:43:14 -0700848 while time.time() < end_time:
849 bonded_devices = pri_droid.bluetoothGetBondedDevices()
Sanket Agarwalc4637db2016-04-06 10:43:14 -0700850 bonded = False
851 for d in bonded_devices:
tturney182241b2016-05-17 16:53:44 -0700852 if d['address'] == target_address:
tturney34ee54d2016-11-16 15:29:02 -0800853 pri_ad.log.info("Successfully bonded to device")
tturney0ab6f012016-05-09 13:59:27 -0700854 return True
Jack Hea518bdb2016-11-01 18:11:06 -0700855 time.sleep(0.1)
Sanket Agarwalc4637db2016-04-06 10:43:14 -0700856 # Timed out trying to bond.
tturney34ee54d2016-11-16 15:29:02 -0800857 pri_ad.log.info("Failed to bond devices.")
Sanket Agarwalc4637db2016-04-06 10:43:14 -0700858 return False
Ang Li73697b32015-12-03 00:41:53 +0000859
tturney46060782016-11-14 16:44:38 -0800860
Phillip Walker827112a2016-09-08 16:27:19 -0700861def connect_pri_to_sec(pri_ad, sec_ad, profiles_set, attempts=2):
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700862 """Connects pri droid to secondary droid.
tturney57eb93a2016-06-21 13:58:06 -0700863
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700864 Args:
Phillip Walker827112a2016-09-08 16:27:19 -0700865 pri_ad: AndroidDroid initiating connection
866 sec_ad: AndroidDroid accepting connection
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700867 profiles_set: Set of profiles to be connected
868 attempts: Number of attempts to try until failure.
869
870 Returns:
871 Pass if True
872 Fail if False
873 """
Phillip Walker827112a2016-09-08 16:27:19 -0700874 device_addr = sec_ad.droid.bluetoothGetLocalAddress()
875 # Allows extra time for the SDP records to be updated.
876 time.sleep(2)
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700877 curr_attempts = 0
878 while curr_attempts < attempts:
Phillip Walker827112a2016-09-08 16:27:19 -0700879 log.info("connect_pri_to_sec curr attempt {} total {}".format(
880 curr_attempts, attempts))
881 if _connect_pri_to_sec(pri_ad, sec_ad, profiles_set):
882 return True
883 curr_attempts += 1
884 log.error("connect_pri_to_sec failed to connect after {} attempts".format(
885 attempts))
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700886 return False
887
Phillip Walker827112a2016-09-08 16:27:19 -0700888
889def _connect_pri_to_sec(pri_ad, sec_ad, profiles_set):
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700890 """Connects pri droid to secondary droid.
891
892 Args:
Phillip Walker827112a2016-09-08 16:27:19 -0700893 pri_ad: AndroidDroid initiating connection.
894 sec_ad: AndroidDroid accepting connection.
tturneyed249972016-09-13 11:07:49 -0700895 profiles_set: Set of profiles to be connected.
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700896
897 Returns:
tturneyed249972016-09-13 11:07:49 -0700898 True of connection is successful, false if unsuccessful.
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700899 """
900 # Check if we support all profiles.
tturneye1f6c722017-08-30 07:03:11 -0700901 supported_profiles = bt_profile_constants.values()
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700902 for profile in profiles_set:
903 if profile not in supported_profiles:
tturney34ee54d2016-11-16 15:29:02 -0800904 pri_ad.log.info("Profile {} is not supported list {}".format(
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700905 profile, supported_profiles))
906 return False
907
908 # First check that devices are bonded.
909 paired = False
Phillip Walker827112a2016-09-08 16:27:19 -0700910 for paired_device in pri_ad.droid.bluetoothGetBondedDevices():
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700911 if paired_device['address'] == \
Timofey Protopopova47c8812018-03-28 11:37:31 -0700912 sec_ad.droid.bluetoothGetLocalAddress():
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700913 paired = True
914 break
915
916 if not paired:
tturney34ee54d2016-11-16 15:29:02 -0800917 pri_ad.log.error("Not paired to {}".format(sec_ad.serial))
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700918 return False
919
920 # Now try to connect them, the following call will try to initiate all
921 # connections.
Stanley Tngc9a123d2017-12-05 17:37:49 -0800922 pri_ad.droid.bluetoothConnectBonded(
923 sec_ad.droid.bluetoothGetLocalAddress())
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700924
tturney46060782016-11-14 16:44:38 -0800925 end_time = time.time() + 10
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700926 profile_connected = set()
tturney46060782016-11-14 16:44:38 -0800927 sec_addr = sec_ad.droid.bluetoothGetLocalAddress()
tturney34ee54d2016-11-16 15:29:02 -0800928 pri_ad.log.info("Profiles to be connected {}".format(profiles_set))
tturney46060782016-11-14 16:44:38 -0800929 # First use APIs to check profile connection state
Stanley Tngc9a123d2017-12-05 17:37:49 -0800930 while (time.time() < end_time
931 and not profile_connected.issuperset(profiles_set)):
932 if (bt_profile_constants['headset_client'] not in profile_connected
933 and bt_profile_constants['headset_client'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800934 if is_hfp_client_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700935 profile_connected.add(bt_profile_constants['headset_client'])
Stanley Tngc9a123d2017-12-05 17:37:49 -0800936 if (bt_profile_constants['a2dp'] not in profile_connected
937 and bt_profile_constants['a2dp'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800938 if is_a2dp_src_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700939 profile_connected.add(bt_profile_constants['a2dp'])
Stanley Tngc9a123d2017-12-05 17:37:49 -0800940 if (bt_profile_constants['a2dp_sink'] not in profile_connected
941 and bt_profile_constants['a2dp_sink'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800942 if is_a2dp_snk_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700943 profile_connected.add(bt_profile_constants['a2dp_sink'])
Stanley Tngc9a123d2017-12-05 17:37:49 -0800944 if (bt_profile_constants['map_mce'] not in profile_connected
945 and bt_profile_constants['map_mce'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800946 if is_map_mce_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700947 profile_connected.add(bt_profile_constants['map_mce'])
Stanley Tngc9a123d2017-12-05 17:37:49 -0800948 if (bt_profile_constants['map'] not in profile_connected
949 and bt_profile_constants['map'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800950 if is_map_mse_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700951 profile_connected.add(bt_profile_constants['map'])
tturney46060782016-11-14 16:44:38 -0800952 time.sleep(0.1)
953 # If APIs fail, try to find the connection broadcast receiver.
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700954 while not profile_connected.issuperset(profiles_set):
955 try:
Phillip Walker827112a2016-09-08 16:27:19 -0700956 profile_event = pri_ad.ed.pop_event(
tturney46060782016-11-14 16:44:38 -0800957 bluetooth_profile_connection_state_changed,
tturneyec1b8f52017-07-26 07:35:06 -0700958 bt_default_timeout + 10)
tturney34ee54d2016-11-16 15:29:02 -0800959 pri_ad.log.info("Got event {}".format(profile_event))
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700960 except Exception:
tturney34ee54d2016-11-16 15:29:02 -0800961 pri_ad.log.error("Did not get {} profiles left {}".format(
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700962 bluetooth_profile_connection_state_changed, profile_connected))
963 return False
964
965 profile = profile_event['data']['profile']
966 state = profile_event['data']['state']
967 device_addr = profile_event['data']['addr']
968
tturneyec1b8f52017-07-26 07:35:06 -0700969 if state == bt_profile_states['connected'] and \
Timofey Protopopova47c8812018-03-28 11:37:31 -0700970 device_addr == sec_ad.droid.bluetoothGetLocalAddress():
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700971 profile_connected.add(profile)
Stanley Tngc9a123d2017-12-05 17:37:49 -0800972 pri_ad.log.info(
973 "Profiles connected until now {}".format(profile_connected))
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700974 # Failure happens inside the while loop. If we came here then we already
975 # connected.
976 return True
Ang Li73697b32015-12-03 00:41:53 +0000977
tturney57eb93a2016-06-21 13:58:06 -0700978
Phillip Walker827112a2016-09-08 16:27:19 -0700979def disconnect_pri_from_sec(pri_ad, sec_ad, profiles_list):
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700980 """
981 Disconnect primary from secondary on a specific set of profiles
982 Args:
Phillip Walker827112a2016-09-08 16:27:19 -0700983 pri_ad - Primary android_device initiating disconnection
984 sec_ad - Secondary android droid (sl4a interface to keep the
985 method signature the same connect_pri_to_sec above)
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700986 profiles_list - List of profiles we want to disconnect from
987
988 Returns:
989 True on Success
990 False on Failure
991 """
992 # Sanity check to see if all the profiles in the given set is supported
tturneyca42e2e2017-08-30 10:22:54 -0700993 supported_profiles = bt_profile_constants.values()
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700994 for profile in profiles_list:
995 if profile not in supported_profiles:
tturney34ee54d2016-11-16 15:29:02 -0800996 pri_ad.log.info("Profile {} is not in supported list {}".format(
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700997 profile, supported_profiles))
998 return False
999
tturney34ee54d2016-11-16 15:29:02 -08001000 pri_ad.log.info(pri_ad.droid.bluetoothGetBondedDevices())
Phillip Walker827112a2016-09-08 16:27:19 -07001001 # Disconnecting on a already disconnected profile is a nop,
1002 # so not checking for the connection state
1003 try:
1004 pri_ad.droid.bluetoothDisconnectConnectedProfile(
1005 sec_ad.droid.bluetoothGetLocalAddress(), profiles_list)
1006 except Exception as err:
tturney34ee54d2016-11-16 15:29:02 -08001007 pri_ad.log.error(
1008 "Exception while trying to disconnect profile(s) {}: {}".format(
1009 profiles_list, err))
Phillip Walker827112a2016-09-08 16:27:19 -07001010 return False
1011
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -07001012 profile_disconnected = set()
tturney34ee54d2016-11-16 15:29:02 -08001013 pri_ad.log.info("Disconnecting from profiles: {}".format(profiles_list))
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -07001014
1015 while not profile_disconnected.issuperset(profiles_list):
1016 try:
Phillip Walker827112a2016-09-08 16:27:19 -07001017 profile_event = pri_ad.ed.pop_event(
Timofey Protopopova47c8812018-03-28 11:37:31 -07001018 bluetooth_profile_connection_state_changed, bt_default_timeout)
tturney34ee54d2016-11-16 15:29:02 -08001019 pri_ad.log.info("Got event {}".format(profile_event))
Timofey Protopopova47c8812018-03-28 11:37:31 -07001020 except Exception as e:
Tom Turney9bce15d2018-06-21 13:00:42 -07001021 pri_ad.log.error(
1022 "Did not disconnect from Profiles. Reason {}".format(e))
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -07001023 return False
1024
1025 profile = profile_event['data']['profile']
1026 state = profile_event['data']['state']
1027 device_addr = profile_event['data']['addr']
1028
tturneyec1b8f52017-07-26 07:35:06 -07001029 if state == bt_profile_states['disconnected'] and \
Timofey Protopopova47c8812018-03-28 11:37:31 -07001030 device_addr == sec_ad.droid.bluetoothGetLocalAddress():
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -07001031 profile_disconnected.add(profile)
Stanley Tngc9a123d2017-12-05 17:37:49 -08001032 pri_ad.log.info(
1033 "Profiles disconnected so far {}".format(profile_disconnected))
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -07001034
1035 return True
1036
1037
tturney1ce8dc62016-02-18 09:55:53 -08001038def take_btsnoop_logs(android_devices, testcase, testname):
tturneyed249972016-09-13 11:07:49 -07001039 """Pull btsnoop logs from an input list of android devices.
1040
1041 Args:
1042 android_devices: the list of Android devices to pull btsnoop logs from.
1043 testcase: Name of the test calss that triggered this snoop log.
1044 testname: Name of the test case that triggered this bug report.
1045 """
tturney1ce8dc62016-02-18 09:55:53 -08001046 for a in android_devices:
tturney03a45ae2016-05-24 15:36:05 -07001047 take_btsnoop_log(a, testcase, testname)
Ang Li73697b32015-12-03 00:41:53 +00001048
tturney46060782016-11-14 16:44:38 -08001049
tturneyed249972016-09-13 11:07:49 -07001050def take_btsnoop_log(ad, testcase, testname):
Ang Li73697b32015-12-03 00:41:53 +00001051 """Grabs the btsnoop_hci log on a device and stores it in the log directory
1052 of the test class.
1053
1054 If you want grab the btsnoop_hci log, call this function with android_device
1055 objects in on_fail. Bug report takes a relative long time to take, so use
1056 this cautiously.
1057
tturneyed249972016-09-13 11:07:49 -07001058 Args:
1059 ad: The android_device instance to take bugreport on.
1060 testcase: Name of the test calss that triggered this snoop log.
1061 testname: Name of the test case that triggered this bug report.
Ang Li73697b32015-12-03 00:41:53 +00001062 """
tturneyed249972016-09-13 11:07:49 -07001063 testname = "".join(x for x in testname if x.isalnum())
tturney45962522016-11-16 13:12:44 -08001064 serial = ad.serial
tturneyf02ac392016-09-21 10:37:35 -07001065 device_model = ad.droid.getBuildModel()
1066 device_model = device_model.replace(" ", "")
1067 out_name = ','.join((testname, device_model, serial))
1068 snoop_path = ad.log_path + "/BluetoothSnoopLogs"
1069 utils.create_dir(snoop_path)
tturneyec1b8f52017-07-26 07:35:06 -07001070 cmd = ''.join(("adb -s ", serial, " pull ", btsnoop_log_path_on_device,
tturney5da68262017-05-02 08:57:52 -07001071 " ", snoop_path + '/' + out_name, ".btsnoop_hci.log"))
tturneyf02ac392016-09-21 10:37:35 -07001072 exe_cmd(cmd)
tturney5d7a1fc2017-06-01 15:12:12 -07001073 try:
1074 cmd = ''.join(
tturneyec1b8f52017-07-26 07:35:06 -07001075 ("adb -s ", serial, " pull ", btsnoop_last_log_path_on_device, " ",
tturney5d7a1fc2017-06-01 15:12:12 -07001076 snoop_path + '/' + out_name, ".btsnoop_hci.log.last"))
1077 exe_cmd(cmd)
1078 except Exception as err:
Stanley Tngc9a123d2017-12-05 17:37:49 -08001079 testcase.log.info(
1080 "File does not exist {}".format(btsnoop_last_log_path_on_device))
Ang Li73697b32015-12-03 00:41:53 +00001081
tturney46060782016-11-14 16:44:38 -08001082
tturneye3170f02016-05-19 14:37:00 -07001083def kill_bluetooth_process(ad):
tturneyed249972016-09-13 11:07:49 -07001084 """Kill Bluetooth process on Android device.
1085
1086 Args:
1087 ad: Android device to kill BT process on.
1088 """
tturney34ee54d2016-11-16 15:29:02 -08001089 ad.log.info("Killing Bluetooth process.")
tturneye3170f02016-05-19 14:37:00 -07001090 pid = ad.adb.shell(
1091 "ps | grep com.android.bluetooth | awk '{print $2}'").decode('ascii')
tturney57eb93a2016-06-21 13:58:06 -07001092 call(["adb -s " + ad.serial + " shell kill " + pid], shell=True)
Ang Li73697b32015-12-03 00:41:53 +00001093
1094
tturney24506942016-08-23 10:53:17 -07001095def orchestrate_rfcomm_connection(client_ad,
1096 server_ad,
tturneyec1b8f52017-07-26 07:35:06 -07001097 accept_timeout_ms=default_rfcomm_timeout_ms,
tturney5da68262017-05-02 08:57:52 -07001098 uuid=None):
tturney24506942016-08-23 10:53:17 -07001099 """Sets up the RFCOMM connection between two Android devices.
tturneye3170f02016-05-19 14:37:00 -07001100
tturney24506942016-08-23 10:53:17 -07001101 Args:
tturneyed249972016-09-13 11:07:49 -07001102 client_ad: the Android device performing the connection.
1103 server_ad: the Android device accepting the connection.
tturney24506942016-08-23 10:53:17 -07001104 Returns:
1105 True if connection was successful, false if unsuccessful.
1106 """
Stanley Tng180a8be2017-11-29 10:53:33 -08001107 result = orchestrate_bluetooth_socket_connection(
1108 client_ad, server_ad, accept_timeout_ms,
1109 (bt_rfcomm_uuids['default_uuid'] if uuid is None else uuid))
1110
1111 return result
1112
1113
1114def orchestrate_bluetooth_socket_connection(
1115 client_ad,
1116 server_ad,
1117 accept_timeout_ms=default_bluetooth_socket_timeout_ms,
1118 uuid=None):
1119 """Sets up the Bluetooth Socket connection between two Android devices.
1120
1121 Args:
1122 client_ad: the Android device performing the connection.
1123 server_ad: the Android device accepting the connection.
1124 Returns:
1125 True if connection was successful, false if unsuccessful.
1126 """
tturney96970fd2016-08-31 17:36:09 -07001127 server_ad.droid.bluetoothStartPairingHelper()
1128 client_ad.droid.bluetoothStartPairingHelper()
Stanley Tng180a8be2017-11-29 10:53:33 -08001129
1130 server_ad.droid.bluetoothSocketConnBeginAcceptThreadUuid(
1131 (bluetooth_socket_conn_test_uuid
1132 if uuid is None else uuid), accept_timeout_ms)
1133 client_ad.droid.bluetoothSocketConnBeginConnectThreadUuid(
1134 server_ad.droid.bluetoothGetLocalAddress(),
1135 (bluetooth_socket_conn_test_uuid if uuid is None else uuid))
1136
tturneyec1b8f52017-07-26 07:35:06 -07001137 end_time = time.time() + bt_default_timeout
tturney24506942016-08-23 10:53:17 -07001138 result = False
1139 test_result = True
1140 while time.time() < end_time:
Stanley Tng180a8be2017-11-29 10:53:33 -08001141 if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
tturney96970fd2016-08-31 17:36:09 -07001142 test_result = True
Stanley Tng180a8be2017-11-29 10:53:33 -08001143 client_ad.log.info("Bluetooth socket Client Connection Active")
tturney24506942016-08-23 10:53:17 -07001144 break
tturney96970fd2016-08-31 17:36:09 -07001145 else:
1146 test_result = False
1147 time.sleep(1)
tturney24506942016-08-23 10:53:17 -07001148 if not test_result:
Stanley Tng180a8be2017-11-29 10:53:33 -08001149 client_ad.log.error(
1150 "Failed to establish a Bluetooth socket connection")
tturney24506942016-08-23 10:53:17 -07001151 return False
1152 return True
tturneye3170f02016-05-19 14:37:00 -07001153
1154
1155def write_read_verify_data(client_ad, server_ad, msg, binary=False):
tturneyed249972016-09-13 11:07:49 -07001156 """Verify that the client wrote data to the server Android device correctly.
1157
1158 Args:
1159 client_ad: the Android device to perform the write.
1160 server_ad: the Android device to read the data written.
1161 msg: the message to write.
1162 binary: if the msg arg is binary or not.
1163
1164 Returns:
1165 True if the data written matches the data read, false if not.
1166 """
tturney34ee54d2016-11-16 15:29:02 -08001167 client_ad.log.info("Write message.")
tturneye3170f02016-05-19 14:37:00 -07001168 try:
1169 if binary:
Stanley Tng180a8be2017-11-29 10:53:33 -08001170 client_ad.droid.bluetoothSocketConnWriteBinary(msg)
tturneye3170f02016-05-19 14:37:00 -07001171 else:
Stanley Tng180a8be2017-11-29 10:53:33 -08001172 client_ad.droid.bluetoothSocketConnWrite(msg)
tturneye3170f02016-05-19 14:37:00 -07001173 except Exception as err:
tturney34ee54d2016-11-16 15:29:02 -08001174 client_ad.log.error("Failed to write data: {}".format(err))
tturneye3170f02016-05-19 14:37:00 -07001175 return False
tturney34ee54d2016-11-16 15:29:02 -08001176 server_ad.log.info("Read message.")
tturneye3170f02016-05-19 14:37:00 -07001177 try:
1178 if binary:
Stanley Tng180a8be2017-11-29 10:53:33 -08001179 read_msg = server_ad.droid.bluetoothSocketConnReadBinary().rstrip(
tturney57eb93a2016-06-21 13:58:06 -07001180 "\r\n")
tturneye3170f02016-05-19 14:37:00 -07001181 else:
Stanley Tng180a8be2017-11-29 10:53:33 -08001182 read_msg = server_ad.droid.bluetoothSocketConnRead()
tturneye3170f02016-05-19 14:37:00 -07001183 except Exception as err:
tturney34ee54d2016-11-16 15:29:02 -08001184 server_ad.log.error("Failed to read data: {}".format(err))
tturneye3170f02016-05-19 14:37:00 -07001185 return False
1186 log.info("Verify message.")
1187 if msg != read_msg:
tturney57eb93a2016-06-21 13:58:06 -07001188 log.error("Mismatch! Read: {}, Expected: {}".format(read_msg, msg))
tturneye3170f02016-05-19 14:37:00 -07001189 return False
1190 return True
tturney51eaa382016-05-17 13:28:24 -07001191
tturney24506942016-08-23 10:53:17 -07001192
1193def clear_bonded_devices(ad):
tturneyed249972016-09-13 11:07:49 -07001194 """Clear bonded devices from the input Android device.
tturney24506942016-08-23 10:53:17 -07001195
1196 Args:
tturneyed249972016-09-13 11:07:49 -07001197 ad: the Android device performing the connection.
tturney24506942016-08-23 10:53:17 -07001198 Returns:
1199 True if clearing bonded devices was successful, false if unsuccessful.
1200 """
1201 bonded_device_list = ad.droid.bluetoothGetBondedDevices()
Jakub Pawlowskibbe68b62018-03-31 03:52:22 +02001202 while bonded_device_list:
1203 device_address = bonded_device_list[0]['address']
tturney24506942016-08-23 10:53:17 -07001204 if not ad.droid.bluetoothUnbond(device_address):
Jakub Pawlowskibbe68b62018-03-31 03:52:22 +02001205 log.error("Failed to unbond {} from {}".format(
1206 device_address, ad.serial))
tturney24506942016-08-23 10:53:17 -07001207 return False
Jakub Pawlowskibbe68b62018-03-31 03:52:22 +02001208 log.info("Successfully unbonded {} from {}".format(
1209 device_address, ad.serial))
1210 #TODO: wait for BOND_STATE_CHANGED intent instead of waiting
1211 time.sleep(1)
1212
1213 # If device was first connected using LE transport, after bonding it is
1214 # accessible through it's LE address, and through it classic address.
1215 # Unbonding it will unbond two devices representing different
1216 # "addresses". Attempt to unbond such already unbonded devices will
1217 # result in bluetoothUnbond returning false.
1218 bonded_device_list = ad.droid.bluetoothGetBondedDevices()
tturney24506942016-08-23 10:53:17 -07001219 return True
1220
tturneyb92368a2016-09-13 10:43:15 -07001221
tturney5da68262017-05-02 08:57:52 -07001222def verify_server_and_client_connected(client_ad, server_ad, log=True):
tturneyed249972016-09-13 11:07:49 -07001223 """Verify that input server and client Android devices are connected.
1224
1225 This code is under the assumption that there will only be
1226 a single connection.
1227
1228 Args:
1229 client_ad: the Android device to check number of active connections.
1230 server_ad: the Android device to check number of active connections.
1231
1232 Returns:
1233 True both server and client have at least 1 active connection,
1234 false if unsuccessful.
1235 """
tturney96970fd2016-08-31 17:36:09 -07001236 test_result = True
Stanley Tng180a8be2017-11-29 10:53:33 -08001237 if len(server_ad.droid.bluetoothSocketConnActiveConnections()) == 0:
tturney5da68262017-05-02 08:57:52 -07001238 if log:
Stanley Tng180a8be2017-11-29 10:53:33 -08001239 server_ad.log.error("No socket connections found on server.")
tturney96970fd2016-08-31 17:36:09 -07001240 test_result = False
Stanley Tng180a8be2017-11-29 10:53:33 -08001241 if len(client_ad.droid.bluetoothSocketConnActiveConnections()) == 0:
tturney5da68262017-05-02 08:57:52 -07001242 if log:
Stanley Tng180a8be2017-11-29 10:53:33 -08001243 client_ad.log.error("No socket connections found on client.")
tturney96970fd2016-08-31 17:36:09 -07001244 test_result = False
1245 return test_result
tturneyb92368a2016-09-13 10:43:15 -07001246
1247
1248def orchestrate_and_verify_pan_connection(pan_dut, panu_dut):
1249 """Setups up a PAN conenction between two android devices.
1250
1251 Args:
1252 pan_dut: the Android device providing tethering services
1253 panu_dut: the Android device using the internet connection from the
1254 pan_dut
1255 Returns:
1256 True if PAN connection and verification is successful,
1257 false if unsuccessful.
1258 """
tturney1e174fd2017-06-01 14:42:20 -07001259 if not toggle_airplane_mode_by_adb(log, panu_dut, True):
tturney34ee54d2016-11-16 15:29:02 -08001260 panu_dut.log.error("Failed to toggle airplane mode on")
tturneyb92368a2016-09-13 10:43:15 -07001261 return False
tturney3282c712017-11-16 11:40:29 -08001262 if not toggle_airplane_mode_by_adb(log, panu_dut, False):
1263 pan_dut.log.error("Failed to toggle airplane mode off")
1264 return False
1265 pan_dut.droid.bluetoothStartConnectionStateChangeMonitor("")
1266 panu_dut.droid.bluetoothStartConnectionStateChangeMonitor("")
tturneyb92368a2016-09-13 10:43:15 -07001267 if not bluetooth_enabled_check(panu_dut):
1268 return False
tturneyc52d2c62017-07-06 13:33:47 -07001269 if not bluetooth_enabled_check(pan_dut):
1270 return False
tturneyb92368a2016-09-13 10:43:15 -07001271 pan_dut.droid.bluetoothPanSetBluetoothTethering(True)
Jack Hea518bdb2016-11-01 18:11:06 -07001272 if not (pair_pri_to_sec(pan_dut, panu_dut)):
tturneyb92368a2016-09-13 10:43:15 -07001273 return False
1274 if not pan_dut.droid.bluetoothPanIsTetheringOn():
tturney34ee54d2016-11-16 15:29:02 -08001275 pan_dut.log.error("Failed to enable Bluetooth tethering.")
tturneyb92368a2016-09-13 10:43:15 -07001276 return False
1277 # Magic sleep needed to give the stack time in between bonding and
1278 # connecting the PAN profile.
tturneyec1b8f52017-07-26 07:35:06 -07001279 time.sleep(pan_connect_timeout)
tturneyb92368a2016-09-13 10:43:15 -07001280 panu_dut.droid.bluetoothConnectBonded(
1281 pan_dut.droid.bluetoothGetLocalAddress())
1282 if not verify_http_connection(log, panu_dut):
tturney34ee54d2016-11-16 15:29:02 -08001283 panu_dut.log.error("Can't verify http connection on PANU device.")
tturneyb92368a2016-09-13 10:43:15 -07001284 if not verify_http_connection(log, pan_dut):
tturney34ee54d2016-11-16 15:29:02 -08001285 pan_dut.log.info(
1286 "Can't verify http connection on PAN service device")
tturneyb92368a2016-09-13 10:43:15 -07001287 return False
1288 return True
Phillip Walker827112a2016-09-08 16:27:19 -07001289
1290
1291def is_hfp_client_device_connected(ad, addr):
1292 """Determines if an AndroidDevice has HFP connectivity to input address
1293
1294 Args:
1295 ad: the Android device
1296 addr: the address that's expected
1297 Returns:
1298 True if connection was successful, false if unsuccessful.
1299 """
1300 devices = ad.droid.bluetoothHfpClientGetConnectedDevices()
tturney34ee54d2016-11-16 15:29:02 -08001301 ad.log.info("Connected HFP Client devices: {}".format(devices))
Phillip Walker827112a2016-09-08 16:27:19 -07001302 if addr in {d['address'] for d in devices}:
1303 return True
1304 return False
1305
1306
1307def is_a2dp_src_device_connected(ad, addr):
1308 """Determines if an AndroidDevice has A2DP connectivity to input address
1309
1310 Args:
1311 ad: the Android device
1312 addr: the address that's expected
1313 Returns:
1314 True if connection was successful, false if unsuccessful.
1315 """
1316 devices = ad.droid.bluetoothA2dpGetConnectedDevices()
tturney34ee54d2016-11-16 15:29:02 -08001317 ad.log.info("Connected A2DP Source devices: {}".format(devices))
Phillip Walker827112a2016-09-08 16:27:19 -07001318 if addr in {d['address'] for d in devices}:
1319 return True
1320 return False
1321
1322
1323def is_a2dp_snk_device_connected(ad, addr):
1324 """Determines if an AndroidDevice has A2DP snk connectivity to input address
1325
1326 Args:
1327 ad: the Android device
1328 addr: the address that's expected
1329 Returns:
1330 True if connection was successful, false if unsuccessful.
1331 """
1332 devices = ad.droid.bluetoothA2dpSinkGetConnectedDevices()
tturney34ee54d2016-11-16 15:29:02 -08001333 ad.log.info("Connected A2DP Sink devices: {}".format(devices))
Phillip Walker827112a2016-09-08 16:27:19 -07001334 if addr in {d['address'] for d in devices}:
1335 return True
1336 return False
1337
1338
1339def is_map_mce_device_connected(ad, addr):
1340 """Determines if an AndroidDevice has MAP MCE connectivity to input address
1341
1342 Args:
1343 ad: the Android device
1344 addr: the address that's expected
1345 Returns:
1346 True if connection was successful, false if unsuccessful.
1347 """
1348 devices = ad.droid.bluetoothMapClientGetConnectedDevices()
tturney34ee54d2016-11-16 15:29:02 -08001349 ad.log.info("Connected MAP MCE devices: {}".format(devices))
Phillip Walker827112a2016-09-08 16:27:19 -07001350 if addr in {d['address'] for d in devices}:
1351 return True
1352 return False
1353
1354
1355def is_map_mse_device_connected(ad, addr):
1356 """Determines if an AndroidDevice has MAP MSE connectivity to input address
1357
1358 Args:
1359 ad: the Android device
1360 addr: the address that's expected
1361 Returns:
1362 True if connection was successful, false if unsuccessful.
1363 """
1364 devices = ad.droid.bluetoothMapGetConnectedDevices()
tturney34ee54d2016-11-16 15:29:02 -08001365 ad.log.info("Connected MAP MSE devices: {}".format(devices))
Phillip Walker827112a2016-09-08 16:27:19 -07001366 if addr in {d['address'] for d in devices}:
1367 return True
1368 return False
Hansong Zhangd78b3b82017-10-03 10:36:12 -07001369
1370
1371def hid_keyboard_report(key, modifier="00"):
1372 """Get the HID keyboard report for the given key
1373
1374 Args:
1375 key: the key we want
1376 modifier: HID keyboard modifier bytes
1377 Returns:
1378 The byte array for the HID report.
1379 """
tturney69e3bb12017-10-26 14:15:05 -07001380 return str(
1381 bytearray.fromhex(" ".join(
1382 [modifier, "00", key, "00", "00", "00", "00", "00"])), "utf-8")
Hansong Zhangd78b3b82017-10-03 10:36:12 -07001383
1384
1385def hid_device_send_key_data_report(host_id, device_ad, key, interval=1):
1386 """Send a HID report simulating a 1-second keyboard press from host_ad to
1387 device_ad
1388
1389 Args:
1390 host_id: the Bluetooth MAC address or name of the HID host
1391 device_ad: HID device
1392 key: the key we want to send
1393 interval: the interval between key press and key release
1394 """
tturney69e3bb12017-10-26 14:15:05 -07001395 device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard,
1396 hid_keyboard_report(key))
Hansong Zhangd78b3b82017-10-03 10:36:12 -07001397 time.sleep(interval)
tturney69e3bb12017-10-26 14:15:05 -07001398 device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard,
1399 hid_keyboard_report("00"))
Timofey Protopopov2651b742018-04-09 16:43:49 -07001400
1401
1402def is_a2dp_connected(sink, source):
1403 """
1404 Convenience Function to see if the 2 devices are connected on
1405 A2dp.
1406 Args:
1407 sink: Audio Sink
1408 source: Audio Source
1409 Returns:
1410 True if Connected
1411 False if Not connected
1412 """
1413
1414 devices = sink.droid.bluetoothA2dpSinkGetConnectedDevices()
1415 for device in devices:
1416 sink.log.info("A2dp Connected device {}".format(device["name"]))
1417 if (device["address"] == source.droid.bluetoothGetLocalAddress()):
1418 return True
1419 return False
Tom Turneyf5b42142018-06-22 14:01:27 -07001420
1421
Tom Turney9bce15d2018-06-21 13:00:42 -07001422def get_device_selector_dictionary(android_device_list):
1423 """Create a dictionary of Bluetooth features vs Android devices.
1424
1425 Args:
1426 android_device_list: The list of Android devices.
1427 Returns:
1428 A dictionary of profiles/features to Android devices.
1429 """
1430 selector_dict = {}
1431 for ad in android_device_list:
1432 uuids = ad.droid.bluetoothGetLocalUuids()
1433
1434 for profile, uuid_const in sig_uuid_constants.items():
1435 uuid_check = sig_uuid_constants['BASE_UUID'].format(
1436 uuid_const).lower()
1437 if uuid_check in uuids:
1438 if profile in selector_dict:
1439 selector_dict[profile].append(ad)
1440 else:
1441 selector_dict[profile] = [ad]
1442
1443 # Various services may not be active during BT startup.
1444 # If the device can be identified through adb shell pm list features
1445 # then try to add them to the appropriate profiles / features.
1446
1447 # Android TV.
1448 if "feature:com.google.android.tv.installed" in ad.features:
1449 ad.log.info("Android TV device found.")
1450 supported_profiles = ['AudioSink']
1451 _add_android_device_to_dictionary(ad, supported_profiles,
1452 selector_dict)
1453
1454 # Android Auto
1455 elif "feature:android.hardware.type.automotive" in ad.features:
1456 ad.log.info("Android Auto device found.")
1457 # Add: AudioSink , A/V_RemoteControl,
1458 supported_profiles = [
1459 'AudioSink', 'A/V_RemoteControl', 'Message Notification Server'
1460 ]
1461 _add_android_device_to_dictionary(ad, supported_profiles,
1462 selector_dict)
1463 # Android Wear
1464 elif "feature:android.hardware.type.watch" in ad.features:
1465 ad.log.info("Android Wear device found.")
1466 supported_profiles = []
1467 _add_android_device_to_dictionary(ad, supported_profiles,
1468 selector_dict)
1469 # Android Phone
1470 elif "feature:android.hardware.telephony" in ad.features:
1471 ad.log.info("Android Phone device found.")
1472 # Add: AudioSink
1473 supported_profiles = [
1474 'AudioSource', 'A/V_RemoteControlTarget',
1475 'Message Access Server'
1476 ]
1477 _add_android_device_to_dictionary(ad, supported_profiles,
1478 selector_dict)
1479 return selector_dict
1480
1481
1482def _add_android_device_to_dictionary(android_device, profile_list,
1483 selector_dict):
1484 """Adds the AndroidDevice and supported features to the selector dictionary
1485
1486 Args:
1487 android_device: The Android device.
1488 profile_list: The list of profiles the Android device supports.
1489 """
1490 for profile in profile_list:
1491 if profile in selector_dict and android_device not in selector_dict[profile]:
1492 selector_dict[profile].append(android_device)
1493 else:
1494 selector_dict[profile] = [android_device]
tprotopopov2e18bbb2018-10-02 10:29:01 -07001495
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -08001496
tprotopopov2e18bbb2018-10-02 10:29:01 -07001497def get_bluetooth_crash_count(android_device):
1498 out = android_device.adb.shell("dumpsys bluetooth_manager")
1499 return int(re.search("crashed(.*\d)", out).group(1))
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -08001500
1501
1502def set_bluetooth_codec(
1503 android_device,
1504 codec_type,
1505 sample_rate,
1506 bits_per_sample,
1507 channel_mode,
1508 codec_specific_1=0):
1509 """Sets the A2DP codec configuration on the AndroidDevice.
1510
1511 Args:
1512 android_device (acts.controllers.android_device.AndroidDevice): the
1513 android device for which to switch the codec.
1514 codec_type (str): the desired codec type. Must be a key in
1515 bt_constants.codec_types.
1516 sample_rate (str): the desired sample rate. Must be a key in
1517 bt_constants.sample_rates.
1518 bits_per_sample (str): the desired bits per sample. Must be a key in
1519 bt_constants.bits_per_samples.
1520 channel_mode (str): the desired channel mode. Must be a key in
1521 bt_constants.channel_modes.
1522 codec_specific_1 (int): the desired bit rate (quality) for LDAC codec.
1523 Returns:
1524 bool: True if the codec config was successfully changed to the desired
1525 values. Else False.
1526 """
1527 message = (
1528 "Set Android Device A2DP Bluetooth codec configuration:\n"
1529 "\tCodec: {codec_type}\n"
1530 "\tSample Rate: {sample_rate}\n"
1531 "\tBits per Sample: {bits_per_sample}\n"
1532 "\tChannel Mode: {channel_mode}".format(
1533 codec_type=codec_type,
1534 sample_rate=sample_rate,
1535 bits_per_sample=bits_per_sample,
1536 channel_mode=channel_mode
1537 )
1538 )
1539 android_device.log.info(message)
1540
1541 # Send SL4A command
1542 droid, ed = android_device.droid, android_device.ed
1543 if not droid.bluetoothA2dpSetCodecConfigPreference(
1544 codec_types[codec_type],
1545 sample_rates[str(sample_rate)],
1546 bits_per_samples[str(bits_per_sample)],
1547 channel_modes[channel_mode],
1548 codec_specific_1
1549 ):
1550 android_device.log.error("SL4A command failed. Codec config was not "
1551 "changed.")
1552 return False
1553 try:
1554 ed.pop_event(bluetooth_a2dp_codec_config_changed, bt_default_timeout)
1555 except Exception:
1556 android_device.log.error("SL4A event not registered. Codec config was "
1557 "not changed.")
1558 return False
1559
1560 # Validate codec value through ADB
1561 command = "dumpsys bluetooth_manager | grep -i 'current codec'"
1562 out = android_device.adb.shell(command)
1563 split_out = out.split(": ")
1564 if len(split_out) != 2:
1565 android_device.log.warning("Could not verify codec config change "
1566 "through ADB.")
1567 elif split_out[1].strip() != codec_type:
1568 android_device.log.error(
1569 "Codec config was not changed.\n"
1570 "\tExpected codec: {exp}\n"
1571 "\tActual codec: {act}".format(
1572 exp=codec_type,
1573 act=split_out[1].strip()
1574 )
1575 )
1576 return False
1577 android_device.log.info("Bluetooth codec successfully changed.")
1578 return True