#/usr/bin/env python3 # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy of # the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. """Bluetooth 1st time force pair and connect test implementation.""" # Quick way to get the Apollo serial number: # python3.5 -c "from acts.controllers.buds_lib.apollo_lib import get_devices; [print(d['serial_number']) for d in get_devices()]" import statistics import time from acts import logger from acts.base_test import BaseTestClass from acts.controllers.buds_lib.test_actions.bt_utils import BTUtils from acts.controllers.buds_lib.test_actions.bt_utils import BTUtilsError from acts.controllers.buds_lib.test_actions.apollo_acts import ApolloTestActions from acts.signals import TestFailure from acts.signals import TestPass from acts.test_decorators import test_tracker_info from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest from acts_contrib.test_utils.bt.bt_test_utils import factory_reset_bluetooth from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test from acts_contrib.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger from acts.utils import set_location_service # The number of pairing and connection attempts PAIR_CONNECT_ATTEMPTS = 200 class BluetoothPairConnectError(TestFailure): pass class BluetoothPairAndConnectTest(BaseTestClass): """Pairs and connects a phone and an Apollo buds device. Attributes: phone: An Android phone object apollo: An Apollo earbuds object apollo_act: An Apollo test action object dut_bt_addr: The Bluetooth address of the Apollo earbuds bt_utils: BTUtils test action object """ def setup_class(self): super().setup_class() # Sanity check of the devices under test # TODO(b/119051823): Investigate using a config validator to replace this. if not self.android_devices: raise ValueError( 'Cannot find android phone (need at least one).') self.phone = self.android_devices[0] if not self.buds_devices: raise ValueError( 'Cannot find apollo device (need at least one).') self.apollo = self.buds_devices[0] self.log.info('Successfully found needed devices.') # Staging the test, create result object, etc. self.apollo_act = ApolloTestActions(self.apollo, self.log) self.dut_bt_addr = self.apollo.bluetooth_address self.bt_utils = BTUtils() self.bt_logger = BluetoothMetricLogger.for_test_case() def setup_test(self): setup_multiple_devices_for_bt_test(self.android_devices) # Make sure Bluetooth is on enable_bluetooth(self.phone.droid, self.phone.ed) set_location_service(self.phone, True) self.apollo_act.factory_reset() self.log.info('===== START BLUETOOTH PAIR AND CONNECT TEST =====') def teardown_test(self): self.log.info('Teardown test, shutting down all services...') self.apollo_act.factory_reset() self.apollo.close() def _get_device_pair_and_connect_times(self): """Gets the pair and connect times of the phone and buds device. Pairs the phone with the buds device. Gets the pair and connect times. Unpairs the devices. Returns: pair_time: The time it takes to pair the devices in ms. connection_time: The time it takes to connect the devices for the first time after pairing. Raises: BluetoothPairConnectError """ try: pair_time = self.bt_utils.bt_pair(self.phone, self.apollo) except BTUtilsError: raise BluetoothPairConnectError('Failed to pair devices') pair_time *= 1000 connection_start_time = time.perf_counter() if not self.apollo_act.wait_for_bluetooth_a2dp_hfp(30): raise BluetoothPairConnectError('Failed to connect devices') connection_end_time = time.perf_counter() connection_time = (connection_end_time - connection_start_time) * 1000 return pair_time, connection_time @BluetoothBaseTest.bt_test_wrap @test_tracker_info(uuid='c914fd08-350d-465a-96cf-970d40e71060') def test_bluetooth_connect(self): # Store metrics metrics = {} pair_connect_success = 0 pair_connect_failures = [] pair_times = [] connect_times = [] first_connection_failure = None for attempt in range(PAIR_CONNECT_ATTEMPTS): self.log.info('Pair and connection attempt {}'.format(attempt + 1)) pair_connect_timestamp = time.strftime(logger.log_line_time_format, time.localtime()) try: pair_time, connect_time = (self. _get_device_pair_and_connect_times()) except BluetoothPairConnectError as err: self.log.error(err) failure_data = {'timestamp': pair_connect_timestamp, 'error': str(err), 'pair_and_connect_attempt': attempt + 1} pair_connect_failures.append(failure_data) if not first_connection_failure: first_connection_failure = err else: connect_times.append(connect_time) pair_times.append(pair_time) pair_connect_success += 1 factory_reset_bluetooth([self.phone]) self.log.info('Factory resetting Apollo device...') self.apollo_act.factory_reset() # Buffer between pair and connect attempts time.sleep(3) metrics['pair_attempt_count'] = PAIR_CONNECT_ATTEMPTS metrics['pair_successful_count'] = pair_connect_success metrics['pair_failed_count'] = (PAIR_CONNECT_ATTEMPTS - pair_connect_success) if len(pair_times) > 0: metrics['pair_max_time_millis'] = int(max(pair_times)) metrics['pair_min_time_millis'] = int(min(pair_times)) metrics['pair_avg_time_millis'] = int(statistics.mean(pair_times)) if len(connect_times) > 0: metrics['first_connection_max_time_millis'] = int( max(connect_times)) metrics['first_connection_min_time_millis'] = int( min(connect_times)) metrics['first_connection_avg_time_millis'] = int( (statistics.mean(connect_times))) if pair_connect_failures: metrics['pair_conn_failure_info'] = pair_connect_failures proto = self.bt_logger.get_results(metrics, self.__class__.__name__, self.phone, self.apollo) self.log.info('Metrics: {}'.format(metrics)) if PAIR_CONNECT_ATTEMPTS != pair_connect_success: raise TestFailure(str(first_connection_failure), extras=proto) else: raise TestPass('Bluetooth pair and connect test passed', extras=proto)