Mark De Ruyter | d9c540a | 2018-05-04 11:21:55 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
tturney | 1bdf77d | 2015-12-28 17:46:13 -0800 | [diff] [blame] | 2 | # |
| 3 | # Copyright (C) 2016 The Android Open Source Project |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 4 | # |
| 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 Li | 5bd83f3 | 2016-05-23 14:39:38 -0700 | [diff] [blame] | 17 | import logging |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 18 | import random |
| 19 | import pprint |
| 20 | import string |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 21 | from queue import Empty |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 22 | import queue |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 23 | import threading |
| 24 | import time |
tturney | 03a45ae | 2016-05-24 15:36:05 -0700 | [diff] [blame] | 25 | from acts import utils |
tprotopopov | 2e18bbb | 2018-10-02 10:29:01 -0700 | [diff] [blame] | 26 | import re |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 27 | from subprocess import call |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 28 | |
Aidan Holloway-bidwell | f8f5151 | 2018-11-06 10:42:14 -0800 | [diff] [blame^] | 29 | from acts.test_utils.bt.bt_constants import bits_per_samples |
| 30 | from acts.test_utils.bt.bt_constants import channel_modes |
| 31 | from acts.test_utils.bt.bt_constants import codec_types |
| 32 | from acts.test_utils.bt.bt_constants import codec_priorities |
| 33 | from acts.test_utils.bt.bt_constants import sample_rates |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 34 | from acts.test_utils.bt.bt_constants import adv_fail |
| 35 | from acts.test_utils.bt.bt_constants import adv_succ |
| 36 | from acts.test_utils.bt.bt_constants import advertising_set_started |
| 37 | from acts.test_utils.bt.bt_constants import advertising_set_stopped |
| 38 | from acts.test_utils.bt.bt_constants import advertising_set_on_own_address_read |
| 39 | from acts.test_utils.bt.bt_constants import advertising_set_stopped |
| 40 | from acts.test_utils.bt.bt_constants import advertising_set_enabled |
| 41 | from acts.test_utils.bt.bt_constants import advertising_set_data_set |
| 42 | from acts.test_utils.bt.bt_constants import advertising_set_scan_response_set |
| 43 | from acts.test_utils.bt.bt_constants import advertising_set_parameters_update |
| 44 | from acts.test_utils.bt.bt_constants import \ |
| 45 | advertising_set_periodic_parameters_updated |
| 46 | from acts.test_utils.bt.bt_constants import advertising_set_periodic_data_set |
| 47 | from acts.test_utils.bt.bt_constants import advertising_set_periodic_enable |
| 48 | from acts.test_utils.bt.bt_constants import batch_scan_not_supported_list |
| 49 | from acts.test_utils.bt.bt_constants import batch_scan_result |
| 50 | from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes |
| 51 | from acts.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers |
Aidan Holloway-bidwell | f8f5151 | 2018-11-06 10:42:14 -0800 | [diff] [blame^] | 52 | from acts.test_utils.bt.bt_constants import bluetooth_a2dp_codec_config_changed |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 53 | from acts.test_utils.bt.bt_constants import bluetooth_off |
| 54 | from acts.test_utils.bt.bt_constants import bluetooth_on |
| 55 | from acts.test_utils.bt.bt_constants import \ |
| 56 | bluetooth_profile_connection_state_changed |
| 57 | from acts.test_utils.bt.bt_constants import bt_default_timeout |
| 58 | from acts.test_utils.bt.bt_constants import bt_discovery_timeout |
| 59 | from acts.test_utils.bt.bt_constants import bt_profile_states |
tturney | b72742f | 2017-08-29 17:46:50 -0700 | [diff] [blame] | 60 | from acts.test_utils.bt.bt_constants import bt_profile_constants |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 61 | from acts.test_utils.bt.bt_constants import bt_rfcomm_uuids |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 62 | from acts.test_utils.bt.bt_constants import bluetooth_socket_conn_test_uuid |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 63 | from acts.test_utils.bt.bt_constants import bt_scan_mode_types |
| 64 | from acts.test_utils.bt.bt_constants import btsnoop_last_log_path_on_device |
| 65 | from acts.test_utils.bt.bt_constants import btsnoop_log_path_on_device |
| 66 | from acts.test_utils.bt.bt_constants import default_rfcomm_timeout_ms |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 67 | from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 68 | from acts.test_utils.bt.bt_constants import mtu_changed |
| 69 | from acts.test_utils.bt.bt_constants import pairing_variant_passkey_confirmation |
| 70 | from acts.test_utils.bt.bt_constants import pan_connect_timeout |
| 71 | from acts.test_utils.bt.bt_constants import small_timeout |
| 72 | from acts.test_utils.bt.bt_constants import scan_result |
| 73 | from acts.test_utils.bt.bt_constants import scan_failed |
Tom Turney | 7b0baa5 | 2018-06-26 11:31:31 -0700 | [diff] [blame] | 74 | from acts.test_utils.bt.bt_constants import sig_uuid_constants |
Hansong Zhang | d78b3b8 | 2017-10-03 10:36:12 -0700 | [diff] [blame] | 75 | from acts.test_utils.bt.bt_constants import hid_id_keyboard |
tturney | 83c28a0 | 2018-03-06 14:30:54 -0800 | [diff] [blame] | 76 | |
tturney | 1e174fd | 2017-06-01 14:42:20 -0700 | [diff] [blame] | 77 | from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 78 | from acts.test_utils.tel.tel_test_utils import verify_http_connection |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 79 | from acts.utils import exe_cmd |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 80 | from acts.utils import create_dir |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 81 | |
Ang Li | 5bd83f3 | 2016-05-23 14:39:38 -0700 | [diff] [blame] | 82 | log = logging |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 83 | |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 84 | advertisements_to_devices = {} |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 85 | |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 86 | |
tturney | 951533f | 2016-06-23 11:24:25 -0700 | [diff] [blame] | 87 | class BtTestUtilsError(Exception): |
| 88 | pass |
| 89 | |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 90 | |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 91 | def scan_and_verify_n_advertisements(scn_ad, max_advertisements): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 92 | """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 | """ |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 103 | 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() |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 111 | while (start_time + bt_default_timeout) > time.time(): |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 112 | event = None |
| 113 | try: |
| 114 | event = scn_ad.ed.pop_event( |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 115 | scan_result.format(scan_callback), bt_default_timeout) |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 116 | except Empty as error: |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 117 | raise BtTestUtilsError( |
| 118 | "Failed to find scan event: {}".format(error)) |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 119 | 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 | |
| 129 | def setup_n_advertisements(adv_ad, num_advertisements): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 130 | """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 | """ |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 139 | adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode( |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 140 | ble_advertise_settings_modes['low_latency']) |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 141 | 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( |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 151 | adv_succ.format(advertise_callback), bt_default_timeout) |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 152 | adv_ad.log.info("Advertisement {} started.".format(i + 1)) |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 153 | except Empty as error: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 154 | adv_ad.log.error("Advertisement {} failed to start.".format(i + 1)) |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 155 | raise BtTestUtilsError( |
| 156 | "Test failed with Empty error: {}".format(error)) |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 157 | return advertise_callback_list |
| 158 | |
| 159 | |
| 160 | def teardown_n_advertisements(adv_ad, num_advertisements, |
| 161 | advertise_callback_list): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 162 | """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 | """ |
tturney | c8e17bf | 2016-07-12 10:32:42 -0700 | [diff] [blame] | 172 | for n in range(num_advertisements): |
| 173 | adv_ad.droid.bleStopBleAdvertising(advertise_callback_list[n]) |
| 174 | return True |
| 175 | |
tturney | 951533f | 2016-06-23 11:24:25 -0700 | [diff] [blame] | 176 | |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 177 | def generate_ble_scan_objects(droid): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 178 | """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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 188 | filter_list = droid.bleGenFilterList() |
| 189 | scan_settings = droid.bleBuildScanSetting() |
| 190 | scan_callback = droid.bleGenScanCallback() |
| 191 | return filter_list, scan_settings, scan_callback |
| 192 | |
| 193 | |
| 194 | def generate_ble_advertise_objects(droid): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 195 | """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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 205 | advertise_callback = droid.bleGenBleAdvertiseCallback() |
| 206 | advertise_data = droid.bleBuildAdvertiseData() |
| 207 | advertise_settings = droid.bleBuildAdvertiseSettings() |
| 208 | return advertise_callback, advertise_data, advertise_settings |
| 209 | |
| 210 | |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 211 | def setup_multiple_devices_for_bt_test(android_devices): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 212 | """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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 226 | log.info("Setting up Android Devices") |
tturney | 03a45ae | 2016-05-24 15:36:05 -0700 | [diff] [blame] | 227 | # TODO: Temp fix for an selinux error. |
| 228 | for ad in android_devices: |
| 229 | ad.adb.shell("setenforce 0") |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 230 | threads = [] |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 231 | try: |
| 232 | for a in android_devices: |
Tom Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 233 | thread = threading.Thread( |
| 234 | target=factory_reset_bluetooth, args=([[a]])) |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 235 | threads.append(thread) |
| 236 | thread.start() |
| 237 | for t in threads: |
| 238 | t.join() |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 239 | |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 240 | for a in android_devices: |
| 241 | d = a.droid |
tturney | 59d50d7 | 2017-11-15 15:10:16 -0800 | [diff] [blame] | 242 | # TODO: Create specific RPC command to instantiate |
| 243 | # BluetoothConnectionFacade. This is just a workaround. |
Stanley Tng | 15e4b92 | 2017-12-06 16:32:41 -0800 | [diff] [blame] | 244 | d.bluetoothStartConnectionStateChangeMonitor("") |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 245 | setup_result = d.bluetoothSetLocalName(generate_id_by_size(4)) |
| 246 | if not setup_result: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 247 | a.log.error("Failed to set device name.") |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 248 | return setup_result |
| 249 | d.bluetoothDisableBLE() |
| 250 | bonded_devices = d.bluetoothGetBondedDevices() |
| 251 | for b in bonded_devices: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 252 | a.log.info("Removing bond for device {}".format(b['address'])) |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 253 | d.bluetoothUnbond(b['address']) |
| 254 | for a in android_devices: |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 255 | 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: |
tturney | 77001ba | 2017-01-04 11:39:54 -0800 | [diff] [blame] | 259 | a.log.warning("Failed to enable Bluetooth Hci Snoop Logging.") |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 260 | except Exception as err: |
| 261 | log.error("Something went wrong in multi device setup: {}".format(err)) |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 262 | return False |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 263 | return setup_result |
| 264 | |
| 265 | |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 266 | def bluetooth_enabled_check(ad): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 267 | """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 | """ |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 276 | if not ad.droid.bluetoothCheckState(): |
| 277 | ad.droid.bluetoothToggleState(True) |
| 278 | expected_bluetooth_on_event_name = bluetooth_on |
| 279 | try: |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 280 | ad.ed.pop_event(expected_bluetooth_on_event_name, |
| 281 | bt_default_timeout) |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 282 | except Empty: |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 283 | ad.log.info( |
| 284 | "Failed to toggle Bluetooth on(no broadcast received).") |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 285 | # Try one more time to poke at the actual state. |
| 286 | if ad.droid.bluetoothCheckState(): |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 287 | ad.log.info(".. actual state is ON") |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 288 | return True |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 289 | ad.log.error(".. actual state is OFF") |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 290 | return False |
| 291 | return True |
| 292 | |
Tom Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 293 | |
| 294 | def wait_for_bluetooth_manager_state(droid, |
| 295 | state=None, |
| 296 | timeout=10, |
| 297 | threshold=5): |
Timofey Protopopov | a66b340 | 2018-05-02 14:09:18 -0700 | [diff] [blame] | 298 | """ 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 Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 316 | log.info("State normalized {}".format( |
| 317 | set(all_states[-threshold:]))) |
Timofey Protopopov | a66b340 | 2018-05-02 14:09:18 -0700 | [diff] [blame] | 318 | 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 Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 326 | "Failed to match bluetooth state, current state {} expected state {}". |
| 327 | format(get_state(), state)) |
Timofey Protopopov | a66b340 | 2018-05-02 14:09:18 -0700 | [diff] [blame] | 328 | return False |
| 329 | |
Tom Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 330 | |
Timofey Protopopov | a66b340 | 2018-05-02 14:09:18 -0700 | [diff] [blame] | 331 | def 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 |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 357 | |
Tom Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 358 | |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 359 | def reset_bluetooth(android_devices): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 360 | """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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 367 | """ |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 368 | for a in android_devices: |
| 369 | droid, ed = a.droid, a.ed |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 370 | a.log.info("Reset state of bluetooth on device.") |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 371 | if droid.bluetoothCheckState() is True: |
| 372 | droid.bluetoothToggleState(False) |
| 373 | expected_bluetooth_off_event_name = bluetooth_off |
| 374 | try: |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 375 | ed.pop_event(expected_bluetooth_off_event_name, |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 376 | bt_default_timeout) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 377 | except Exception: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 378 | a.log.error("Failed to toggle Bluetooth off.") |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 379 | return False |
| 380 | # temp sleep for b/17723234 |
| 381 | time.sleep(3) |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 382 | if not bluetooth_enabled_check(a): |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 383 | return False |
| 384 | return True |
| 385 | |
| 386 | |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 387 | def determine_max_advertisements(android_device): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 388 | """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 | """ |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 397 | android_device.log.info( |
| 398 | "Determining number of maximum concurrent advertisements...") |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 399 | advertisement_count = 0 |
tturney | b9d9424 | 2016-07-06 17:28:59 -0700 | [diff] [blame] | 400 | bt_enabled = False |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 401 | expected_bluetooth_on_event_name = bluetooth_on |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 402 | 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, |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 406 | bt_default_timeout) |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 407 | except Exception: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 408 | android_device.log.info( |
| 409 | "Failed to toggle Bluetooth on(no broadcast received).") |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 410 | # Try one more time to poke at the actual state. |
| 411 | if android_device.droid.bluetoothCheckState() is True: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 412 | android_device.log.info(".. actual state is ON") |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 413 | else: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 414 | android_device.log.error( |
tturney | 77001ba | 2017-01-04 11:39:54 -0800 | [diff] [blame] | 415 | "Failed to turn Bluetooth on. Setting default advertisements to 1" |
| 416 | ) |
tturney | b9d9424 | 2016-07-06 17:28:59 -0700 | [diff] [blame] | 417 | advertisement_count = -1 |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 418 | 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 Pawlowski | 86acec4 | 2016-11-22 12:59:12 -0800 | [diff] [blame] | 428 | |
| 429 | regex = "(" + adv_succ.format( |
| 430 | advertise_callback) + "|" + adv_fail.format( |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 431 | advertise_callback) + ")" |
Jakub Pawlowski | 86acec4 | 2016-11-22 12:59:12 -0800 | [diff] [blame] | 432 | # wait for either success or failure event |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 433 | evt = android_device.ed.pop_events(regex, bt_default_timeout, |
| 434 | small_timeout) |
Jakub Pawlowski | 86acec4 | 2016-11-22 12:59:12 -0800 | [diff] [blame] | 435 | if evt[0]["name"] == adv_succ.format(advertise_callback): |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 436 | advertisement_count += 1 |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 437 | android_device.log.info( |
| 438 | "Advertisement {} started.".format(advertisement_count)) |
Jakub Pawlowski | 86acec4 | 2016-11-22 12:59:12 -0800 | [diff] [blame] | 439 | else: |
| 440 | error = evt[0]["data"]["Error"] |
| 441 | if error == "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS": |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 442 | android_device.log.info( |
| 443 | "Advertisement failed to start. Reached max " + |
| 444 | "advertisements at {}".format(advertisement_count)) |
Jakub Pawlowski | 86acec4 | 2016-11-22 12:59:12 -0800 | [diff] [blame] | 445 | break |
| 446 | else: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 447 | raise BtTestUtilsError( |
| 448 | "Expected ADVERTISE_FAILED_TOO_MANY_ADVERTISERS," + |
| 449 | " but received bad error code {}".format(error)) |
tturney | f02ac39 | 2016-09-21 10:37:35 -0700 | [diff] [blame] | 450 | try: |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 451 | for adv in advertise_callback_list: |
| 452 | android_device.droid.bleStopBleAdvertising(adv) |
tturney | f02ac39 | 2016-09-21 10:37:35 -0700 | [diff] [blame] | 453 | except Exception: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 454 | android_device.log.error( |
| 455 | "Failed to stop advertisingment, resetting Bluetooth.") |
tturney | f02ac39 | 2016-09-21 10:37:35 -0700 | [diff] [blame] | 456 | reset_bluetooth([android_device]) |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 457 | return advertisement_count |
| 458 | |
| 459 | |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 460 | def get_advanced_droid_list(android_devices): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 461 | """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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 473 | droid_list = [] |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 474 | for a in android_devices: |
| 475 | d, e = a.droid, a.ed |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 476 | model = d.getBuildModel() |
tturney | 0ab6f01 | 2016-05-09 13:59:27 -0700 | [diff] [blame] | 477 | max_advertisements = 1 |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 478 | batch_scan_supported = True |
| 479 | if model in advertisements_to_devices.keys(): |
| 480 | max_advertisements = advertisements_to_devices[model] |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 481 | else: |
| 482 | max_advertisements = determine_max_advertisements(a) |
tturney | b9d9424 | 2016-07-06 17:28:59 -0700 | [diff] [blame] | 483 | max_tries = 3 |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 484 | # Retry to calculate max advertisements |
tturney | b9d9424 | 2016-07-06 17:28:59 -0700 | [diff] [blame] | 485 | while max_advertisements == -1 and max_tries > 0: |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 486 | a.log.info( |
| 487 | "Attempts left to determine max advertisements: {}".format( |
| 488 | max_tries)) |
tturney | b9d9424 | 2016-07-06 17:28:59 -0700 | [diff] [blame] | 489 | max_advertisements = determine_max_advertisements(a) |
| 490 | max_tries -= 1 |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 491 | advertisements_to_devices[model] = max_advertisements |
| 492 | if model in batch_scan_not_supported_list: |
| 493 | batch_scan_supported = False |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 494 | 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 | |
tturney | c12b6ec | 2016-03-30 13:39:40 -0700 | [diff] [blame] | 504 | def generate_id_by_size( |
| 505 | size, |
| 506 | chars=( |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 507 | string.ascii_lowercase + string.ascii_uppercase + string.digits)): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 508 | """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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 517 | return ''.join(random.choice(chars) for _ in range(size)) |
| 518 | |
| 519 | |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 520 | def cleanup_scanners_and_advertisers(scn_android_device, scn_callback_list, |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 521 | adv_android_device, adv_callback_list): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 522 | """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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 530 | """ |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 531 | scan_droid, scan_ed = scn_android_device.droid, scn_android_device.ed |
| 532 | adv_droid = adv_android_device.droid |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 533 | try: |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 534 | for scan_callback in scn_callback_list: |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 535 | scan_droid.bleStopBleScan(scan_callback) |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 536 | except Exception as err: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 537 | scn_android_device.log.debug( |
| 538 | "Failed to stop LE scan... reseting Bluetooth. Error {}".format( |
| 539 | err)) |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 540 | reset_bluetooth([scn_android_device]) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 541 | try: |
| 542 | for adv_callback in adv_callback_list: |
| 543 | adv_droid.bleStopBleAdvertising(adv_callback) |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 544 | except Exception as err: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 545 | adv_android_device.log.debug( |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 546 | "Failed to stop LE advertisement... reseting Bluetooth. Error {}". |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 547 | format(err)) |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 548 | reset_bluetooth([adv_android_device]) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 549 | |
| 550 | |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 551 | def get_mac_address_of_generic_advertisement(scan_ad, adv_ad): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 552 | """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 | """ |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 563 | adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True) |
| 564 | adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode( |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 565 | ble_advertise_settings_modes['low_latency']) |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 566 | adv_ad.droid.bleSetAdvertiseSettingsIsConnectable(True) |
| 567 | adv_ad.droid.bleSetAdvertiseSettingsTxPowerLevel( |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 568 | ble_advertise_settings_tx_powers['high']) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 569 | advertise_callback, advertise_data, advertise_settings = ( |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 570 | generate_ble_advertise_objects(adv_ad.droid)) |
tturney | c12b6ec | 2016-03-30 13:39:40 -0700 | [diff] [blame] | 571 | adv_ad.droid.bleStartBleAdvertising(advertise_callback, advertise_data, |
| 572 | advertise_settings) |
tturney | 951533f | 2016-06-23 11:24:25 -0700 | [diff] [blame] | 573 | try: |
tturney | b9d9424 | 2016-07-06 17:28:59 -0700 | [diff] [blame] | 574 | adv_ad.ed.pop_event( |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 575 | adv_succ.format(advertise_callback), bt_default_timeout) |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 576 | except Empty as err: |
tturney | b9d9424 | 2016-07-06 17:28:59 -0700 | [diff] [blame] | 577 | raise BtTestUtilsError( |
| 578 | "Advertiser did not start successfully {}".format(err)) |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 579 | filter_list = scan_ad.droid.bleGenFilterList() |
| 580 | scan_settings = scan_ad.droid.bleBuildScanSetting() |
| 581 | scan_callback = scan_ad.droid.bleGenScanCallback() |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 582 | scan_ad.droid.bleSetScanFilterDeviceName( |
| 583 | adv_ad.droid.bluetoothGetLocalName()) |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 584 | scan_ad.droid.bleBuildScanFilter(filter_list) |
| 585 | scan_ad.droid.bleStartBleScan(filter_list, scan_settings, scan_callback) |
tturney | 951533f | 2016-06-23 11:24:25 -0700 | [diff] [blame] | 586 | try: |
| 587 | event = scan_ad.ed.pop_event( |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 588 | "BleScan{}onScanResults".format(scan_callback), bt_default_timeout) |
tturney | 50f851d | 2016-07-07 11:07:37 -0700 | [diff] [blame] | 589 | except Empty as err: |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 590 | raise BtTestUtilsError( |
| 591 | "Scanner did not find advertisement {}".format(err)) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 592 | mac_address = event['data']['Result']['deviceInfo']['address'] |
tturney | 83c28a0 | 2018-03-06 14:30:54 -0800 | [diff] [blame] | 593 | return mac_address, advertise_callback, scan_callback |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 594 | |
Tom Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 595 | |
Sanket Agarwal | c8fdc1a | 2016-07-06 16:39:34 -0700 | [diff] [blame] | 596 | def 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: |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 603 | ed.pop_event(expected_bluetooth_on_event_name, bt_default_timeout) |
Sanket Agarwal | c8fdc1a | 2016-07-06 16:39:34 -0700 | [diff] [blame] | 604 | 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 Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 614 | |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 615 | def disable_bluetooth(droid): |
| 616 | """Disable Bluetooth on input Droid object. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 617 | |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 618 | Args: |
| 619 | droid: The droid object to disable Bluetooth on. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 620 | |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 621 | Returns: |
| 622 | True if successful, false if unsuccessful. |
| 623 | """ |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 624 | if droid.bluetoothCheckState() is True: |
| 625 | droid.bluetoothToggleState(False) |
| 626 | if droid.bluetoothCheckState() is True: |
tturney | 69e3bb1 | 2017-10-26 14:15:05 -0700 | [diff] [blame] | 627 | log.error("Failed to toggle Bluetooth off.") |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 628 | return False |
| 629 | return True |
| 630 | |
| 631 | |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 632 | def set_bt_scan_mode(ad, scan_mode_value): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 633 | """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 | """ |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 642 | droid, ed = ad.droid, ad.ed |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 643 | if scan_mode_value == bt_scan_mode_types['state_off']: |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 644 | disable_bluetooth(droid) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 645 | scan_mode = droid.bluetoothGetScanMode() |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 646 | reset_bluetooth([ad]) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 647 | if scan_mode != scan_mode_value: |
| 648 | return False |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 649 | elif scan_mode_value == bt_scan_mode_types['none']: |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 650 | droid.bluetoothMakeUndiscoverable() |
| 651 | scan_mode = droid.bluetoothGetScanMode() |
| 652 | if scan_mode != scan_mode_value: |
| 653 | return False |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 654 | elif scan_mode_value == bt_scan_mode_types['connectable']: |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 655 | droid.bluetoothMakeUndiscoverable() |
| 656 | droid.bluetoothMakeConnectable() |
| 657 | scan_mode = droid.bluetoothGetScanMode() |
| 658 | if scan_mode != scan_mode_value: |
| 659 | return False |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 660 | elif (scan_mode_value == bt_scan_mode_types['connectable_discoverable']): |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 661 | 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 | |
| 671 | def set_device_name(droid, name): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 672 | """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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 681 | droid.bluetoothSetLocalName(name) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 682 | time.sleep(2) |
| 683 | droid_name = droid.bluetoothGetLocalName() |
| 684 | if droid_name != name: |
| 685 | return False |
| 686 | return True |
| 687 | |
| 688 | |
| 689 | def check_device_supported_profiles(droid): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 690 | """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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 698 | 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 Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 703 | profile_dict['a2dp_sink'] = droid.bluetoothA2dpSinkIsReady() |
| 704 | profile_dict['hfp_client'] = droid.bluetoothHfpClientIsReady() |
| 705 | profile_dict['pbap_client'] = droid.bluetoothPbapClientIsReady() |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 706 | return profile_dict |
| 707 | |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 708 | |
Ram Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 709 | def 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 Agarwal | bd9689b | 2016-08-24 11:14:53 -0700 | [diff] [blame] | 724 | def 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: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 727 | host_ad.log.info("Profile {} on {} for {} set to priority {}".format( |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 728 | profile, host_ad.droid.bluetoothGetLocalName(), |
tturney | 77001ba | 2017-01-04 11:39:54 -0800 | [diff] [blame] | 729 | client_ad.droid.bluetoothGetLocalAddress(), priority.value)) |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 730 | if bt_profile_constants['a2dp_sink'] == profile: |
Sanket Agarwal | bd9689b | 2016-08-24 11:14:53 -0700 | [diff] [blame] | 731 | host_ad.droid.bluetoothA2dpSinkSetPriority( |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 732 | client_ad.droid.bluetoothGetLocalAddress(), priority.value) |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 733 | elif bt_profile_constants['headset_client'] == profile: |
Sanket Agarwal | bd9689b | 2016-08-24 11:14:53 -0700 | [diff] [blame] | 734 | host_ad.droid.bluetoothHfpClientSetPriority( |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 735 | client_ad.droid.bluetoothGetLocalAddress(), priority.value) |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 736 | elif bt_profile_constants['pbap_client'] == profile: |
Sanket Agarwal | bd9689b | 2016-08-24 11:14:53 -0700 | [diff] [blame] | 737 | host_ad.droid.bluetoothPbapClientSetPriority( |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 738 | client_ad.droid.bluetoothGetLocalAddress(), priority.value) |
Sanket Agarwal | bd9689b | 2016-08-24 11:14:53 -0700 | [diff] [blame] | 739 | else: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 740 | host_ad.log.error( |
| 741 | "Profile {} not yet supported for priority settings".format( |
| 742 | profile)) |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 743 | |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 744 | |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 745 | def pair_pri_to_sec(pri_ad, sec_ad, attempts=2, auto_confirm=True): |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 746 | """Pairs pri droid to secondary droid. |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 747 | |
| 748 | Args: |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 749 | pri_ad: Android device initiating connection |
| 750 | sec_ad: Android device accepting connection |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 751 | attempts: Number of attempts to try until failure. |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 752 | auto_confirm: Auto confirm passkey match for both devices |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 753 | |
| 754 | Returns: |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 755 | Pass if True |
| 756 | Fail if False |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 757 | """ |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 758 | pri_ad.droid.bluetoothStartConnectionStateChangeMonitor( |
| 759 | sec_ad.droid.bluetoothGetLocalAddress()) |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 760 | curr_attempts = 0 |
| 761 | while curr_attempts < attempts: |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 762 | if _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm): |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 763 | return True |
Jack He | ff07eb6 | 2016-12-01 13:52:40 -0800 | [diff] [blame] | 764 | # 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 {}" |
tturney | 77001ba | 2017-01-04 11:39:54 -0800 | [diff] [blame] | 768 | .format(str(curr_attempts))) |
Jack He | ff07eb6 | 2016-12-01 13:52:40 -0800 | [diff] [blame] | 769 | return False |
| 770 | if not clear_bonded_devices(sec_ad): |
| 771 | log.error("Failed to clear bond for secondary device at attempt {}" |
tturney | 77001ba | 2017-01-04 11:39:54 -0800 | [diff] [blame] | 772 | .format(str(curr_attempts))) |
Jack He | ff07eb6 | 2016-12-01 13:52:40 -0800 | [diff] [blame] | 773 | return False |
| 774 | # Wait 2 seconds after unbound |
| 775 | time.sleep(2) |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 776 | curr_attempts += 1 |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 777 | log.error("pair_pri_to_sec failed to connect after {} attempts".format( |
| 778 | str(attempts))) |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 779 | return False |
| 780 | |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 781 | |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 782 | def _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( |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 788 | event_name="BluetoothActionPairingRequest", |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 789 | timeout=bt_default_timeout) |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 790 | pri_variant = pri_pairing_req["data"]["PairingVariant"] |
| 791 | pri_pin = pri_pairing_req["data"]["Pin"] |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 792 | pri_ad.log.info("Primary device received Pin: {}, Variant: {}".format( |
| 793 | pri_pin, pri_variant)) |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 794 | sec_pairing_req = sec_ad.ed.pop_event( |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 795 | event_name="BluetoothActionPairingRequest", |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 796 | timeout=bt_default_timeout) |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 797 | sec_variant = sec_pairing_req["data"]["PairingVariant"] |
| 798 | sec_pin = sec_pairing_req["data"]["Pin"] |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 799 | sec_ad.log.info("Secondary device received Pin: {}, Variant: {}" |
| 800 | .format(sec_pin, sec_variant)) |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 801 | except Empty as err: |
| 802 | log.error("Wait for pin error: {}".format(err)) |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 803 | log.error("Pairing request state, Primary: {}, Secondary: {}".format( |
| 804 | pri_pairing_req, sec_pairing_req)) |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 805 | return False |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 806 | if pri_variant == sec_variant == pairing_variant_passkey_confirmation: |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 807 | 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") |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 812 | pri_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm", |
| 813 | str(confirmation)) |
| 814 | sec_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm", |
| 815 | str(confirmation)) |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 816 | if not confirmation: |
| 817 | return False |
| 818 | elif pri_variant != sec_variant: |
Jack He | ff07eb6 | 2016-12-01 13:52:40 -0800 | [diff] [blame] | 819 | log.error("Pairing variant mismatched, abort connection") |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 820 | return False |
| 821 | return True |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 822 | |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 823 | |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 824 | def _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm): |
| 825 | # Enable discovery on sec_ad so that pri_ad can find it. |
Sanket Agarwal | c4637db | 2016-04-06 10:43:14 -0700 | [diff] [blame] | 826 | # The timeout here is based on how much time it would take for two devices |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 827 | # 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() |
tturney | 77001ba | 2017-01-04 11:39:54 -0800 | [diff] [blame] | 832 | log.info("Bonding device {} to {}".format( |
| 833 | pri_droid.bluetoothGetLocalAddress(), |
| 834 | sec_droid.bluetoothGetLocalAddress())) |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 835 | sec_droid.bluetoothMakeDiscoverable(bt_default_timeout) |
tturney | 182241b | 2016-05-17 16:53:44 -0700 | [diff] [blame] | 836 | target_address = sec_droid.bluetoothGetLocalAddress() |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 837 | log.debug("Starting paring helper on each device") |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 838 | pri_droid.bluetoothStartPairingHelper(auto_confirm) |
| 839 | sec_droid.bluetoothStartPairingHelper(auto_confirm) |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 840 | pri_ad.log.info("Primary device starting discovery and executing bond") |
tturney | 182241b | 2016-05-17 16:53:44 -0700 | [diff] [blame] | 841 | result = pri_droid.bluetoothDiscoverAndBond(target_address) |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 842 | if not auto_confirm: |
| 843 | if not _wait_for_passkey_match(pri_ad, sec_ad): |
| 844 | return False |
Sanket Agarwal | c4637db | 2016-04-06 10:43:14 -0700 | [diff] [blame] | 845 | # Loop until we have bonded successfully or timeout. |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 846 | end_time = time.time() + bt_default_timeout |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 847 | pri_ad.log.info("Verifying devices are bonded") |
Sanket Agarwal | c4637db | 2016-04-06 10:43:14 -0700 | [diff] [blame] | 848 | while time.time() < end_time: |
| 849 | bonded_devices = pri_droid.bluetoothGetBondedDevices() |
Sanket Agarwal | c4637db | 2016-04-06 10:43:14 -0700 | [diff] [blame] | 850 | bonded = False |
| 851 | for d in bonded_devices: |
tturney | 182241b | 2016-05-17 16:53:44 -0700 | [diff] [blame] | 852 | if d['address'] == target_address: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 853 | pri_ad.log.info("Successfully bonded to device") |
tturney | 0ab6f01 | 2016-05-09 13:59:27 -0700 | [diff] [blame] | 854 | return True |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 855 | time.sleep(0.1) |
Sanket Agarwal | c4637db | 2016-04-06 10:43:14 -0700 | [diff] [blame] | 856 | # Timed out trying to bond. |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 857 | pri_ad.log.info("Failed to bond devices.") |
Sanket Agarwal | c4637db | 2016-04-06 10:43:14 -0700 | [diff] [blame] | 858 | return False |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 859 | |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 860 | |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 861 | def connect_pri_to_sec(pri_ad, sec_ad, profiles_set, attempts=2): |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 862 | """Connects pri droid to secondary droid. |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 863 | |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 864 | Args: |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 865 | pri_ad: AndroidDroid initiating connection |
| 866 | sec_ad: AndroidDroid accepting connection |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 867 | 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 Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 874 | device_addr = sec_ad.droid.bluetoothGetLocalAddress() |
| 875 | # Allows extra time for the SDP records to be updated. |
| 876 | time.sleep(2) |
Sanket Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 877 | curr_attempts = 0 |
| 878 | while curr_attempts < attempts: |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 879 | 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 Agarwal | c96b9cc | 2016-08-18 12:01:04 -0700 | [diff] [blame] | 886 | return False |
| 887 | |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 888 | |
| 889 | def _connect_pri_to_sec(pri_ad, sec_ad, profiles_set): |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 890 | """Connects pri droid to secondary droid. |
| 891 | |
| 892 | Args: |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 893 | pri_ad: AndroidDroid initiating connection. |
| 894 | sec_ad: AndroidDroid accepting connection. |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 895 | profiles_set: Set of profiles to be connected. |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 896 | |
| 897 | Returns: |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 898 | True of connection is successful, false if unsuccessful. |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 899 | """ |
| 900 | # Check if we support all profiles. |
tturney | e1f6c72 | 2017-08-30 07:03:11 -0700 | [diff] [blame] | 901 | supported_profiles = bt_profile_constants.values() |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 902 | for profile in profiles_set: |
| 903 | if profile not in supported_profiles: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 904 | pri_ad.log.info("Profile {} is not supported list {}".format( |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 905 | profile, supported_profiles)) |
| 906 | return False |
| 907 | |
| 908 | # First check that devices are bonded. |
| 909 | paired = False |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 910 | for paired_device in pri_ad.droid.bluetoothGetBondedDevices(): |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 911 | if paired_device['address'] == \ |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 912 | sec_ad.droid.bluetoothGetLocalAddress(): |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 913 | paired = True |
| 914 | break |
| 915 | |
| 916 | if not paired: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 917 | pri_ad.log.error("Not paired to {}".format(sec_ad.serial)) |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 918 | return False |
| 919 | |
| 920 | # Now try to connect them, the following call will try to initiate all |
| 921 | # connections. |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 922 | pri_ad.droid.bluetoothConnectBonded( |
| 923 | sec_ad.droid.bluetoothGetLocalAddress()) |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 924 | |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 925 | end_time = time.time() + 10 |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 926 | profile_connected = set() |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 927 | sec_addr = sec_ad.droid.bluetoothGetLocalAddress() |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 928 | pri_ad.log.info("Profiles to be connected {}".format(profiles_set)) |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 929 | # First use APIs to check profile connection state |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 930 | 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): |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 934 | if is_hfp_client_device_connected(pri_ad, sec_addr): |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 935 | profile_connected.add(bt_profile_constants['headset_client']) |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 936 | if (bt_profile_constants['a2dp'] not in profile_connected |
| 937 | and bt_profile_constants['a2dp'] in profiles_set): |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 938 | if is_a2dp_src_device_connected(pri_ad, sec_addr): |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 939 | profile_connected.add(bt_profile_constants['a2dp']) |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 940 | if (bt_profile_constants['a2dp_sink'] not in profile_connected |
| 941 | and bt_profile_constants['a2dp_sink'] in profiles_set): |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 942 | if is_a2dp_snk_device_connected(pri_ad, sec_addr): |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 943 | profile_connected.add(bt_profile_constants['a2dp_sink']) |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 944 | if (bt_profile_constants['map_mce'] not in profile_connected |
| 945 | and bt_profile_constants['map_mce'] in profiles_set): |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 946 | if is_map_mce_device_connected(pri_ad, sec_addr): |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 947 | profile_connected.add(bt_profile_constants['map_mce']) |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 948 | if (bt_profile_constants['map'] not in profile_connected |
| 949 | and bt_profile_constants['map'] in profiles_set): |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 950 | if is_map_mse_device_connected(pri_ad, sec_addr): |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 951 | profile_connected.add(bt_profile_constants['map']) |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 952 | time.sleep(0.1) |
| 953 | # If APIs fail, try to find the connection broadcast receiver. |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 954 | while not profile_connected.issuperset(profiles_set): |
| 955 | try: |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 956 | profile_event = pri_ad.ed.pop_event( |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 957 | bluetooth_profile_connection_state_changed, |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 958 | bt_default_timeout + 10) |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 959 | pri_ad.log.info("Got event {}".format(profile_event)) |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 960 | except Exception: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 961 | pri_ad.log.error("Did not get {} profiles left {}".format( |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 962 | 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 | |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 969 | if state == bt_profile_states['connected'] and \ |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 970 | device_addr == sec_ad.droid.bluetoothGetLocalAddress(): |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 971 | profile_connected.add(profile) |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 972 | pri_ad.log.info( |
| 973 | "Profiles connected until now {}".format(profile_connected)) |
Sanket Agarwal | d09c7d8 | 2016-05-17 20:24:51 -0700 | [diff] [blame] | 974 | # Failure happens inside the while loop. If we came here then we already |
| 975 | # connected. |
| 976 | return True |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 977 | |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 978 | |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 979 | def disconnect_pri_from_sec(pri_ad, sec_ad, profiles_list): |
Ram Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 980 | """ |
| 981 | Disconnect primary from secondary on a specific set of profiles |
| 982 | Args: |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 983 | 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 Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 986 | 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 |
tturney | ca42e2e | 2017-08-30 10:22:54 -0700 | [diff] [blame] | 993 | supported_profiles = bt_profile_constants.values() |
Ram Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 994 | for profile in profiles_list: |
| 995 | if profile not in supported_profiles: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 996 | pri_ad.log.info("Profile {} is not in supported list {}".format( |
Ram Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 997 | profile, supported_profiles)) |
| 998 | return False |
| 999 | |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1000 | pri_ad.log.info(pri_ad.droid.bluetoothGetBondedDevices()) |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 1001 | # 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: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1007 | pri_ad.log.error( |
| 1008 | "Exception while trying to disconnect profile(s) {}: {}".format( |
| 1009 | profiles_list, err)) |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 1010 | return False |
| 1011 | |
Ram Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 1012 | profile_disconnected = set() |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1013 | pri_ad.log.info("Disconnecting from profiles: {}".format(profiles_list)) |
Ram Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 1014 | |
| 1015 | while not profile_disconnected.issuperset(profiles_list): |
| 1016 | try: |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 1017 | profile_event = pri_ad.ed.pop_event( |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 1018 | bluetooth_profile_connection_state_changed, bt_default_timeout) |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1019 | pri_ad.log.info("Got event {}".format(profile_event)) |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 1020 | except Exception as e: |
Tom Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 1021 | pri_ad.log.error( |
| 1022 | "Did not disconnect from Profiles. Reason {}".format(e)) |
Ram Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 1023 | return False |
| 1024 | |
| 1025 | profile = profile_event['data']['profile'] |
| 1026 | state = profile_event['data']['state'] |
| 1027 | device_addr = profile_event['data']['addr'] |
| 1028 | |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 1029 | if state == bt_profile_states['disconnected'] and \ |
Timofey Protopopov | a47c881 | 2018-03-28 11:37:31 -0700 | [diff] [blame] | 1030 | device_addr == sec_ad.droid.bluetoothGetLocalAddress(): |
Ram Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 1031 | profile_disconnected.add(profile) |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 1032 | pri_ad.log.info( |
| 1033 | "Profiles disconnected so far {}".format(profile_disconnected)) |
Ram Periathiruvadi | e3f9a70 | 2016-07-26 17:41:59 -0700 | [diff] [blame] | 1034 | |
| 1035 | return True |
| 1036 | |
| 1037 | |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 1038 | def take_btsnoop_logs(android_devices, testcase, testname): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1039 | """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 | """ |
tturney | 1ce8dc6 | 2016-02-18 09:55:53 -0800 | [diff] [blame] | 1046 | for a in android_devices: |
tturney | 03a45ae | 2016-05-24 15:36:05 -0700 | [diff] [blame] | 1047 | take_btsnoop_log(a, testcase, testname) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 1048 | |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 1049 | |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1050 | def take_btsnoop_log(ad, testcase, testname): |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 1051 | """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 | |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1058 | 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 Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 1062 | """ |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1063 | testname = "".join(x for x in testname if x.isalnum()) |
tturney | 4596252 | 2016-11-16 13:12:44 -0800 | [diff] [blame] | 1064 | serial = ad.serial |
tturney | f02ac39 | 2016-09-21 10:37:35 -0700 | [diff] [blame] | 1065 | 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) |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 1070 | cmd = ''.join(("adb -s ", serial, " pull ", btsnoop_log_path_on_device, |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 1071 | " ", snoop_path + '/' + out_name, ".btsnoop_hci.log")) |
tturney | f02ac39 | 2016-09-21 10:37:35 -0700 | [diff] [blame] | 1072 | exe_cmd(cmd) |
tturney | 5d7a1fc | 2017-06-01 15:12:12 -0700 | [diff] [blame] | 1073 | try: |
| 1074 | cmd = ''.join( |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 1075 | ("adb -s ", serial, " pull ", btsnoop_last_log_path_on_device, " ", |
tturney | 5d7a1fc | 2017-06-01 15:12:12 -0700 | [diff] [blame] | 1076 | snoop_path + '/' + out_name, ".btsnoop_hci.log.last")) |
| 1077 | exe_cmd(cmd) |
| 1078 | except Exception as err: |
Stanley Tng | c9a123d | 2017-12-05 17:37:49 -0800 | [diff] [blame] | 1079 | testcase.log.info( |
| 1080 | "File does not exist {}".format(btsnoop_last_log_path_on_device)) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 1081 | |
tturney | 4606078 | 2016-11-14 16:44:38 -0800 | [diff] [blame] | 1082 | |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1083 | def kill_bluetooth_process(ad): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1084 | """Kill Bluetooth process on Android device. |
| 1085 | |
| 1086 | Args: |
| 1087 | ad: Android device to kill BT process on. |
| 1088 | """ |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1089 | ad.log.info("Killing Bluetooth process.") |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1090 | pid = ad.adb.shell( |
| 1091 | "ps | grep com.android.bluetooth | awk '{print $2}'").decode('ascii') |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 1092 | call(["adb -s " + ad.serial + " shell kill " + pid], shell=True) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 1093 | |
| 1094 | |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1095 | def orchestrate_rfcomm_connection(client_ad, |
| 1096 | server_ad, |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 1097 | accept_timeout_ms=default_rfcomm_timeout_ms, |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 1098 | uuid=None): |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1099 | """Sets up the RFCOMM connection between two Android devices. |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1100 | |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1101 | Args: |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1102 | client_ad: the Android device performing the connection. |
| 1103 | server_ad: the Android device accepting the connection. |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1104 | Returns: |
| 1105 | True if connection was successful, false if unsuccessful. |
| 1106 | """ |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1107 | 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 | |
| 1114 | def 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 | """ |
tturney | 96970fd | 2016-08-31 17:36:09 -0700 | [diff] [blame] | 1127 | server_ad.droid.bluetoothStartPairingHelper() |
| 1128 | client_ad.droid.bluetoothStartPairingHelper() |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1129 | |
| 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 | |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 1137 | end_time = time.time() + bt_default_timeout |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1138 | result = False |
| 1139 | test_result = True |
| 1140 | while time.time() < end_time: |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1141 | if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0: |
tturney | 96970fd | 2016-08-31 17:36:09 -0700 | [diff] [blame] | 1142 | test_result = True |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1143 | client_ad.log.info("Bluetooth socket Client Connection Active") |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1144 | break |
tturney | 96970fd | 2016-08-31 17:36:09 -0700 | [diff] [blame] | 1145 | else: |
| 1146 | test_result = False |
| 1147 | time.sleep(1) |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1148 | if not test_result: |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1149 | client_ad.log.error( |
| 1150 | "Failed to establish a Bluetooth socket connection") |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1151 | return False |
| 1152 | return True |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1153 | |
| 1154 | |
| 1155 | def write_read_verify_data(client_ad, server_ad, msg, binary=False): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1156 | """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 | """ |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1167 | client_ad.log.info("Write message.") |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1168 | try: |
| 1169 | if binary: |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1170 | client_ad.droid.bluetoothSocketConnWriteBinary(msg) |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1171 | else: |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1172 | client_ad.droid.bluetoothSocketConnWrite(msg) |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1173 | except Exception as err: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1174 | client_ad.log.error("Failed to write data: {}".format(err)) |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1175 | return False |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1176 | server_ad.log.info("Read message.") |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1177 | try: |
| 1178 | if binary: |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1179 | read_msg = server_ad.droid.bluetoothSocketConnReadBinary().rstrip( |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 1180 | "\r\n") |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1181 | else: |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1182 | read_msg = server_ad.droid.bluetoothSocketConnRead() |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1183 | except Exception as err: |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1184 | server_ad.log.error("Failed to read data: {}".format(err)) |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1185 | return False |
| 1186 | log.info("Verify message.") |
| 1187 | if msg != read_msg: |
tturney | 57eb93a | 2016-06-21 13:58:06 -0700 | [diff] [blame] | 1188 | log.error("Mismatch! Read: {}, Expected: {}".format(read_msg, msg)) |
tturney | e3170f0 | 2016-05-19 14:37:00 -0700 | [diff] [blame] | 1189 | return False |
| 1190 | return True |
tturney | 51eaa38 | 2016-05-17 13:28:24 -0700 | [diff] [blame] | 1191 | |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1192 | |
| 1193 | def clear_bonded_devices(ad): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1194 | """Clear bonded devices from the input Android device. |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1195 | |
| 1196 | Args: |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1197 | ad: the Android device performing the connection. |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1198 | Returns: |
| 1199 | True if clearing bonded devices was successful, false if unsuccessful. |
| 1200 | """ |
| 1201 | bonded_device_list = ad.droid.bluetoothGetBondedDevices() |
Jakub Pawlowski | bbe68b6 | 2018-03-31 03:52:22 +0200 | [diff] [blame] | 1202 | while bonded_device_list: |
| 1203 | device_address = bonded_device_list[0]['address'] |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1204 | if not ad.droid.bluetoothUnbond(device_address): |
Jakub Pawlowski | bbe68b6 | 2018-03-31 03:52:22 +0200 | [diff] [blame] | 1205 | log.error("Failed to unbond {} from {}".format( |
| 1206 | device_address, ad.serial)) |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1207 | return False |
Jakub Pawlowski | bbe68b6 | 2018-03-31 03:52:22 +0200 | [diff] [blame] | 1208 | 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() |
tturney | 2450694 | 2016-08-23 10:53:17 -0700 | [diff] [blame] | 1219 | return True |
| 1220 | |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1221 | |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 1222 | def verify_server_and_client_connected(client_ad, server_ad, log=True): |
tturney | ed24997 | 2016-09-13 11:07:49 -0700 | [diff] [blame] | 1223 | """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 | """ |
tturney | 96970fd | 2016-08-31 17:36:09 -0700 | [diff] [blame] | 1236 | test_result = True |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1237 | if len(server_ad.droid.bluetoothSocketConnActiveConnections()) == 0: |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 1238 | if log: |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1239 | server_ad.log.error("No socket connections found on server.") |
tturney | 96970fd | 2016-08-31 17:36:09 -0700 | [diff] [blame] | 1240 | test_result = False |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1241 | if len(client_ad.droid.bluetoothSocketConnActiveConnections()) == 0: |
tturney | 5da6826 | 2017-05-02 08:57:52 -0700 | [diff] [blame] | 1242 | if log: |
Stanley Tng | 180a8be | 2017-11-29 10:53:33 -0800 | [diff] [blame] | 1243 | client_ad.log.error("No socket connections found on client.") |
tturney | 96970fd | 2016-08-31 17:36:09 -0700 | [diff] [blame] | 1244 | test_result = False |
| 1245 | return test_result |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1246 | |
| 1247 | |
| 1248 | def 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 | """ |
tturney | 1e174fd | 2017-06-01 14:42:20 -0700 | [diff] [blame] | 1259 | if not toggle_airplane_mode_by_adb(log, panu_dut, True): |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1260 | panu_dut.log.error("Failed to toggle airplane mode on") |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1261 | return False |
tturney | 3282c71 | 2017-11-16 11:40:29 -0800 | [diff] [blame] | 1262 | 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("") |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1267 | if not bluetooth_enabled_check(panu_dut): |
| 1268 | return False |
tturney | c52d2c6 | 2017-07-06 13:33:47 -0700 | [diff] [blame] | 1269 | if not bluetooth_enabled_check(pan_dut): |
| 1270 | return False |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1271 | pan_dut.droid.bluetoothPanSetBluetoothTethering(True) |
Jack He | a518bdb | 2016-11-01 18:11:06 -0700 | [diff] [blame] | 1272 | if not (pair_pri_to_sec(pan_dut, panu_dut)): |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1273 | return False |
| 1274 | if not pan_dut.droid.bluetoothPanIsTetheringOn(): |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1275 | pan_dut.log.error("Failed to enable Bluetooth tethering.") |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1276 | return False |
| 1277 | # Magic sleep needed to give the stack time in between bonding and |
| 1278 | # connecting the PAN profile. |
tturney | ec1b8f5 | 2017-07-26 07:35:06 -0700 | [diff] [blame] | 1279 | time.sleep(pan_connect_timeout) |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1280 | panu_dut.droid.bluetoothConnectBonded( |
| 1281 | pan_dut.droid.bluetoothGetLocalAddress()) |
| 1282 | if not verify_http_connection(log, panu_dut): |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1283 | panu_dut.log.error("Can't verify http connection on PANU device.") |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1284 | if not verify_http_connection(log, pan_dut): |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1285 | pan_dut.log.info( |
| 1286 | "Can't verify http connection on PAN service device") |
tturney | b92368a | 2016-09-13 10:43:15 -0700 | [diff] [blame] | 1287 | return False |
| 1288 | return True |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 1289 | |
| 1290 | |
| 1291 | def 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() |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1301 | ad.log.info("Connected HFP Client devices: {}".format(devices)) |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 1302 | if addr in {d['address'] for d in devices}: |
| 1303 | return True |
| 1304 | return False |
| 1305 | |
| 1306 | |
| 1307 | def 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() |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1317 | ad.log.info("Connected A2DP Source devices: {}".format(devices)) |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 1318 | if addr in {d['address'] for d in devices}: |
| 1319 | return True |
| 1320 | return False |
| 1321 | |
| 1322 | |
| 1323 | def 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() |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1333 | ad.log.info("Connected A2DP Sink devices: {}".format(devices)) |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 1334 | if addr in {d['address'] for d in devices}: |
| 1335 | return True |
| 1336 | return False |
| 1337 | |
| 1338 | |
| 1339 | def 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() |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1349 | ad.log.info("Connected MAP MCE devices: {}".format(devices)) |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 1350 | if addr in {d['address'] for d in devices}: |
| 1351 | return True |
| 1352 | return False |
| 1353 | |
| 1354 | |
| 1355 | def 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() |
tturney | 34ee54d | 2016-11-16 15:29:02 -0800 | [diff] [blame] | 1365 | ad.log.info("Connected MAP MSE devices: {}".format(devices)) |
Phillip Walker | 827112a | 2016-09-08 16:27:19 -0700 | [diff] [blame] | 1366 | if addr in {d['address'] for d in devices}: |
| 1367 | return True |
| 1368 | return False |
Hansong Zhang | d78b3b8 | 2017-10-03 10:36:12 -0700 | [diff] [blame] | 1369 | |
| 1370 | |
| 1371 | def 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 | """ |
tturney | 69e3bb1 | 2017-10-26 14:15:05 -0700 | [diff] [blame] | 1380 | return str( |
| 1381 | bytearray.fromhex(" ".join( |
| 1382 | [modifier, "00", key, "00", "00", "00", "00", "00"])), "utf-8") |
Hansong Zhang | d78b3b8 | 2017-10-03 10:36:12 -0700 | [diff] [blame] | 1383 | |
| 1384 | |
| 1385 | def 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 | """ |
tturney | 69e3bb1 | 2017-10-26 14:15:05 -0700 | [diff] [blame] | 1395 | device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard, |
| 1396 | hid_keyboard_report(key)) |
Hansong Zhang | d78b3b8 | 2017-10-03 10:36:12 -0700 | [diff] [blame] | 1397 | time.sleep(interval) |
tturney | 69e3bb1 | 2017-10-26 14:15:05 -0700 | [diff] [blame] | 1398 | device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard, |
| 1399 | hid_keyboard_report("00")) |
Timofey Protopopov | 2651b74 | 2018-04-09 16:43:49 -0700 | [diff] [blame] | 1400 | |
| 1401 | |
| 1402 | def 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 Turney | f5b4214 | 2018-06-22 14:01:27 -0700 | [diff] [blame] | 1420 | |
| 1421 | |
Tom Turney | 9bce15d | 2018-06-21 13:00:42 -0700 | [diff] [blame] | 1422 | def 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 | |
| 1482 | def _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] |
tprotopopov | 2e18bbb | 2018-10-02 10:29:01 -0700 | [diff] [blame] | 1495 | |
Aidan Holloway-bidwell | f8f5151 | 2018-11-06 10:42:14 -0800 | [diff] [blame^] | 1496 | |
tprotopopov | 2e18bbb | 2018-10-02 10:29:01 -0700 | [diff] [blame] | 1497 | def 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-bidwell | f8f5151 | 2018-11-06 10:42:14 -0800 | [diff] [blame^] | 1500 | |
| 1501 | |
| 1502 | def 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 |