1 #/usr/bin/env python3.4 2 # 3 # Copyright (C) 2016 The Android Open Source Project 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 """ 17 Test script to automate the Bluetooth Audio Funhaus. 18 """ 19 from acts.keys import Config 20 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest 21 from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check 22 from acts.utils import bypass_setup_wizard 23 from acts.utils import create_dir 24 from acts.utils import exe_cmd 25 from acts.utils import sync_device_time 26 from queue import Empty 27 import json 28 import time 29 import os 30 31 32 class BtFunhausTest(BluetoothBaseTest): 33 music_file_to_play = "" 34 device_fails_to_connect_list = [] 35 36 def __init__(self, controllers): 37 BluetoothBaseTest.__init__(self, controllers) 38 39 def on_fail(self, test_name, begin_time): 40 self._collect_bluetooth_manager_dumpsys_logs(self.android_devices) 41 super(BluetoothBaseTest, self).on_fail(test_name, begin_time) 42 43 def setup_class(self): 44 for ad in self.android_devices: 45 sync_device_time(ad) 46 # Disable Bluetooth HCI Snoop Logs for audio tests 47 ad.droid.bluetoothConfigHciSnoopLog(False) 48 if not bypass_setup_wizard(ad): 49 self.log.debug( 50 "Failed to bypass setup wizard, continuing test.") 51 if not "bt_config" in self.user_params.keys(): 52 self.log.error("Missing mandatory user config \"bt_config\"!") 53 return False 54 bt_config_map_file = self.user_params["bt_config"] 55 return self._setup_bt_config(bt_config_map_file) 56 57 def _setup_bt_config(self, bt_config_map_file): 58 bt_config_map = {} 59 bt_conf_path = "/data/misc/bluedroid/bt_config.conf" 60 if not os.path.isfile(bt_config_map_file): 61 bt_config_map_file = os.path.join( 62 self.user_params[Config.key_config_path], bt_config_map_file) 63 if not os.path.isfile(bt_config_map_file): 64 self.log.error("Unable to load bt_config file {}.".format( 65 bt_config_map_file)) 66 return False 67 try: 68 f = open(bt_config_map_file, 'r') 69 bt_config_map = json.load(f) 70 f.close() 71 except FileNotFoundError: 72 self.log.error("File not found: {}.".format(bt_config_map_file)) 73 return False 74 # Connected devices return all upper case mac addresses. 75 # Make the peripheral_info address attribute upper case 76 # in order to ensure the BT mac addresses match. 77 for serial in bt_config_map.keys(): 78 mac_address = bt_config_map[serial]["peripheral_info"][ 79 "address"].upper() 80 bt_config_map[serial]["peripheral_info"]["address"] = mac_address 81 for ad in self.android_devices: 82 serial = ad.serial 83 84 # Verify serial number in bt_config_map 85 self.log.info("Verify serial number of Android device in config.") 86 if serial not in bt_config_map.keys(): 87 self.log.error( 88 "Missing android device serial {} in bt_config.".format( 89 serial)) 90 return False 91 # Push the bt_config.conf file to Android device 92 self.log.info("Pushing bt_config.conf file to Android device.") 93 config_path = bt_config_map[serial]["config_path"] 94 if not os.path.isfile(config_path): 95 config_path = os.path.join( 96 self.user_params[Config.key_config_path], config_path) 97 if not os.path.isfile(config_path): 98 self.log.error("Unable to load bt_config file {}.".format( 99 config_path)) 100 return False 101 ad.adb.push("{} {}".format(config_path, bt_conf_path)) 102 103 # Add music to the Android device 104 self.log.info("Pushing music to the Android device.") 105 music_path_str = "music_path" 106 android_music_path = "/sdcard/Music/" 107 if music_path_str not in self.user_params: 108 self.log.error("Need music for audio testcases...") 109 return False 110 111 music_path = self.user_params[music_path_str] 112 if not os.path.isdir(music_path): 113 music_path = os.path.join( 114 self.user_params[Config.key_config_path], music_path) 115 if not os.path.isdir(music_path): 116 self.log.error("Unable to find music directory {}.".format( 117 music_path)) 118 return False 119 self._add_music_to_primary_android_device(ad, music_path, 120 android_music_path) 121 ad.reboot() 122 123 # Verify Bluetooth is enabled 124 self.log.info("Verifying Bluetooth is enabled on Android Device.") 125 if not bluetooth_enabled_check(ad): 126 self.log.error("Failed to toggle on Bluetooth on device {}". 127 format(serial)) 128 return False 129 130 # Verify Bluetooth device is connected 131 self.log.info( 132 "Waiting up to 10 seconds for device to reconnect...") 133 connected_devices = ad.droid.bluetoothGetConnectedDevices() 134 start_time = time.time() 135 wait_time = 10 136 result = False 137 while time.time() < start_time + wait_time: 138 connected_devices = ad.droid.bluetoothGetConnectedDevices() 139 if len(connected_devices) > 0: 140 if bt_config_map[serial]["peripheral_info"]["address"] in { 141 d['address'] 142 for d in connected_devices 143 }: 144 result = True 145 break 146 else: 147 try: 148 ad.droid.bluetoothConnectBonded(bt_config_map[serial][ 149 "peripheral_info"]["address"]) 150 except Exception as err: 151 self.log.error( 152 "Failed to connect bonded. Err: {}".format(err)) 153 if not result: 154 self.log.info("Connected Devices: {}".format( 155 connected_devices)) 156 self.log.info("Bonded Devices: {}".format( 157 ad.droid.bluetoothGetBondedDevices())) 158 self.log.error( 159 "Failed to connect to peripheral name: {}, address: {}". 160 format(bt_config_map[serial]["peripheral_info"]["name"], 161 bt_config_map[serial]["peripheral_info"][ 162 "address"])) 163 self.device_fails_to_connect_list.append("{}:{}".format( 164 serial, bt_config_map[serial]["peripheral_info"]["name"])) 165 if len(self.device_fails_to_connect_list) == len(self.android_devices): 166 self.log.error("All devices failed to reconnect.") 167 return False 168 return True 169 170 def _add_music_to_primary_android_device(self, ad, music_path, 171 android_music_path): 172 for dirname, dirnames, filenames in os.walk(music_path): 173 for filename in filenames: 174 self.music_file_to_play = filename 175 file = os.path.join(dirname, filename) 176 #TODO: Handle file paths with spaces 177 ad.adb.push("{} {}".format(file, android_music_path)) 178 return True 179 180 def _collect_bluetooth_manager_dumpsys_logs(self, ads): 181 for ad in ads: 182 serial = ad.serial 183 out_name = "{}_{}".format(serial, "bluetooth_dumpsys.txt") 184 dumpsys_path = ''.join((ad.log_path, "/BluetoothDumpsys")) 185 create_dir(dumpsys_path) 186 cmd = ''.join( 187 ("adb -s ", serial, " shell dumpsys bluetooth_manager > ", 188 dumpsys_path, "/", out_name)) 189 exe_cmd(cmd) 190 191 @BluetoothBaseTest.bt_test_wrap 192 def test_run_bt_audio_12_hours(self): 193 """Test audio quality over 12 hours. 194 195 This test is designed to run Bluetooth audio for 12 hours 196 and collect relavant logs. If all devices disconnect during 197 the test or Bluetooth is off on all devices, then fail the 198 test. 199 200 Steps: 201 1. For each Android device connected run steps 2-5. 202 2. Open and play media file of music pushed to device 203 3. Set media to loop indefintely. 204 4. After 12 hours collect bluetooth_manager dumpsys information 205 5. Stop media player 206 207 Expected Result: 208 Audio plays for 12 hours over Bluetooth 209 210 Returns: 211 Pass if True 212 Fail if False 213 214 TAGS: Classic, A2DP 215 Priority: 1 216 """ 217 for ad in self.android_devices: 218 ad.droid.mediaPlayOpen("file:///sdcard/Music/{}".format( 219 self.music_file_to_play)) 220 ad.droid.mediaPlaySetLooping(True) 221 self.log.info("Music is now playing on device {}".format( 222 ad.serial)) 223 224 sleep_interval = 120 225 twelve_hours_in_seconds = 43200 226 end_time = time.time() + twelve_hours_in_seconds 227 test_result = True 228 bluetooth_off_list = [] 229 device_not_connected_list = [] 230 while time.time() < end_time: 231 for ad in self.android_devices: 232 serial = ad.serial 233 if (not ad.droid.bluetoothCheckState() and 234 serial not in bluetooth_off_list): 235 self.log.error( 236 "Device {}'s Bluetooth state is off.".format(serial)) 237 bluetooth_off_list.append(serial) 238 if (ad.droid.bluetoothGetConnectedDevices() == 0 and 239 serial not in device_not_connected_list): 240 self.log.error( 241 "Device {} not connected to any Bluetooth devices.". 242 format(serial)) 243 device_not_connected_list.append(serial) 244 if len(bluetooth_off_list) == len(self.android_devices): 245 self.log.error( 246 "Bluetooth off on all Android devices. Ending Test") 247 return False 248 if len(device_not_connected_list) == len(self.android_devices): 249 self.log.error( 250 "Every Android device has no device connected.") 251 return False 252 time.sleep(sleep_interval) 253 254 self._collect_bluetooth_manager_dumpsys_logs(self.android_devices) 255 for ad in self.android_devices: 256 ad.droid.mediaPlayStopAll() 257 if len(device_not_connected_list) > 0 or len(bluetooth_off_list) > 0: 258 self.log.info("Devices reported as not connected: {}".format( 259 device_not_connected_list)) 260 self.log.info("Devices reported with Bluetooth state off: {}". 261 format(bluetooth_off_list)) 262 return False 263 return True 264 265 def test_setup_fail_if_devices_not_connected(self): 266 """Test for devices connected or not during setup. 267 268 This test is designed to fail if the number of devices having 269 connection issues at time of setup is greater than 0. This lets 270 the test runner know of the stability of the testbed. 271 272 Steps: 273 1. Check lenght of self.device_fails_to_connect_list 274 275 Expected Result: 276 No device should be in a disconnected state. 277 278 Returns: 279 Pass if True 280 Fail if False 281 282 TAGS: None 283 Priority: 1 284 """ 285 if len(self.device_fails_to_connect_list) > 0: 286 self.log.error("Devices failed to reconnect:\n{}".format( 287 self.device_fails_to_connect_list)) 288 return False 289 return True 290