193 lines
7.7 KiB
Python
193 lines
7.7 KiB
Python
|
|
#/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)
|