1#!/usr/bin/env python3 2# 3# Copyright 2016 - Google 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of 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, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16""" 17 Base Class for Defining Common Bluetooth Test Functionality 18""" 19 20import threading 21import time 22import traceback 23import os 24from acts.base_test import BaseTestClass 25from acts.signals import TestSignal 26from acts.utils import dump_string_to_file 27 28from acts.libs.proto.proto_utils import parse_proto_to_ascii 29from acts_contrib.test_utils.bt.bt_metrics_utils import get_bluetooth_metrics 30from acts_contrib.test_utils.bt.bt_test_utils import get_device_selector_dictionary 31from acts_contrib.test_utils.bt.bt_test_utils import reset_bluetooth 32from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test 33from acts_contrib.test_utils.bt.bt_test_utils import take_btsnoop_logs 34from acts_contrib.test_utils.bt.ble_lib import BleLib 35from acts_contrib.test_utils.bt.bta_lib import BtaLib 36from acts_contrib.test_utils.bt.config_lib import ConfigLib 37from acts_contrib.test_utils.bt.gattc_lib import GattClientLib 38from acts_contrib.test_utils.bt.gatts_lib import GattServerLib 39from acts_contrib.test_utils.bt.rfcomm_lib import RfcommLib 40from acts_contrib.test_utils.bt.shell_commands_lib import ShellCommands 41 42 43class BluetoothBaseTest(BaseTestClass): 44 DEFAULT_TIMEOUT = 10 45 start_time = 0 46 timer_list = [] 47 48 def collect_bluetooth_manager_metrics_logs(self, ads, test_name): 49 """ 50 Collect Bluetooth metrics logs, save an ascii log to disk and return 51 both binary and ascii logs to caller 52 :param ads: list of active Android devices 53 :return: List of binary metrics logs, 54 List of ascii metrics logs 55 """ 56 bluetooth_logs = [] 57 bluetooth_logs_ascii = [] 58 for ad in ads: 59 serial = ad.serial 60 out_name = "{}_{}_{}".format(serial, test_name, 61 "bluetooth_metrics.txt") 62 bluetooth_log = get_bluetooth_metrics(ad) 63 bluetooth_log_ascii = parse_proto_to_ascii(bluetooth_log) 64 dump_string_to_file(bluetooth_log_ascii, 65 os.path.join(ad.metrics_path, out_name)) 66 bluetooth_logs.append(bluetooth_log) 67 bluetooth_logs_ascii.append(bluetooth_log_ascii) 68 return bluetooth_logs, bluetooth_logs_ascii 69 70 # Use for logging in the test cases to facilitate 71 # faster log lookup and reduce ambiguity in logging. 72 @staticmethod 73 def bt_test_wrap(fn): 74 def _safe_wrap_test_case(self, *args, **kwargs): 75 test_id = "{}:{}:{}".format(self.__class__.__name__, fn.__name__, 76 time.time()) 77 log_string = "[Test ID] {}".format(test_id) 78 self.log.info(log_string) 79 try: 80 for ad in self.android_devices: 81 ad.droid.logI("Started " + log_string) 82 result = fn(self, *args, **kwargs) 83 for ad in self.android_devices: 84 ad.droid.logI("Finished " + log_string) 85 if result is not True and "bt_auto_rerun" in self.user_params: 86 self.teardown_test() 87 log_string = "[Rerun Test ID] {}. 1st run failed.".format( 88 test_id) 89 self.log.info(log_string) 90 self.setup_test() 91 for ad in self.android_devices: 92 ad.droid.logI("Rerun Started " + log_string) 93 result = fn(self, *args, **kwargs) 94 if result is True: 95 self.log.info("Rerun passed.") 96 elif result is False: 97 self.log.info("Rerun failed.") 98 else: 99 # In the event that we have a non-bool or null 100 # retval, we want to clearly distinguish this in the 101 # logs from an explicit failure, though the test will 102 # still be considered a failure for reporting purposes. 103 self.log.info("Rerun indeterminate.") 104 result = False 105 return result 106 except TestSignal: 107 raise 108 except Exception as e: 109 self.log.error(traceback.format_exc()) 110 self.log.error(str(e)) 111 raise 112 return fn(self, *args, **kwargs) 113 114 return _safe_wrap_test_case 115 116 def setup_class(self): 117 super().setup_class() 118 for ad in self.android_devices: 119 self._setup_bt_libs(ad) 120 if 'preferred_device_order' in self.user_params: 121 prefered_device_order = self.user_params['preferred_device_order'] 122 for i, ad in enumerate(self.android_devices): 123 if ad.serial in prefered_device_order: 124 index = prefered_device_order.index(ad.serial) 125 self.android_devices[i], self.android_devices[index] = \ 126 self.android_devices[index], self.android_devices[i] 127 128 if "reboot_between_test_class" in self.user_params: 129 threads = [] 130 for a in self.android_devices: 131 thread = threading.Thread( 132 target=self._reboot_device, args=([a])) 133 threads.append(thread) 134 thread.start() 135 for t in threads: 136 t.join() 137 if not setup_multiple_devices_for_bt_test(self.android_devices): 138 return False 139 self.device_selector = get_device_selector_dictionary( 140 self.android_devices) 141 if "bluetooth_proto_path" in self.user_params: 142 for ad in self.android_devices: 143 ad.metrics_path = os.path.join(ad.log_path, "BluetoothMetrics") 144 os.makedirs(ad.metrics_path, exist_ok=True) 145 # Clear metrics. 146 get_bluetooth_metrics(ad) 147 return True 148 149 def teardown_class(self): 150 if "bluetooth_proto_path" in self.user_params: 151 # Collect metrics here bassed off class name 152 bluetooth_logs, bluetooth_logs_ascii = \ 153 self.collect_bluetooth_manager_metrics_logs( 154 self.android_devices, self.__class__.__name__) 155 156 def setup_test(self): 157 self.timer_list = [] 158 for a in self.android_devices: 159 a.ed.clear_all_events() 160 a.droid.setScreenTimeout(500) 161 a.droid.wakeUpNow() 162 return True 163 164 def on_fail(self, test_name, begin_time): 165 self.log.debug( 166 "Test {} failed. Gathering bugreport and btsnoop logs".format( 167 test_name)) 168 take_btsnoop_logs(self.android_devices, self, test_name) 169 self._take_bug_report(test_name, begin_time) 170 for _ in range(5): 171 if reset_bluetooth(self.android_devices): 172 break 173 else: 174 self.log.error("Failed to reset Bluetooth... retrying.") 175 return 176 177 def _get_time_in_milliseconds(self): 178 return int(round(time.time() * 1000)) 179 180 def start_timer(self): 181 self.start_time = self._get_time_in_milliseconds() 182 183 def end_timer(self): 184 total_time = self._get_time_in_milliseconds() - self.start_time 185 self.timer_list.append(total_time) 186 self.start_time = 0 187 return total_time 188 189 def log_stats(self): 190 if self.timer_list: 191 self.log.info("Overall list {}".format(self.timer_list)) 192 self.log.info("Average of list {}".format( 193 sum(self.timer_list) / float(len(self.timer_list)))) 194 self.log.info("Maximum of list {}".format(max(self.timer_list))) 195 self.log.info("Minimum of list {}".format(min(self.timer_list))) 196 self.log.info("Total items in list {}".format( 197 len(self.timer_list))) 198 self.timer_list = [] 199 200 def _setup_bt_libs(self, android_device): 201 # Bluetooth Low Energy library. 202 setattr(android_device, "ble", BleLib( 203 log=self.log, dut=android_device)) 204 # Bluetooth Adapter library. 205 setattr(android_device, "bta", BtaLib( 206 log=self.log, dut=android_device)) 207 # Bluetooth stack config library. 208 setattr(android_device, "config", 209 ConfigLib(log=self.log, dut=android_device)) 210 # GATT Client library. 211 setattr(android_device, "gattc", 212 GattClientLib(log=self.log, dut=android_device)) 213 # GATT Server library. 214 setattr(android_device, "gatts", 215 GattServerLib(log=self.log, dut=android_device)) 216 # RFCOMM library. 217 setattr(android_device, "rfcomm", 218 RfcommLib(log=self.log, dut=android_device)) 219 # Shell command library 220 setattr(android_device, "shell", 221 ShellCommands(log=self.log, dut=android_device)) 222 # Setup Android Device feature list 223 setattr(android_device, "features", 224 android_device.adb.shell("pm list features").split("\n")) 225