blob: 4084b8cbccb7d8c1f3f611387e1270d1f11e056a [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
Xianyuan Jia0e39e552019-01-24 17:17:47 -080018import os
Ang Li73697b32015-12-03 00:41:53 +000019import random
Xianyuan Jia0e39e552019-01-24 17:17:47 -080020import re
Ang Li73697b32015-12-03 00:41:53 +000021import string
Ang Li73697b32015-12-03 00:41:53 +000022import threading
23import time
Xianyuan Jia0e39e552019-01-24 17:17:47 -080024from queue import Empty
tturneye3170f02016-05-19 14:37:00 -070025from subprocess import call
Ang Li73697b32015-12-03 00:41:53 +000026
Qif9fa6ba2020-01-02 10:55:58 -080027from acts import asserts
tturneyec1b8f52017-07-26 07:35:06 -070028from acts.test_utils.bt.bt_constants import adv_fail
29from acts.test_utils.bt.bt_constants import adv_succ
tturneyec1b8f52017-07-26 07:35:06 -070030from acts.test_utils.bt.bt_constants import batch_scan_not_supported_list
Joe Brennan6dea5072020-05-09 15:42:11 -070031from acts.test_utils.bt.bt_constants import batch_scan_result
Xianyuan Jia0e39e552019-01-24 17:17:47 -080032from acts.test_utils.bt.bt_constants import bits_per_samples
tturneyec1b8f52017-07-26 07:35:06 -070033from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
34from acts.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -080035from acts.test_utils.bt.bt_constants import bluetooth_a2dp_codec_config_changed
tturneyec1b8f52017-07-26 07:35:06 -070036from acts.test_utils.bt.bt_constants import bluetooth_off
37from acts.test_utils.bt.bt_constants import bluetooth_on
38from acts.test_utils.bt.bt_constants import \
39 bluetooth_profile_connection_state_changed
Timofey Protopopova47c8812018-03-28 11:37:31 -070040from acts.test_utils.bt.bt_constants import bluetooth_socket_conn_test_uuid
Xianyuan Jia0e39e552019-01-24 17:17:47 -080041from acts.test_utils.bt.bt_constants import bt_default_timeout
42from acts.test_utils.bt.bt_constants import bt_profile_constants
43from acts.test_utils.bt.bt_constants import bt_profile_states
44from acts.test_utils.bt.bt_constants import bt_rfcomm_uuids
tturneyec1b8f52017-07-26 07:35:06 -070045from acts.test_utils.bt.bt_constants import bt_scan_mode_types
46from acts.test_utils.bt.bt_constants import btsnoop_last_log_path_on_device
47from acts.test_utils.bt.bt_constants import btsnoop_log_path_on_device
Xianyuan Jia0e39e552019-01-24 17:17:47 -080048from acts.test_utils.bt.bt_constants import channel_modes
49from acts.test_utils.bt.bt_constants import codec_types
Stanley Tng180a8be2017-11-29 10:53:33 -080050from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
Xianyuan Jia0e39e552019-01-24 17:17:47 -080051from acts.test_utils.bt.bt_constants import default_rfcomm_timeout_ms
52from acts.test_utils.bt.bt_constants import hid_id_keyboard
tturneyec1b8f52017-07-26 07:35:06 -070053from acts.test_utils.bt.bt_constants import pairing_variant_passkey_confirmation
54from acts.test_utils.bt.bt_constants import pan_connect_timeout
Xianyuan Jia0e39e552019-01-24 17:17:47 -080055from acts.test_utils.bt.bt_constants import sample_rates
tturneyec1b8f52017-07-26 07:35:06 -070056from acts.test_utils.bt.bt_constants import scan_result
Tom Turney7b0baa52018-06-26 11:31:31 -070057from acts.test_utils.bt.bt_constants import sig_uuid_constants
Xianyuan Jia0e39e552019-01-24 17:17:47 -080058from acts.test_utils.bt.bt_constants import small_timeout
tturney1e174fd2017-06-01 14:42:20 -070059from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
tturneyb92368a2016-09-13 10:43:15 -070060from acts.test_utils.tel.tel_test_utils import verify_http_connection
Ang Li73697b32015-12-03 00:41:53 +000061from acts.utils import exe_cmd
62
Xianyuan Jia0e39e552019-01-24 17:17:47 -080063from acts import utils
64
Ang Li5bd83f32016-05-23 14:39:38 -070065log = logging
Ang Li73697b32015-12-03 00:41:53 +000066
tturney57eb93a2016-06-21 13:58:06 -070067advertisements_to_devices = {}
Ang Li73697b32015-12-03 00:41:53 +000068
Ang Li73697b32015-12-03 00:41:53 +000069
tturney951533f2016-06-23 11:24:25 -070070class BtTestUtilsError(Exception):
71 pass
72
tturneyb92368a2016-09-13 10:43:15 -070073
Bill Leung46b69352018-11-09 17:28:39 -080074def _add_android_device_to_dictionary(android_device, profile_list,
75 selector_dict):
76 """Adds the AndroidDevice and supported features to the selector dictionary
tturneyed249972016-09-13 11:07:49 -070077
78 Args:
Bill Leung46b69352018-11-09 17:28:39 -080079 android_device: The Android device.
80 profile_list: The list of profiles the Android device supports.
tturneyed249972016-09-13 11:07:49 -070081 """
Bill Leung46b69352018-11-09 17:28:39 -080082 for profile in profile_list:
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -070083 if profile in selector_dict and android_device not in selector_dict[
84 profile]:
Bill Leung46b69352018-11-09 17:28:39 -080085 selector_dict[profile].append(android_device)
86 else:
87 selector_dict[profile] = [android_device]
Ang Li73697b32015-12-03 00:41:53 +000088
89
Aidan Holloway-bidwelle5882a52019-08-14 12:27:29 -070090def bluetooth_enabled_check(ad, timeout_sec=5):
tturneyed249972016-09-13 11:07:49 -070091 """Checks if the Bluetooth state is enabled, if not it will attempt to
92 enable it.
93
94 Args:
95 ad: The Android device list to enable Bluetooth on.
Aidan Holloway-bidwelle5882a52019-08-14 12:27:29 -070096 timeout_sec: number of seconds to wait for toggle to take effect.
tturneyed249972016-09-13 11:07:49 -070097
98 Returns:
99 True if successful, false if unsuccessful.
100 """
tturney50f851d2016-07-07 11:07:37 -0700101 if not ad.droid.bluetoothCheckState():
102 ad.droid.bluetoothToggleState(True)
103 expected_bluetooth_on_event_name = bluetooth_on
104 try:
tturneyec1b8f52017-07-26 07:35:06 -0700105 ad.ed.pop_event(expected_bluetooth_on_event_name,
106 bt_default_timeout)
tturney50f851d2016-07-07 11:07:37 -0700107 except Empty:
tturney5da68262017-05-02 08:57:52 -0700108 ad.log.info(
109 "Failed to toggle Bluetooth on(no broadcast received).")
tturney50f851d2016-07-07 11:07:37 -0700110 # Try one more time to poke at the actual state.
111 if ad.droid.bluetoothCheckState():
tturney34ee54d2016-11-16 15:29:02 -0800112 ad.log.info(".. actual state is ON")
tturney50f851d2016-07-07 11:07:37 -0700113 return True
tturney34ee54d2016-11-16 15:29:02 -0800114 ad.log.error(".. actual state is OFF")
tturney50f851d2016-07-07 11:07:37 -0700115 return False
Aidan Holloway-bidwelle5882a52019-08-14 12:27:29 -0700116 end_time = time.time() + timeout_sec
117 while not ad.droid.bluetoothCheckState() and time.time() < end_time:
118 time.sleep(1)
119 return ad.droid.bluetoothCheckState()
tturney50f851d2016-07-07 11:07:37 -0700120
Tom Turney9bce15d2018-06-21 13:00:42 -0700121
Bill Leung46b69352018-11-09 17:28:39 -0800122def check_device_supported_profiles(droid):
123 """Checks for Android device supported profiles.
tturneyed249972016-09-13 11:07:49 -0700124
125 Args:
Bill Leung46b69352018-11-09 17:28:39 -0800126 droid: The droid object to query.
tturneyed249972016-09-13 11:07:49 -0700127
128 Returns:
Bill Leung46b69352018-11-09 17:28:39 -0800129 A dictionary of supported profiles.
Ang Li73697b32015-12-03 00:41:53 +0000130 """
Bill Leung46b69352018-11-09 17:28:39 -0800131 profile_dict = {}
132 profile_dict['hid'] = droid.bluetoothHidIsReady()
133 profile_dict['hsp'] = droid.bluetoothHspIsReady()
134 profile_dict['a2dp'] = droid.bluetoothA2dpIsReady()
135 profile_dict['avrcp'] = droid.bluetoothAvrcpIsReady()
136 profile_dict['a2dp_sink'] = droid.bluetoothA2dpSinkIsReady()
137 profile_dict['hfp_client'] = droid.bluetoothHfpClientIsReady()
138 profile_dict['pbap_client'] = droid.bluetoothPbapClientIsReady()
139 return profile_dict
Ang Li73697b32015-12-03 00:41:53 +0000140
141
tturneyed249972016-09-13 11:07:49 -0700142def cleanup_scanners_and_advertisers(scn_android_device, scn_callback_list,
tturney1ce8dc62016-02-18 09:55:53 -0800143 adv_android_device, adv_callback_list):
tturneyed249972016-09-13 11:07:49 -0700144 """Try to gracefully stop all scanning and advertising instances.
145
146 Args:
147 scn_android_device: The Android device that is actively scanning.
148 scn_callback_list: The scan callback id list that needs to be stopped.
149 adv_android_device: The Android device that is actively advertising.
150 adv_callback_list: The advertise callback id list that needs to be
151 stopped.
Ang Li73697b32015-12-03 00:41:53 +0000152 """
tturney1ce8dc62016-02-18 09:55:53 -0800153 scan_droid, scan_ed = scn_android_device.droid, scn_android_device.ed
154 adv_droid = adv_android_device.droid
Ang Li73697b32015-12-03 00:41:53 +0000155 try:
tturneyed249972016-09-13 11:07:49 -0700156 for scan_callback in scn_callback_list:
Ang Li73697b32015-12-03 00:41:53 +0000157 scan_droid.bleStopBleScan(scan_callback)
tturneye3170f02016-05-19 14:37:00 -0700158 except Exception as err:
tturney34ee54d2016-11-16 15:29:02 -0800159 scn_android_device.log.debug(
160 "Failed to stop LE scan... reseting Bluetooth. Error {}".format(
161 err))
tturney1ce8dc62016-02-18 09:55:53 -0800162 reset_bluetooth([scn_android_device])
Ang Li73697b32015-12-03 00:41:53 +0000163 try:
164 for adv_callback in adv_callback_list:
165 adv_droid.bleStopBleAdvertising(adv_callback)
tturneye3170f02016-05-19 14:37:00 -0700166 except Exception as err:
tturney34ee54d2016-11-16 15:29:02 -0800167 adv_android_device.log.debug(
tturneyb92368a2016-09-13 10:43:15 -0700168 "Failed to stop LE advertisement... reseting Bluetooth. Error {}".
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700169 format(err))
tturney1ce8dc62016-02-18 09:55:53 -0800170 reset_bluetooth([adv_android_device])
Ang Li73697b32015-12-03 00:41:53 +0000171
172
Bill Leung46b69352018-11-09 17:28:39 -0800173def clear_bonded_devices(ad):
174 """Clear bonded devices from the input Android device.
tturneyed249972016-09-13 11:07:49 -0700175
176 Args:
Bill Leung46b69352018-11-09 17:28:39 -0800177 ad: the Android device performing the connection.
tturneyed249972016-09-13 11:07:49 -0700178 Returns:
Bill Leung46b69352018-11-09 17:28:39 -0800179 True if clearing bonded devices was successful, false if unsuccessful.
tturneyed249972016-09-13 11:07:49 -0700180 """
Bill Leung46b69352018-11-09 17:28:39 -0800181 bonded_device_list = ad.droid.bluetoothGetBondedDevices()
182 while bonded_device_list:
183 device_address = bonded_device_list[0]['address']
184 if not ad.droid.bluetoothUnbond(device_address):
185 log.error("Failed to unbond {} from {}".format(
186 device_address, ad.serial))
187 return False
188 log.info("Successfully unbonded {} from {}".format(
189 device_address, ad.serial))
190 #TODO: wait for BOND_STATE_CHANGED intent instead of waiting
191 time.sleep(1)
Ang Li73697b32015-12-03 00:41:53 +0000192
Bill Leung46b69352018-11-09 17:28:39 -0800193 # If device was first connected using LE transport, after bonding it is
194 # accessible through it's LE address, and through it classic address.
195 # Unbonding it will unbond two devices representing different
196 # "addresses". Attempt to unbond such already unbonded devices will
197 # result in bluetoothUnbond returning false.
198 bonded_device_list = ad.droid.bluetoothGetBondedDevices()
Sanket Agarwalc8fdc1a2016-07-06 16:39:34 -0700199 return True
200
Tom Turney9bce15d2018-06-21 13:00:42 -0700201
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700202def connect_phone_to_headset(android,
203 headset,
204 timeout=bt_default_timeout,
Aidan Holloway-bidwell9e44b9d2018-11-07 14:33:27 -0800205 connection_check_period=10):
206 """Connects android phone to bluetooth headset.
207 Headset object must have methods power_on and enter_pairing_mode,
208 and attribute mac_address.
209
210 Args:
211 android: AndroidDevice object with SL4A installed.
212 headset: Object with attribute mac_address and methods power_on and
213 enter_pairing_mode.
214 timeout: Seconds to wait for devices to connect.
215 connection_check_period: how often to check for connection once the
216 SL4A connect RPC has been sent.
217 Returns:
218 connected (bool): True if devices are paired and connected by end of
219 method. False otherwise.
220 """
Qie7ed5932019-11-05 15:04:09 -0800221 headset_mac_address = headset.mac_address
222 connected = is_a2dp_src_device_connected(android, headset_mac_address)
Aidan Holloway-bidwell9e44b9d2018-11-07 14:33:27 -0800223 log.info('Devices connected before pair attempt: %s' % connected)
Aidan Holloway-bidwelle5882a52019-08-14 12:27:29 -0700224 if not connected:
225 # Turn on headset and initiate pairing mode.
226 headset.enter_pairing_mode()
227 android.droid.bluetoothStartPairingHelper()
Aidan Holloway-bidwell9e44b9d2018-11-07 14:33:27 -0800228 start_time = time.time()
229 # If already connected, skip pair and connect attempt.
230 while not connected and (time.time() - start_time < timeout):
Qi49a414b2019-08-21 09:04:03 -0700231 bonded_info = android.droid.bluetoothGetBondedDevices()
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700232 if headset.mac_address not in [
233 info["address"] for info in bonded_info
234 ]:
Aidan Holloway-bidwell9e44b9d2018-11-07 14:33:27 -0800235 # Use SL4A to pair and connect with headset.
Qic4be4872019-11-03 12:49:49 -0800236 headset.enter_pairing_mode()
Qie7ed5932019-11-05 15:04:09 -0800237 android.droid.bluetoothDiscoverAndBond(headset_mac_address)
Aidan Holloway-bidwell9e44b9d2018-11-07 14:33:27 -0800238 else: # Device is bonded but not connected
Qie7ed5932019-11-05 15:04:09 -0800239 android.droid.bluetoothConnectBonded(headset_mac_address)
Aidan Holloway-bidwell9e44b9d2018-11-07 14:33:27 -0800240 log.info('Waiting for connection...')
241 time.sleep(connection_check_period)
242 # Check for connection.
Qie7ed5932019-11-05 15:04:09 -0800243 connected = is_a2dp_src_device_connected(android, headset_mac_address)
Aidan Holloway-bidwell9e44b9d2018-11-07 14:33:27 -0800244 log.info('Devices connected after pair attempt: %s' % connected)
245 return connected
246
247
Phillip Walker827112a2016-09-08 16:27:19 -0700248def connect_pri_to_sec(pri_ad, sec_ad, profiles_set, attempts=2):
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700249 """Connects pri droid to secondary droid.
tturney57eb93a2016-06-21 13:58:06 -0700250
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700251 Args:
Phillip Walker827112a2016-09-08 16:27:19 -0700252 pri_ad: AndroidDroid initiating connection
253 sec_ad: AndroidDroid accepting connection
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700254 profiles_set: Set of profiles to be connected
255 attempts: Number of attempts to try until failure.
256
257 Returns:
258 Pass if True
259 Fail if False
260 """
Phillip Walker827112a2016-09-08 16:27:19 -0700261 device_addr = sec_ad.droid.bluetoothGetLocalAddress()
262 # Allows extra time for the SDP records to be updated.
263 time.sleep(2)
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700264 curr_attempts = 0
265 while curr_attempts < attempts:
Phillip Walker827112a2016-09-08 16:27:19 -0700266 log.info("connect_pri_to_sec curr attempt {} total {}".format(
267 curr_attempts, attempts))
268 if _connect_pri_to_sec(pri_ad, sec_ad, profiles_set):
269 return True
270 curr_attempts += 1
271 log.error("connect_pri_to_sec failed to connect after {} attempts".format(
272 attempts))
Sanket Agarwalc96b9cc2016-08-18 12:01:04 -0700273 return False
274
Phillip Walker827112a2016-09-08 16:27:19 -0700275
276def _connect_pri_to_sec(pri_ad, sec_ad, profiles_set):
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700277 """Connects pri droid to secondary droid.
278
279 Args:
Phillip Walker827112a2016-09-08 16:27:19 -0700280 pri_ad: AndroidDroid initiating connection.
281 sec_ad: AndroidDroid accepting connection.
tturneyed249972016-09-13 11:07:49 -0700282 profiles_set: Set of profiles to be connected.
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700283
284 Returns:
tturneyed249972016-09-13 11:07:49 -0700285 True of connection is successful, false if unsuccessful.
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700286 """
287 # Check if we support all profiles.
tturneye1f6c722017-08-30 07:03:11 -0700288 supported_profiles = bt_profile_constants.values()
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700289 for profile in profiles_set:
290 if profile not in supported_profiles:
tturney34ee54d2016-11-16 15:29:02 -0800291 pri_ad.log.info("Profile {} is not supported list {}".format(
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700292 profile, supported_profiles))
293 return False
294
295 # First check that devices are bonded.
296 paired = False
Phillip Walker827112a2016-09-08 16:27:19 -0700297 for paired_device in pri_ad.droid.bluetoothGetBondedDevices():
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700298 if paired_device['address'] == \
Timofey Protopopova47c8812018-03-28 11:37:31 -0700299 sec_ad.droid.bluetoothGetLocalAddress():
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700300 paired = True
301 break
302
303 if not paired:
tturney34ee54d2016-11-16 15:29:02 -0800304 pri_ad.log.error("Not paired to {}".format(sec_ad.serial))
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700305 return False
306
307 # Now try to connect them, the following call will try to initiate all
308 # connections.
Stanley Tngc9a123d2017-12-05 17:37:49 -0800309 pri_ad.droid.bluetoothConnectBonded(
310 sec_ad.droid.bluetoothGetLocalAddress())
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700311
tturney46060782016-11-14 16:44:38 -0800312 end_time = time.time() + 10
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700313 profile_connected = set()
tturney46060782016-11-14 16:44:38 -0800314 sec_addr = sec_ad.droid.bluetoothGetLocalAddress()
tturney34ee54d2016-11-16 15:29:02 -0800315 pri_ad.log.info("Profiles to be connected {}".format(profiles_set))
tturney46060782016-11-14 16:44:38 -0800316 # First use APIs to check profile connection state
Stanley Tngc9a123d2017-12-05 17:37:49 -0800317 while (time.time() < end_time
318 and not profile_connected.issuperset(profiles_set)):
319 if (bt_profile_constants['headset_client'] not in profile_connected
320 and bt_profile_constants['headset_client'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800321 if is_hfp_client_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700322 profile_connected.add(bt_profile_constants['headset_client'])
Stanley Tngc9a123d2017-12-05 17:37:49 -0800323 if (bt_profile_constants['a2dp'] not in profile_connected
324 and bt_profile_constants['a2dp'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800325 if is_a2dp_src_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700326 profile_connected.add(bt_profile_constants['a2dp'])
Stanley Tngc9a123d2017-12-05 17:37:49 -0800327 if (bt_profile_constants['a2dp_sink'] not in profile_connected
328 and bt_profile_constants['a2dp_sink'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800329 if is_a2dp_snk_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700330 profile_connected.add(bt_profile_constants['a2dp_sink'])
Stanley Tngc9a123d2017-12-05 17:37:49 -0800331 if (bt_profile_constants['map_mce'] not in profile_connected
332 and bt_profile_constants['map_mce'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800333 if is_map_mce_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700334 profile_connected.add(bt_profile_constants['map_mce'])
Stanley Tngc9a123d2017-12-05 17:37:49 -0800335 if (bt_profile_constants['map'] not in profile_connected
336 and bt_profile_constants['map'] in profiles_set):
tturney46060782016-11-14 16:44:38 -0800337 if is_map_mse_device_connected(pri_ad, sec_addr):
tturneyec1b8f52017-07-26 07:35:06 -0700338 profile_connected.add(bt_profile_constants['map'])
tturney46060782016-11-14 16:44:38 -0800339 time.sleep(0.1)
340 # If APIs fail, try to find the connection broadcast receiver.
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700341 while not profile_connected.issuperset(profiles_set):
342 try:
Phillip Walker827112a2016-09-08 16:27:19 -0700343 profile_event = pri_ad.ed.pop_event(
tturney46060782016-11-14 16:44:38 -0800344 bluetooth_profile_connection_state_changed,
tturneyec1b8f52017-07-26 07:35:06 -0700345 bt_default_timeout + 10)
tturney34ee54d2016-11-16 15:29:02 -0800346 pri_ad.log.info("Got event {}".format(profile_event))
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700347 except Exception:
tturney34ee54d2016-11-16 15:29:02 -0800348 pri_ad.log.error("Did not get {} profiles left {}".format(
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700349 bluetooth_profile_connection_state_changed, profile_connected))
350 return False
351
352 profile = profile_event['data']['profile']
353 state = profile_event['data']['state']
354 device_addr = profile_event['data']['addr']
tturneyec1b8f52017-07-26 07:35:06 -0700355 if state == bt_profile_states['connected'] and \
Timofey Protopopova47c8812018-03-28 11:37:31 -0700356 device_addr == sec_ad.droid.bluetoothGetLocalAddress():
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700357 profile_connected.add(profile)
Stanley Tngc9a123d2017-12-05 17:37:49 -0800358 pri_ad.log.info(
359 "Profiles connected until now {}".format(profile_connected))
Sanket Agarwald09c7d82016-05-17 20:24:51 -0700360 # Failure happens inside the while loop. If we came here then we already
361 # connected.
362 return True
Ang Li73697b32015-12-03 00:41:53 +0000363
tturney57eb93a2016-06-21 13:58:06 -0700364
Bill Leung46b69352018-11-09 17:28:39 -0800365def determine_max_advertisements(android_device):
366 """Determines programatically how many advertisements the Android device
367 supports.
368
369 Args:
370 android_device: The Android device to determine max advertisements of.
371
372 Returns:
373 The maximum advertisement count.
374 """
375 android_device.log.info(
376 "Determining number of maximum concurrent advertisements...")
377 advertisement_count = 0
378 bt_enabled = False
379 expected_bluetooth_on_event_name = bluetooth_on
380 if not android_device.droid.bluetoothCheckState():
381 android_device.droid.bluetoothToggleState(True)
382 try:
383 android_device.ed.pop_event(expected_bluetooth_on_event_name,
384 bt_default_timeout)
385 except Exception:
386 android_device.log.info(
387 "Failed to toggle Bluetooth on(no broadcast received).")
388 # Try one more time to poke at the actual state.
389 if android_device.droid.bluetoothCheckState() is True:
390 android_device.log.info(".. actual state is ON")
391 else:
392 android_device.log.error(
393 "Failed to turn Bluetooth on. Setting default advertisements to 1"
394 )
395 advertisement_count = -1
396 return advertisement_count
397 advertise_callback_list = []
398 advertise_data = android_device.droid.bleBuildAdvertiseData()
399 advertise_settings = android_device.droid.bleBuildAdvertiseSettings()
400 while (True):
401 advertise_callback = android_device.droid.bleGenBleAdvertiseCallback()
402 advertise_callback_list.append(advertise_callback)
403
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700404 android_device.droid.bleStartBleAdvertising(advertise_callback,
405 advertise_data,
406 advertise_settings)
Bill Leung46b69352018-11-09 17:28:39 -0800407
408 regex = "(" + adv_succ.format(
409 advertise_callback) + "|" + adv_fail.format(
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700410 advertise_callback) + ")"
Bill Leung46b69352018-11-09 17:28:39 -0800411 # wait for either success or failure event
412 evt = android_device.ed.pop_events(regex, bt_default_timeout,
413 small_timeout)
414 if evt[0]["name"] == adv_succ.format(advertise_callback):
415 advertisement_count += 1
416 android_device.log.info(
417 "Advertisement {} started.".format(advertisement_count))
418 else:
419 error = evt[0]["data"]["Error"]
420 if error == "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS":
421 android_device.log.info(
422 "Advertisement failed to start. Reached max " +
423 "advertisements at {}".format(advertisement_count))
424 break
425 else:
426 raise BtTestUtilsError(
427 "Expected ADVERTISE_FAILED_TOO_MANY_ADVERTISERS," +
428 " but received bad error code {}".format(error))
429 try:
430 for adv in advertise_callback_list:
431 android_device.droid.bleStopBleAdvertising(adv)
432 except Exception:
433 android_device.log.error(
434 "Failed to stop advertisingment, resetting Bluetooth.")
435 reset_bluetooth([android_device])
436 return advertisement_count
437
438
439def disable_bluetooth(droid):
440 """Disable Bluetooth on input Droid object.
441
442 Args:
443 droid: The droid object to disable Bluetooth on.
444
445 Returns:
446 True if successful, false if unsuccessful.
447 """
448 if droid.bluetoothCheckState() is True:
449 droid.bluetoothToggleState(False)
450 if droid.bluetoothCheckState() is True:
451 log.error("Failed to toggle Bluetooth off.")
452 return False
453 return True
454
455
Phillip Walker827112a2016-09-08 16:27:19 -0700456def disconnect_pri_from_sec(pri_ad, sec_ad, profiles_list):
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700457 """
458 Disconnect primary from secondary on a specific set of profiles
459 Args:
Phillip Walker827112a2016-09-08 16:27:19 -0700460 pri_ad - Primary android_device initiating disconnection
461 sec_ad - Secondary android droid (sl4a interface to keep the
462 method signature the same connect_pri_to_sec above)
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700463 profiles_list - List of profiles we want to disconnect from
464
465 Returns:
466 True on Success
467 False on Failure
468 """
469 # Sanity check to see if all the profiles in the given set is supported
tturneyca42e2e2017-08-30 10:22:54 -0700470 supported_profiles = bt_profile_constants.values()
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700471 for profile in profiles_list:
472 if profile not in supported_profiles:
tturney34ee54d2016-11-16 15:29:02 -0800473 pri_ad.log.info("Profile {} is not in supported list {}".format(
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700474 profile, supported_profiles))
475 return False
476
tturney34ee54d2016-11-16 15:29:02 -0800477 pri_ad.log.info(pri_ad.droid.bluetoothGetBondedDevices())
Phillip Walker827112a2016-09-08 16:27:19 -0700478 # Disconnecting on a already disconnected profile is a nop,
479 # so not checking for the connection state
480 try:
481 pri_ad.droid.bluetoothDisconnectConnectedProfile(
482 sec_ad.droid.bluetoothGetLocalAddress(), profiles_list)
483 except Exception as err:
tturney34ee54d2016-11-16 15:29:02 -0800484 pri_ad.log.error(
485 "Exception while trying to disconnect profile(s) {}: {}".format(
486 profiles_list, err))
Phillip Walker827112a2016-09-08 16:27:19 -0700487 return False
488
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700489 profile_disconnected = set()
tturney34ee54d2016-11-16 15:29:02 -0800490 pri_ad.log.info("Disconnecting from profiles: {}".format(profiles_list))
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700491
492 while not profile_disconnected.issuperset(profiles_list):
493 try:
Phillip Walker827112a2016-09-08 16:27:19 -0700494 profile_event = pri_ad.ed.pop_event(
Timofey Protopopova47c8812018-03-28 11:37:31 -0700495 bluetooth_profile_connection_state_changed, bt_default_timeout)
tturney34ee54d2016-11-16 15:29:02 -0800496 pri_ad.log.info("Got event {}".format(profile_event))
Timofey Protopopova47c8812018-03-28 11:37:31 -0700497 except Exception as e:
Tom Turney9bce15d2018-06-21 13:00:42 -0700498 pri_ad.log.error(
499 "Did not disconnect from Profiles. Reason {}".format(e))
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700500 return False
501
502 profile = profile_event['data']['profile']
503 state = profile_event['data']['state']
504 device_addr = profile_event['data']['addr']
505
tturneyec1b8f52017-07-26 07:35:06 -0700506 if state == bt_profile_states['disconnected'] and \
Timofey Protopopova47c8812018-03-28 11:37:31 -0700507 device_addr == sec_ad.droid.bluetoothGetLocalAddress():
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700508 profile_disconnected.add(profile)
Stanley Tngc9a123d2017-12-05 17:37:49 -0800509 pri_ad.log.info(
510 "Profiles disconnected so far {}".format(profile_disconnected))
Ram Periathiruvadie3f9a702016-07-26 17:41:59 -0700511
512 return True
513
514
Bill Leung46b69352018-11-09 17:28:39 -0800515def enable_bluetooth(droid, ed):
516 if droid.bluetoothCheckState() is True:
517 return True
tturneyed249972016-09-13 11:07:49 -0700518
Bill Leung46b69352018-11-09 17:28:39 -0800519 droid.bluetoothToggleState(True)
520 expected_bluetooth_on_event_name = bluetooth_on
tturney5d7a1fc2017-06-01 15:12:12 -0700521 try:
Bill Leung46b69352018-11-09 17:28:39 -0800522 ed.pop_event(expected_bluetooth_on_event_name, bt_default_timeout)
523 except Exception:
524 log.info("Failed to toggle Bluetooth on (no broadcast received)")
525 if droid.bluetoothCheckState() is True:
526 log.info(".. actual state is ON")
Timofey Protopopov2651b742018-04-09 16:43:49 -0700527 return True
Bill Leung46b69352018-11-09 17:28:39 -0800528 log.info(".. actual state is OFF")
529 return False
530
531 return True
532
533
534def factory_reset_bluetooth(android_devices):
535 """Clears Bluetooth stack of input Android device list.
536
537 Args:
538 android_devices: The Android device list to reset Bluetooth
539
540 Returns:
541 True if successful, false if unsuccessful.
542 """
543 for a in android_devices:
544 droid, ed = a.droid, a.ed
545 a.log.info("Reset state of bluetooth on device.")
546 if not bluetooth_enabled_check(a):
547 return False
548 # TODO: remove device unbond b/79418045
549 # Temporary solution to ensure all devices are unbonded
550 bonded_devices = droid.bluetoothGetBondedDevices()
551 for b in bonded_devices:
552 a.log.info("Removing bond for device {}".format(b['address']))
553 droid.bluetoothUnbond(b['address'])
554
555 droid.bluetoothFactoryReset()
556 wait_for_bluetooth_manager_state(droid)
557 if not enable_bluetooth(droid, ed):
558 return False
559 return True
560
561
562def generate_ble_advertise_objects(droid):
563 """Generate generic LE advertise objects.
564
565 Args:
566 droid: The droid object to generate advertise LE objects from.
567
568 Returns:
569 advertise_callback: The generated advertise callback id.
570 advertise_data: The generated advertise data id.
571 advertise_settings: The generated advertise settings id.
572 """
573 advertise_callback = droid.bleGenBleAdvertiseCallback()
574 advertise_data = droid.bleBuildAdvertiseData()
575 advertise_settings = droid.bleBuildAdvertiseSettings()
576 return advertise_callback, advertise_data, advertise_settings
577
578
579def generate_ble_scan_objects(droid):
580 """Generate generic LE scan objects.
581
582 Args:
583 droid: The droid object to generate LE scan objects from.
584
585 Returns:
586 filter_list: The generated scan filter list id.
587 scan_settings: The generated scan settings id.
588 scan_callback: The generated scan callback id.
589 """
590 filter_list = droid.bleGenFilterList()
591 scan_settings = droid.bleBuildScanSetting()
592 scan_callback = droid.bleGenScanCallback()
593 return filter_list, scan_settings, scan_callback
594
595
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700596def generate_id_by_size(size,
597 chars=(string.ascii_lowercase +
598 string.ascii_uppercase + string.digits)):
Bill Leung46b69352018-11-09 17:28:39 -0800599 """Generate random ascii characters of input size and input char types
600
601 Args:
602 size: Input size of string.
603 chars: (Optional) Chars to use in generating a random string.
604
605 Returns:
606 String of random input chars at the input size.
607 """
608 return ''.join(random.choice(chars) for _ in range(size))
609
610
611def get_advanced_droid_list(android_devices):
612 """Add max_advertisement and batch_scan_supported attributes to input
613 Android devices
614
615 This will programatically determine maximum LE advertisements of each
616 input Android device.
617
618 Args:
619 android_devices: The Android devices to setup.
620
621 Returns:
622 List of Android devices with new attribtues.
623 """
624 droid_list = []
625 for a in android_devices:
626 d, e = a.droid, a.ed
627 model = d.getBuildModel()
628 max_advertisements = 1
629 batch_scan_supported = True
630 if model in advertisements_to_devices.keys():
631 max_advertisements = advertisements_to_devices[model]
632 else:
633 max_advertisements = determine_max_advertisements(a)
634 max_tries = 3
635 # Retry to calculate max advertisements
636 while max_advertisements == -1 and max_tries > 0:
637 a.log.info(
638 "Attempts left to determine max advertisements: {}".format(
639 max_tries))
640 max_advertisements = determine_max_advertisements(a)
641 max_tries -= 1
642 advertisements_to_devices[model] = max_advertisements
643 if model in batch_scan_not_supported_list:
644 batch_scan_supported = False
645 role = {
646 'droid': d,
647 'ed': e,
648 'max_advertisements': max_advertisements,
649 'batch_scan_supported': batch_scan_supported
650 }
651 droid_list.append(role)
652 return droid_list
653
654
655def get_bluetooth_crash_count(android_device):
656 out = android_device.adb.shell("dumpsys bluetooth_manager")
657 return int(re.search("crashed(.*\d)", out).group(1))
Tom Turneyf5b42142018-06-22 14:01:27 -0700658
659
Abhinav Jadond231ced2020-05-08 16:37:16 -0700660def read_otp(ad):
661 """Reads and parses the OTP output to return TX power backoff
662
663 Reads the OTP registers from the phone, parses them to return a
664 dict of TX power backoffs for different power levels
665
666 Args:
667 ad : android device object
668
669 Returns :
670 otp_dict : power backoff dict
671 """
672
673 ad.adb.shell('svc bluetooth disable')
674 time.sleep(2)
675 otp_output = ad.adb.shell('bluetooth_sar_test -r')
676 ad.adb.shell('svc bluetooth enable')
677 time.sleep(2)
678 otp_dict = {
Abhinav Jadon190014a2020-07-23 03:38:35 -0700679 "BR": {
Abhinav Jadond231ced2020-05-08 16:37:16 -0700680 "10": 0,
681 "9": 0,
682 "8": 0
683 },
684 "EDR": {
685 "10": 0,
686 "9": 0,
687 "8": 0
688 },
689 "BLE": {
690 "10": 0,
691 "9": 0,
692 "8": 0
693 }
694 }
695
Abhinav Jadon190014a2020-07-23 03:38:35 -0700696 otp_regex = '\s+\[\s+PL10:\s+(\d+)\s+PL9:\s+(\d+)*\s+PL8:\s+(\d+)\s+\]'
Abhinav Jadond231ced2020-05-08 16:37:16 -0700697
698 for key in otp_dict:
699 bank_list = re.findall("{}{}".format(key, otp_regex), otp_output)
700 for bank_tuple in bank_list:
701 if ('0', '0', '0') != bank_tuple:
702 [otp_dict[key]["10"], otp_dict[key]["9"],
703 otp_dict[key]["8"]] = bank_tuple
Abhinav Jadond231ced2020-05-08 16:37:16 -0700704 return otp_dict
705
706
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700707def get_bt_metric(ad_list, duration=1, tag="bt_metric", processed=True):
708 """ Function to get the bt metric from logcat.
709
710 Captures logcat for the specified duration and returns the bqr results.
711 Takes list of android objects as input. If a single android object is given,
712 converts it into a list.
713
714 Args:
715 ad_list: list of android_device objects
716 duration: time duration (seconds) for which the logcat is parsed.
717 tag: tag to be appended to the logcat dump.
718 processed: flag to process bqr output.
719
720 Returns:
721 metrics_dict: dict of metrics for each android device.
722 """
723
724 # Defining bqr quantitites and their regex to extract
Abhinav Jadon190014a2020-07-23 03:38:35 -0700725 regex_dict = {
726 "vsp_txpl": "VSP_TxPL:\s(\S+)",
727 "pwlv": "PwLv:\s(\S+)",
728 "rssi": "RSSI:\s[-](\d+)"
729 }
730 metrics_dict = {"rssi": {}, "pwlv": {}, "vsp_txpl": {}}
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700731
732 # Converting a single android device object to list
733 if not isinstance(ad_list, list):
734 ad_list = [ad_list]
735
736 #Time sync with the test machine
737 for ad in ad_list:
738 ad.droid.setTime(int(round(time.time() * 1000)))
739 time.sleep(0.5)
740
741 begin_time = utils.get_current_epoch_time()
742 time.sleep(duration)
743 end_time = utils.get_current_epoch_time()
744
745 for ad in ad_list:
746 bt_rssi_log = ad.cat_adb_log(tag, begin_time, end_time)
Abhinav Jadon190014a2020-07-23 03:38:35 -0700747 bqr_tag = "Handle:"
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700748
749 # Extracting supporting bqr quantities
750 for metric, regex in regex_dict.items():
751 bqr_metric = []
752 file_bt_log = open(bt_rssi_log, "r")
753 for line in file_bt_log:
754 if bqr_tag in line:
755 if re.findall(regex, line):
756 m = re.findall(regex, line)[0].strip(",")
757 bqr_metric.append(m)
758 metrics_dict[metric][ad.serial] = bqr_metric
759
Abhinav Jadon190014a2020-07-23 03:38:35 -0700760 # Ensures back-compatibility for vsp_txpl enabled DUTs
761 if metrics_dict["vsp_txpl"][ad.serial]:
762 metrics_dict["pwlv"][ad.serial] = metrics_dict["vsp_txpl"][
763 ad.serial]
764
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700765 # Formatting the raw data
766 metrics_dict["rssi"][ad.serial] = [
767 (-1) * int(x) for x in metrics_dict["rssi"][ad.serial]
768 ]
769 metrics_dict["pwlv"][ad.serial] = [
770 int(x, 16) for x in metrics_dict["pwlv"][ad.serial]
771 ]
772
773 # Processing formatted data if processing is required
774 if processed:
775 # Computes the average RSSI
776 metrics_dict["rssi"][ad.serial] = round(
777 sum(metrics_dict["rssi"][ad.serial]) /
778 len(metrics_dict["rssi"][ad.serial]), 2)
779 # Returns last noted value for power level
780 metrics_dict["pwlv"][ad.serial] = metrics_dict["pwlv"][
781 ad.serial][-1]
782
783 return metrics_dict
784
785
786def get_bt_rssi(ad, duration=1, processed=True):
787 """Function to get average bt rssi from logcat.
788
789 This function returns the average RSSI for the given duration. RSSI values are
790 extracted from BQR.
791
792 Args:
793 ad: (list of) android_device object.
794 duration: time duration(seconds) for which logcat is parsed.
795
796 Returns:
797 avg_rssi: average RSSI on each android device for the given duration.
798 """
799 function_tag = "get_bt_rssi"
800 bqr_results = get_bt_metric(ad,
801 duration,
802 tag=function_tag,
803 processed=processed)
804 return bqr_results["rssi"]
805
806
Abhinav Jadon5a1cf8f2019-11-13 10:16:39 -0800807def enable_bqr(
Abhinav Jadon190014a2020-07-23 03:38:35 -0700808 ad_list,
809 bqr_interval=10,
810 bqr_event_mask=15,
Abhinav Jadon5a1cf8f2019-11-13 10:16:39 -0800811):
Abhinav Jadon8692d492019-11-05 15:29:56 -0800812 """Sets up BQR reporting.
813
814 Sets up BQR to report BT metrics at the requested frequency and toggles
815 airplane mode for the bqr settings to take effect.
816
817 Args:
818 ad_list: an android_device or list of android devices.
819 """
820 # Converting a single android device object to list
821 if not isinstance(ad_list, list):
822 ad_list = [ad_list]
823
824 for ad in ad_list:
825 #Setting BQR parameters
826 ad.adb.shell("setprop persist.bluetooth.bqr.event_mask {}".format(
827 bqr_event_mask))
828 ad.adb.shell("setprop persist.bluetooth.bqr.min_interval_ms {}".format(
829 bqr_interval))
830
831 ## Toggle airplane mode
Qicd428aa2019-11-08 09:44:11 -0800832 ad.droid.connectivityToggleAirplaneMode(True)
833 ad.droid.connectivityToggleAirplaneMode(False)
Abhinav Jadon8692d492019-11-05 15:29:56 -0800834
835
Abhinav Jadon5a1cf8f2019-11-13 10:16:39 -0800836def disable_bqr(ad_list):
837 """Disables BQR reporting.
838
839 Args:
840 ad_list: an android_device or list of android devices.
841 """
842 # Converting a single android device object to list
843 if not isinstance(ad_list, list):
844 ad_list = [ad_list]
845
846 DISABLE_BQR_MASK = 0
847
848 for ad in ad_list:
849 #Disabling BQR
850 ad.adb.shell("setprop persist.bluetooth.bqr.event_mask {}".format(
851 DISABLE_BQR_MASK))
852
853 ## Toggle airplane mode
854 ad.droid.connectivityToggleAirplaneMode(True)
855 ad.droid.connectivityToggleAirplaneMode(False)
856
857
Tom Turney9bce15d2018-06-21 13:00:42 -0700858def get_device_selector_dictionary(android_device_list):
859 """Create a dictionary of Bluetooth features vs Android devices.
860
861 Args:
862 android_device_list: The list of Android devices.
863 Returns:
864 A dictionary of profiles/features to Android devices.
865 """
866 selector_dict = {}
867 for ad in android_device_list:
868 uuids = ad.droid.bluetoothGetLocalUuids()
869
870 for profile, uuid_const in sig_uuid_constants.items():
871 uuid_check = sig_uuid_constants['BASE_UUID'].format(
872 uuid_const).lower()
Aidan Holloway-bidwell9e44b9d2018-11-07 14:33:27 -0800873 if uuids and uuid_check in uuids:
Tom Turney9bce15d2018-06-21 13:00:42 -0700874 if profile in selector_dict:
875 selector_dict[profile].append(ad)
876 else:
877 selector_dict[profile] = [ad]
878
879 # Various services may not be active during BT startup.
880 # If the device can be identified through adb shell pm list features
881 # then try to add them to the appropriate profiles / features.
882
883 # Android TV.
884 if "feature:com.google.android.tv.installed" in ad.features:
885 ad.log.info("Android TV device found.")
886 supported_profiles = ['AudioSink']
887 _add_android_device_to_dictionary(ad, supported_profiles,
888 selector_dict)
889
890 # Android Auto
891 elif "feature:android.hardware.type.automotive" in ad.features:
892 ad.log.info("Android Auto device found.")
893 # Add: AudioSink , A/V_RemoteControl,
894 supported_profiles = [
895 'AudioSink', 'A/V_RemoteControl', 'Message Notification Server'
896 ]
897 _add_android_device_to_dictionary(ad, supported_profiles,
898 selector_dict)
899 # Android Wear
900 elif "feature:android.hardware.type.watch" in ad.features:
901 ad.log.info("Android Wear device found.")
902 supported_profiles = []
903 _add_android_device_to_dictionary(ad, supported_profiles,
904 selector_dict)
905 # Android Phone
906 elif "feature:android.hardware.telephony" in ad.features:
907 ad.log.info("Android Phone device found.")
908 # Add: AudioSink
909 supported_profiles = [
910 'AudioSource', 'A/V_RemoteControlTarget',
911 'Message Access Server'
912 ]
913 _add_android_device_to_dictionary(ad, supported_profiles,
914 selector_dict)
915 return selector_dict
916
917
Bill Leung46b69352018-11-09 17:28:39 -0800918def get_mac_address_of_generic_advertisement(scan_ad, adv_ad):
919 """Start generic advertisement and get it's mac address by LE scanning.
Tom Turney9bce15d2018-06-21 13:00:42 -0700920
921 Args:
Bill Leung46b69352018-11-09 17:28:39 -0800922 scan_ad: The Android device to use as the scanner.
923 adv_ad: The Android device to use as the advertiser.
924
925 Returns:
926 mac_address: The mac address of the advertisement.
927 advertise_callback: The advertise callback id of the active
928 advertisement.
Tom Turney9bce15d2018-06-21 13:00:42 -0700929 """
Bill Leung46b69352018-11-09 17:28:39 -0800930 adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
931 adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
932 ble_advertise_settings_modes['low_latency'])
933 adv_ad.droid.bleSetAdvertiseSettingsIsConnectable(True)
934 adv_ad.droid.bleSetAdvertiseSettingsTxPowerLevel(
935 ble_advertise_settings_tx_powers['high'])
936 advertise_callback, advertise_data, advertise_settings = (
937 generate_ble_advertise_objects(adv_ad.droid))
938 adv_ad.droid.bleStartBleAdvertising(advertise_callback, advertise_data,
939 advertise_settings)
940 try:
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -0700941 adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
942 bt_default_timeout)
Bill Leung46b69352018-11-09 17:28:39 -0800943 except Empty as err:
944 raise BtTestUtilsError(
945 "Advertiser did not start successfully {}".format(err))
946 filter_list = scan_ad.droid.bleGenFilterList()
947 scan_settings = scan_ad.droid.bleBuildScanSetting()
948 scan_callback = scan_ad.droid.bleGenScanCallback()
949 scan_ad.droid.bleSetScanFilterDeviceName(
950 adv_ad.droid.bluetoothGetLocalName())
951 scan_ad.droid.bleBuildScanFilter(filter_list)
952 scan_ad.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
953 try:
954 event = scan_ad.ed.pop_event(
955 "BleScan{}onScanResults".format(scan_callback), bt_default_timeout)
956 except Empty as err:
957 raise BtTestUtilsError(
958 "Scanner did not find advertisement {}".format(err))
959 mac_address = event['data']['Result']['deviceInfo']['address']
960 return mac_address, advertise_callback, scan_callback
961
962
963def hid_device_send_key_data_report(host_id, device_ad, key, interval=1):
964 """Send a HID report simulating a 1-second keyboard press from host_ad to
965 device_ad
966
967 Args:
968 host_id: the Bluetooth MAC address or name of the HID host
969 device_ad: HID device
970 key: the key we want to send
971 interval: the interval between key press and key release
972 """
973 device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard,
974 hid_keyboard_report(key))
975 time.sleep(interval)
976 device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard,
977 hid_keyboard_report("00"))
978
979
980def hid_keyboard_report(key, modifier="00"):
981 """Get the HID keyboard report for the given key
982
983 Args:
984 key: the key we want
985 modifier: HID keyboard modifier bytes
986 Returns:
987 The byte array for the HID report.
988 """
989 return str(
990 bytearray.fromhex(" ".join(
991 [modifier, "00", key, "00", "00", "00", "00", "00"])), "utf-8")
992
993
994def is_a2dp_connected(sink, source):
995 """
996 Convenience Function to see if the 2 devices are connected on
997 A2dp.
998 Args:
999 sink: Audio Sink
1000 source: Audio Source
1001 Returns:
1002 True if Connected
1003 False if Not connected
1004 """
1005
1006 devices = sink.droid.bluetoothA2dpSinkGetConnectedDevices()
1007 for device in devices:
1008 sink.log.info("A2dp Connected device {}".format(device["name"]))
1009 if (device["address"] == source.droid.bluetoothGetLocalAddress()):
1010 return True
1011 return False
1012
1013
1014def is_a2dp_snk_device_connected(ad, addr):
1015 """Determines if an AndroidDevice has A2DP snk connectivity to input address
1016
1017 Args:
1018 ad: the Android device
1019 addr: the address that's expected
1020 Returns:
1021 True if connection was successful, false if unsuccessful.
1022 """
1023 devices = ad.droid.bluetoothA2dpSinkGetConnectedDevices()
1024 ad.log.info("Connected A2DP Sink devices: {}".format(devices))
1025 if addr in {d['address'] for d in devices}:
1026 return True
1027 return False
1028
1029
1030def is_a2dp_src_device_connected(ad, addr):
1031 """Determines if an AndroidDevice has A2DP connectivity to input address
1032
1033 Args:
1034 ad: the Android device
1035 addr: the address that's expected
1036 Returns:
1037 True if connection was successful, false if unsuccessful.
1038 """
1039 devices = ad.droid.bluetoothA2dpGetConnectedDevices()
1040 ad.log.info("Connected A2DP Source devices: {}".format(devices))
1041 if addr in {d['address'] for d in devices}:
1042 return True
1043 return False
1044
1045
1046def is_hfp_client_device_connected(ad, addr):
1047 """Determines if an AndroidDevice has HFP connectivity to input address
1048
1049 Args:
1050 ad: the Android device
1051 addr: the address that's expected
1052 Returns:
1053 True if connection was successful, false if unsuccessful.
1054 """
1055 devices = ad.droid.bluetoothHfpClientGetConnectedDevices()
1056 ad.log.info("Connected HFP Client devices: {}".format(devices))
1057 if addr in {d['address'] for d in devices}:
1058 return True
1059 return False
1060
1061
1062def is_map_mce_device_connected(ad, addr):
1063 """Determines if an AndroidDevice has MAP MCE connectivity to input address
1064
1065 Args:
1066 ad: the Android device
1067 addr: the address that's expected
1068 Returns:
1069 True if connection was successful, false if unsuccessful.
1070 """
1071 devices = ad.droid.bluetoothMapClientGetConnectedDevices()
1072 ad.log.info("Connected MAP MCE devices: {}".format(devices))
1073 if addr in {d['address'] for d in devices}:
1074 return True
1075 return False
1076
1077
1078def is_map_mse_device_connected(ad, addr):
1079 """Determines if an AndroidDevice has MAP MSE connectivity to input address
1080
1081 Args:
1082 ad: the Android device
1083 addr: the address that's expected
1084 Returns:
1085 True if connection was successful, false if unsuccessful.
1086 """
1087 devices = ad.droid.bluetoothMapGetConnectedDevices()
1088 ad.log.info("Connected MAP MSE devices: {}".format(devices))
1089 if addr in {d['address'] for d in devices}:
1090 return True
1091 return False
1092
1093
1094def kill_bluetooth_process(ad):
1095 """Kill Bluetooth process on Android device.
1096
1097 Args:
1098 ad: Android device to kill BT process on.
1099 """
1100 ad.log.info("Killing Bluetooth process.")
1101 pid = ad.adb.shell(
1102 "ps | grep com.android.bluetooth | awk '{print $2}'").decode('ascii')
1103 call(["adb -s " + ad.serial + " shell kill " + pid], shell=True)
1104
1105
1106def log_energy_info(android_devices, state):
1107 """Logs energy info of input Android devices.
1108
1109 Args:
1110 android_devices: input Android device list to log energy info from.
1111 state: the input state to log. Usually 'Start' or 'Stop' for logging.
1112
1113 Returns:
1114 A logging string of the Bluetooth energy info reported.
1115 """
1116 return_string = "{} Energy info collection:\n".format(state)
1117 # Bug: b/31966929
1118 return return_string
1119
1120
1121def orchestrate_and_verify_pan_connection(pan_dut, panu_dut):
1122 """Setups up a PAN conenction between two android devices.
1123
1124 Args:
1125 pan_dut: the Android device providing tethering services
1126 panu_dut: the Android device using the internet connection from the
1127 pan_dut
1128 Returns:
1129 True if PAN connection and verification is successful,
1130 false if unsuccessful.
1131 """
1132 if not toggle_airplane_mode_by_adb(log, panu_dut, True):
1133 panu_dut.log.error("Failed to toggle airplane mode on")
1134 return False
1135 if not toggle_airplane_mode_by_adb(log, panu_dut, False):
1136 pan_dut.log.error("Failed to toggle airplane mode off")
1137 return False
1138 pan_dut.droid.bluetoothStartConnectionStateChangeMonitor("")
1139 panu_dut.droid.bluetoothStartConnectionStateChangeMonitor("")
1140 if not bluetooth_enabled_check(panu_dut):
1141 return False
1142 if not bluetooth_enabled_check(pan_dut):
1143 return False
1144 pan_dut.droid.bluetoothPanSetBluetoothTethering(True)
1145 if not (pair_pri_to_sec(pan_dut, panu_dut)):
1146 return False
1147 if not pan_dut.droid.bluetoothPanIsTetheringOn():
1148 pan_dut.log.error("Failed to enable Bluetooth tethering.")
1149 return False
1150 # Magic sleep needed to give the stack time in between bonding and
1151 # connecting the PAN profile.
1152 time.sleep(pan_connect_timeout)
1153 panu_dut.droid.bluetoothConnectBonded(
1154 pan_dut.droid.bluetoothGetLocalAddress())
1155 if not verify_http_connection(log, panu_dut):
1156 panu_dut.log.error("Can't verify http connection on PANU device.")
1157 if not verify_http_connection(log, pan_dut):
1158 pan_dut.log.info(
1159 "Can't verify http connection on PAN service device")
1160 return False
1161 return True
1162
1163
1164def orchestrate_bluetooth_socket_connection(
Abhinav Jadond231ced2020-05-08 16:37:16 -07001165 client_ad,
1166 server_ad,
1167 accept_timeout_ms=default_bluetooth_socket_timeout_ms,
1168 uuid=None):
Bill Leung46b69352018-11-09 17:28:39 -08001169 """Sets up the Bluetooth Socket connection between two Android devices.
1170
1171 Args:
1172 client_ad: the Android device performing the connection.
1173 server_ad: the Android device accepting the connection.
1174 Returns:
1175 True if connection was successful, false if unsuccessful.
1176 """
1177 server_ad.droid.bluetoothStartPairingHelper()
1178 client_ad.droid.bluetoothStartPairingHelper()
1179
1180 server_ad.droid.bluetoothSocketConnBeginAcceptThreadUuid(
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001181 (bluetooth_socket_conn_test_uuid if uuid is None else uuid),
1182 accept_timeout_ms)
Bill Leung46b69352018-11-09 17:28:39 -08001183 client_ad.droid.bluetoothSocketConnBeginConnectThreadUuid(
1184 server_ad.droid.bluetoothGetLocalAddress(),
1185 (bluetooth_socket_conn_test_uuid if uuid is None else uuid))
1186
1187 end_time = time.time() + bt_default_timeout
1188 result = False
1189 test_result = True
1190 while time.time() < end_time:
1191 if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
1192 test_result = True
1193 client_ad.log.info("Bluetooth socket Client Connection Active")
1194 break
Tom Turney9bce15d2018-06-21 13:00:42 -07001195 else:
Bill Leung46b69352018-11-09 17:28:39 -08001196 test_result = False
1197 time.sleep(1)
1198 if not test_result:
1199 client_ad.log.error(
1200 "Failed to establish a Bluetooth socket connection")
1201 return False
1202 return True
tprotopopov2e18bbb2018-10-02 10:29:01 -07001203
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -08001204
Bill Leung46b69352018-11-09 17:28:39 -08001205def orchestrate_rfcomm_connection(client_ad,
1206 server_ad,
1207 accept_timeout_ms=default_rfcomm_timeout_ms,
1208 uuid=None):
1209 """Sets up the RFCOMM connection between two Android devices.
1210
1211 Args:
1212 client_ad: the Android device performing the connection.
1213 server_ad: the Android device accepting the connection.
1214 Returns:
1215 True if connection was successful, false if unsuccessful.
1216 """
1217 result = orchestrate_bluetooth_socket_connection(
1218 client_ad, server_ad, accept_timeout_ms,
1219 (bt_rfcomm_uuids['default_uuid'] if uuid is None else uuid))
1220
1221 return result
1222
1223
1224def pair_pri_to_sec(pri_ad, sec_ad, attempts=2, auto_confirm=True):
1225 """Pairs pri droid to secondary droid.
1226
1227 Args:
1228 pri_ad: Android device initiating connection
1229 sec_ad: Android device accepting connection
1230 attempts: Number of attempts to try until failure.
1231 auto_confirm: Auto confirm passkey match for both devices
1232
1233 Returns:
1234 Pass if True
1235 Fail if False
1236 """
1237 pri_ad.droid.bluetoothStartConnectionStateChangeMonitor(
1238 sec_ad.droid.bluetoothGetLocalAddress())
1239 curr_attempts = 0
1240 while curr_attempts < attempts:
1241 if _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm):
1242 return True
1243 # Wait 2 seconds before unbound
1244 time.sleep(2)
1245 if not clear_bonded_devices(pri_ad):
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001246 log.error(
1247 "Failed to clear bond for primary device at attempt {}".format(
1248 str(curr_attempts)))
Bill Leung46b69352018-11-09 17:28:39 -08001249 return False
1250 if not clear_bonded_devices(sec_ad):
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001251 log.error(
1252 "Failed to clear bond for secondary device at attempt {}".
1253 format(str(curr_attempts)))
Bill Leung46b69352018-11-09 17:28:39 -08001254 return False
1255 # Wait 2 seconds after unbound
1256 time.sleep(2)
1257 curr_attempts += 1
1258 log.error("pair_pri_to_sec failed to connect after {} attempts".format(
1259 str(attempts)))
1260 return False
1261
1262
1263def _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm):
1264 # Enable discovery on sec_ad so that pri_ad can find it.
1265 # The timeout here is based on how much time it would take for two devices
1266 # to pair with each other once pri_ad starts seeing devices.
1267 pri_droid = pri_ad.droid
1268 sec_droid = sec_ad.droid
1269 pri_ad.ed.clear_all_events()
1270 sec_ad.ed.clear_all_events()
1271 log.info("Bonding device {} to {}".format(
1272 pri_droid.bluetoothGetLocalAddress(),
1273 sec_droid.bluetoothGetLocalAddress()))
1274 sec_droid.bluetoothMakeDiscoverable(bt_default_timeout)
1275 target_address = sec_droid.bluetoothGetLocalAddress()
1276 log.debug("Starting paring helper on each device")
1277 pri_droid.bluetoothStartPairingHelper(auto_confirm)
1278 sec_droid.bluetoothStartPairingHelper(auto_confirm)
1279 pri_ad.log.info("Primary device starting discovery and executing bond")
1280 result = pri_droid.bluetoothDiscoverAndBond(target_address)
1281 if not auto_confirm:
1282 if not _wait_for_passkey_match(pri_ad, sec_ad):
1283 return False
1284 # Loop until we have bonded successfully or timeout.
1285 end_time = time.time() + bt_default_timeout
1286 pri_ad.log.info("Verifying devices are bonded")
1287 while time.time() < end_time:
1288 bonded_devices = pri_droid.bluetoothGetBondedDevices()
1289 bonded = False
1290 for d in bonded_devices:
1291 if d['address'] == target_address:
1292 pri_ad.log.info("Successfully bonded to device")
1293 return True
1294 time.sleep(0.1)
1295 # Timed out trying to bond.
1296 pri_ad.log.info("Failed to bond devices.")
1297 return False
1298
1299
1300def reset_bluetooth(android_devices):
1301 """Resets Bluetooth state of input Android device list.
1302
1303 Args:
1304 android_devices: The Android device list to reset Bluetooth state on.
1305
1306 Returns:
1307 True if successful, false if unsuccessful.
1308 """
1309 for a in android_devices:
1310 droid, ed = a.droid, a.ed
1311 a.log.info("Reset state of bluetooth on device.")
1312 if droid.bluetoothCheckState() is True:
1313 droid.bluetoothToggleState(False)
1314 expected_bluetooth_off_event_name = bluetooth_off
1315 try:
1316 ed.pop_event(expected_bluetooth_off_event_name,
1317 bt_default_timeout)
1318 except Exception:
1319 a.log.error("Failed to toggle Bluetooth off.")
1320 return False
1321 # temp sleep for b/17723234
1322 time.sleep(3)
1323 if not bluetooth_enabled_check(a):
1324 return False
1325 return True
1326
1327
1328def scan_and_verify_n_advertisements(scn_ad, max_advertisements):
1329 """Verify that input number of advertisements can be found from the scanning
1330 Android device.
1331
1332 Args:
1333 scn_ad: The Android device to start LE scanning on.
1334 max_advertisements: The number of advertisements the scanner expects to
1335 find.
1336
1337 Returns:
1338 True if successful, false if unsuccessful.
1339 """
1340 test_result = False
1341 address_list = []
1342 filter_list = scn_ad.droid.bleGenFilterList()
1343 scn_ad.droid.bleBuildScanFilter(filter_list)
1344 scan_settings = scn_ad.droid.bleBuildScanSetting()
1345 scan_callback = scn_ad.droid.bleGenScanCallback()
1346 scn_ad.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
1347 start_time = time.time()
1348 while (start_time + bt_default_timeout) > time.time():
1349 event = None
1350 try:
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001351 event = scn_ad.ed.pop_event(scan_result.format(scan_callback),
1352 bt_default_timeout)
Bill Leung46b69352018-11-09 17:28:39 -08001353 except Empty as error:
1354 raise BtTestUtilsError(
1355 "Failed to find scan event: {}".format(error))
1356 address = event['data']['Result']['deviceInfo']['address']
1357 if address not in address_list:
1358 address_list.append(address)
1359 if len(address_list) == max_advertisements:
1360 test_result = True
1361 break
1362 scn_ad.droid.bleStopBleScan(scan_callback)
1363 return test_result
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -08001364
1365
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001366def set_bluetooth_codec(android_device,
1367 codec_type,
1368 sample_rate,
1369 bits_per_sample,
1370 channel_mode,
1371 codec_specific_1=0):
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -08001372 """Sets the A2DP codec configuration on the AndroidDevice.
1373
1374 Args:
1375 android_device (acts.controllers.android_device.AndroidDevice): the
1376 android device for which to switch the codec.
1377 codec_type (str): the desired codec type. Must be a key in
1378 bt_constants.codec_types.
1379 sample_rate (str): the desired sample rate. Must be a key in
1380 bt_constants.sample_rates.
1381 bits_per_sample (str): the desired bits per sample. Must be a key in
1382 bt_constants.bits_per_samples.
1383 channel_mode (str): the desired channel mode. Must be a key in
1384 bt_constants.channel_modes.
1385 codec_specific_1 (int): the desired bit rate (quality) for LDAC codec.
1386 Returns:
1387 bool: True if the codec config was successfully changed to the desired
1388 values. Else False.
1389 """
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001390 message = ("Set Android Device A2DP Bluetooth codec configuration:\n"
1391 "\tCodec: {codec_type}\n"
1392 "\tSample Rate: {sample_rate}\n"
1393 "\tBits per Sample: {bits_per_sample}\n"
1394 "\tChannel Mode: {channel_mode}".format(
1395 codec_type=codec_type,
1396 sample_rate=sample_rate,
1397 bits_per_sample=bits_per_sample,
1398 channel_mode=channel_mode))
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -08001399 android_device.log.info(message)
1400
1401 # Send SL4A command
1402 droid, ed = android_device.droid, android_device.ed
1403 if not droid.bluetoothA2dpSetCodecConfigPreference(
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001404 codec_types[codec_type], sample_rates[str(sample_rate)],
1405 bits_per_samples[str(bits_per_sample)],
1406 channel_modes[channel_mode], codec_specific_1):
1407 android_device.log.warning(
1408 "SL4A command returned False. Codec was not "
1409 "changed.")
Aidan Holloway-bidwell477807a2019-02-01 15:29:00 -08001410 else:
1411 try:
1412 ed.pop_event(bluetooth_a2dp_codec_config_changed,
1413 bt_default_timeout)
1414 except Exception:
1415 android_device.log.warning("SL4A event not registered. Codec "
1416 "may not have been changed.")
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -08001417
1418 # Validate codec value through ADB
Aidan Holloway-bidwell477807a2019-02-01 15:29:00 -08001419 # TODO (aidanhb): validate codec more robustly using SL4A
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -08001420 command = "dumpsys bluetooth_manager | grep -i 'current codec'"
1421 out = android_device.adb.shell(command)
1422 split_out = out.split(": ")
1423 if len(split_out) != 2:
1424 android_device.log.warning("Could not verify codec config change "
1425 "through ADB.")
Aidan Holloway-bidwell9e44b9d2018-11-07 14:33:27 -08001426 elif split_out[1].strip().upper() != codec_type:
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001427 android_device.log.error("Codec config was not changed.\n"
1428 "\tExpected codec: {exp}\n"
1429 "\tActual codec: {act}".format(
1430 exp=codec_type, act=split_out[1].strip()))
Aidan Holloway-bidwellf8f51512018-11-06 10:42:14 -08001431 return False
1432 android_device.log.info("Bluetooth codec successfully changed.")
1433 return True
Bill Leung46b69352018-11-09 17:28:39 -08001434
1435
1436def set_bt_scan_mode(ad, scan_mode_value):
1437 """Set Android device's Bluetooth scan mode.
1438
1439 Args:
1440 ad: The Android device to set the scan mode on.
1441 scan_mode_value: The value to set the scan mode to.
1442
1443 Returns:
1444 True if successful, false if unsuccessful.
1445 """
1446 droid, ed = ad.droid, ad.ed
1447 if scan_mode_value == bt_scan_mode_types['state_off']:
1448 disable_bluetooth(droid)
1449 scan_mode = droid.bluetoothGetScanMode()
1450 reset_bluetooth([ad])
1451 if scan_mode != scan_mode_value:
1452 return False
1453 elif scan_mode_value == bt_scan_mode_types['none']:
1454 droid.bluetoothMakeUndiscoverable()
1455 scan_mode = droid.bluetoothGetScanMode()
1456 if scan_mode != scan_mode_value:
1457 return False
1458 elif scan_mode_value == bt_scan_mode_types['connectable']:
1459 droid.bluetoothMakeUndiscoverable()
1460 droid.bluetoothMakeConnectable()
1461 scan_mode = droid.bluetoothGetScanMode()
1462 if scan_mode != scan_mode_value:
1463 return False
1464 elif (scan_mode_value == bt_scan_mode_types['connectable_discoverable']):
1465 droid.bluetoothMakeDiscoverable()
1466 scan_mode = droid.bluetoothGetScanMode()
1467 if scan_mode != scan_mode_value:
1468 return False
1469 else:
1470 # invalid scan mode
1471 return False
1472 return True
1473
1474
1475def set_device_name(droid, name):
1476 """Set and check Bluetooth local name on input droid object.
1477
1478 Args:
1479 droid: Droid object to set local name on.
1480 name: the Bluetooth local name to set.
1481
1482 Returns:
1483 True if successful, false if unsuccessful.
1484 """
1485 droid.bluetoothSetLocalName(name)
1486 time.sleep(2)
1487 droid_name = droid.bluetoothGetLocalName()
1488 if droid_name != name:
1489 return False
1490 return True
1491
1492
1493def set_profile_priority(host_ad, client_ad, profiles, priority):
1494 """Sets the priority of said profile(s) on host_ad for client_ad"""
1495 for profile in profiles:
1496 host_ad.log.info("Profile {} on {} for {} set to priority {}".format(
1497 profile, host_ad.droid.bluetoothGetLocalName(),
1498 client_ad.droid.bluetoothGetLocalAddress(), priority.value))
1499 if bt_profile_constants['a2dp_sink'] == profile:
1500 host_ad.droid.bluetoothA2dpSinkSetPriority(
1501 client_ad.droid.bluetoothGetLocalAddress(), priority.value)
1502 elif bt_profile_constants['headset_client'] == profile:
1503 host_ad.droid.bluetoothHfpClientSetPriority(
1504 client_ad.droid.bluetoothGetLocalAddress(), priority.value)
1505 elif bt_profile_constants['pbap_client'] == profile:
1506 host_ad.droid.bluetoothPbapClientSetPriority(
1507 client_ad.droid.bluetoothGetLocalAddress(), priority.value)
1508 else:
1509 host_ad.log.error(
1510 "Profile {} not yet supported for priority settings".format(
1511 profile))
1512
1513
1514def setup_multiple_devices_for_bt_test(android_devices):
1515 """A common setup routine for Bluetooth on input Android device list.
1516
1517 Things this function sets up:
1518 1. Resets Bluetooth
1519 2. Set Bluetooth local name to random string of size 4
1520 3. Disable BLE background scanning.
1521 4. Enable Bluetooth snoop logging.
1522
1523 Args:
1524 android_devices: Android device list to setup Bluetooth on.
1525
1526 Returns:
1527 True if successful, false if unsuccessful.
1528 """
1529 log.info("Setting up Android Devices")
1530 # TODO: Temp fix for an selinux error.
1531 for ad in android_devices:
1532 ad.adb.shell("setenforce 0")
1533 threads = []
1534 try:
1535 for a in android_devices:
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001536 thread = threading.Thread(target=factory_reset_bluetooth,
1537 args=([[a]]))
Bill Leung46b69352018-11-09 17:28:39 -08001538 threads.append(thread)
1539 thread.start()
1540 for t in threads:
1541 t.join()
1542
1543 for a in android_devices:
1544 d = a.droid
1545 # TODO: Create specific RPC command to instantiate
1546 # BluetoothConnectionFacade. This is just a workaround.
1547 d.bluetoothStartConnectionStateChangeMonitor("")
1548 setup_result = d.bluetoothSetLocalName(generate_id_by_size(4))
1549 if not setup_result:
1550 a.log.error("Failed to set device name.")
1551 return setup_result
1552 d.bluetoothDisableBLE()
Ted Wange4c70722019-02-21 16:06:53 +08001553 utils.set_location_service(a, True)
Bill Leung46b69352018-11-09 17:28:39 -08001554 bonded_devices = d.bluetoothGetBondedDevices()
1555 for b in bonded_devices:
1556 a.log.info("Removing bond for device {}".format(b['address']))
1557 d.bluetoothUnbond(b['address'])
1558 for a in android_devices:
Aidan Holloway-bidwelle5882a52019-08-14 12:27:29 -07001559 a.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
1560 getprop_result = a.adb.shell(
1561 "getprop persist.bluetooth.btsnooplogmode") == "full"
Bill Leung46b69352018-11-09 17:28:39 -08001562 if not getprop_result:
1563 a.log.warning("Failed to enable Bluetooth Hci Snoop Logging.")
1564 except Exception as err:
1565 log.error("Something went wrong in multi device setup: {}".format(err))
1566 return False
1567 return setup_result
1568
1569
1570def setup_n_advertisements(adv_ad, num_advertisements):
1571 """Setup input number of advertisements on input Android device.
1572
1573 Args:
1574 adv_ad: The Android device to start LE advertisements on.
1575 num_advertisements: The number of advertisements to start.
1576
1577 Returns:
1578 advertise_callback_list: List of advertisement callback ids.
1579 """
1580 adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
1581 ble_advertise_settings_modes['low_latency'])
1582 advertise_data = adv_ad.droid.bleBuildAdvertiseData()
1583 advertise_settings = adv_ad.droid.bleBuildAdvertiseSettings()
1584 advertise_callback_list = []
1585 for i in range(num_advertisements):
1586 advertise_callback = adv_ad.droid.bleGenBleAdvertiseCallback()
1587 advertise_callback_list.append(advertise_callback)
1588 adv_ad.droid.bleStartBleAdvertising(advertise_callback, advertise_data,
1589 advertise_settings)
1590 try:
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001591 adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
1592 bt_default_timeout)
Bill Leung46b69352018-11-09 17:28:39 -08001593 adv_ad.log.info("Advertisement {} started.".format(i + 1))
1594 except Empty as error:
1595 adv_ad.log.error("Advertisement {} failed to start.".format(i + 1))
1596 raise BtTestUtilsError(
1597 "Test failed with Empty error: {}".format(error))
1598 return advertise_callback_list
1599
1600
1601def take_btsnoop_log(ad, testcase, testname):
1602 """Grabs the btsnoop_hci log on a device and stores it in the log directory
1603 of the test class.
1604
1605 If you want grab the btsnoop_hci log, call this function with android_device
1606 objects in on_fail. Bug report takes a relative long time to take, so use
1607 this cautiously.
1608
1609 Args:
1610 ad: The android_device instance to take bugreport on.
1611 testcase: Name of the test calss that triggered this snoop log.
1612 testname: Name of the test case that triggered this bug report.
1613 """
1614 testname = "".join(x for x in testname if x.isalnum())
1615 serial = ad.serial
1616 device_model = ad.droid.getBuildModel()
1617 device_model = device_model.replace(" ", "")
1618 out_name = ','.join((testname, device_model, serial))
Xianyuan Jia0e39e552019-01-24 17:17:47 -08001619 snoop_path = os.path.join(ad.device_log_path, 'BluetoothSnoopLogs')
Mark De Ruyter72f8df92020-02-12 13:44:49 -08001620 os.makedirs(snoop_path, exist_ok=True)
Bill Leung46b69352018-11-09 17:28:39 -08001621 cmd = ''.join(("adb -s ", serial, " pull ", btsnoop_log_path_on_device,
1622 " ", snoop_path + '/' + out_name, ".btsnoop_hci.log"))
1623 exe_cmd(cmd)
1624 try:
1625 cmd = ''.join(
1626 ("adb -s ", serial, " pull ", btsnoop_last_log_path_on_device, " ",
1627 snoop_path + '/' + out_name, ".btsnoop_hci.log.last"))
1628 exe_cmd(cmd)
1629 except Exception as err:
1630 testcase.log.info(
1631 "File does not exist {}".format(btsnoop_last_log_path_on_device))
1632
1633
1634def take_btsnoop_logs(android_devices, testcase, testname):
1635 """Pull btsnoop logs from an input list of android devices.
1636
1637 Args:
1638 android_devices: the list of Android devices to pull btsnoop logs from.
1639 testcase: Name of the test calss that triggered this snoop log.
1640 testname: Name of the test case that triggered this bug report.
1641 """
1642 for a in android_devices:
1643 take_btsnoop_log(a, testcase, testname)
1644
1645
1646def teardown_n_advertisements(adv_ad, num_advertisements,
1647 advertise_callback_list):
1648 """Stop input number of advertisements on input Android device.
1649
1650 Args:
1651 adv_ad: The Android device to stop LE advertisements on.
1652 num_advertisements: The number of advertisements to stop.
1653 advertise_callback_list: The list of advertisement callbacks to stop.
1654
1655 Returns:
1656 True if successful, false if unsuccessful.
1657 """
1658 for n in range(num_advertisements):
1659 adv_ad.droid.bleStopBleAdvertising(advertise_callback_list[n])
1660 return True
1661
1662
1663def verify_server_and_client_connected(client_ad, server_ad, log=True):
1664 """Verify that input server and client Android devices are connected.
1665
1666 This code is under the assumption that there will only be
1667 a single connection.
1668
1669 Args:
1670 client_ad: the Android device to check number of active connections.
1671 server_ad: the Android device to check number of active connections.
1672
1673 Returns:
1674 True both server and client have at least 1 active connection,
1675 false if unsuccessful.
1676 """
1677 test_result = True
1678 if len(server_ad.droid.bluetoothSocketConnActiveConnections()) == 0:
1679 if log:
1680 server_ad.log.error("No socket connections found on server.")
1681 test_result = False
1682 if len(client_ad.droid.bluetoothSocketConnActiveConnections()) == 0:
1683 if log:
1684 client_ad.log.error("No socket connections found on client.")
1685 test_result = False
1686 return test_result
1687
1688
1689def wait_for_bluetooth_manager_state(droid,
1690 state=None,
1691 timeout=10,
1692 threshold=5):
1693 """ Waits for BlueTooth normalized state or normalized explicit state
1694 args:
1695 droid: droid device object
1696 state: expected BlueTooth state
1697 timeout: max timeout threshold
1698 threshold: list len of bt state
1699 Returns:
1700 True if successful, false if unsuccessful.
1701 """
1702 all_states = []
1703 get_state = lambda: droid.bluetoothGetLeState()
1704 start_time = time.time()
1705 while time.time() < start_time + timeout:
1706 all_states.append(get_state())
1707 if len(all_states) >= threshold:
1708 # for any normalized state
1709 if state is None:
1710 if len(set(all_states[-threshold:])) == 1:
1711 log.info("State normalized {}".format(
1712 set(all_states[-threshold:])))
1713 return True
1714 else:
1715 # explicit check against normalized state
1716 if set([state]).issubset(all_states[-threshold:]):
1717 return True
1718 time.sleep(0.5)
1719 log.error(
1720 "Bluetooth state fails to normalize" if state is None else
1721 "Failed to match bluetooth state, current state {} expected state {}".
1722 format(get_state(), state))
1723 return False
1724
1725
1726def _wait_for_passkey_match(pri_ad, sec_ad):
1727 pri_pin, sec_pin = -1, 1
1728 pri_variant, sec_variant = -1, 1
1729 pri_pairing_req, sec_pairing_req = None, None
1730 try:
1731 pri_pairing_req = pri_ad.ed.pop_event(
1732 event_name="BluetoothActionPairingRequest",
1733 timeout=bt_default_timeout)
1734 pri_variant = pri_pairing_req["data"]["PairingVariant"]
1735 pri_pin = pri_pairing_req["data"]["Pin"]
1736 pri_ad.log.info("Primary device received Pin: {}, Variant: {}".format(
1737 pri_pin, pri_variant))
1738 sec_pairing_req = sec_ad.ed.pop_event(
1739 event_name="BluetoothActionPairingRequest",
1740 timeout=bt_default_timeout)
1741 sec_variant = sec_pairing_req["data"]["PairingVariant"]
1742 sec_pin = sec_pairing_req["data"]["Pin"]
Abhinav Jadon3efdbcc2019-09-11 15:01:43 -07001743 sec_ad.log.info(
1744 "Secondary device received Pin: {}, Variant: {}".format(
1745 sec_pin, sec_variant))
Bill Leung46b69352018-11-09 17:28:39 -08001746 except Empty as err:
1747 log.error("Wait for pin error: {}".format(err))
1748 log.error("Pairing request state, Primary: {}, Secondary: {}".format(
1749 pri_pairing_req, sec_pairing_req))
1750 return False
1751 if pri_variant == sec_variant == pairing_variant_passkey_confirmation:
1752 confirmation = pri_pin == sec_pin
1753 if confirmation:
1754 log.info("Pairing code matched, accepting connection")
1755 else:
1756 log.info("Pairing code mismatched, rejecting connection")
1757 pri_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
1758 str(confirmation))
1759 sec_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
1760 str(confirmation))
1761 if not confirmation:
1762 return False
1763 elif pri_variant != sec_variant:
1764 log.error("Pairing variant mismatched, abort connection")
1765 return False
1766 return True
1767
1768
1769def write_read_verify_data(client_ad, server_ad, msg, binary=False):
1770 """Verify that the client wrote data to the server Android device correctly.
1771
1772 Args:
1773 client_ad: the Android device to perform the write.
1774 server_ad: the Android device to read the data written.
1775 msg: the message to write.
1776 binary: if the msg arg is binary or not.
1777
1778 Returns:
1779 True if the data written matches the data read, false if not.
1780 """
1781 client_ad.log.info("Write message.")
1782 try:
1783 if binary:
1784 client_ad.droid.bluetoothSocketConnWriteBinary(msg)
1785 else:
1786 client_ad.droid.bluetoothSocketConnWrite(msg)
1787 except Exception as err:
1788 client_ad.log.error("Failed to write data: {}".format(err))
1789 return False
1790 server_ad.log.info("Read message.")
1791 try:
1792 if binary:
1793 read_msg = server_ad.droid.bluetoothSocketConnReadBinary().rstrip(
1794 "\r\n")
1795 else:
1796 read_msg = server_ad.droid.bluetoothSocketConnRead()
1797 except Exception as err:
1798 server_ad.log.error("Failed to read data: {}".format(err))
1799 return False
1800 log.info("Verify message.")
1801 if msg != read_msg:
1802 log.error("Mismatch! Read: {}, Expected: {}".format(read_msg, msg))
1803 return False
1804 return True
Qif9fa6ba2020-01-02 10:55:58 -08001805
1806
1807class MediaControlOverSl4a(object):
1808 """Media control using sl4a facade for general purpose.
1809
1810 """
1811 def __init__(self, android_device, music_file):
1812 """Initialize the media_control class.
1813
1814 Args:
1815 android_dut: android_device object
1816 music_file: location of the music file
1817 """
1818 self.android_device = android_device
1819 self.music_file = music_file
1820
1821 def play(self):
1822 """Play media.
1823
1824 """
1825 self.android_device.droid.mediaPlayOpen('file://%s' % self.music_file,
1826 'default', True)
1827 playing = self.android_device.droid.mediaIsPlaying()
1828 asserts.assert_true(playing,
1829 'Failed to play music %s' % self.music_file)
1830
1831 def pause(self):
1832 """Pause media.
1833
1834 """
1835 self.android_device.droid.mediaPlayPause('default')
1836 paused = not self.android_device.droid.mediaIsPlaying()
1837 asserts.assert_true(paused,
1838 'Failed to pause music %s' % self.music_file)
1839
1840 def resume(self):
1841 """Resume media.
1842
1843 """
1844 self.android_device.droid.mediaPlayStart('default')
1845 playing = self.android_device.droid.mediaIsPlaying()
1846 asserts.assert_true(playing,
1847 'Failed to play music %s' % self.music_file)
1848
1849 def stop(self):
1850 """Stop media.
1851
1852 """
1853 self.android_device.droid.mediaPlayStop('default')
1854 stopped = not self.android_device.droid.mediaIsPlaying()
1855 asserts.assert_true(stopped,
1856 'Failed to stop music %s' % self.music_file)