Remove l2cap uuid from RfcommTest am: 4158463531 am: c1bb3beb72
am: d3032a6253
Change-Id: I63191179f5365e9ce7d75720d9bc0f94c9bf37e8
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index a07b050..70ccbf0 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,6 @@
[Hook Scripts]
acts_base_class_test = ./acts/framework/tests/acts_base_class_test.py
+acts_libs_ota_tests = ./acts/framework/tests/libs/ota/unittest_bundle.py
acts_adb_test = ./acts/framework/tests/acts_adb_test.py
acts_android_device_test = ./acts/framework/tests/acts_android_device_test.py
acts_asserts_test = ./acts/framework/tests/acts_asserts_test.py
@@ -15,10 +16,10 @@
acts_import_test_utils_test = ./acts/framework/tests/acts_import_test_utils_test.py
acts_import_unit_test = ./acts/framework/tests/acts_import_unit_test.py
acts_relay_controller_test = ./acts/framework/tests/acts_relay_controller_test.py
-commit_message_hook = ./tools/commit_message_check.py
+test_runner_test = ./acts/framework/tests/test_runner_test.py
+keyword_check = ./tools/keyword_check.py
yapf_hook = ./tools/yapf_checker.py
-commit_message_check = ./tools/commit_message_check.py
-lab_test = ./tools/lab/test_main.py
+lab_test = ./tools/lab/lab_upload_hooks.py
[Builtin Hooks]
commit_msg_bug_field = true
diff --git a/acts/README.md b/acts/README.md
index caa2f0c..a4e9950 100644
--- a/acts/README.md
+++ b/acts/README.md
@@ -75,7 +75,8 @@
On Ubuntu, sudo apt-get install python3.4 python3-setuptools
2. Run "python3.4 setup.py install" with elevated permissions
3. To verify ACTS is ready to go, at the location for README, and run:
- cd tests/ && act.py -c acts_sanity_test_config.json -tc IntegrationTest
+ cd framework/tests/ \
+ && act.py -c acts_sanity_test_config.json -tc IntegrationTest
After installation, `act.py` will be in usr/bin and can be called as command
line utilities. Components in ACTS are importable under the package "acts."
@@ -92,7 +93,7 @@
Above, the command `act.py -c acts_sanity_test_config.json -tc IntegrationTest`
was run to verify ACTS was properly set up.
Below are the components of that command:
-- `acts.py`: is the script that runs the test
+- `act.py`: is the script that runs the test
- -c acts_sanity_test_config: is the flag and name of the configuration file
to be used in the test
- -tc IntegrationTest: is the name of the test case
diff --git a/acts/framework/acts/asserts.py b/acts/framework/acts/asserts.py
index 939ff9c..5046b3b 100644
--- a/acts/framework/acts/asserts.py
+++ b/acts/framework/acts/asserts.py
@@ -44,6 +44,7 @@
extras: An optional field for extra information to be included in
test result.
"""
+ my_msg = None
try:
_pyunit_proxy.assertEqual(first, second)
except Exception as e:
@@ -55,6 +56,8 @@
my_msg = str(e)
if msg:
my_msg = "%s %s" % (my_msg, msg)
+ # This is a hack to remove the stacktrace produced by the above exception.
+ if my_msg is not None:
fail(my_msg, extras=extras)
@@ -75,6 +78,7 @@
:param extras: Extra object passed to test failure handler
:return:
"""
+ my_msg = None
try:
if delta:
_pyunit_proxy.assertAlmostEqual(
@@ -86,6 +90,8 @@
my_msg = str(e)
if msg:
my_msg = "%s %s" % (my_msg, msg)
+ # This is a hack to remove the stacktrace produced by the above exception.
+ if my_msg is not None:
fail(my_msg, extras=extras)
diff --git a/acts/framework/acts/base_test.py b/acts/framework/acts/base_test.py
index 7be85a9..d5b0db9 100755
--- a/acts/framework/acts/base_test.py
+++ b/acts/framework/acts/base_test.py
@@ -13,11 +13,10 @@
# 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.
-
import logging
import os
-import time
import traceback
+from concurrent.futures import ThreadPoolExecutor
from acts import asserts
from acts import keys
@@ -26,7 +25,6 @@
from acts import signals
from acts import tracelogger
from acts import utils
-from acts.test_utils.tel.tel_test_utils import run_multithread_func
# Macro strings for test result reporting
TEST_CASE_TOKEN = "[Test Case]"
@@ -179,10 +177,15 @@
self.current_test_name = test_name
try:
# Write test start token to adb log if android device is attached.
- for ad in self.android_devices:
- ad.droid.logV("%s BEGIN %s" % (TEST_CASE_TOKEN, test_name))
- except:
- pass
+ if hasattr(self, 'android_devices'):
+ for ad in self.android_devices:
+ if not ad.skip_sl4a:
+ ad.droid.logV("%s BEGIN %s" % (TEST_CASE_TOKEN,
+ test_name))
+ except Exception as e:
+ self.log.warning(
+ 'Unable to send BEGIN log command to all devices.')
+ self.log.warning('Error: %s' % e)
return self.setup_test()
def setup_test(self):
@@ -195,17 +198,20 @@
Implementation is optional.
"""
+ return True
def _teardown_test(self, test_name):
"""Proxy function to guarantee the base implementation of teardown_test
is called.
"""
+ self.log.debug('Tearing down test %s' % test_name)
try:
# Write test end token to adb log if android device is attached.
for ad in self.android_devices:
ad.droid.logV("%s END %s" % (TEST_CASE_TOKEN, test_name))
- except:
- pass
+ except Exception as e:
+ self.log.warning('Unable to send END log command to all devices.')
+ self.log.warning('Error: %s' % e)
try:
self.teardown_test()
finally:
@@ -229,7 +235,7 @@
if record.details:
self.log.error(record.details)
self.log.info(RESULT_LINE_TEMPLATE, record.test_name, record.result)
- self.on_fail(record.test_name, record.log_begin_time)
+ self.on_fail(record.test_name, record.begin_time)
def on_fail(self, test_name, begin_time):
"""A function that is executed upon a test case failure.
@@ -253,7 +259,7 @@
if msg:
self.log.info(msg)
self.log.info(RESULT_LINE_TEMPLATE, record.test_name, record.result)
- self.on_pass(record.test_name, record.log_begin_time)
+ self.on_pass(record.test_name, record.begin_time)
def on_pass(self, test_name, begin_time):
"""A function that is executed upon a test case passing.
@@ -275,7 +281,7 @@
"""
self.log.info(RESULT_LINE_TEMPLATE, record.test_name, record.result)
self.log.info("Reason to skip: %s", record.details)
- self.on_skip(record.test_name, record.log_begin_time)
+ self.on_skip(record.test_name, record.begin_time)
def on_skip(self, test_name, begin_time):
"""A function that is executed upon a test case being skipped.
@@ -297,7 +303,7 @@
"""
self.log.info(RESULT_LINE_TEMPLATE, record.test_name, record.result)
self.log.info("Reason to block: %s", record.details)
- self.on_blocked(record.test_name, record.log_begin_time)
+ self.on_blocked(record.test_name, record.begin_time)
def on_blocked(self, test_name, begin_time):
"""A function that is executed upon a test begin skipped.
@@ -316,7 +322,7 @@
case.
"""
self.log.exception(record.details)
- self.on_exception(record.test_name, record.log_begin_time)
+ self.on_exception(record.test_name, record.begin_time)
def on_exception(self, test_name, begin_time):
"""A function that is executed upon an unhandled exception from a test
@@ -368,7 +374,8 @@
is_generate_trigger = False
tr_record = records.TestResultRecord(test_name, self.TAG)
tr_record.test_begin()
- self.begin_time = tr_record.log_begin_time
+ self.begin_time = int(tr_record.begin_time)
+ self.log_begin_time = tr_record.log_begin_time
self.test_name = tr_record.test_name
self.log.info("%s %s", TEST_CASE_TOKEN, test_name)
verdict = None
@@ -395,7 +402,11 @@
tr_record.add_error("teardown_test", e)
self._exec_procedure_func(self._on_exception, tr_record)
except (signals.TestFailure, AssertionError) as e:
- self.log.error(e)
+ if self.user_params.get(
+ keys.Config.key_test_failure_tracebacks.value, False):
+ self.log.exception(e)
+ else:
+ self.log.error(e)
tr_record.test_fail(e)
self._exec_procedure_func(self._on_fail, tr_record)
except signals.TestSkip as e:
@@ -425,15 +436,11 @@
self._exec_procedure_func(self._on_exception, tr_record)
self._exec_procedure_func(self._on_fail, tr_record)
else:
- # Keep supporting return False for now.
- # TODO(angli): Deprecate return False support.
if verdict or (verdict is None):
# Test passed.
tr_record.test_pass()
self._exec_procedure_func(self._on_pass, tr_record)
return
- # Test failed because it didn't return True.
- # This should be removed eventually.
tr_record.test_fail()
self._exec_procedure_func(self._on_fail, tr_record)
finally:
@@ -499,10 +506,10 @@
if format_args:
self.exec_one_testcase(test_name, test_func,
- args + (setting, ), **kwargs)
+ args + (setting,), **kwargs)
else:
self.exec_one_testcase(test_name, test_func,
- (setting, ) + args, **kwargs)
+ (setting,) + args, **kwargs)
if len(self.results.passed) - previous_success_cnt != 1:
failed_settings.append(setting)
@@ -679,17 +686,36 @@
user.
"""
- def _ad_take_reports(self, ad, test_name, begin_time):
+ def _ad_take_bugreport(self, ad, test_name, begin_time):
+ for i in range(3):
+ try:
+ ad.take_bug_report(test_name, begin_time)
+ return True
+ except Exception as e:
+ ad.log.error("bugreport attempt %s error: %s", i + 1, e)
+
+ def _ad_take_extra_logs(self, ad, test_name, begin_time):
+ result = True
+ if getattr(ad, "qxdm_log", False):
+ # Gather qxdm log modified 3 minutes earlier than test start time
+ if begin_time:
+ qxdm_begin_time = begin_time - 1000 * 60 * 3
+ else:
+ qxdm_begin_time = None
+ try:
+ ad.get_qxdm_logs(test_name, qxdm_begin_time)
+ except Exception as e:
+ ad.log.error("Failed to get QXDM log for %s with error %s",
+ test_name, e)
+ result = False
+
try:
- ad.take_bug_report(test_name, begin_time)
- bugreport_path = os.path.join(ad.log_path, test_name)
- utils.create_dir(bugreport_path)
- ad.check_crash_report(test_name, begin_time, True)
- if getattr(ad, "qxdm_log", False):
- ad.get_qxdm_logs()
+ ad.check_crash_report(test_name, begin_time, log_crash_report=True)
except Exception as e:
- ad.log.error("Failed to take a bug report for %s with error %s",
+ ad.log.error("Failed to check crash report for %s with error %s",
test_name, e)
+ result = False
+ return result
def _skip_bug_report(self):
"""A function to check whether we should skip creating a bug report."""
@@ -720,25 +746,29 @@
if self._skip_bug_report():
return
- tasks = [(self._ad_take_reports, (ad, test_name, begin_time))
- for ad in self.android_devices]
- run_multithread_func(self.log, tasks)
+ executor = ThreadPoolExecutor(max_workers=10)
+ for ad in getattr(self, 'android_devices', []):
+ executor.submit(self._ad_take_bugreport, ad, test_name, begin_time)
+ executor.submit(self._ad_take_extra_logs, ad, test_name,
+ begin_time)
+ executor.shutdown()
def _reboot_device(self, ad):
ad.log.info("Rebooting device.")
ad = ad.reboot()
def _cleanup_logger_sessions(self):
- for (logger, session) in self.logger_sessions:
- self.log.info("Resetting a diagnostic session %s, %s", logger,
+ for (mylogger, session) in self.logger_sessions:
+ self.log.info("Resetting a diagnostic session %s, %s", mylogger,
session)
- logger.reset()
+ mylogger.reset()
self.logger_sessions = []
def _pull_diag_logs(self, test_name, begin_time):
- for (logger, session) in self.logger_sessions:
- self.log.info("Pulling diagnostic session %s", logger)
- logger.stop(session)
- diag_path = os.path.join(self.log_path, begin_time)
+ for (mylogger, session) in self.logger_sessions:
+ self.log.info("Pulling diagnostic session %s", mylogger)
+ mylogger.stop(session)
+ diag_path = os.path.join(
+ self.log_path, logger.epoch_to_log_line_timestamp(begin_time))
utils.create_dir(diag_path)
- logger.pull(session, diag_path)
+ mylogger.pull(session, diag_path)
diff --git a/acts/framework/acts/bin/act.py b/acts/framework/acts/bin/act.py
index 2e533cd..f784d01 100755
--- a/acts/framework/acts/bin/act.py
+++ b/acts/framework/acts/bin/act.py
@@ -18,7 +18,6 @@
import argparse
import multiprocessing
-import os
import signal
import sys
import traceback
@@ -91,8 +90,8 @@
Each test run will be in its own process.
Args:
- parsed_config: A list of dicts, each is a set of configs for one
- test_runner.TestRunner.
+ parsed_configs: A list of dicts, each is a set of configs for one
+ test_runner.TestRunner.
test_identifiers: A list of tuples, each identifies what test case to
run on what test class.
repeat: Number of times to iterate the specified tests.
@@ -145,11 +144,6 @@
"""This is a sample implementation of a cli entry point for ACTS test
execution.
- Alternatively, you could directly invoke an ACTS test script:
-
- python3 MyTest.py -c my_config.json
-
- See acts.test_runner.main for more details.
Or you could implement your own cli entry point using acts.config_parser
functions and acts.test_runner.execute_one_test_class.
"""
@@ -227,7 +221,7 @@
'-r',
'--random',
action="store_true",
- help=("If set, tests will be executed in random order."))
+ help="If set, tests will be executed in random order.")
parser.add_argument(
'-ti',
'--test_case_iterations',
diff --git a/acts/framework/acts/config_parser.py b/acts/framework/acts/config_parser.py
index 176e7c3..498ce38 100755
--- a/acts/framework/acts/config_parser.py
+++ b/acts/framework/acts/config_parser.py
@@ -25,7 +25,8 @@
# An environment variable defining the base location for ACTS logs.
_ENV_ACTS_LOGPATH = 'ACTS_LOGPATH'
-
+# An environment variable that enables test case failures to log stack traces.
+_ENV_TEST_FAILURE_TRACEBACKS = 'ACTS_TEST_FAILURE_TRACEBACKS'
# An environment variable defining the test search paths for ACTS.
_ENV_ACTS_TESTPATHS = 'ACTS_TESTPATHS'
_PATH_SEPARATOR = ':'
@@ -100,6 +101,8 @@
Args:
testbed_configs: A list of testbed configuration json objects.
+ config_path : The path to the config file, which can be used to
+ generate absolute paths from relative paths in configs.
Raises:
If any part of the configuration is invalid, ActsConfigError is raised.
@@ -110,13 +113,6 @@
_validate_testbed_name(name)
-def _verify_test_class_name(test_cls_name):
- if not test_cls_name.endswith("Test"):
- raise ActsConfigError(
- ("Requested test class '%s' does not follow the test class naming "
- "convention *Test.") % test_cls_name)
-
-
def gen_term_signal_handler(test_runners):
def termination_sig_handler(signal_num, frame):
print('Received sigterm %s.' % signal_num)
@@ -149,14 +145,12 @@
if len(tokens) == 1:
# This should be considered a test class name
test_cls_name = tokens[0]
- _verify_test_class_name(test_cls_name)
- return (test_cls_name, None)
+ return test_cls_name, None
elif len(tokens) == 2:
# This should be considered a test class name followed by
# a list of test case names.
test_cls_name, test_case_names = tokens
clean_names = []
- _verify_test_class_name(test_cls_name)
for elem in test_case_names.split(','):
test_case_name = elem.strip()
if not test_case_name.startswith("test_"):
@@ -166,7 +160,7 @@
"naming convention test_*.") % (test_case_name,
test_cls_name))
clean_names.append(test_case_name)
- return (test_cls_name, clean_names)
+ return test_cls_name, clean_names
def parse_test_list(test_list):
@@ -186,7 +180,7 @@
Args:
test_identifiers: A list of test classes/cases.
- random_iterations: The range of random iterations for each case.
+ test_case_iterations: The range of random iterations for each case.
Returns:
A list of randomized test cases.
"""
@@ -223,7 +217,7 @@
override_test_args=None,
override_random=None,
override_test_case_iterations=None):
- """Processes the test configuration file provied by user.
+ """Processes the test configuration file provided by the user.
Loads the configuration file into a json object, unpacks each testbed
config into its own json object, and validate the configuration in the
@@ -280,18 +274,23 @@
'if you have the correct testbed names.' % name)
testbeds = tbs
- if (not keys.Config.key_log_path.value in configs
+ if (keys.Config.key_log_path.value not in configs
and _ENV_ACTS_LOGPATH in os.environ):
print('Using environment log path: %s' %
(os.environ[_ENV_ACTS_LOGPATH]))
configs[keys.Config.key_log_path.value] = os.environ[_ENV_ACTS_LOGPATH]
- if (not keys.Config.key_test_paths.value in configs
+ if (keys.Config.key_test_paths.value not in configs
and _ENV_ACTS_TESTPATHS in os.environ):
print('Using environment test paths: %s' %
(os.environ[_ENV_ACTS_TESTPATHS]))
configs[keys.Config.key_test_paths.value] = os.environ[
_ENV_ACTS_TESTPATHS].split(_PATH_SEPARATOR)
+ if (keys.Config.key_test_failure_tracebacks not in configs
+ and _ENV_TEST_FAILURE_TRACEBACKS in os.environ):
+ configs[keys.Config.key_test_failure_tracebacks.value] = os.environ[
+ _ENV_TEST_FAILURE_TRACEBACKS]
+ # Add the global paths to the global config.
k_log_path = keys.Config.key_log_path.value
configs[k_log_path] = utils.abs_path(configs[k_log_path])
diff --git a/acts/framework/acts/controllers/__init__.py b/acts/framework/acts/controllers/__init__.py
index 2cc6f43..41e48df 100644
--- a/acts/framework/acts/controllers/__init__.py
+++ b/acts/framework/acts/controllers/__init__.py
@@ -25,5 +25,5 @@
"""This is a list of all the top level controller modules"""
__all__ = [
"android_device", "attenuator", "monsoon", "access_point", "iperf_server",
- "packet_sender"
+ "packet_sender", "arduino_wifi_dongle"
]
diff --git a/acts/framework/acts/controllers/access_point.py b/acts/framework/acts/controllers/access_point.py
index 4b7919f..b861cdc 100755
--- a/acts/framework/acts/controllers/access_point.py
+++ b/acts/framework/acts/controllers/access_point.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - Google, Inc.
#
@@ -19,6 +19,7 @@
import logging
import time
+from acts import logger
from acts.controllers.ap_lib import ap_get_interface
from acts.controllers.ap_lib import bridge_interface
from acts.controllers.ap_lib import dhcp_config
@@ -96,7 +97,7 @@
Attributes:
ssh: The ssh connection to this ap.
- ssh_settings: The ssh settings being used by the ssh conneciton.
+ ssh_settings: The ssh settings being used by the ssh connection.
dhcp_settings: The dhcp server settings being used.
"""
@@ -106,6 +107,9 @@
configs: configs for the access point from config file.
"""
self.ssh_settings = settings.from_config(configs['ssh_config'])
+ self.log = logger.create_logger(lambda msg: '[Access Point|%s] %s' % (
+ self.ssh_settings.hostname, msg))
+
if 'ap_subnet' in configs:
self._AP_2G_SUBNET_STR = configs['ap_subnet']['2g']
self._AP_5G_SUBNET_STR = configs['ap_subnet']['5g']
@@ -144,23 +148,40 @@
Bring down hostapd if instance is running, bring down all bridge
interfaces.
"""
- # Stop hostapd instance if running
try:
- self.ssh.run('stop hostapd')
- except job.Error:
- logging.debug('No hostapd running')
- # Bring down all wireless interfaces
- for iface in self.wlan:
- WLAN_DOWN = 'ifconfig {} down'.format(iface)
- self.ssh.run(WLAN_DOWN)
- # Bring down all bridge interfaces
- bridge_interfaces = self.interfaces.get_bridge_interface()
- if bridge_interfaces:
- for iface in bridge_interfaces:
- BRIDGE_DOWN = 'ifconfig {} down'.format(iface)
- BRIDGE_DEL = 'brctl delbr {}'.format(iface)
- self.ssh.run(BRIDGE_DOWN)
- self.ssh.run(BRIDGE_DEL)
+ # This is necessary for Gale/Whirlwind flashed with dev channel image
+ # Unused interfaces such as existing hostapd daemon, guest, mesh
+ # interfaces need to be brought down as part of the AP initialization
+ # process, otherwise test would fail.
+ try:
+ self.ssh.run('stop hostapd')
+ except job.Error:
+ self.log.debug('No hostapd running')
+ # Bring down all wireless interfaces
+ for iface in self.wlan:
+ WLAN_DOWN = 'ifconfig {} down'.format(iface)
+ self.ssh.run(WLAN_DOWN)
+ # Bring down all bridge interfaces
+ bridge_interfaces = self.interfaces.get_bridge_interface()
+ if bridge_interfaces:
+ for iface in bridge_interfaces:
+ BRIDGE_DOWN = 'ifconfig {} down'.format(iface)
+ BRIDGE_DEL = 'brctl delbr {}'.format(iface)
+ self.ssh.run(BRIDGE_DOWN)
+ self.ssh.run(BRIDGE_DEL)
+ except Exception:
+ # TODO(b/76101464): APs may not clean up properly from previous
+ # runs. Rebooting the AP can put them back into the correct state.
+ self.log.exception('Unable to bring down hostapd. Rebooting.')
+ # Reboot the AP.
+ try:
+ self.ssh.run('reboot')
+ # This sleep ensures the device had time to go down.
+ time.sleep(10)
+ self.ssh.run('echo connected', timeout=300)
+ except Exception as e:
+ self.log.exception("Error in rebooting AP: %s", e)
+ raise
def start_ap(self, hostapd_config, additional_parameters=None):
"""Starts as an ap using a set of configurations.
@@ -174,7 +195,7 @@
Args:
hostapd_config: hostapd_config.HostapdConfig, The configurations
to use when starting up the ap.
- additional_parameters: A dicitonary of parameters that can sent
+ additional_parameters: A dictionary of parameters that can sent
directly into the hostapd config file. This
can be used for debugging and or adding one
off parameters into the config.
@@ -321,7 +342,7 @@
"""
if identifier not in list(self._aps.keys()):
- raise ValueError('Invalid identifer %s given' % identifier)
+ raise ValueError('Invalid identifier %s given' % identifier)
instance = self._aps.get(identifier)
@@ -351,7 +372,7 @@
"""Called to take down the entire access point.
When called will stop all aps running on this host, shutdown the dhcp
- server, and stop the ssh conneciton.
+ server, and stop the ssh connection.
"""
if self._aps:
diff --git a/acts/framework/acts/controllers/adb.py b/acts/framework/acts/controllers/adb.py
index 3461071..562cbb8 100644
--- a/acts/framework/acts/controllers/adb.py
+++ b/acts/framework/acts/controllers/adb.py
@@ -163,8 +163,8 @@
result = job.run(cmd, ignore_status=True, timeout=timeout)
ret, out, err = result.exit_status, result.stdout, result.stderr
- logging.debug("cmd: %s, stdout: %s, stderr: %s, ret: %s", cmd, out,
- err, ret)
+ if DEVICE_OFFLINE_REGEX.match(err):
+ raise AdbError(cmd=cmd, stdout=out, stderr=err, ret_code=ret)
if "Result: Parcel" in out:
return parsing_parcel_output(out)
if ignore_status:
@@ -178,14 +178,14 @@
return self._exec_cmd(' '.join((self.adb_str, name, arg_str)),
**kwargs)
- def _exec_cmd_nb(self, cmd):
+ def _exec_cmd_nb(self, cmd, **kwargs):
"""Executes adb commands in a new shell, non blocking.
Args:
cmds: A string that is the adb command to execute.
"""
- job.run_async(cmd)
+ return job.run_async(cmd, **kwargs)
def _exec_adb_cmd_nb(self, name, arg_str, **kwargs):
return self._exec_cmd_nb(' '.join((self.adb_str, name, arg_str)),
diff --git a/acts/framework/acts/controllers/android_device.py b/acts/framework/acts/controllers/android_device.py
index f388c29..03097f0 100755
--- a/acts/framework/acts/controllers/android_device.py
+++ b/acts/framework/acts/controllers/android_device.py
@@ -18,10 +18,11 @@
from builtins import open
from datetime import datetime
+import collections
import logging
+import math
import os
import re
-import shellescape
import socket
import time
@@ -46,7 +47,9 @@
CRASH_REPORT_PATHS = ("/data/tombstones/", "/data/vendor/ramdump/",
"/data/ramdump/", "/data/vendor/ssrdump",
"/data/vendor/ramdump/bluetooth")
-CRASH_REPORT_SKIPS = ("RAMDUMP_RESERVED", "RAMDUMP_STATUS", "bluetooth")
+CRASH_REPORT_SKIPS = ("RAMDUMP_RESERVED", "RAMDUMP_STATUS", "RAMDUMP_OUTPUT",
+ "bluetooth")
+DEFAULT_QXDM_LOG_PATH = "/data/vendor/radio/diag_logs"
BUG_REPORT_TIMEOUT = 1800
PULL_TIMEOUT = 300
PORT_RETRY_COUNT = 3
@@ -340,7 +343,6 @@
test_name: Name of the test case that triggered this bug report.
begin_time: Logline format timestamp taken when the test started.
"""
- begin_time = acts_logger.normalize_log_line_timestamp(begin_time)
def take_br(test_name, begin_time, ad):
ad.take_bug_report(test_name, begin_time)
@@ -386,12 +388,17 @@
self.adb = adb.AdbProxy(serial, ssh_connection=ssh_connection)
self.fastboot = fastboot.FastbootProxy(
serial, ssh_connection=ssh_connection)
+ self.adb_logcat_file_path = os.path.join(
+ log_path_base, 'AndroidDevice%s' % serial,
+ "adblog,{},{}.txt".format(self.model, serial))
if not self.is_bootloader:
self.root_adb()
self._ssh_connection = ssh_connection
self.skip_sl4a = False
self.crash_report = None
+ self.data_accounting = collections.defaultdict(int)
self._sl4a_manager = sl4a_manager.Sl4aManager(self.adb)
+ self.last_logcat_timestamp = None
def clean_up(self):
"""Cleans up the AndroidDevice object and releases any resources it
@@ -413,13 +420,13 @@
skip_sl4a: Does not attempt to start SL4A if True.
skip_setup_wizard: Whether or not to skip the setup wizard.
"""
+ if skip_setup_wizard:
+ self.exit_setup_wizard()
try:
self.start_adb_logcat()
except:
self.log.exception("Failed to start adb logcat!")
raise
- if skip_setup_wizard:
- self.exit_setup_wizard()
if not skip_sl4a:
try:
droid, ed = self.get_droid()
@@ -638,8 +645,9 @@
except (IndexError, ValueError) as e:
# Possible ValueError from string to int cast.
# Possible IndexError from split.
- self.log.warn('Command \"%s\" returned output line: '
- '\"%s\".\nError: %s', cmd, out, e)
+ self.log.warn(
+ 'Command \"%s\" returned output line: '
+ '\"%s\".\nError: %s', cmd, out, e)
except Exception as e:
self.log.warn(
'Device fails to check if %s running with \"%s\"\n'
@@ -658,9 +666,11 @@
"""
return self._sl4a_manager.sessions[droid.uid].get_event_dispatcher()
- def _is_timestamp_in_range(self, target, begin_time, end_time):
- low = acts_logger.logline_timestamp_comparator(begin_time, target) <= 0
- high = acts_logger.logline_timestamp_comparator(end_time, target) >= 0
+ def _is_timestamp_in_range(self, target, log_begin_time, log_end_time):
+ low = acts_logger.logline_timestamp_comparator(log_begin_time,
+ target) <= 0
+ high = acts_logger.logline_timestamp_comparator(log_end_time,
+ target) >= 0
return low and high
def cat_adb_log(self, tag, begin_time):
@@ -669,20 +679,20 @@
Args:
tag: An identifier of the time period, usualy the name of a test.
- begin_time: Logline format timestamp of the beginning of the time
- period.
+ begin_time: Epoch time of the beginning of the time period.
"""
+ log_begin_time = acts_logger.epoch_to_log_line_timestamp(begin_time)
if not self.adb_logcat_file_path:
raise AndroidDeviceError(
("Attempting to cat adb log when none has"
" been collected on Android device %s.") % self.serial)
- end_time = acts_logger.get_log_line_timestamp()
+ log_end_time = acts_logger.get_log_line_timestamp()
self.log.debug("Extracting adb log from logcat.")
adb_excerpt_path = os.path.join(self.log_path, "AdbLogExcerpts")
utils.create_dir(adb_excerpt_path)
f_name = os.path.basename(self.adb_logcat_file_path)
out_name = f_name.replace("adblog,", "").replace(".txt", "")
- out_name = ",{},{}.txt".format(begin_time, out_name)
+ out_name = ",{},{}.txt".format(log_begin_time, out_name)
tag_len = utils.MAX_FILENAME_LEN - len(out_name)
tag = tag[:tag_len]
out_name = tag + out_name
@@ -702,8 +712,8 @@
line_time = line[:acts_logger.log_line_timestamp_len]
if not acts_logger.is_valid_logline_timestamp(line_time):
continue
- if self._is_timestamp_in_range(line_time, begin_time,
- end_time):
+ if self._is_timestamp_in_range(line_time, log_begin_time,
+ log_end_time):
in_range = True
if not line.endswith('\n'):
line += '\n'
@@ -744,10 +754,13 @@
extra_params = self.adb_logcat_param
else:
extra_params = "-b all"
-
- # TODO(markdr): Pull 'adb -s %SERIAL' from the AdbProxy object.
- cmd = "adb -s {} logcat -T 1 -v year {} >> {}".format(
- self.serial, extra_params, logcat_file_path)
+ if self.last_logcat_timestamp:
+ begin_at = '-T "%s"' % self.last_logcat_timestamp
+ else:
+ begin_at = '-T 1'
+ # TODO(markdr): Pull 'adb -s %SERIAL' from the AdbProxy object.
+ cmd = "adb -s {} logcat {} -v year {} >> {}".format(
+ self.serial, begin_at, extra_params, logcat_file_path)
self.adb_logcat_process = utils.start_standing_subprocess(cmd)
self.adb_logcat_file_path = logcat_file_path
@@ -758,6 +771,13 @@
raise AndroidDeviceError(
"Android device %s does not have an ongoing adb logcat collection."
% self.serial)
+ # Set the last timestamp to the current timestamp. This may cause
+ # a race condition that allows the same line to be logged twice,
+ # but it does not pose a problem for our logging purposes.
+ logcat_output = self.adb.logcat('-t 1 -v year')
+ next_line = logcat_output.find('\n')
+ self.last_logcat_timestamp = logcat_output[next_line + 1:
+ next_line + 24]
utils.stop_standing_subprocess(self.adb_logcat_process)
self.adb_logcat_process = None
@@ -794,8 +814,9 @@
'pm list packages | grep -w "package:%s"' % package_name))
except Exception as err:
- self.log.error('Could not determine if %s is installed. '
- 'Received error:\n%s', package_name, err)
+ self.log.error(
+ 'Could not determine if %s is installed. '
+ 'Received error:\n%s', package_name, err)
return False
def is_sl4a_installed(self):
@@ -819,8 +840,9 @@
self.log.info("apk %s is running", package_name)
return True
except Exception as e:
- self.log.warn("Device fails to check is %s running by %s "
- "Exception %s", package_name, cmd, e)
+ self.log.warn(
+ "Device fails to check is %s running by %s "
+ "Exception %s", package_name, cmd, e)
continue
self.log.debug("apk %s is not running", package_name)
return False
@@ -854,7 +876,7 @@
Args:
test_name: Name of the test case that triggered this bug report.
- begin_time: Logline format timestamp taken when the test started.
+ begin_time: Epoch time when the test started.
"""
self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT)
new_br = True
@@ -868,8 +890,10 @@
new_br = False
br_path = os.path.join(self.log_path, test_name)
utils.create_dir(br_path)
+ time_stamp = acts_logger.normalize_log_line_timestamp(
+ acts_logger.epoch_to_log_line_timestamp(begin_time))
out_name = "AndroidDevice%s_%s" % (
- self.serial, begin_time.replace(" ", "_").replace(":", "-"))
+ self.serial, time_stamp.replace(" ", "_").replace(":", "-"))
out_name = "%s.zip" % out_name if new_br else "%s.txt" % out_name
full_out_path = os.path.join(br_path, out_name)
# in case device restarted, wait for adb interface to return
@@ -878,9 +902,9 @@
if new_br:
out = self.adb.shell("bugreportz", timeout=BUG_REPORT_TIMEOUT)
if not out.startswith("OK"):
- raise AndroidDeviceError("Failed to take bugreport on %s: %s" %
- (self.serial, out))
- br_out_path = out.split(':')[1].strip()
+ raise AndroidDeviceError(
+ "Failed to take bugreport on %s: %s" % (self.serial, out))
+ br_out_path = out.split(':')[1].strip().split()[0]
self.adb.pull("%s %s" % (br_out_path, full_out_path))
else:
self.adb.bugreport(
@@ -889,19 +913,27 @@
full_out_path)
self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT)
- def get_file_names(self, directory):
+ def get_file_names(self,
+ directory,
+ begin_time=None,
+ skip_files=[],
+ match_string=None):
"""Get files names with provided directory."""
- # -1 (the number one) prints one file per line.
- out = self.adb.shell(
- "ls -1 %s" % directory, ignore_status=True)
- if "Permission denied" in out:
- self.root_adb()
- out = self.adb.shell(
- "ls -1 %s" % directory, ignore_status=True)
- if out and "No such" not in out:
- return out.split('\n')
- else:
+ cmd = "find %s -type f" % directory
+ if begin_time:
+ current_time = utils.get_current_epoch_time()
+ seconds = int(math.ceil((current_time - begin_time) / 1000.0))
+ cmd = "%s -mtime -%ss" % (cmd, seconds)
+ if match_string:
+ cmd = "%s -iname %s" % (cmd, match_string)
+ for skip_file in skip_files:
+ cmd = "%s ! -iname %s" % (cmd, skip_file)
+ out = self.adb.shell(cmd, ignore_status=True)
+ if not out or "No such" in out or "Permission denied" in out:
return []
+ files = out.split("\n")
+ self.log.debug("Find files in directory %s: %s", directory, files)
+ return files
def pull_files(self, files, remote_path=None):
"""Pull files from devies."""
@@ -917,22 +949,20 @@
log_crash_report=False):
"""check crash report on the device."""
crash_reports = []
- if begin_time:
- begin_time = "%s-%s" % (datetime.now().year, begin_time)
- begin_time = datetime.strptime(begin_time, "%Y-%m-%d %H:%M:%S.%f")
for crash_path in CRASH_REPORT_PATHS:
- for report in self.get_file_names(crash_path):
- if report in CRASH_REPORT_SKIPS:
- continue
- file_path = os.path.join(crash_path, report)
- if begin_time:
- file_time = self.adb.shell('stat -c "%%y" %s' % file_path)
- file_time = datetime.strptime(file_time[:-3],
- "%Y-%m-%d %H:%M:%S.%f")
- if begin_time < file_time:
- crash_reports.append(file_path)
- else:
- crash_reports.append(file_path)
+ crashes = self.get_file_names(
+ crash_path,
+ skip_files=CRASH_REPORT_SKIPS,
+ begin_time=begin_time)
+ if crash_path == "/data/tombstones/" and crashes:
+ tombstones = crashes[:]
+ for tombstone in tombstones:
+ if self.adb.shell(
+ 'cat %s | grep "crash_dump failed to dump process"'
+ % tombstone):
+ crashes.remove(tombstone)
+ if crashes:
+ crash_reports.extend(crashes)
if crash_reports and log_crash_report:
test_name = test_name or time.strftime("%Y-%m-%d-%Y-%H-%M-%S")
crash_log_path = os.path.join(self.log_path, test_name,
@@ -941,7 +971,7 @@
self.pull_files(crash_reports, crash_log_path)
return crash_reports
- def get_qxdm_logs(self):
+ def get_qxdm_logs(self, test_name="", begin_time=None):
"""Get qxdm logs."""
# Sleep 10 seconds for the buffered log to be written in qxdm log file
time.sleep(10)
@@ -949,12 +979,23 @@
qxdm_logs = self.get_file_names(
log_path, begin_time=begin_time, match_string="*.qmdl")
if qxdm_logs:
- qxdm_path = os.path.join(self.log_path, "QXDM_Logs")
- utils.create_dir(qxdm_path)
- self.pull_files(qxdm_logs, qxdm_path)
+ qxdm_log_path = os.path.join(self.log_path, test_name,
+ "QXDM_%s" % self.serial)
+ utils.create_dir(qxdm_log_path)
+ self.log.info("Pull QXDM Log %s to %s", qxdm_logs, qxdm_log_path)
+ self.pull_files(qxdm_logs, qxdm_log_path)
+ else:
+ self.log.error("Didn't find QXDM logs in %s." % log_path)
+ if "Verizon" in self.adb.getprop("gsm.sim.operator.alpha"):
+ omadm_log_path = os.path.join(self.log_path, test_name,
+ "OMADM_%s" % self.serial)
+ utils.create_dir(omadm_log_path)
+ self.log.info("Pull OMADM Log")
self.adb.pull(
- "/firmware/image/qdsp6m.qdb %s" % qxdm_path,
- timeout=PULL_TIMEOUT, ignore_status=True)
+ "/data/data/com.android.omadm.service/files/dm/log/ %s" %
+ omadm_log_path,
+ timeout=PULL_TIMEOUT,
+ ignore_status=True)
def start_new_session(self, max_connections=None, server_port=None):
"""Start a new session in sl4a.
@@ -982,6 +1023,28 @@
"""
self._sl4a_manager.terminate_all_sessions()
+ def run_iperf_client_nb(self,
+ server_host,
+ extra_args="",
+ timeout=IPERF_TIMEOUT,
+ log_file_path=None):
+ """Start iperf client on the device asynchronously.
+
+ Return status as true if iperf client start successfully.
+ And data flow information as results.
+
+ Args:
+ server_host: Address of the iperf server.
+ extra_args: A string representing extra arguments for iperf client,
+ e.g. "-i 1 -t 30".
+ log_file_path: The complete file path to log the results.
+
+ """
+ cmd = "iperf3 -c {} {}".format(server_host, extra_args)
+ if log_file_path:
+ cmd += " --logfile {} &".format(log_file_path)
+ self.adb.shell_nb(cmd)
+
def run_iperf_client(self,
server_host,
extra_args="",
@@ -1069,11 +1132,34 @@
self.adb.reboot()
self.wait_for_boot_completion()
self.root_adb()
- if stop_at_lock_screen and self.is_screen_lock_enabled():
+ if stop_at_lock_screen:
return
+ if not self.ensure_screen_on():
+ self.log.error("User window cannot come up")
+ raise AndroidDeviceError("User window cannot come up")
self.start_services(self.skip_sl4a)
- def search_logcat(self, matching_string):
+ def restart_runtime(self):
+ """Restarts android runtime.
+
+ Terminate all sl4a sessions, restarts runtime, wait for framework
+ complete restart, and restart an sl4a session if restart_sl4a is True.
+ """
+ self.stop_services()
+ self.log.info("Restarting android runtime")
+ self.adb.shell("stop")
+ # Reset the boot completed flag before we restart the framework
+ # to correctly detect when the framework has fully come up.
+ self.adb.shell("setprop sys.boot_completed 0")
+ self.adb.shell("start")
+ self.wait_for_boot_completion()
+ self.root_adb()
+ if not self.ensure_screen_on():
+ self.log.error("User window cannot come up")
+ raise AndroidDeviceError("User window cannot come up")
+ self.start_services(self.skip_sl4a)
+
+ def search_logcat(self, matching_string, begin_time=None):
"""Search logcat message with given string.
Args:
@@ -1093,7 +1179,8 @@
begin_time)
cmd_option = '%s -t "%s"' % (cmd_option, log_begin_time)
out = self.adb.logcat(
- '-b all -d | grep "%s"' % matching_string, ignore_status=True)
+ '%s | grep "%s"' % (cmd_option, matching_string),
+ ignore_status=True)
if not out: return []
result = []
logs = re.findall(r'(\S+\s\S+)(.*%s.*)' % re.escape(matching_string),
@@ -1229,7 +1316,7 @@
if "unable to open" in out:
self.root_adb()
out = self.adb.shell(cmd, ignore_status=True)
- if ",0,'0'" not in out:
+ if ",0,'0'" not in out and out != "":
self.log.info("Screen lock is enabled")
return True
return False
@@ -1270,7 +1357,8 @@
self.send_keycode("WAKEUP")
def go_to_sleep(self):
- self.send_keycode("SLEEP")
+ if self.is_screen_awake():
+ self.send_keycode("SLEEP")
def send_keycode_number_pad(self, number):
self.send_keycode("NUMPAD_%s" % number)
@@ -1291,9 +1379,38 @@
self.send_keycode("BACK")
def exit_setup_wizard(self):
- self.adb.shell(
- "am start -n com.google.android.setupwizard/.SetupWizardExitActivity"
- )
+ if not self.is_user_setup_complete() or self.is_setupwizard_on():
+ self.adb.shell(
+ "pm disable %s" % self.get_setupwizard_package_name())
+ # Wait up to 5 seconds for user_setup_complete to be updated
+ end_time = time.time() + 5
+ while time.time() < end_time:
+ if self.is_user_setup_complete() or not self.is_setupwizard_on():
+ return
+
+ # If fail to exit setup wizard, set local.prop and reboot
+ if not self.is_user_setup_complete() and self.is_setupwizard_on():
+ self.adb.shell("echo ro.test_harness=1 > /data/local.prop")
+ self.adb.shell("chmod 644 /data/local.prop")
+ self.reboot(stop_at_lock_screen=True)
+
+ def get_setupwizard_package_name(self):
+ """Finds setupwizard package/.activity
+
+ Bypass setupwizard or setupwraith depending on device.
+
+ Returns:
+ packageName/.ActivityName
+ """
+ packages_to_skip = "'setupwizard|setupwraith'"
+ android_package_name = "com.google.android"
+ package = self.adb.shell(
+ "pm list packages -f | grep -E {} | grep {}".format(
+ packages_to_skip, android_package_name))
+ wizard_package = re.split("=", package)[1]
+ activity = re.search("(.*?).apk", package, re.IGNORECASE).groups()[0]
+ self.log.info("%s/.%sActivity" % (wizard_package, activity))
+ return "%s/.%sActivity" % (wizard_package, activity)
class AndroidDeviceLoggerAdapter(logging.LoggerAdapter):
diff --git a/acts/framework/acts/controllers/anritsu_lib/_anritsu_utils.py b/acts/framework/acts/controllers/anritsu_lib/_anritsu_utils.py
index 8e5445a..0da016f 100644
--- a/acts/framework/acts/controllers/anritsu_lib/_anritsu_utils.py
+++ b/acts/framework/acts/controllers/anritsu_lib/_anritsu_utils.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+#!/usr/bin/env python3.4
#
# Copyright 2016 - The Android Open Source Project
#
@@ -13,10 +13,10 @@
# 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.
-
"""
Utility functions for for Anritsu Signalling Tester.
"""
+# yapf: disable
OPERATION_COMPLETE = 1
NO_ERROR = 0
@@ -229,3 +229,4 @@
def __str__(self):
return self._error_message
+# yapf: enable
diff --git a/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py b/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py
index f82c6bd..d3369bf 100644
--- a/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py
+++ b/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+#!/usr/bin/env python3.4
#
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/acts/controllers/anritsu_lib/md8475a.py b/acts/framework/acts/controllers/anritsu_lib/md8475a.py
index 5b7f0e3..a6c0e13 100644
--- a/acts/framework/acts/controllers/anritsu_lib/md8475a.py
+++ b/acts/framework/acts/controllers/anritsu_lib/md8475a.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+#!/usr/bin/env python3.4
#
# Copyright 2016 - The Android Open Source Project
#
@@ -54,6 +54,23 @@
IDENTITY_REQ_DATA_LEN = 24
SEQ_LOG_MESSAGE_START_INDEX = 60
+WCDMA_BANDS = {
+ "I": "1",
+ "II": "2",
+ "III": "3",
+ "IV": "4",
+ "V": "5",
+ "VI": "6",
+ "VII": "7",
+ "VIII": "8",
+ "IX": "9",
+ "X": "10",
+ "XI": "11",
+ "XII": "12",
+ "XIII": "13",
+ "XIV": "14"
+}
+
def create(configs, logger):
objs = []
@@ -651,6 +668,8 @@
# this is needed before Sync command is supported in 6.40a
if self.send_query("IMSVNSTAT? 1") == "RUNNING":
self.send_command("IMSSTOPVN 1")
+ if self.send_query("IMSVNSTAT? 2") == "RUNNING":
+ self.send_command("IMSSTOPVN 2")
stat = self.send_query("STAT?")
# Stop simulation if its is RUNNING
if stat == "RUNNING":
@@ -710,8 +729,19 @@
error = int(
self.send_query("SIMMODEL %s;ERROR?" % sim_model,
COMMAND_COMPLETE_WAIT_TIME))
- if error:
- return False
+ if error: # Try again if first set SIMMODEL fails
+ time.sleep(3)
+ if "WLAN" in sim_model:
+ new_sim_model = sim_model[:-5]
+ error = int(
+ self.send_query("SIMMODEL %s;ERROR?" % new_sim_model,
+ COMMAND_COMPLETE_WAIT_TIME))
+ time.sleep(3)
+ error = int(
+ self.send_query("SIMMODEL %s;ERROR?" % sim_model,
+ COMMAND_COMPLETE_WAIT_TIME))
+ if error:
+ return False
# Reset every time after SIMMODEL is set because SIMMODEL will load
# some of the contents from previous parameter files.
self.reset()
@@ -865,6 +895,34 @@
bts_number, rat_info = self.send_query("CAMPINGCELL?").split(",")
return bts_number, rat_info
+ def get_supported_bands(self, rat):
+ """ Gets the supported bands from UE capability information
+
+ Args:
+ rat: LTE or WCDMA
+
+ Returns:
+ returns a list of bnads
+ """
+ cmd = "UEINFO? "
+ if rat == "LTE":
+ cmd += "L"
+ elif rat == "WCDMA":
+ cmd += "W"
+ else:
+ raise ValueError('The rat argument needs to be "LTE" or "WCDMA"')
+ cmd += "_SupportedBand"
+ result = self.send_query(cmd).split(",")
+ if result == "NONE":
+ return None
+ if rat == "WCDMA":
+ bands = []
+ for band in result:
+ bands.append(WCDMA_BANDS[band])
+ return bands
+ else:
+ return result
+
def start_testcase(self):
""" Starts a test case on Anritsu
@@ -1506,8 +1564,14 @@
Returns:
None
"""
- cmd = "OLVL {},{}".format(level, self._bts_number)
- self._anritsu.send_command(cmd)
+ counter = 1
+ while float(level) != float(self.output_level):
+ if counter > 3:
+ raise AnritsuError("Fail to set output level in 3 tries!")
+ cmd = "OLVL {},{}".format(level, self._bts_number)
+ self._anritsu.send_command(cmd)
+ counter += 1
+ time.sleep(1)
@property
def input_level(self):
@@ -1532,8 +1596,14 @@
Returns:
None
"""
- cmd = "RFLVL {},{}".format(level, self._bts_number)
- self._anritsu.send_command(cmd)
+ counter = 1
+ while float(level) != float(self.input_level):
+ if counter > 3:
+ raise AnritsuError("Fail to set intput level in 3 tries!")
+ cmd = "RFLVL {},{}".format(level, self._bts_number)
+ self._anritsu.send_command(cmd)
+ counter += 1
+ time.sleep(1)
@property
def band(self):
@@ -2468,6 +2538,38 @@
self._anritsu.send_command(cmd)
@property
+ def lte_scheduling_mode(self):
+ """ Gets the Scheduling mode of the LTE cell
+
+ Args:
+ None
+
+ Returns:
+ Scheduling mode
+ """
+ cmd = "SCHEDULEMODE? " + self._bts_number
+ return self._anritsu.send_query(cmd)
+
+ @lte_scheduling_mode.setter
+ def lte_scheduling_mode(self, mode):
+ """ Sets the Scheduling mode of the LTE cell
+
+ Args:
+ mode: STATIC (default) or DYNAMIC
+
+ Returns:
+ None
+ """
+ counter = 1
+ while mode != self.lte_scheduling_mode:
+ if counter > 3:
+ raise AnritsuError("Fail to set scheduling mode in 3 tries!")
+ cmd = "SCHEDULEMODE {},{}".format(mode, self._bts_number)
+ self._anritsu.send_command(cmd)
+ counter += 1
+ time.sleep(1)
+
+ @property
def lte_mcs_dl(self):
""" Gets the Modulation and Coding scheme (DL) of the LTE cell
@@ -3420,6 +3522,58 @@
cmd = "PDNVNID {},{}".format(self._pdn_number, vnid)
self._anritsu.send_command(cmd)
+ @property
+ def pdn_apn_name(self):
+ """ Get PDN APN NAME
+
+ Args:
+ None
+
+ Returns:
+ PDN APN NAME
+ """
+ cmd = "PDNCHECKAPN? " + self._pdn_number
+ return self._anritsu.send_query(cmd)
+
+ @pdn_apn_name.setter
+ def pdn_apn_name(self, name):
+ """ Set PDN APN NAME
+
+ Args:
+ name: fast.t-mobile.com, ims
+
+ Returns:
+ None
+ """
+ cmd = "PDNCHECKAPN {},{}".format(self._pdn_number, name)
+ self._anritsu.send_command(cmd)
+
+ @property
+ def pdn_qci(self):
+ """ Get PDN QCI Value
+
+ Args:
+ None
+
+ Returns:
+ PDN QCI Value
+ """
+ cmd = "PDNQCIDEFAULT? " + self._pdn_number
+ return self._anritsu.send_query(cmd)
+
+ @pdn_qci.setter
+ def pdn_qci(self, qci_value):
+ """ Set PDN QCI Value
+
+ Args:
+ qci_value: 5, 9
+
+ Returns:
+ None
+ """
+ cmd = "PDNQCIDEFAULT {},{}".format(self._pdn_number, qci_value)
+ self._anritsu.send_command(cmd)
+
class _TriggerMessage(object):
'''Class to interact with trigger message handling supported by MD8475 '''
@@ -3555,6 +3709,32 @@
self._anritsu.send_command(cmd)
@property
+ def imscscf_iptype(self):
+ """ Gets CSCF IP Type
+
+ Args:
+ None
+
+ Returns:
+ CSCF IP Type
+ """
+ cmd = "IMSCSCFIPTYPE? " + self._vnid
+ return self._anritsu.send_query(cmd)
+
+ @imscscf_iptype.setter
+ def imscscf_iptype(self, iptype):
+ """ Set CSCF IP Type
+
+ Args:
+ iptype: IPV4, IPV6, IPV4V6
+
+ Returns:
+ None
+ """
+ cmd = "IMSCSCFIPTYPE {},{}".format(self._vnid, iptype)
+ self._anritsu.send_command(cmd)
+
+ @property
def cscf_monitoring_ua(self):
""" Get CSCF Monitoring UA URI
@@ -3581,6 +3761,146 @@
self._anritsu.send_command(cmd)
@property
+ def cscf_host_name(self):
+ """ Get CSCF Host Name
+
+ Args:
+ None
+
+ Returns:
+ CSCF Host Name
+ """
+ cmd = "IMSCSCFNAME? " + self._vnid
+ return self._anritsu.send_query(cmd)
+
+ @cscf_host_name.setter
+ def cscf_host_name(self, host_name):
+ """ Set CSCF Host Name
+
+ Args:
+ host_name: CSCF Host Name
+
+ Returns:
+ None
+ """
+ cmd = "IMSCSCFNAME {},{}".format(self._vnid, host_name)
+ self._anritsu.send_command(cmd)
+
+ @property
+ def cscf_ims_authentication(self):
+ """ Get CSCF IMS Auth Value
+
+ Args:
+ None
+
+ Returns:
+ CSCF IMS Auth
+ """
+ cmd = "IMSCSCFAUTH? " + self._vnid
+ return self._anritsu.send_query(cmd)
+
+ @cscf_ims_authentication.setter
+ def cscf_ims_authentication(self, on_off):
+ """ Set CSCF IMS Auth Value
+
+ Args:
+ on_off: CSCF IMS Auth ENABLE/DISABLE
+
+ Returns:
+ None
+ """
+ cmd = "IMSCSCFAUTH {},{}".format(self._vnid, on_off)
+ self._anritsu.send_command(cmd)
+
+ @property
+ def cscf_virtual_ua(self):
+ """ Get CSCF Virtual UA URI
+
+ Args:
+ None
+
+ Returns:
+ CSCF Virtual UA URI
+ """
+ cmd = "IMSCSCFVUAURI? " + self._vnid
+ return self._anritsu.send_query(cmd)
+
+ @cscf_virtual_ua.setter
+ def cscf_virtual_ua(self, ua_uri):
+ """ Set CSCF Virtual UA URI
+
+ Args:
+ ua_uri: CSCF Virtual UA URI
+
+ Returns:
+ None
+ """
+ cmd = "IMSCSCFVUAURI {},{}".format(self._vnid, ua_uri)
+ self._anritsu.send_command(cmd)
+
+ @property
+ def tmo_cscf_userslist_add(self):
+ """ Get CSCF USERLIST
+
+ Args:
+ None
+
+ Returns:
+ CSCF USERLIST
+ """
+ cmd = "IMSCSCFUSERSLIST? " + self._vnid
+ return self._anritsu.send_query(cmd)
+
+ @tmo_cscf_userslist_add.setter
+ def tmo_cscf_userslist_add(self, username):
+ """ Set CSCF USER to USERLIST
+ This is needed if IMS AUTH is enabled
+
+ Args:
+ username: CSCF Username
+
+ Returns:
+ None
+ """
+ cmd = "IMSCSCFUSERSLISTADD {},{},00112233445566778899AABBCCDDEEFF,TS34108,AKAV1_MD5,\
+ OPC,00000000000000000000000000000000,8000,TRUE,FALSE,0123456789ABCDEF0123456789ABCDEF,\
+ 54CDFEAB9889000001326754CDFEAB98,6754CDFEAB9889BAEFDC457623100132,\
+ 326754CDFEAB9889BAEFDC4576231001,TRUE,TRUE,TRUE".format(
+ self._vnid, username)
+ self._anritsu.send_command(cmd)
+
+ @property
+ def vzw_cscf_userslist_add(self):
+ """ Get CSCF USERLIST
+
+ Args:
+ None
+
+ Returns:
+ CSCF USERLIST
+ """
+ cmd = "IMSCSCFUSERSLIST? " + self._vnid
+ return self._anritsu.send_query(cmd)
+
+ @vzw_cscf_userslist_add.setter
+ def vzw_cscf_userslist_add(self, username):
+ """ Set CSCF USER to USERLIST
+ This is needed if IMS AUTH is enabled
+
+ Args:
+ username: CSCF Username
+
+ Returns:
+ None
+ """
+ cmd = "IMSCSCFUSERSLISTADD {},{},465B5CE8B199B49FAA5F0A2EE238A6BC,MILENAGE,AKAV1_MD5,\
+ OP,5F1D289C5D354D0A140C2548F5F3E3BA,8000,TRUE,FALSE,0123456789ABCDEF0123456789ABCDEF,\
+ 54CDFEAB9889000001326754CDFEAB98,6754CDFEAB9889BAEFDC457623100132,\
+ 326754CDFEAB9889BAEFDC4576231001,TRUE,TRUE,TRUE".format(
+ self._vnid, username)
+ self._anritsu.send_command(cmd)
+
+ @property
def dns(self):
""" Gets DNS Enable status
@@ -3635,6 +3955,32 @@
self._anritsu.send_command(cmd)
@property
+ def ndp_prefix(self):
+ """ Gets NDP IPv6 Prefix
+
+ Args:
+ None
+
+ Returns:
+ NDP IPv6 Prefix
+ """
+ cmd = "IMSNDPPREFIX? " + self._vnid
+ return self._anritsu.send_query(cmd)
+
+ @ndp_prefix.setter
+ def ndp_prefix(self, prefix_addr):
+ """ Set NDP IPv6 Prefix
+
+ Args:
+ prefix_addr: NDP IPV6 Prefix Addr
+
+ Returns:
+ None
+ """
+ cmd = "IMSNDPPREFIX {},{},64".format(self._vnid, prefix_addr)
+ self._anritsu.send_command(cmd)
+
+ @property
def psap(self):
""" Gets PSAP Enable status
diff --git a/acts/framework/acts/controllers/anritsu_lib/mg3710a.py b/acts/framework/acts/controllers/anritsu_lib/mg3710a.py
index f112dbf..9c6cf46 100644
--- a/acts/framework/acts/controllers/anritsu_lib/mg3710a.py
+++ b/acts/framework/acts/controllers/anritsu_lib/mg3710a.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+#!/usr/bin/env python3.4
#
# Copyright 2016 - The Android Open Source Project
#
@@ -695,8 +695,8 @@
Returns:
frequency offset
"""
- return self.send_query("SOUR{}:RAD:ARB:WM{}:FREQ:OFFS?".format(sg,
- a_or_b))
+ return self.send_query(
+ "SOUR{}:RAD:ARB:WM{}:FREQ:OFFS?".format(sg, a_or_b))
def set_arb_freq_offset_aorb(self, a_or_b, offset, sg=1):
""" Sets the frequency offset of Pattern A/Pattern B based on Baseband
diff --git a/acts/framework/acts/controllers/ap_lib/ap_get_interface.py b/acts/framework/acts/controllers/ap_lib/ap_get_interface.py
index 65c8938..b80add5 100644
--- a/acts/framework/acts/controllers/ap_lib/ap_get_interface.py
+++ b/acts/framework/acts/controllers/ap_lib/ap_get_interface.py
@@ -140,6 +140,10 @@
if wan:
return wan
+ output = self.ssh.run('ifconfig')
+ interfaces_all = output.stdout.split('\n')
+ logging.info("IFCONFIG output = %s" % interfaces_all)
+
raise ApInterfacesError('No WAN interface available')
def get_lan_interface(self):
diff --git a/acts/framework/acts/controllers/ap_lib/dhcp_server.py b/acts/framework/acts/controllers/ap_lib/dhcp_server.py
index 6d81360..19d6d73 100644
--- a/acts/framework/acts/controllers/ap_lib/dhcp_server.py
+++ b/acts/framework/acts/controllers/ap_lib/dhcp_server.py
@@ -29,7 +29,7 @@
class DhcpServer(object):
"""Manages the dhcp server program.
- Only one of these can run in an enviroment at a time.
+ Only one of these can run in an environment at a time.
Attributes:
config: The dhcp server configuration that is being used.
@@ -76,8 +76,9 @@
self._shell.delete_file(self._log_file)
self._shell.touch_file(self._lease_file)
- dhcpd_command = '%s -cf "%s" -lf %s -f""' % (
- self.PROGRAM_FILE, self._config_file, self._lease_file)
+ dhcpd_command = '%s -cf "%s" -lf %s -f""' % (self.PROGRAM_FILE,
+ self._config_file,
+ self._lease_file)
base_command = 'cd "%s"; %s' % (self._working_dir, dhcpd_command)
job_str = '%s > "%s" 2>&1' % (base_command, self._log_file)
self._runner.run_async(job_str)
@@ -96,7 +97,7 @@
def is_alive(self):
"""
Returns:
- True if the deamon is running.
+ True if the daemon is running.
"""
return self._shell.is_alive(self._identifier)
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd.py b/acts/framework/acts/controllers/ap_lib/hostapd.py
index 9f78d79..e28e8e5 100644
--- a/acts/framework/acts/controllers/ap_lib/hostapd.py
+++ b/acts/framework/acts/controllers/ap_lib/hostapd.py
@@ -61,7 +61,7 @@
Args:
config: Configs to start the hostapd with.
timeout: Time to wait for DHCP server to come up.
- additional_parameters: A dicitonary of parameters that can sent
+ additional_parameters: A dictionary of parameters that can sent
directly into the hostapd config file. This
can be used for debugging and or adding one
off parameters into the config.
@@ -69,7 +69,7 @@
Returns:
True if the daemon could be started. Note that the daemon can still
start and not work. Invalid configurations can take a long amount
- of time to be produced, and because the daemon runs indefinetly
+ of time to be produced, and because the daemon runs indefinitely
it's impossible to wait on. If you need to check if configs are ok
then periodic checks to is_running and logs should be used.
"""
@@ -103,7 +103,7 @@
def is_alive(self):
"""
Returns:
- True if the deamon is running.
+ True if the daemon is running.
"""
return self._shell.is_alive(self._identifier)
@@ -166,8 +166,8 @@
if bad_config:
raise Error('Interface failed to start', self)
- bad_config = self._shell.search_file("Interface %s wasn't started" %
- self._interface, self._log_file)
+ bad_config = self._shell.search_file(
+ "Interface %s wasn't started" % self._interface, self._log_file)
if bad_config:
raise Error('Interface failed to start', self)
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd_constants.py b/acts/framework/acts/controllers/ap_lib/hostapd_constants.py
index 11cfaa5..a190e71 100755
--- a/acts/framework/acts/controllers/ap_lib/hostapd_constants.py
+++ b/acts/framework/acts/controllers/ap_lib/hostapd_constants.py
@@ -59,12 +59,12 @@
2457: 10,
2462: 11,
# 12, 13 are only legitimate outside the US.
- 2467: 12,
- 2472: 13,
+ # 2467: 12,
+ # 2472: 13,
# 14 is for Japan, DSSS and CCK only.
- 2484: 14,
+ # 2484: 14,
# 34 valid in Japan.
- 5170: 34,
+ # 5170: 34,
# 36-116 valid in the US, except 38, 42, and 46, which have
# mixed international support.
5180: 36,
@@ -74,6 +74,7 @@
5220: 44,
5230: 46,
5240: 48,
+ # DFS channels.
5260: 52,
5280: 56,
5300: 60,
@@ -94,8 +95,12 @@
# 144 is supported by a subset of WiFi chips
# (e.g. bcm4354, but not ath9k).
5720: 144,
+ # End DFS channels.
5745: 149,
+ 5755: 151,
5765: 153,
+ 5775: 155,
+ 5795: 159,
5785: 157,
5805: 161,
5825: 165
diff --git a/acts/framework/acts/controllers/arduino_wifi_dongle.py b/acts/framework/acts/controllers/arduino_wifi_dongle.py
new file mode 100644
index 0000000..7978f30
--- /dev/null
+++ b/acts/framework/acts/controllers/arduino_wifi_dongle.py
@@ -0,0 +1,392 @@
+#!/usr/bin/env python3
+#
+# Copyright 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.
+
+import logging
+import os
+import re
+import serial
+import subprocess
+import threading
+import time
+
+from acts import logger
+from acts import signals
+from acts import tracelogger
+from acts import utils
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+from datetime import datetime
+
+ACTS_CONTROLLER_CONFIG_NAME = "ArduinoWifiDongle"
+ACTS_CONTROLLER_REFERENCE_NAME = "arduino_wifi_dongles"
+
+WIFI_DONGLE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
+WIFI_DONGLE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!"
+
+DEV = "/dev/"
+IP = "IP: "
+STATUS = "STATUS: "
+SSID = "SSID: "
+RSSI = "RSSI: "
+PING = "PING: "
+SCAN_BEGIN = "Scan Begin"
+SCAN_END = "Scan End"
+READ_TIMEOUT = 10
+BAUD_RATE = 9600
+TMP_DIR = "tmp/"
+SSID_KEY = wutils.WifiEnums.SSID_KEY
+PWD_KEY = wutils.WifiEnums.PWD_KEY
+
+
+class ArduinoWifiDongleError(signals.ControllerError):
+ pass
+
+class DoesNotExistError(ArduinoWifiDongleError):
+ """Raised when something that does not exist is referenced."""
+
+def create(configs):
+ """Creates ArduinoWifiDongle objects.
+
+ Args:
+ configs: A list of dicts or a list of serial numbers, each representing
+ a configuration of a arduino wifi dongle.
+
+ Returns:
+ A list of Wifi dongle objects.
+ """
+ wcs = []
+ if not configs:
+ raise ArduinoWifiDongleError(WIFI_DONGLE_EMPTY_CONFIG_MSG)
+ elif not isinstance(configs, list):
+ raise ArduinoWifiDongleError(WIFI_DONGLE_NOT_LIST_CONFIG_MSG)
+ elif isinstance(configs[0], str):
+ # Configs is a list of serials.
+ wcs = get_instances(configs)
+ else:
+ # Configs is a list of dicts.
+ wcs = get_instances_with_configs(configs)
+
+ return wcs
+
+def destroy(wcs):
+ for wc in wcs:
+ wc.clean_up()
+
+def get_instances(configs):
+ wcs = []
+ for s in configs:
+ wcs.append(ArduinoWifiDongle(s))
+ return wcs
+
+def get_instances_with_configs(configs):
+ wcs = []
+ for c in configs:
+ try:
+ s = c.pop("serial")
+ except KeyError:
+ raise ArduinoWifiDongleError(
+ "'serial' is missing for ArduinoWifiDongle config %s." % c)
+ wcs.append(ArduinoWifiDongle(s))
+ return wcs
+
+class ArduinoWifiDongle(object):
+ """Class representing an arduino wifi dongle.
+
+ Each object of this class represents one wifi dongle in ACTS.
+
+ Attribtues:
+ serial: Short serial number of the wifi dongle in string.
+ port: The terminal port the dongle is connected to in string.
+ log: A logger adapted from root logger with added token specific to an
+ ArduinoWifiDongle instance.
+ log_file_fd: File handle of the log file.
+ set_logging: Logging for the dongle is enabled when this param is set
+ lock: Lock to acquire and release set_logging variable
+ ssid: SSID of the wifi network the dongle is connected to.
+ ip_addr: IP address on the wifi interface.
+ scan_results: Most recent scan results.
+ ping: Ping status in bool - ping to www.google.com
+ """
+ def __init__(self, serial=''):
+ """Initializes the ArduinoWifiDongle object."""
+ self.serial = serial
+ self.port = self._get_serial_port()
+ self.log = logger.create_tagged_trace_logger(
+ "ArduinoWifiDongle|%s" % self.serial)
+ log_path_base = getattr(logging, "log_path", "/tmp/logs")
+ self.log_file_path = os.path.join(
+ log_path_base, "ArduinoWifiDongle_%s_serial_log.txt" % self.serial)
+ self.log_file_fd = open(self.log_file_path, "a")
+
+ self.set_logging = True
+ self.lock = threading.Lock()
+ self.start_controller_log()
+
+ self.ssid = None
+ self.ip_addr = None
+ self.status = 0
+ self.scan_results = []
+ self.scanning = False
+ self.ping = False
+
+ try:
+ os.stat(TMP_DIR)
+ except:
+ os.mkdir(TMP_DIR)
+
+ def clean_up(self):
+ """Cleans up the ArduinoifiDongle object and releases any resources it
+ claimed.
+ """
+ self.stop_controller_log()
+ self.log_file_fd.close()
+
+ def _get_serial_port(self):
+ """Get the serial port for a given ArduinoWifiDongle serial number.
+
+ Returns:
+ Serial port in string if the dongle is attached.
+ """
+ if not self.serial:
+ raise ArduinoWifiDongleError(
+ "Wifi dongle's serial should not be empty")
+ cmd = "ls %s" % DEV
+ serial_ports = utils.exe_cmd(cmd).decode("utf-8", "ignore").split("\n")
+ for port in serial_ports:
+ if "USB" not in port:
+ continue
+ tty_port = "%s%s" % (DEV, port)
+ cmd = "udevadm info %s" % tty_port
+ udev_output = utils.exe_cmd(cmd).decode("utf-8", "ignore")
+ result = re.search("ID_SERIAL_SHORT=(.*)\n", udev_output)
+ if self.serial == result.group(1):
+ logging.info("Found wifi dongle %s at serial port %s" %
+ (self.serial, tty_port))
+ return tty_port
+ raise ArduinoWifiDongleError("Wifi dongle %s is specified in config"
+ " but is not attached." % self.serial)
+
+ def write(self, arduino, file_path, network=None):
+ """Write an ino file to the arduino wifi dongle.
+
+ Args:
+ arduino: path of the arduino executable.
+ file_path: path of the ino file to flash onto the dongle.
+ network: wifi network to connect to.
+
+ Returns:
+ True: if the write is sucessful.
+ False: if not.
+ """
+ return_result = True
+ self.stop_controller_log("Flashing %s\n" % file_path)
+ cmd = arduino + file_path + " --upload --port " + self.port
+ if network:
+ cmd = self._update_ino_wifi_network(arduino, file_path, network)
+ self.log.info("Command is %s" % cmd)
+ proc = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+ out, err = proc.communicate()
+ return_code = proc.returncode
+ if return_code != 0:
+ self.log.error("Failed to write file %s" % return_code)
+ return_result = False
+ self.start_controller_log("Flashing complete\n")
+ return return_result
+
+ def _update_ino_wifi_network(self, arduino, file_path, network):
+ """Update wifi network in the ino file.
+
+ Args:
+ arduino: path of the arduino executable.
+ file_path: path of the ino file to flash onto the dongle
+ network: wifi network to update the ino file with
+
+ Returns:
+ cmd: arduino command to run to flash the ino file
+ """
+ tmp_file = "%s%s" % (TMP_DIR, file_path.split('/')[-1])
+ utils.exe_cmd("cp %s %s" % (file_path, tmp_file))
+ ssid = network[SSID_KEY]
+ pwd = network[PWD_KEY]
+ sed_cmd = "sed -i 's/\"wifi_tethering_test\"/\"%s\"/' %s" % (ssid, tmp_file)
+ utils.exe_cmd(sed_cmd)
+ sed_cmd = "sed -i 's/\"password\"/\"%s\"/' %s" % (pwd, tmp_file)
+ utils.exe_cmd(sed_cmd)
+ cmd = "%s %s --upload --port %s" %(arduino, tmp_file, self.port)
+ return cmd
+
+ def start_controller_log(self, msg=None):
+ """Reads the serial port and writes the data to ACTS log file.
+
+ This method depends on the logging enabled in the .ino files. The logs
+ are read from the serial port and are written to the ACTS log after
+ adding a timestamp to the data.
+
+ Args:
+ msg: Optional param to write to the log file.
+ """
+ if msg:
+ curr_time = str(datetime.now())
+ self.log_file_fd.write(curr_time + " INFO: " + msg)
+ t = threading.Thread(target=self._start_log)
+ t.daemon = True
+ t.start()
+
+ def stop_controller_log(self, msg=None):
+ """Stop the controller log.
+
+ Args:
+ msg: Optional param to write to the log file.
+ """
+ with self.lock:
+ self.set_logging = False
+ if msg:
+ curr_time = str(datetime.now())
+ self.log_file_fd.write(curr_time + " INFO: " + msg)
+
+ def _start_log(self):
+ """Target method called by start_controller_log().
+
+ This method is called as a daemon thread, which continously reads the
+ serial port. Stops when set_logging is set to False or when the test
+ ends.
+ """
+ self.set_logging = True
+ ser = serial.Serial(self.port, BAUD_RATE)
+ while True:
+ curr_time = str(datetime.now())
+ data = ser.readline().decode("utf-8", "ignore")
+ self._set_vars(data)
+ with self.lock:
+ if not self.set_logging:
+ break
+ self.log_file_fd.write(curr_time + " " + data)
+
+ def _set_vars(self, data):
+ """Sets the variables by reading from the serial port.
+
+ Wifi dongle data such as wifi status, ip address, scan results
+ are read from the serial port and saved inside the class.
+
+ Args:
+ data: New line from the serial port.
+ """
+ # 'data' represents each line retrieved from the device's serial port.
+ # since we depend on the serial port logs to get the attributes of the
+ # dongle, every line has the format of {ino_file: method: param: value}.
+ # We look for the attribute in the log and retrieve its value.
+ # Ex: data = "connect_wifi: loop(): STATUS: 3" then val = "3"
+ # Similarly, we check when the scan has begun and ended and get all the
+ # scan results in between.
+ if data.count(":") != 3:
+ return
+ val = data.split(":")[-1].lstrip().rstrip()
+ if SCAN_BEGIN in data:
+ self.scan_results = []
+ self.scanning = True
+ elif SCAN_END in data:
+ self.scanning = False
+ elif self.scanning:
+ self.scan_results.append(data)
+ elif IP in data:
+ self.ip_addr = None if val == "0.0.0.0" else val
+ elif SSID in data:
+ self.ssid = val
+ elif STATUS in data:
+ self.status = int(val)
+ elif PING in data:
+ self.ping = False if int(val) == 0 else True
+
+ def ip_address(self, exp_result=True, timeout=READ_TIMEOUT):
+ """Get the ip address of the wifi dongle.
+
+ Args:
+ exp_result: True if IP address is expected (wifi connected).
+ timeout: Optional param that specifies the wait time for the IP
+ address to come up on the dongle.
+
+ Returns:
+ IP: addr in string, if wifi connected.
+ None if not connected.
+ """
+ curr_time = time.time()
+ while time.time() < curr_time + timeout:
+ if (exp_result and self.ip_addr) or \
+ (not exp_result and not self.ip_addr):
+ break
+ time.sleep(1)
+ return self.ip_addr
+
+ def wifi_status(self, exp_result=True, timeout=READ_TIMEOUT):
+ """Get wifi status on the dongle.
+
+ Returns:
+ True: if wifi is connected.
+ False: if not connected.
+ """
+ curr_time = time.time()
+ while time.time() < curr_time + timeout:
+ if (exp_result and self.status == 3) or \
+ (not exp_result and not self.status):
+ break
+ time.sleep(1)
+ return self.status == 3
+
+ def wifi_scan(self, exp_result=True, timeout=READ_TIMEOUT):
+ """Get the wifi scan results.
+
+ Args:
+ exp_result: True if scan results are expected.
+ timeout: Optional param that specifies the wait time for the scan
+ results to come up on the dongle.
+
+ Returns:
+ list of dictionaries each with SSID and RSSI of the network
+ found in the scan.
+ """
+ scan_networks = []
+ d = {}
+ curr_time = time.time()
+ while time.time() < curr_time + timeout:
+ if (exp_result and self.scan_results) or \
+ (not exp_result and not self.scan_results):
+ break
+ time.sleep(1)
+ for i in range(len(self.scan_results)):
+ if SSID in self.scan_results[i]:
+ d = {}
+ d[SSID] = self.scan_results[i].split(":")[-1].rstrip()
+ elif RSSI in self.scan_results[i]:
+ d[RSSI] = self.scan_results[i].split(":")[-1].rstrip()
+ scan_networks.append(d)
+
+ return scan_networks
+
+ def ping_status(self, exp_result=True, timeout=READ_TIMEOUT):
+ """ Get ping status on the dongle.
+
+ Returns:
+ True: if ping is successful
+ False: if not successful
+ """
+ curr_time = time.time()
+ while time.time() < curr_time + timeout:
+ if (exp_result and self.ping) or \
+ (not exp_result and not self.ping):
+ break
+ time.sleep(1)
+ return self.ping
diff --git a/acts/framework/acts/controllers/attenuator.py b/acts/framework/acts/controllers/attenuator.py
index 38457a6..1864b81 100644
--- a/acts/framework/acts/controllers/attenuator.py
+++ b/acts/framework/acts/controllers/attenuator.py
@@ -18,9 +18,11 @@
import logging
from acts.keys import Config
+from acts.libs.proc import job
ACTS_CONTROLLER_CONFIG_NAME = "Attenuator"
ACTS_CONTROLLER_REFERENCE_NAME = "attenuators"
+_ATTENUATOR_OPEN_RETRIES = 3
def create(configs):
@@ -35,8 +37,23 @@
inst_cnt = c["InstrumentCount"]
attn_inst = module.AttenuatorInstrument(inst_cnt)
attn_inst.model = attn_model
- insts = attn_inst.open(c[Config.key_address.value],
- c[Config.key_port.value])
+ for attempt_number in range(1, _ATTENUATOR_OPEN_RETRIES + 1):
+ try:
+ insts = attn_inst.open(c[Config.key_address.value],
+ c[Config.key_port.value])
+ except Exception as e:
+ logging.error('Attempt %s to open connection to attenuator '
+ 'failed: %s' % (attempt_number, e))
+ if attempt_number == _ATTENUATOR_OPEN_RETRIES:
+ ping_output = job.run(
+ 'ping %s -c 1 -w 1' % c[Config.key_address.value])
+ if ping_output.exit_status == 1:
+ logging.error('Unable to ping attenuator at %s' %
+ c[Config.key_address.value])
+ else:
+ logging.error('Able to ping attenuator at %s' %
+ c[Config.key_address.value])
+ raise
for i in range(inst_cnt):
attn = Attenuator(attn_inst, idx=i)
if "Paths" in c:
@@ -50,7 +67,8 @@
def destroy(objs):
- return
+ for attn in objs:
+ attn.instrument.close()
r"""
diff --git a/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py b/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py
index 2e98ec6..22b0318 100644
--- a/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py
+++ b/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py
@@ -20,7 +20,7 @@
User code shouldn't need to directly access this class.
"""
-
+import logging
import telnetlib
from acts.controllers import attenuator
@@ -44,7 +44,7 @@
def open(self, host, port=23):
if self._tn:
self._tn.close()
-
+ logging.debug("Attenuator IP = %s" % host)
self._tn = telnetlib.Telnet()
self._tn.open(host, port, 10)
diff --git a/acts/framework/acts/controllers/chameleon_controller.py b/acts/framework/acts/controllers/chameleon_controller.py
new file mode 100644
index 0000000..e4b50e7
--- /dev/null
+++ b/acts/framework/acts/controllers/chameleon_controller.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - 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.
+
+import logging
+import time
+import xmlrpc.client
+from subprocess import call
+
+from acts import signals
+
+ACTS_CONTROLLER_CONFIG_NAME = "ChameleonDevice"
+ACTS_CONTROLLER_REFERENCE_NAME = "chameleon_devices"
+
+CHAMELEON_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
+CHAMELEON_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!"
+
+audio_bus_endpoints = {
+ 'CROS_HEADPHONE': 'Cros device headphone',
+ 'CROS_EXTERNAL_MICROPHONE': 'Cros device external microphone',
+ 'PERIPHERAL_MICROPHONE': 'Peripheral microphone',
+ 'PERIPHERAL_SPEAKER': 'Peripheral speaker',
+ 'FPGA_LINEOUT': 'Chameleon FPGA line-out',
+ 'FPGA_LINEIN': 'Chameleon FPGA line-in',
+ 'BLUETOOTH_OUTPUT': 'Bluetooth module output',
+ 'BLUETOOTH_INPUT': 'Bluetooth module input'
+}
+
+
+class ChameleonDeviceError(signals.ControllerError):
+ pass
+
+
+def create(configs):
+ if not configs:
+ raise ChameleonDeviceError(CHAMELEON_DEVICE_EMPTY_CONFIG_MSG)
+ elif not isinstance(configs, list):
+ raise ChameleonDeviceError(CHAMELEON_DEVICE_NOT_LIST_CONFIG_MSG)
+ elif isinstance(configs[0], str):
+ # Configs is a list of IP addresses
+ chameleons = get_instances(configs)
+ return chameleons
+
+
+def destroy(chameleons):
+ for chameleon in chameleons:
+ del chameleon
+
+
+def get_info(chameleons):
+ """Get information on a list of ChameleonDevice objects.
+
+ Args:
+ ads: A list of ChameleonDevice objects.
+
+ Returns:
+ A list of dict, each representing info for ChameleonDevice objects.
+ """
+ device_info = []
+ for chameleon in chameleons:
+ info = {"address": chameleon.address, "port": chameleon.port}
+ device_info.append(info)
+ return device_info
+
+
+def get_instances(ips):
+ """Create ChameleonDevice instances from a list of IPs.
+
+ Args:
+ ips: A list of Chameleon IPs.
+
+ Returns:
+ A list of ChameleonDevice objects.
+ """
+ return [ChameleonDevice(ip) for ip in ips]
+
+
+class ChameleonDevice:
+ """Class representing a Chameleon device.
+
+ Each object of this class represents one Chameleon device in ACTS.
+
+ Attributes:
+ address: The full address to contact the Chameleon device at
+ client: The ServiceProxy of the XMLRPC client.
+ log: A logger object.
+ port: The TCP port number of the Chameleon device.
+ """
+
+ def __init__(self, ip="", port=9992):
+ self.ip = ip
+ self.log = logging.getLogger()
+ self.port = port
+ self.address = "http://{}:{}".format(ip, self.port)
+ try:
+ self.client = xmlrpc.client.ServerProxy(
+ self.address, allow_none=True, verbose=False)
+ except ConnectionRefusedError as err:
+ self.log.exception(
+ "Failed to connect to Chameleon Device at: {}".format(
+ self.address))
+ self.client.Reset()
+
+ def pull_file(self, chameleon_location, destination):
+ """Pulls a file from the Chameleon device. Usually the raw audio file.
+
+ Args:
+ chameleon_location: The path to the file on the Chameleon device
+ destination: The destination to where to pull it locally.
+ """
+ # TODO: (tturney) implement
+ self.log.error("Definition not yet implemented")
+
+ def start_capturing_audio(self, port_id, has_file=True):
+ """Starts capturing audio.
+
+ Args:
+ port_id: The ID of the audio input port.
+ has_file: True for saving audio data to file. False otherwise.
+ """
+ self.client.StartCapturingAudio(port_id, has_file)
+
+ def stop_capturing_audio(self, port_id):
+ """Stops capturing audio.
+
+ Args:
+ port_id: The ID of the audio input port.
+ Returns:
+ List contain the location of the recorded audio and a dictionary
+ of values relating to the raw audio including: file_type, channel,
+ sample_format, and rate.
+ """
+ return self.client.StopCapturingAudio(port_id)
+
+ def audio_board_connect(self, bus_number, endpoint):
+ """Connects an endpoint to an audio bus.
+
+ Args:
+ bus_number: 1 or 2 for audio bus 1 or bus 2.
+ endpoint: An endpoint defined in audio_bus_endpoints.
+ """
+ self.client.AudioBoardConnect(bus_number, endpoint)
+
+ def audio_board_disconnect(self, bus_number, endpoint):
+ """Connects an endpoint to an audio bus.
+
+ Args:
+ bus_number: 1 or 2 for audio bus 1 or bus 2.
+ endpoint: An endpoint defined in audio_bus_endpoints.
+ """
+ self.client.AudioBoardDisconnect(bus_number, endpoint)
+
+ def audio_board_disable_bluetooth(self):
+ """Disables Bluetooth module on audio board."""
+ self.client.AudioBoardDisableBluetooth()
+
+ def audio_board_clear_routes(self, bus_number):
+ """Clears routes on an audio bus.
+
+ Args:
+ bus_number: 1 or 2 for audio bus 1 or bus 2.
+ """
+ self.client.AudioBoardClearRoutes(bus_number)
+
+ def scp(self, source, destination):
+ """Copies files from the Chameleon device to the host machine.
+
+ Args:
+ source: The file path on the Chameleon board.
+ dest: The file path on the host machine.
+ """
+ cmd = "scp root@{}:/{} {}".format(self.ip, source, destination)
+ try:
+ call(cmd.split(" "))
+ except FileNotFoundError as err:
+ self.log.exception("File not found {}".format(source))
diff --git a/acts/framework/acts/controllers/iperf_client.py b/acts/framework/acts/controllers/iperf_client.py
new file mode 100644
index 0000000..2ba3919
--- /dev/null
+++ b/acts/framework/acts/controllers/iperf_client.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - 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.
+"""
+Starts iperf client on host machine.
+"""
+from acts import utils
+import os
+import subprocess
+
+
+class IPerfClient():
+ """Class that handles iperf3 client operations."""
+
+ def __init__(self, port, ip_address, log_path):
+ self.port = port
+ self.log_path = os.path.join(log_path, "iPerf{}".format(self.port))
+ self.iperf_cmd = "iperf3 -c {} -i1".format(ip_address)
+ self.iperf_process = None
+ self.log_files = []
+ self.started = False
+
+ def start(self, extra_args="", tag=""):
+ """Starts iperf client on specified port on host machine.
+
+ Args:
+ extra_args: A string representing extra arguments to start iperf
+ client with.
+ tag: Appended to log file name to identify logs from different
+ iperf runs.
+
+ Returns:
+ full_out_path: Iperf result path.
+ """
+ if self.started:
+ return
+ utils.create_dir(self.log_path)
+ if tag:
+ tag = tag + ','
+ out_file_name = "IPerfClient,{},{}{}.log".format(
+ self.port, tag, len(self.log_files))
+ full_out_path = os.path.join(self.log_path, out_file_name)
+ cmd = "{} {}".format(self.iperf_cmd, extra_args)
+ cmd = cmd.split()
+ with open(full_out_path, "w") as f:
+ subprocess.call(cmd, stdout=f)
+ self.log_files.append(full_out_path)
+ self.started = True
+ return full_out_path
diff --git a/acts/framework/acts/controllers/iperf_server.py b/acts/framework/acts/controllers/iperf_server.py
index c5724ae..9b952c5 100755
--- a/acts/framework/acts/controllers/iperf_server.py
+++ b/acts/framework/acts/controllers/iperf_server.py
@@ -291,13 +291,14 @@
"""
if not self.started:
return
- self.ssh_session.run_async("kill {}".format(str(self.iperf_process)))
+ self.ssh_session.run_async("kill -9 {}".format(
+ str(self.iperf_process)))
iperf_result = self.ssh_session.run(
"cat iperf_server_port{}.log".format(self.port))
with open(self.full_out_path, 'w') as f:
f.write(iperf_result.stdout)
- self.ssh_session.run_async(
- "rm iperf_server_port{}.log".format(self.port))
+ self.ssh_session.run_async("rm iperf_server_port{}.log".format(
+ self.port))
self.started = False
@@ -350,7 +351,7 @@
"""
if not self.started:
return
- self.adb_device.adb.shell("kill {}".format(self.iperf_process))
+ self.adb_device.adb.shell("kill -9 {}".format(self.iperf_process))
iperf_result = self.adb_device.adb.shell(
"cat {}/iperf_server_port{}.log".format(self.adb_log_path,
self.port))
diff --git a/acts/framework/acts/controllers/native.py b/acts/framework/acts/controllers/native.py
index 86b8523..aa70f85 100644
--- a/acts/framework/acts/controllers/native.py
+++ b/acts/framework/acts/controllers/native.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+#!/usr/bin/env python3.4
#
# Copyright (C) 2009 Google Inc.
#
diff --git a/acts/framework/acts/controllers/relay_lib/dongles.py b/acts/framework/acts/controllers/relay_lib/dongles.py
index 518caad..34ab1c4 100644
--- a/acts/framework/acts/controllers/relay_lib/dongles.py
+++ b/acts/framework/acts/controllers/relay_lib/dongles.py
@@ -23,7 +23,7 @@
# Necessary timeout inbetween commands
CMD_TIMEOUT = 1.2
# Pairing mode activation wait time
-PAIRING_MODE_WAIT_TIME = 6
+PAIRING_MODE_WAIT_TIME = 4.5
SINGLE_ACTION_SHORT_WAIT_TIME = 0.6
SINGLE_ACTION_LONG_WAIT_TIME = 2.0
MISSING_RELAY_MSG = 'Relay config for Three button "%s" missing relay "%s".'
diff --git a/acts/framework/acts/controllers/relay_lib/fugu_remote.py b/acts/framework/acts/controllers/relay_lib/fugu_remote.py
index 4a9a4e7..92316b1 100644
--- a/acts/framework/acts/controllers/relay_lib/fugu_remote.py
+++ b/acts/framework/acts/controllers/relay_lib/fugu_remote.py
@@ -68,23 +68,23 @@
Holds down the 'Home' and 'Back' buttons for a little over 5 seconds.
"""
with SynchronizeRelays():
- self.relays[Buttons.HOME.value].set_nc()
- self.relays[Buttons.BACK.value].set_nc()
+ self.hold_down(Buttons.HOME.value)
+ self.hold_down(Buttons.BACK.value)
time.sleep(PAIRING_MODE_WAIT_TIME)
with SynchronizeRelays():
- self.relays[Buttons.HOME.value].set_no()
- self.relays[Buttons.BACK.value].set_no()
+ self.release(Buttons.HOME.value)
+ self.release(Buttons.BACK.value)
def press_play_pause(self):
"""Briefly presses the Play/Pause button."""
- self.relays[Buttons.PLAY_PAUSE.value].set_nc_for()
+ self.press(Buttons.PLAY_PAUSE.value)
def press_home(self):
"""Briefly presses the Home button."""
- self.relays[Buttons.HOME.value].set_nc_for()
+ self.press(Buttons.HOME.value)
def press_back(self):
"""Briefly presses the Back button."""
- self.relays[Buttons.BACK.value].set_nc_for()
+ self.press(Buttons.BACK.value)
diff --git a/acts/framework/acts/controllers/relay_lib/i6s_headset.py b/acts/framework/acts/controllers/relay_lib/i6s_headset.py
new file mode 100644
index 0000000..c1d3dc6
--- /dev/null
+++ b/acts/framework/acts/controllers/relay_lib/i6s_headset.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+#
+# Copyright 2017 - 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.
+
+import enum
+
+from acts.controllers.relay_lib.errors import RelayConfigError
+from acts.controllers.relay_lib.generic_relay_device import GenericRelayDevice
+from acts.controllers.relay_lib.helpers import validate_key
+
+PAIRING_MODE_WAIT_TIME = 3
+WAIT_TIME = 0.1
+MISSING_RELAY_MSG = 'Relay config for i6s Headset "%s" missing relay "%s".'
+
+
+class Buttons(enum.Enum):
+ Power = "Power"
+ Answer_Call = "Answer"
+ Initiate_Call = "Call"
+ Next = 'Next'
+ Previous = "Previous"
+ Play_pause = 'Play_pause'
+ Pair = "Pair"
+ Volume_up = "Volume_up"
+ Volume_down = "Volume_down"
+
+
+class I6sHeadset(GenericRelayDevice):
+
+ def __init__(self, config, relay_rig):
+ GenericRelayDevice.__init__(self, config, relay_rig)
+ self.mac_address = validate_key('mac_address', config, str,
+ 'I6sHeadset')
+ for button in Buttons:
+ self.ensure_config_contains_relay(button.value)
+
+ def setup(self):
+ GenericRelayDevice.setup(self)
+
+ def clean_up(self):
+ """Turns off headset."""
+ self.relays[Buttons.Pair.value].set_no_for(PAIRING_MODE_WAIT_TIME)
+
+ def ensure_config_contains_relay(self, relay_name):
+ """
+ Throws an error if the relay does not exist.
+
+ Args:
+ relay_name:relay_name to be checked.
+ """
+ if relay_name not in self.relays:
+ raise RelayConfigError(MISSING_RELAY_MSG % (self.name, relay_name))
+
+ def pairing_mode(self):
+ """Sets relay in paring mode."""
+ self.relays[Buttons.Pair.value].set_no_for(PAIRING_MODE_WAIT_TIME)
+
+ def power_on(self):
+ """Power on relay."""
+ self.relays[Buttons.Power.value].set_no_for(WAIT_TIME)
+
+ def play_pause(self):
+ """
+ Sets relay to
+ Play state : if there is no A2DP_streaming.
+ Pause state : if there is A2DP_streaming.
+ """
+ self.relays[Buttons.Play_pause.value].set_no_for(WAIT_TIME)
+
+ def skip_next(self):
+ """Skips to next song from relay_device."""
+ self.relays[Buttons.Next.value].set_no_for(WAIT_TIME)
+
+ def skip_previous(self):
+ """Skips to previous song from relay_device."""
+ self.relays[Buttons.Previous.value].set_no_for(WAIT_TIME)
+
+ def volume_up(self):
+ """Increases volume from relay_device."""
+ self.relays[Buttons.Volume_up.value].set_no_for(WAIT_TIME)
+
+ def volume_down(self):
+ """Decreases volume from relay_device."""
+ self.relays[Buttons.Volume_down.value].set_no_for(WAIT_TIME)
+
+ def initiate_call_from_hf(self):
+ """Initiate call from relay device."""
+ for i in range(0, 2):
+ self.relays[Buttons.Initiate_Call.value].set_no_for(WAIT_TIME)
+ return True
+
+ def accept_call(self):
+ """Accepts call from relay device."""
+ self.relays[Buttons.Answer_Call.value].set_no_for(WAIT_TIME)
+ return True
diff --git a/acts/framework/acts/controllers/relay_lib/logitech_headset.py b/acts/framework/acts/controllers/relay_lib/logitech_headset.py
new file mode 100644
index 0000000..fadbc2c
--- /dev/null
+++ b/acts/framework/acts/controllers/relay_lib/logitech_headset.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+#
+# Copyright 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.
+"""
+Device Details:
+https://www.logitech.com/en-in/product/bluetooth-audio-adapter#specification-tabular
+"""
+import enum
+from acts.controllers.relay_lib.errors import RelayConfigError
+from acts.controllers.relay_lib.generic_relay_device import GenericRelayDevice
+from acts.controllers.relay_lib.helpers import validate_key
+
+PAIRING_MODE_WAIT_TIME = 5
+WAIT_TIME = 0.1
+MISSING_RELAY_MSG = 'Relay config for logitech Headset "%s" missing relay "%s".'
+
+
+class Buttons(enum.Enum):
+ Power = "Power"
+ Pair = "Pair"
+
+
+class LogitechAudioReceiver(GenericRelayDevice):
+ def __init__(self, config, relay_rig):
+ GenericRelayDevice.__init__(self, config, relay_rig)
+ self.mac_address = validate_key('mac_address', config, str,
+ 'LogitechAudioReceiver')
+ for button in Buttons:
+ self.ensure_config_contains_relay(button.value)
+
+ def setup(self):
+ GenericRelayDevice.setup(self)
+
+ def clean_up(self):
+ """Sets all relays to their default state (off)."""
+ GenericRelayDevice.clean_up(self)
+
+ def ensure_config_contains_relay(self, relay_name):
+ """
+ Throws an error if the relay does not exist.
+
+ Args:
+ relay_name:relay_name to be checked.
+ """
+ if relay_name not in self.relays:
+ raise RelayConfigError(MISSING_RELAY_MSG % (self.name, relay_name))
+
+ def power_on(self):
+ """Power on relay."""
+ self.relays[Buttons.Power.value].set_nc()
+
+ def pairing_mode(self):
+ """Sets relay in paring mode."""
+ self.relays[Buttons.Pair.value].set_nc()
diff --git a/acts/framework/acts/controllers/relay_lib/rdl_relay_board.py b/acts/framework/acts/controllers/relay_lib/rdl_relay_board.py
new file mode 100644
index 0000000..ad2acf0
--- /dev/null
+++ b/acts/framework/acts/controllers/relay_lib/rdl_relay_board.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 - 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.
+
+from acts.controllers.relay_lib.relay import RelayState
+from acts.controllers.relay_lib.usb_relay_board_base import UsbRelayBoardBase
+from pylibftdi import BitBangDevice
+
+
+class RdlRelayBoard(UsbRelayBoardBase):
+ def set(self, relay_position, value):
+ """Returns the current status of the passed in relay.
+
+ Args:
+ relay_position: Relay position.
+ value: Turn_on or Turn_off the relay for the given relay_position.
+ """
+ with BitBangDevice(self.device) as bb:
+ if value == RelayState.NO:
+ bb.port |= self.address[relay_position]
+ else:
+ bb.port &= ~(self.address[relay_position])
+ self.status_dict[relay_position] = value
diff --git a/acts/framework/acts/controllers/relay_lib/relay_rig.py b/acts/framework/acts/controllers/relay_lib/relay_rig.py
index 3f4ca05..c384acd 100644
--- a/acts/framework/acts/controllers/relay_lib/relay_rig.py
+++ b/acts/framework/acts/controllers/relay_lib/relay_rig.py
@@ -15,10 +15,15 @@
# limitations under the License.
from acts.controllers.relay_lib.errors import RelayConfigError
from acts.controllers.relay_lib.helpers import validate_key
+from acts.controllers.relay_lib.rdl_relay_board import RdlRelayBoard
from acts.controllers.relay_lib.sain_smart_board import SainSmartBoard
+from acts.controllers.relay_lib.sain_smart_8_channel_usb_relay_board import SainSmart8ChannelUsbRelayBoard
from acts.controllers.relay_lib.generic_relay_device import GenericRelayDevice
from acts.controllers.relay_lib.fugu_remote import FuguRemote
+from acts.controllers.relay_lib.i6s_headset import I6sHeadset
+from acts.controllers.relay_lib.logitech_headset import LogitechAudioReceiver
from acts.controllers.relay_lib.sony_xb2_speaker import SonyXB2Speaker
+from acts.controllers.relay_lib.sony_xb20_speaker import SonyXB20Speaker
from acts.controllers.relay_lib.ak_xb10_speaker import AkXB10Speaker
from acts.controllers.relay_lib.dongles import SingleButtonDongle
from acts.controllers.relay_lib.dongles import ThreeButtonDongle
@@ -45,14 +50,22 @@
# A dict of lambdas that instantiate relay board upon invocation.
# The key is the class type name, the value is the lambda.
_board_constructors = {
- 'SainSmartBoard': lambda x: SainSmartBoard(x),
+ 'SainSmartBoard':
+ lambda x: SainSmartBoard(x),
+ 'RdlRelayBoard':
+ lambda x: RdlRelayBoard(x),
+ 'SainSmart8ChannelUsbRelayBoard':
+ lambda x: SainSmart8ChannelUsbRelayBoard(x),
}
# Similar to the dict above, except for devices.
_device_constructors = {
'GenericRelayDevice': lambda x, rig: GenericRelayDevice(x, rig),
'FuguRemote': lambda x, rig: FuguRemote(x, rig),
+ 'I6sHeadset': lambda x, rig: I6sHeadset(x, rig),
+ "LogitechAudioReceiver" :lambda x, rig: LogitechAudioReceiver(x, rig),
'SonyXB2Speaker': lambda x, rig: SonyXB2Speaker(x, rig),
+ 'SonyXB20Speaker': lambda x, rig: SonyXB20Speaker(x, rig),
'AkXB10Speaker': lambda x, rig: AkXB10Speaker(x, rig),
'SingleButtonDongle': lambda x, rig: SingleButtonDongle(x, rig),
'ThreeButtonDongle': lambda x, rig: ThreeButtonDongle(x, rig),
diff --git a/acts/framework/acts/controllers/relay_lib/sain_smart_8_channel_usb_relay_board.py b/acts/framework/acts/controllers/relay_lib/sain_smart_8_channel_usb_relay_board.py
new file mode 100644
index 0000000..5cc5f6f
--- /dev/null
+++ b/acts/framework/acts/controllers/relay_lib/sain_smart_8_channel_usb_relay_board.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+#
+# Copyright 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.
+
+from acts.controllers.relay_lib.relay import RelayState
+from acts.controllers.relay_lib.usb_relay_board_base import UsbRelayBoardBase
+from pylibftdi import BitBangDevice
+""" This library is to control the sainsmart board.
+
+Device:
+ https://www.sainsmart.com/products/8-channel-12v-usb-relay-module
+
+Additional setup steps:
+Change out pip/pip3 and python2.7/3.4 based on python version
+1. pip install pylibftdi
+2. pip install usblib1
+3. sudo apt-get install libftdi-dev
+4. Make this file /etc/udev/rules.d/99-libftdi.rules with root and add the lines below:
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", GROUP="dialout", MODE="0660"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", GROUP="dialout", MODE="0660"
+5. Connect USB relay to computer and power board with necessary connectors
+6. Verify device is found by: python -m pylibftdi.examples.list_devices
+6a. Example output: FTDI:FT245R USB FIFO:A9079L5D
+7. The FIFO value is going to be your device name in the config
+8. Your config should look something like this (note FIFO name is used here):
+
+{
+ "_description": "This is an example skeleton of a ficticious relay.",
+ "testbed": [{
+ "_description": "A testbed with one relay",
+ "name": "relay_test",
+ "RelayDevice": {
+ "boards": [{
+ "type": "SainSmart8ChannelUsbRelayBoard",
+ "name": "ttyUSB0",
+ "device": "A9079L5D"
+ }],
+ "devices": [{
+ "type": "SingleButtonDongle",
+ "name": "aukey",
+ "mac_address": "e9:08:ef:2b:47:a1",
+ "relays": {
+ "Action": "ttyUSB0/1"
+ }
+
+ }]
+ }
+ }],
+ "logpath": "/tmp/logs",
+ "testpaths": ["../tests"]
+}
+"""
+
+
+class SainSmart8ChannelUsbRelayBoard(UsbRelayBoardBase):
+ def set(self, relay_position, value):
+ """Returns the current status of the passed in relay.
+
+ Note that this board acts in reverse of normal relays.
+ EG: NO = NC and NC = NO
+
+ Args:
+ relay_position: Relay position.
+ value: Turn_on or Turn_off the relay for the given relay_position.
+ """
+ with BitBangDevice(self.device) as bb:
+ if value == RelayState.NO:
+ bb.port &= ~(self.address[relay_position])
+ else:
+ bb.port |= self.address[relay_position]
+ self.status_dict[relay_position] = value
diff --git a/acts/framework/acts/controllers/relay_lib/sain_smart_board.py b/acts/framework/acts/controllers/relay_lib/sain_smart_board.py
index 2d27816..a341c03 100644
--- a/acts/framework/acts/controllers/relay_lib/sain_smart_board.py
+++ b/acts/framework/acts/controllers/relay_lib/sain_smart_board.py
@@ -92,6 +92,13 @@
def _sync_status_dict(self):
"""Returns a dictionary of relays and there current state."""
result = self._load_page(self.HIDDEN_STATUS_PAGE)
+ if 'TUX' not in result:
+ raise RelayDeviceConnectionError(
+ 'Sainsmart board with URL %s has not completed initialization '
+ 'after its IP was set, and must be power-cycled to prevent '
+ 'random disconnections. After power-cycling, make sure %s/%s '
+ 'has TUX appear in its output.' %
+ (self.base_url, self.base_url, self.HIDDEN_STATUS_PAGE))
status_string = re.search(r'">([01]*)TUX', result).group(1)
self.status_dict = dict()
diff --git a/acts/framework/acts/controllers/relay_lib/sony_xb20_speaker.py b/acts/framework/acts/controllers/relay_lib/sony_xb20_speaker.py
new file mode 100644
index 0000000..904c404
--- /dev/null
+++ b/acts/framework/acts/controllers/relay_lib/sony_xb20_speaker.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+#
+# Copyright 2017 - 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.
+import time
+import enum
+import logging
+from acts.controllers.relay_lib.generic_relay_device import GenericRelayDevice
+from acts.controllers.relay_lib.errors import RelayConfigError
+from acts.controllers.relay_lib.helpers import validate_key
+
+PAIRING_MODE_WAIT_TIME = 6
+POWER_TOGGLE_WAIT_TIME = 1
+MISSING_RELAY_MSG = 'Relay config for Sony XB20 "%s" missing relay "%s".'
+
+log = logging
+
+class Buttons(enum.Enum):
+ POWER = 'Power'
+
+class SonyXB20Speaker(GenericRelayDevice):
+ """Sony XB20 Bluetooth Speaker model
+
+ Wraps the button presses, as well as the special features like pairing.
+ """
+
+ def __init__(self, config, relay_rig):
+ GenericRelayDevice.__init__(self, config, relay_rig)
+
+ self.mac_address = validate_key('mac_address', config, str, 'sony_xb20')
+
+ for button in Buttons:
+ self.ensure_config_contains_relay(button.value)
+
+ def ensure_config_contains_relay(self, relay_name):
+ """Throws an error if the relay does not exist."""
+ if relay_name not in self.relays:
+ raise RelayConfigError(MISSING_RELAY_MSG % (self.name, relay_name))
+
+ def _hold_button(self, button, seconds):
+ self.hold_down(button.value)
+ time.sleep(seconds)
+ self.release(button.value)
+
+ def power_on(self):
+ self._hold_button(Buttons.POWER, POWER_TOGGLE_WAIT_TIME)
+
+ def power_off(self):
+ self._hold_button(Buttons.POWER, POWER_TOGGLE_WAIT_TIME)
+
+ def enter_pairing_mode(self):
+ self._hold_button(Buttons.POWER, PAIRING_MODE_WAIT_TIME)
+
+ def setup(self):
+ """Sets all relays to their default state (off)."""
+ GenericRelayDevice.setup(self)
+
+ def clean_up(self):
+ """Sets all relays to their default state (off)."""
+ GenericRelayDevice.clean_up(self)
diff --git a/acts/framework/acts/controllers/relay_lib/usb_relay_board_base.py b/acts/framework/acts/controllers/relay_lib/usb_relay_board_base.py
new file mode 100644
index 0000000..bfecaf2
--- /dev/null
+++ b/acts/framework/acts/controllers/relay_lib/usb_relay_board_base.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+#
+# Copyright 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.
+
+from acts.controllers.relay_lib.relay import RelayState
+from acts.controllers.relay_lib.relay_board import RelayBoard
+from pylibftdi import BitBangDevice
+
+
+class UsbRelayBoardBase(RelayBoard):
+
+ VALID_RELAY_POSITIONS = [1, 2, 3, 4, 5, 6, 7, 8]
+ NUM_RELAYS = 8
+
+ def __init__(self, config):
+ self.status_dict = dict()
+ self.device = config["device"]
+ super(UsbRelayBoardBase, self).__init__(config)
+ self.address = {
+ 1: 0x1,
+ 2: 0x2,
+ 3: 0x4,
+ 4: 0x8,
+ 5: 0x10,
+ 6: 0x20,
+ 7: 0x40,
+ 8: 0x80,
+ "select_all": 0xFF
+ }
+
+ def get_relay_position_list(self):
+ return self.VALID_RELAY_POSITIONS
+
+ def test_bit(self, int_type, offset):
+ """Function to get status for the given relay position.
+
+ Args:
+ int_type: Port value for given relay.
+ offset: offset for given Relay_position.
+
+ Returns:
+ returns current status for given relay_position.
+ """
+ mask = 1 << offset
+ return (int_type & mask)
+
+ def _get_relay_state(self, data, relay):
+ """Function to get status for the given relay position.
+
+ Args:
+ data: Port value for given relay.
+ relay: Relay_position.
+
+ Returns:
+ returns current status for given relay_position.
+ """
+ if relay == 1:
+ return self.test_bit(data, 1)
+ if relay == 2:
+ return self.test_bit(data, 3)
+ if relay == 3:
+ return self.test_bit(data, 5)
+ if relay == 4:
+ return self.test_bit(data, 7)
+ if relay == 5:
+ return self.test_bit(data, 2)
+ if relay == 6:
+ return self.test_bit(data, 4)
+ if relay == 7:
+ return self.test_bit(data, 6)
+ if relay == 8:
+ return self.test_bit(data, 8)
+
+ def get_relay_status(self, relay_position):
+ """Get relay status for the given relay position.
+
+ Args:
+ relay_position: Status for given Relay position.
+
+ Returns:
+ returns current status for given relay_position.
+ """
+ with BitBangDevice(self.device) as bb:
+ self.status_dict[relay_position] = self._get_relay_state(
+ bb.port, relay_position)
+ return self.status_dict[relay_position]
+
+ def set(self, relay_position, value):
+ """Returns the current status of the passed in relay.
+
+ Args:
+ relay_position: Relay position.
+ value: Turn_on or Turn_off the relay for the given relay_position.
+ """
+ raise NotImplementedError
diff --git a/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py b/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py
index d55ba38..4b4efea 100644
--- a/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py
+++ b/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py
@@ -22,7 +22,8 @@
from acts.controllers.sl4a_lib import sl4a_session
from acts.controllers.sl4a_lib import error_reporter
-FIND_PORT_RETRIES = 3
+ATTEMPT_INTERVAL = .25
+MAX_WAIT_ON_SERVER_SECONDS = 5
_SL4A_LAUNCH_SERVER_CMD = (
'am startservice -a com.googlecode.android_scripting.action.LAUNCH_SERVER '
@@ -127,7 +128,7 @@
of ADB/device."""
self.error_reporter.create_error_report(self, session, connection)
- def start_sl4a_server(self, device_port, try_interval=.01):
+ def start_sl4a_server(self, device_port, try_interval=ATTEMPT_INTERVAL):
"""Opens a server socket connection on SL4A.
Args:
@@ -146,13 +147,15 @@
# Launch a server through SL4A.
self.adb.shell(_SL4A_LAUNCH_SERVER_CMD % device_port)
- # There is a small chance that the server has not come up yet by the
- # time the launch command has finished. Try to read get the listening
- # port again after a small amount of time.
- for _ in range(FIND_PORT_RETRIES):
+ # There is a chance that the server has not come up yet by the time the
+ # launch command has finished. Try to read get the listening port again
+ # after a small amount of time.
+ time_left = MAX_WAIT_ON_SERVER_SECONDS
+ while time_left > 0:
port = self._get_open_listening_port()
if port is None:
time.sleep(try_interval)
+ time_left -= try_interval
else:
return port
@@ -176,12 +179,15 @@
'server connections cannot be verified.')
return _SL4A_USER_FIND_PORT_CMD
+ def _get_all_ports(self):
+ return self.adb.shell(self._get_all_ports_command()).split()
+
def _get_open_listening_port(self):
"""Returns any open, listening port found for SL4A.
Will return none if no port is found.
"""
- possible_ports = self.adb.shell(self._get_all_ports_command()).split()
+ possible_ports = self._get_all_ports()
self.log.debug('SL4A Ports found: %s' % possible_ports)
# Acquire the lock. We lock this method because if multiple threads
@@ -215,12 +221,14 @@
if self.adb.shell(
'ps | grep "S com.googlecode.android_scripting"'):
# Close all SL4A servers not opened by this manager.
- ports = self.adb.shell(self._get_all_ports_command()).split()
- for port in ports:
- self.adb.shell(_SL4A_CLOSE_SERVER_CMD % port)
- else:
- # Start the service if it is not up already.
- self.adb.shell(_SL4A_START_SERVICE_CMD)
+ # TODO(markdr): revert back to closing all ports after
+ # b/76147680 is resolved.
+ self.adb.shell(
+ 'kill -9 $(pidof com.googlecode.android_scripting)')
+ self.adb.shell(
+ 'settings put global hidden_api_blacklist_exemptions "*"')
+ # Start the service if it is not up already.
+ self.adb.shell(_SL4A_START_SERVICE_CMD)
def obtain_sl4a_server(self, server_port):
"""Obtain an SL4A server port.
@@ -277,6 +285,20 @@
for _, session in self.sessions.items():
session.terminate()
self.sessions = {}
- for port in self._sl4a_ports:
+ self._close_all_ports()
+
+ def _close_all_ports(self, try_interval=ATTEMPT_INTERVAL):
+ """Closes all ports opened on SL4A."""
+ ports = self._get_all_ports()
+ for port in set.union(self._sl4a_ports, ports):
self.adb.shell(_SL4A_CLOSE_SERVER_CMD % port)
+ time_left = MAX_WAIT_ON_SERVER_SECONDS
+ while time_left > 0 and self._get_open_listening_port():
+ time.sleep(try_interval)
+ time_left -= try_interval
+
+ if time_left <= 0:
+ self.log.warning(
+ 'Unable to close all un-managed servers! Server ports that are '
+ 'still open are %s' % self._get_open_listening_port())
self._sl4a_ports = set()
diff --git a/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py b/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py
index e36754f..074d9d8 100644
--- a/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py
+++ b/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py
@@ -172,7 +172,7 @@
# Verify and obtain the port opened by SL4A.
try:
# Connect to the port that has been forwarded to the device.
- client_socket.connect(('localhost', ports.forwarded_port))
+ client_socket.connect(('127.0.0.1', ports.forwarded_port))
except socket.timeout:
raise rpc_client.Sl4aConnectionError(
'SL4A has not connected over the specified port within the '
diff --git a/acts/framework/acts/controllers/sniffer_lib/local/tcpdump.py b/acts/framework/acts/controllers/sniffer_lib/local/tcpdump.py
index a633c3d..a464e8e 100644
--- a/acts/framework/acts/controllers/sniffer_lib/local/tcpdump.py
+++ b/acts/framework/acts/controllers/sniffer_lib/local/tcpdump.py
@@ -18,6 +18,7 @@
from acts.controllers import sniffer
from acts.controllers.sniffer_lib.local import local_base
+
class Sniffer(local_base.SnifferLocalBase):
"""This class defines a sniffer which uses tcpdump as its back-end
"""
@@ -27,12 +28,13 @@
"""
self._executable_path = None
- super().__init__(config_path, logger, base_configs=base_configs)
+ super(local_base.SnifferLocalBase).__init__(
+ config_path, logger, base_configs=base_configs)
self._executable_path = shutil.which("tcpdump")
if self._executable_path is None:
raise sniffer.SnifferError(
- "Cannot find a path to the 'tcpdump' executable")
+ "Cannot find a path to the 'tcpdump' executable")
def get_descriptor(self):
"""See base class documentation
@@ -44,7 +46,9 @@
"""
return "tcpdump"
- def _get_command_line(self, additional_args=None, duration=None,
+ def _get_command_line(self,
+ additional_args=None,
+ duration=None,
packet_count=None):
cmd = "{} -i {} -w {}".format(self._executable_path, self._interface,
self._temp_capture_file_path)
diff --git a/acts/framework/acts/controllers/utils_lib/commands/ip.py b/acts/framework/acts/controllers/utils_lib/commands/ip.py
index 0c1c025..9dc98b3 100644
--- a/acts/framework/acts/controllers/utils_lib/commands/ip.py
+++ b/acts/framework/acts/controllers/utils_lib/commands/ip.py
@@ -15,6 +15,8 @@
import ipaddress
import re
+from acts.libs.proc import job
+
class LinuxIpCommand(object):
"""Interface for doing standard IP commands on a linux system.
@@ -87,7 +89,7 @@
self._runner.run('ip addr add %s dev %s' %
(address, net_interface))
- def remove_ipv4_address(self, net_interface, address):
+ def remove_ipv4_address(self, net_interface, address, ignore_status=False):
"""Remove an ipv4 address.
Removes an ipv4 address from a network interface.
@@ -97,8 +99,13 @@
ipv4 address from (eg. wlan0).
address: ipaddress.IPv4Interface or ipaddress.IPv4Address,
The ip address to remove from the net_interface.
+ ignore_status: True if the exit status can be ignored
+ Returns:
+ The job result from a the command
"""
- self._runner.run('ip addr del %s dev %s' % (address, net_interface))
+ return self._runner.run(
+ 'ip addr del %s dev %s' % (address, net_interface),
+ ignore_status=ignore_status)
def set_ipv4_address(self, net_interface, address, broadcast=None):
"""Set the ipv4 address.
@@ -127,4 +134,23 @@
ip_info = self.get_ipv4_addresses(net_interface)
for address, _ in ip_info:
- self.remove_ipv4_address(net_interface, address)
+ result = self.remove_ipv4_address(net_interface, address,
+ ignore_status=True)
+ # It is possible that the address has already been removed by the
+ # time this command has been called. In such a case, we would get
+ # this error message.
+ error_msg = 'RTNETLINK answers: Cannot assign requested address'
+ if result.exit_status != 0:
+ if error_msg in result.stderr:
+ # If it was removed by another process, log a warning
+ if address not in self.get_ipv4_addresses(net_interface):
+ self._runner.log.warning(
+ 'Unable to remove address %s. The address was '
+ 'removed by another process.' % address)
+ continue
+ # If it was not removed, raise an error
+ self._runner.log.error(
+ 'Unable to remove address %s. The address is still '
+ 'registered to %s, despite call for removal.' %
+ (address, net_interface))
+ raise job.Error(result)
diff --git a/acts/framework/acts/controllers/utils_lib/commands/shell.py b/acts/framework/acts/controllers/utils_lib/commands/shell.py
index eee65e1..ac232da 100644
--- a/acts/framework/acts/controllers/utils_lib/commands/shell.py
+++ b/acts/framework/acts/controllers/utils_lib/commands/shell.py
@@ -130,8 +130,8 @@
True if the string or pattern was found, False otherwise.
"""
try:
- self.run('grep %s %s' %
- (shellescape.quote(search_string), file_name))
+ self.run('grep %s %s' % (shellescape.quote(search_string),
+ file_name))
return True
except job.Error:
return False
@@ -195,7 +195,7 @@
that match the identifier until either all are dead or the timeout
finishes.
- Programs are guranteed to be killed after running this command.
+ Programs are guaranteed to be killed after running this command.
Args:
identifier: A string used to identify the program.
@@ -230,7 +230,7 @@
Args:
pid: The process id of the program to kill.
- sig: The singal to send.
+ sig: The signal to send.
Raises:
job.Error: Raised when the signal fail to reach
diff --git a/acts/framework/acts/controllers/utils_lib/ssh/connection.py b/acts/framework/acts/controllers/utils_lib/ssh/connection.py
index 9d238da..b969e00 100644
--- a/acts/framework/acts/controllers/utils_lib/ssh/connection.py
+++ b/acts/framework/acts/controllers/utils_lib/ssh/connection.py
@@ -13,7 +13,6 @@
# limitations under the License.
import collections
-import logging
import os
import re
import shutil
@@ -22,17 +21,18 @@
import time
import uuid
+from acts import logger
from acts.controllers.utils_lib import host_utils
from acts.controllers.utils_lib.ssh import formatter
from acts.libs.proc import job
class Error(Exception):
- """An error occured during an ssh operation."""
+ """An error occurred during an ssh operation."""
class CommandError(Exception):
- """An error occured with the command.
+ """An error occurred with the command.
Attributes:
result: The results of the ssh command that had the error.
@@ -46,8 +46,9 @@
self.result = result
def __str__(self):
- return 'cmd: %s\nstdout: %s\nstderr: %s' % (
- self.result.command, self.result.stdout, self.result.stderr)
+ return 'cmd: %s\nstdout: %s\nstderr: %s' % (self.result.command,
+ self.result.stdout,
+ self.result.stderr)
_Tunnel = collections.namedtuple('_Tunnel',
@@ -71,7 +72,7 @@
def __init__(self, settings):
"""
Args:
- settings: The ssh settings to use for this conneciton.
+ settings: The ssh settings to use for this connection.
formatter: The object that will handle formatting ssh command
for use with the background job.
"""
@@ -82,17 +83,23 @@
self._master_ssh_tempdir = None
self._tunnels = list()
+ def log_line(msg):
+ return '[SshConnection | %s] %s' % (self._settings.hostname, msg)
+
+ self.log = logger.create_logger(log_line)
+
def __del__(self):
self.close()
def setup_master_ssh(self, timeout_seconds=5):
"""Sets up the master ssh connection.
- Sets up the inital master ssh connection if it has not already been
+ Sets up the initial master ssh connection if it has not already been
started.
Args:
- timeout_seconds: The time to wait for the master ssh connection to be made.
+ timeout_seconds: The time to wait for the master ssh connection to
+ be made.
Raises:
Error: When setting up the master ssh connection fails.
@@ -102,8 +109,8 @@
socket_path = self.socket_path
if (not os.path.exists(socket_path) or
self._master_ssh_proc.poll() is not None):
- logging.debug('Master ssh connection to %s is down.',
- self._settings.hostname)
+ self.log.debug('Master ssh connection to %s is down.',
+ self._settings.hostname)
self._cleanup_master_ssh()
if self._master_ssh_proc is None:
@@ -127,8 +134,7 @@
self._settings,
extra_flags=extra_flags,
extra_options=extra_options)
- logging.info('Starting master ssh connection to %s',
- self._settings.hostname)
+ self.log.info('Starting master ssh connection.')
self._master_ssh_proc = job.run_async(master_cmd)
end_time = time.time() + timeout_seconds
@@ -146,7 +152,8 @@
timeout=3600,
ignore_status=False,
env=None,
- io_encoding='utf-8'):
+ io_encoding='utf-8',
+ attempts=2):
"""Runs a remote command over ssh.
Will ssh to a remote host and run a command. This method will
@@ -159,8 +166,9 @@
ignore_status: bool True to ignore the exit code of the remote
subprocess. Note that if you do ignore status codes,
you should handle non-zero exit codes explicitly.
- env: dict enviroment variables to setup on the remote host.
+ env: dict environment variables to setup on the remote host.
io_encoding: str unicode encoding of command output.
+ attempts: Number of attempts before giving up on command failures.
Returns:
A job.Result containing the results of the ssh command.
@@ -170,14 +178,16 @@
Error: When the ssh connection failed to be created.
CommandError: Ssh worked, but the command had an error executing.
"""
+ if attempts == 0:
+ return None
if env is None:
env = {}
try:
self.setup_master_ssh(self._settings.connect_timeout)
except Error:
- logging.warning('Failed to create master ssh connection, using '
- 'normal ssh connection.')
+ self.log.warning('Failed to create master ssh connection, using '
+ 'normal ssh connection.')
extra_options = {'BatchMode': True}
if self._master_ssh_proc:
@@ -191,9 +201,8 @@
dns_retry_count = 2
while True:
- result = job.run(terminal_command,
- ignore_status=True,
- timeout=timeout)
+ result = job.run(
+ terminal_command, ignore_status=True, timeout=timeout)
output = result.stdout
# Check for a connected message to prevent false negatives.
@@ -212,7 +221,7 @@
duration=result.duration,
did_timeout=result.did_timeout,
encoding=result._encoding)
- if result.exit_status:
+ if result.exit_status and not ignore_status:
raise job.Error(result)
return result
@@ -226,7 +235,7 @@
dns_retry_count -= 1
if not dns_retry_count:
raise Error('DNS failed to find host.', result)
- logging.debug('Failed to connecto to host, retrying...')
+ self.log.debug('Failed to connect to host, retrying...')
else:
break
@@ -250,7 +259,15 @@
if unknown_host:
raise Error('Unknown host.', result)
- raise Error('The job failed for unkown reasons.', result)
+ self.log.error('An unknown error has occurred. Job result: %s' % result)
+ ping_output = job.run(
+ 'ping %s -c 3 -w 1' % self._settings.hostname, ignore_status=True)
+ self.log.error('Ping result: %s' % ping_output)
+ if attempts > 1:
+ self._cleanup_master_ssh()
+ self.run(command, timeout, ignore_status, env, io_encoding,
+ attempts - 1)
+ raise Error('The job failed for unknown reasons.', result)
def run_async(self, command, env=None):
"""Starts up a background command over ssh.
@@ -261,7 +278,7 @@
Args:
command: The command to execute over ssh. Can be either a string
or a list.
- env: A dictonary of enviroment variables to setup on the remote
+ env: A dictonary of environment variables to setup on the remote
host.
Returns:
@@ -290,14 +307,14 @@
"""
# If a master SSH connection is running, kill it.
if self._master_ssh_proc is not None:
- logging.debug('Nuking master_ssh_job.')
+ self.log.debug('Nuking master_ssh_job.')
self._master_ssh_proc.kill()
self._master_ssh_proc.wait()
self._master_ssh_proc = None
# Remove the temporary directory for the master SSH socket.
if self._master_ssh_tempdir is not None:
- logging.debug('Cleaning master_ssh_tempdir.')
+ self.log.debug('Cleaning master_ssh_tempdir.')
shutil.rmtree(self._master_ssh_tempdir)
self._master_ssh_tempdir = None
@@ -335,13 +352,13 @@
self._settings,
extra_flags=extra_flags,
extra_options=extra_options)
- logging.debug('Full tunnel command: %s', tunnel_cmd)
+ self.log.debug('Full tunnel command: %s', tunnel_cmd)
# Exec the ssh process directly so that when we deliver signals, we
# deliver them straight to the child process.
tunnel_proc = job.run_async(tunnel_cmd)
- logging.debug('Started ssh tunnel, local = %d'
- ' remote = %d, pid = %d', local_port, port,
- tunnel_proc.pid)
+ self.log.debug('Started ssh tunnel, local = %d'
+ ' remote = %d, pid = %d', local_port, port,
+ tunnel_proc.pid)
self._tunnels.append(_Tunnel(local_port, port, tunnel_proc))
return local_port
@@ -394,9 +411,9 @@
"""
# TODO: This may belong somewhere else: b/3257251
free_port_cmd = (
- 'python -c "import socket; s=socket.socket(); '
- 's.bind((\'%s\', 0)); print(s.getsockname()[1]); s.close()"'
- ) % interface_name
+ 'python -c "import socket; s=socket.socket(); '
+ 's.bind((\'%s\', 0)); print(s.getsockname()[1]); s.close()"'
+ ) % interface_name
port = int(self.run(free_port_cmd).stdout)
# Yield to the os to ensure the port gets cleaned up.
time.sleep(0.001)
diff --git a/acts/framework/acts/jsonrpc.py b/acts/framework/acts/jsonrpc.py
deleted file mode 100644
index 944a96f..0000000
--- a/acts/framework/acts/jsonrpc.py
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2016- Google, Inc.
-#
-# 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.
-"""
-A simple JSON RPC client.
-"""
-import json
-import time
-from urllib import request
-
-
-class HTTPError(Exception):
- pass
-
-
-class RemoteError(Exception):
- pass
-
-
-def JSONCounter():
- """A counter that generates JSON RPC call IDs.
-
- Follows the increasing integer sequence. Every time this function is
- called, the next number in the sequence is returned.
- """
- i = 0
- while True:
- yield i
- i += 1
-
-
-class JSONRPCClient:
- COUNTER = JSONCounter()
- headers = {'content-type': 'application/json'}
-
- def __init__(self, baseurl):
- self._baseurl = baseurl
-
- def call(self, path, methodname=None, *args):
- """Wrapper for the internal _call method.
-
- A retry is performed if the initial call fails to compensate for
- unstable networks.
-
- Params:
- path: Path of the rpc service to be appended to the base url.
- methodname: Method name of the RPC call.
- args: A tuple of arguments for the RPC call.
-
- Returns:
- The returned message of the JSON RPC call from the server.
- """
- try:
- return self._call(path, methodname, *args)
- except:
- # Take five and try again
- time.sleep(5)
- return self._call(path, methodname, *args)
-
- def _post_json(self, url, payload):
- """Performs an HTTP POST request with a JSON payload.
-
- Params:
- url: The full URL to post the payload to.
- payload: A JSON string to be posted to server.
-
- Returns:
- The HTTP response code and text.
- """
- req = request.Request(url)
- req.add_header('Content-Type', 'application/json')
- resp = request.urlopen(req, data=payload.encode("utf-8"))
- txt = resp.read()
- return resp.code, txt.decode('utf-8')
-
- def _call(self, path, methodname=None, *args):
- """Performs a JSON RPC call and return the response.
-
- Params:
- path: Path of the rpc service to be appended to the base url.
- methodname: Method name of the RPC call.
- args: A tuple of arguments for the RPC call.
-
- Returns:
- The returned message of the JSON RPC call from the server.
-
- Raises:
- HTTPError: Raised if the http post return code is not 200.
- RemoteError: Raised if server returned an error.
- """
- jsonid = next(JSONRPCClient.COUNTER)
- payload = json.dumps({"method": methodname,
- "params": args,
- "id": jsonid})
- url = self._baseurl + path
- status_code, text = self._post_json(url, payload)
- if status_code != 200:
- raise HTTPError(text)
- r = json.loads(text)
- if r['error']:
- raise RemoteError(r['error'])
- return r['result']
-
- def sys(self, *args):
- return self.call("sys", *args)
-
- def __getattr__(self, name):
- def rpc_call(*args):
- return self.call('uci', name, *args)
-
- return rpc_call
diff --git a/acts/framework/acts/keys.py b/acts/framework/acts/keys.py
index 695f8e4..a0aa52a 100644
--- a/acts/framework/acts/keys.py
+++ b/acts/framework/acts/keys.py
@@ -35,8 +35,10 @@
key_address = "Address"
key_random = "random"
key_test_case_iterations = "test_case_iterations"
+ key_test_failure_tracebacks = "test_failure_tracebacks"
# Config names for controllers packaged in ACTS.
key_android_device = "AndroidDevice"
+ key_chameleon_device = "ChameleonDevice"
key_native_android_device = "NativeAndroidDevice"
key_relay_device = "RelayDevice"
key_access_point = "AccessPoint"
@@ -45,6 +47,7 @@
key_packet_sender = "PacketSender"
key_monsoon = "Monsoon"
key_sniffer = "Sniffer"
+ key_arduino_wifi_dongle = "ArduinoWifiDongle"
# Internal keys, used internally, not exposed to user's config files.
ikey_user_param = "user_params"
ikey_testbed_name = "testbed_name"
@@ -54,6 +57,7 @@
# module name of controllers packaged in ACTS.
m_key_monsoon = "monsoon"
m_key_android_device = "android_device"
+ m_key_chameleon_device = "chameleon_controller"
m_key_native_android_device = "native_android_device"
m_key_relay_device = "relay_device_controller"
m_key_access_point = "access_point"
@@ -61,6 +65,7 @@
m_key_iperf_server = "iperf_server"
m_key_packet_sender = "packet_sender"
m_key_sniffer = "sniffer"
+ m_key_arduino_wifi_dongle = "arduino_wifi_dongle"
# A list of keys whose values in configs should not be passed to test
# classes without unpacking first.
@@ -68,9 +73,17 @@
# Controller names packaged with ACTS.
builtin_controller_names = [
- key_android_device, key_native_android_device, key_relay_device,
- key_access_point, key_attenuator, key_iperf_server, key_packet_sender,
- key_monsoon, key_sniffer
+ key_android_device,
+ key_native_android_device,
+ key_relay_device,
+ key_access_point,
+ key_attenuator,
+ key_iperf_server,
+ key_packet_sender,
+ key_monsoon,
+ key_sniffer,
+ key_chameleon_device,
+ key_arduino_wifi_dongle,
]
# Keys that are file or folder paths.
diff --git a/acts/framework/acts/libs/ota/ota_runners/ota_runner.py b/acts/framework/acts/libs/ota/ota_runners/ota_runner.py
index 45bc4c6..7e1c4ca 100644
--- a/acts/framework/acts/libs/ota/ota_runners/ota_runner.py
+++ b/acts/framework/acts/libs/ota/ota_runners/ota_runner.py
@@ -50,12 +50,13 @@
self.android_device.root_adb()
log.info('Root complete.')
if self.android_device.skip_sl4a:
- self.android_device.log.info("Skipping SL4A install.")
+ self.android_device.log.info('Skipping SL4A install.')
else:
for _ in range(3):
- self.android_device.log.info("Re-installing SL4A.")
+ self.android_device.log.info('Re-installing SL4A from "%s".',
+ self.get_sl4a_apk())
self.android_device.adb.install(
- "-r -g %s" % self.get_sl4a_apk(), ignore_status=True)
+ '-r -g %s' % self.get_sl4a_apk(), ignore_status=True)
time.sleep(SL4A_SERVICE_SETUP_TIME)
if self.android_device.is_sl4a_installed():
break
diff --git a/acts/framework/acts/libs/ota/ota_updater.py b/acts/framework/acts/libs/ota/ota_updater.py
index ed300aa..a43c9ce 100644
--- a/acts/framework/acts/libs/ota/ota_updater.py
+++ b/acts/framework/acts/libs/ota/ota_updater.py
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from acts import utils
from acts.libs.ota.ota_runners import ota_runner_factory
# Maps AndroidDevices to OtaRunners
@@ -54,10 +55,13 @@
_check_initialization(android_device)
try:
ota_runners[android_device].update()
- except:
+ except Exception as e:
if ignore_update_errors:
return
- raise
+ android_device.log.error(e)
+ android_device.take_bug_report('ota_update',
+ utils.get_current_epoch_time())
+ raise e
def can_update(android_device):
diff --git a/acts/framework/acts/libs/proc/job.py b/acts/framework/acts/libs/proc/job.py
index 84c4993..472e86b 100644
--- a/acts/framework/acts/libs/proc/job.py
+++ b/acts/framework/acts/libs/proc/job.py
@@ -192,10 +192,13 @@
A subprocess.Popen object representing the created subprocess.
"""
- return subprocess.Popen(
+ proc = subprocess.Popen(
command,
env=env,
- close_fds=True,
+ preexec_fn=os.setpgrp,
shell=not isinstance(command, list),
stdout=DEVNULL,
stderr=subprocess.STDOUT)
+ logging.debug("command %s started with pid %s", command, proc.pid)
+ return proc
+
diff --git a/acts/framework/acts/logger.py b/acts/framework/acts/logger.py
index 92f1e5e..2f94f83 100755
--- a/acts/framework/acts/logger.py
+++ b/acts/framework/acts/logger.py
@@ -255,3 +255,17 @@
>>> lambda log_message: return 'string'
"""
return tracelogger.TraceLogger(LoggerAdapter(logging_lambda))
+
+
+def create_tagged_trace_logger(tag=''):
+ """Returns a logger that logs each line with the given prefix.
+
+ Args:
+ tag: The tag of the log line, E.g. if tag == tag123, the output
+ line would be:
+
+ <TESTBED> <TIME> <LOG_LEVEL> [tag123] logged message
+ """
+ def logging_lambda(msg):
+ return '[%s] %s' % (tag, msg)
+ return create_logger(logging_lambda)
diff --git a/acts/framework/acts/records.py b/acts/framework/acts/records.py
index a36ccb3..7cf94d8 100644
--- a/acts/framework/acts/records.py
+++ b/acts/framework/acts/records.py
@@ -175,10 +175,10 @@
return "%s %s %s" % (t, self.test_name, self.result)
def to_dict(self):
- """Gets a dictionary representating the content of this class.
+ """Gets a dictionary representing the content of this class.
Returns:
- A dictionary representating the content of this class.
+ A dictionary representing the content of this class.
"""
d = {}
d[TestResultEnums.RECORD_NAME] = self.test_name
diff --git a/acts/framework/acts/test_runner.py b/acts/framework/acts/test_runner.py
index 70b3400..fdb9099 100644
--- a/acts/framework/acts/test_runner.py
+++ b/acts/framework/acts/test_runner.py
@@ -18,10 +18,10 @@
standard_library.install_aliases()
-import argparse
import copy
import importlib
import inspect
+import fnmatch
import logging
import os
import pkgutil
@@ -36,81 +36,10 @@
from acts import utils
-def main():
- """Execute the test class in a test module.
-
- This is the default entry point for running a test script file directly.
- In this case, only one test class in a test script is allowed.
-
- To make your test script executable, add the following to your file:
-
- from acts import test_runner
- ...
- if __name__ == "__main__":
- test_runner.main()
-
- If you want to implement your own cli entry point, you could use function
- execute_one_test_class(test_class, test_config, test_identifier)
- """
- # Parse cli args.
- parser = argparse.ArgumentParser(description="ACTS Test Executable.")
- parser.add_argument(
- '-c',
- '--config',
- nargs=1,
- type=str,
- required=True,
- metavar="<PATH>",
- help="Path to the test configuration file.")
- parser.add_argument(
- '--test_case',
- nargs='+',
- type=str,
- metavar="[test_a test_b...]",
- help="A list of test case names in the test script.")
- parser.add_argument(
- '-tb',
- '--test_bed',
- nargs='+',
- type=str,
- metavar="[<TEST BED NAME1> <TEST BED NAME2> ...]",
- help="Specify which test beds to run tests on.")
- args = parser.parse_args(sys.argv[1:])
- # Load test config file.
- test_configs = config_parser.load_test_config_file(args.config[0],
- args.test_bed)
- # Find the test class in the test script.
- test_class = _find_test_class()
- test_class_name = test_class.__name__
- # Parse test case specifiers if exist.
- test_case_names = None
- if args.test_case:
- test_case_names = args.test_case
- test_identifier = [(test_class_name, test_case_names)]
- # Execute the test class with configs.
- ok = True
- for config in test_configs:
- try:
- result = execute_one_test_class(test_class, config,
- test_identifier)
- if not result:
- logging.error(
- 'Results for config %s have returned empty.' % config)
- ok = result and ok
- except signals.TestAbortAll:
- pass
- except:
- logging.exception("Error occurred when executing test bed %s",
- config[keys.Config.key_testbed.value])
- ok = False
- if not ok:
- sys.exit(1)
-
-
def _find_test_class():
"""Finds the test class in a test script.
- Walk through module memebers and find the subclass of BaseTestClass. Only
+ Walk through module members and find the subclass of BaseTestClass. Only
one subclass is allowed in a test script.
Returns:
@@ -133,7 +62,7 @@
"""Executes one specific test class.
You could call this function in your own cli test entry point if you choose
- not to use act.py or test_runner.main.
+ not to use act.py.
Args:
test_class: A subclass of acts.base_test.BaseTestClass that has the test
@@ -178,7 +107,8 @@
self.controller_registry: A dictionary that holds the controller
objects used in a test run.
self.test_classes: A dictionary where we can look up the test classes
- by name to instantiate.
+ by name to instantiate. Supports unix shell style
+ wildcards.
self.run_list: A list of tuples specifying what tests to run.
self.results: The test result object used to record the results of
this test run.
@@ -537,9 +467,8 @@
ValueError is raised if the requested test class could not be found
in the test_paths directories.
"""
- try:
- test_cls = self.test_classes[test_cls_name]
- except KeyError:
+ matches = fnmatch.filter(self.test_classes.keys(), test_cls_name)
+ if not matches:
self.log.info(
"Cannot find test class %s or classes matching pattern, "
"skipping for now." % test_cls_name)
@@ -547,22 +476,29 @@
record.test_skip(signals.TestSkip("Test class does not exist."))
self.results.add_record(record)
return
- if self.test_configs.get(keys.Config.key_random.value) or (
- "Preflight" in test_cls_name) or "Postflight" in test_cls_name:
- test_case_iterations = 1
- else:
- test_case_iterations = self.test_configs.get(
- keys.Config.key_test_case_iterations.value, 1)
+ if matches != [test_cls_name]:
+ self.log.info("Found classes matching pattern %s: %s",
+ test_cls_name, matches)
- with test_cls(self.test_run_info) as test_cls_instance:
- try:
- cls_result = test_cls_instance.run(test_cases,
- test_case_iterations)
- self.results += cls_result
- self._write_results_json_str()
- except signals.TestAbortAll as e:
- self.results += e.results
- raise e
+ for test_cls_name_match in matches:
+ test_cls = self.test_classes[test_cls_name_match]
+ if self.test_configs.get(keys.Config.key_random.value) or (
+ "Preflight" in test_cls_name_match) or (
+ "Postflight" in test_cls_name_match):
+ test_case_iterations = 1
+ else:
+ test_case_iterations = self.test_configs.get(
+ keys.Config.key_test_case_iterations.value, 1)
+
+ with test_cls(self.test_run_info) as test_cls_instance:
+ try:
+ cls_result = test_cls_instance.run(test_cases,
+ test_case_iterations)
+ self.results += cls_result
+ self._write_results_json_str()
+ except signals.TestAbortAll as e:
+ self.results += e.results
+ raise e
def run(self, test_class=None):
"""Executes test cases.
@@ -593,6 +529,7 @@
for test_cls_name, test_case_names in self.run_list:
if not self.running:
break
+
if test_case_names:
self.log.debug("Executing test cases %s in test class %s.",
test_case_names, test_cls_name)
diff --git a/acts/framework/acts/test_utils/audio_analysis_lib/__init__.py b/acts/framework/acts/test_utils/audio_analysis_lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/test_utils/audio_analysis_lib/__init__.py
diff --git a/acts/framework/acts/test_utils/audio_analysis_lib/audio_analysis.py b/acts/framework/acts/test_utils/audio_analysis_lib/audio_analysis.py
new file mode 100644
index 0000000..8450601
--- /dev/null
+++ b/acts/framework/acts/test_utils/audio_analysis_lib/audio_analysis.py
@@ -0,0 +1,436 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - 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.
+"""This module provides utilities to do audio data analysis."""
+
+import logging
+import numpy
+import operator
+
+# The default block size of pattern matching.
+ANOMALY_DETECTION_BLOCK_SIZE = 120
+
+# Only peaks with coefficient greater than 0.01 of the first peak should be
+# considered. Note that this correspond to -40dB in the spectrum.
+DEFAULT_MIN_PEAK_RATIO = 0.01
+
+# The minimum RMS value of meaningful audio data.
+MEANINGFUL_RMS_THRESHOLD = 0.001
+
+# The minimal signal norm value.
+_MINIMUM_SIGNAL_NORM = 0.001
+
+# The default pattern mathing threshold. By experiment, this threshold
+# can tolerate normal noise of 0.3 amplitude when sine wave signal
+# amplitude is 1.
+PATTERN_MATCHING_THRESHOLD = 0.85
+
+# Window size for peak detection.
+PEAK_WINDOW_SIZE_HZ = 20
+
+
+class RMSTooSmallError(Exception):
+ """Error when signal RMS is too small."""
+ pass
+
+
+class EmptyDataError(Exception):
+ """Error when signal is empty."""
+ pass
+
+
+def normalize_signal(signal, saturate_value):
+ """Normalizes the signal with respect to the saturate value.
+
+ Args:
+ signal: A list for one-channel PCM data.
+ saturate_value: The maximum value that the PCM data might be.
+
+ Returns:
+ A numpy array containing normalized signal. The normalized signal has
+ value -1 and 1 when it saturates.
+
+ """
+ signal = numpy.array(signal)
+ return signal / float(saturate_value)
+
+
+def spectral_analysis(signal,
+ rate,
+ min_peak_ratio=DEFAULT_MIN_PEAK_RATIO,
+ peak_window_size_hz=PEAK_WINDOW_SIZE_HZ):
+ """Gets the dominant frequencies by spectral analysis.
+
+ Args:
+ signal: A list of numbers for one-channel PCM data. This should be
+ normalized to [-1, 1] so the function can check if signal RMS
+ is too small to be meaningful.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ min_peak_ratio: The minimum peak_i/peak_0 ratio such that the
+ peaks other than the greatest one should be
+ considered.
+ This is to ignore peaks that are too small compared
+ to the first peak peak_0.
+ peak_window_size_hz: The window size in Hz to find the peaks.
+ The minimum differences between found peaks will
+ be half of this value.
+
+ Returns:
+ A list of tuples:
+ [(peak_frequency_0, peak_coefficient_0),
+ (peak_frequency_1, peak_coefficient_1),
+ (peak_frequency_2, peak_coefficient_2), ...]
+ where the tuples are sorted by coefficients. The last
+ peak_coefficient will be no less than peak_coefficient *
+ min_peak_ratio. If RMS is less than MEANINGFUL_RMS_THRESHOLD,
+ returns [(0, 0)].
+
+ """
+ # Checks the signal is meaningful.
+ if len(signal) == 0:
+ raise EmptyDataError('Signal data is empty')
+
+ signal_rms = numpy.linalg.norm(signal) / numpy.sqrt(len(signal))
+ logging.debug('signal RMS = %s', signal_rms)
+
+ # If RMS is too small, set dominant frequency and coefficient to 0.
+ if signal_rms < MEANINGFUL_RMS_THRESHOLD:
+ logging.warning(
+ 'RMS %s is too small to be meaningful. Set frequency to 0.',
+ signal_rms)
+ return [(0, 0)]
+
+ logging.debug('Doing spectral analysis ...')
+
+ # First, pass signal through a window function to mitigate spectral leakage.
+ y_conv_w = signal * numpy.hanning(len(signal))
+
+ length = len(y_conv_w)
+
+ # x_f is the frequency in Hz, y_f is the transformed coefficient.
+ x_f = _rfft_freq(length, rate)
+ y_f = 2.0 / length * numpy.fft.rfft(y_conv_w)
+
+ # y_f is complex so consider its absolute value for magnitude.
+ abs_y_f = numpy.abs(y_f)
+ threshold = max(abs_y_f) * min_peak_ratio
+
+ # Suppresses all coefficients that are below threshold.
+ for i in range(len(abs_y_f)):
+ if abs_y_f[i] < threshold:
+ abs_y_f[i] = 0
+
+ # Gets the peak detection window size in indice.
+ # x_f[1] is the frequency difference per index.
+ peak_window_size = int(peak_window_size_hz / x_f[1])
+
+ # Detects peaks.
+ peaks = peak_detection(abs_y_f, peak_window_size)
+
+ # Transform back the peak location from index to frequency.
+ results = []
+ for index, value in peaks:
+ results.append((x_f[int(index)], value))
+ return results
+
+
+def _rfft_freq(length, rate):
+ """Gets the frequency at each index of real FFT.
+
+ Args:
+ length: The window length of FFT.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+
+ Returns:
+ A numpy array containing frequency corresponding to numpy.fft.rfft
+ result at each index.
+
+ """
+ # The difference in Hz between each index.
+ val = rate / float(length)
+ # Only care half of frequencies for FFT on real signal.
+ result_length = length // 2 + 1
+ return numpy.linspace(0, (result_length - 1) * val, result_length)
+
+
+def peak_detection(array, window_size):
+ """Detects peaks in an array.
+
+ A point (i, array[i]) is a peak if array[i] is the maximum among
+ array[i - half_window_size] to array[i + half_window_size].
+ If array[i - half_window_size] to array[i + half_window_size] are all equal,
+ then there is no peak in this window.
+ Note that we only consider peak with value greater than 0.
+
+ Args:
+ array: The input array to detect peaks in. Array is a list of
+ absolute values of the magnitude of transformed coefficient.
+
+ window_size: The window to detect peaks.
+
+ Returns:
+ A list of tuples:
+ [(peak_index_1, peak_value_1), (peak_index_2, peak_value_2), ...]
+ where the tuples are sorted by peak values.
+
+ """
+ half_window_size = window_size / 2
+ length = len(array)
+
+ def mid_is_peak(array, mid, left, right):
+ """Checks if value at mid is the largest among left to right in array.
+
+ Args:
+ array: A list of numbers.
+ mid: The mid index.
+ left: The left index.
+ rigth: The right index.
+
+ Returns:
+ A tuple (is_peak, next_candidate)
+ is_peak is True if array[index] is the maximum among numbers
+ in array between index [left, right] inclusively.
+ next_candidate is the index of next candidate for peak if
+ is_peak is False. It is the index of maximum value in
+ [mid + 1, right]. If is_peak is True, next_candidate is
+ right + 1.
+
+ """
+ value_mid = array[int(mid)]
+ is_peak = True
+ next_peak_candidate_index = None
+
+ # Check the left half window.
+ for index in range(int(left), int(mid)):
+ if array[index] >= value_mid:
+ is_peak = False
+ break
+
+ # Mid is at the end of array.
+ if mid == right:
+ return is_peak, right + 1
+
+ # Check the right half window and also record next candidate.
+ # Favor the larger index for next_peak_candidate_index.
+ for index in range(int(right), int(mid), -1):
+ if (next_peak_candidate_index is None or
+ array[index] > array[next_peak_candidate_index]):
+ next_peak_candidate_index = index
+
+ if array[next_peak_candidate_index] >= value_mid:
+ is_peak = False
+
+ if is_peak:
+ next_peak_candidate_index = right + 1
+
+ return is_peak, next_peak_candidate_index
+
+ results = []
+ mid = 0
+ next_candidate_idx = None
+ while mid < length:
+ left = max(0, mid - half_window_size)
+ right = min(length - 1, mid + half_window_size)
+
+ # Only consider value greater than 0.
+ if array[int(mid)] == 0:
+ mid = mid + 1
+ continue
+
+ is_peak, next_candidate_idx = mid_is_peak(array, mid, left, right)
+
+ if is_peak:
+ results.append((mid, array[int(mid)]))
+
+ # Use the next candidate found in [mid + 1, right], or right + 1.
+ mid = next_candidate_idx
+
+ # Sort the peaks by values.
+ return sorted(results, key=lambda x: x[1], reverse=True)
+
+
+def anomaly_detection(signal,
+ rate,
+ freq,
+ block_size=ANOMALY_DETECTION_BLOCK_SIZE,
+ threshold=PATTERN_MATCHING_THRESHOLD):
+ """Detects anomaly in a sine wave signal.
+
+ This method detects anomaly in a sine wave signal by matching
+ patterns of each block.
+ For each moving window of block in the test signal, checks if there
+ is any block in golden signal that is similar to this block of test signal.
+ If there is such a block in golden signal, then this block of test
+ signal is matched and there is no anomaly in this block of test signal.
+ If there is any block in test signal that is not matched, then this block
+ covers an anomaly.
+ The block of test signal starts from index 0, and proceeds in steps of
+ half block size. The overlapping of test signal blocks makes sure there must
+ be at least one block covering the transition from sine wave to anomaly.
+
+ Args:
+ signal: A 1-D array-like object for 1-channel PCM data.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ freq: The expected frequency of signal.
+ block_size: The block size in samples to detect anomaly.
+ threshold: The threshold of correlation index to be judge as matched.
+
+ Returns:
+ A list containing detected anomaly time in seconds.
+
+ """
+ if len(signal) == 0:
+ raise EmptyDataError('Signal data is empty')
+
+ golden_y = _generate_golden_pattern(rate, freq, block_size)
+
+ results = []
+
+ for start in range(0, len(signal), int(block_size / 2)):
+ end = start + block_size
+ test_signal = signal[start:end]
+ matched = _moving_pattern_matching(golden_y, test_signal, threshold)
+ if not matched:
+ results.append(start)
+
+ results = [float(x) / rate for x in results]
+
+ return results
+
+
+def _generate_golden_pattern(rate, freq, block_size):
+ """Generates a golden pattern of certain frequency.
+
+ The golden pattern must cover all the possibilities of waveforms in a
+ block. So, we need a golden pattern covering 1 period + 1 block size,
+ such that the test block can start anywhere in a period, and extends
+ a block size.
+
+ |period |1 bk|
+ | | |
+ . . . .
+ . . . .
+ . . .
+
+ Args:
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ freq: The frequency of golden pattern.
+ block_size: The block size in samples to detect anomaly.
+
+ Returns:
+ A 1-D array for golden pattern.
+
+ """
+ samples_in_a_period = int(rate / freq) + 1
+ samples_in_golden_pattern = samples_in_a_period + block_size
+ golden_x = numpy.linspace(0.0, (samples_in_golden_pattern - 1) * 1.0 /
+ rate, samples_in_golden_pattern)
+ golden_y = numpy.sin(freq * 2.0 * numpy.pi * golden_x)
+ return golden_y
+
+
+def _moving_pattern_matching(golden_signal, test_signal, threshold):
+ """Checks if test_signal is similar to any block of golden_signal.
+
+ Compares test signal with each block of golden signal by correlation
+ index. If there is any block of golden signal that is similar to
+ test signal, then it is matched.
+
+ Args:
+ golden_signal: A 1-D array for golden signal.
+ test_signal: A 1-D array for test signal.
+ threshold: The threshold of correlation index to be judge as matched.
+
+ Returns:
+ True if there is a match. False otherwise.
+
+ ValueError: if test signal is longer than golden signal.
+
+ """
+ if len(golden_signal) < len(test_signal):
+ raise ValueError('Test signal is longer than golden signal')
+
+ block_length = len(test_signal)
+ number_of_movings = len(golden_signal) - block_length + 1
+ correlation_indices = []
+ for moving_index in range(number_of_movings):
+ # Cuts one block of golden signal from start index.
+ # The block length is the same as test signal.
+ start = moving_index
+ end = start + block_length
+ golden_signal_block = golden_signal[start:end]
+ try:
+ correlation_index = _get_correlation_index(golden_signal_block,
+ test_signal)
+ except TestSignalNormTooSmallError:
+ logging.info(
+ 'Caught one block of test signal that has no meaningful norm')
+ return False
+ correlation_indices.append(correlation_index)
+
+ # Checks if the maximum correlation index is high enough.
+ max_corr = max(correlation_indices)
+ if max_corr < threshold:
+ logging.debug('Got one unmatched block with max_corr: %s', max_corr)
+ return False
+ return True
+
+
+class GoldenSignalNormTooSmallError(Exception):
+ """Exception when golden signal norm is too small."""
+ pass
+
+
+class TestSignalNormTooSmallError(Exception):
+ """Exception when test signal norm is too small."""
+ pass
+
+
+def _get_correlation_index(golden_signal, test_signal):
+ """Computes correlation index of two signal of same length.
+
+ Args:
+ golden_signal: An 1-D array-like object.
+ test_signal: An 1-D array-like object.
+
+ Raises:
+ ValueError: if two signal have different lengths.
+ GoldenSignalNormTooSmallError: if golden signal norm is too small
+ TestSignalNormTooSmallError: if test signal norm is too small.
+
+ Returns:
+ The correlation index.
+ """
+ if len(golden_signal) != len(test_signal):
+ raise ValueError('Only accepts signal of same length: %s, %s' %
+ (len(golden_signal), len(test_signal)))
+
+ norm_golden = numpy.linalg.norm(golden_signal)
+ norm_test = numpy.linalg.norm(test_signal)
+ if norm_golden <= _MINIMUM_SIGNAL_NORM:
+ raise GoldenSignalNormTooSmallError(
+ 'No meaningful data as norm is too small.')
+ if norm_test <= _MINIMUM_SIGNAL_NORM:
+ raise TestSignalNormTooSmallError(
+ 'No meaningful data as norm is too small.')
+
+ # The 'valid' cross correlation result of two signals of same length will
+ # contain only one number.
+ correlation = numpy.correlate(golden_signal, test_signal, 'valid')[0]
+ return correlation / (norm_golden * norm_test)
diff --git a/acts/framework/acts/test_utils/audio_analysis_lib/audio_data.py b/acts/framework/acts/test_utils/audio_analysis_lib/audio_data.py
new file mode 100644
index 0000000..b123025
--- /dev/null
+++ b/acts/framework/acts/test_utils/audio_analysis_lib/audio_data.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - 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.
+"""This module provides abstraction of audio data."""
+
+import contextlib
+import copy
+import numpy
+import struct
+from io import StringIO
+"""The dict containing information on how to parse sample from raw data.
+
+Keys: The sample format as in aplay command.
+Values: A dict containing:
+ message: Human-readable sample format.
+ dtype_str: Data type used in numpy dtype. Check
+ https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html
+ for supported data type.
+ size_bytes: Number of bytes for one sample.
+"""
+SAMPLE_FORMATS = dict(
+ S32_LE=dict(
+ message='Signed 32-bit integer, little-endian',
+ dtype_str='<i',
+ size_bytes=4),
+ S16_LE=dict(
+ message='Signed 16-bit integer, little-endian',
+ dtype_str='<i',
+ size_bytes=2))
+
+
+def get_maximum_value_from_sample_format(sample_format):
+ """Gets the maximum value from sample format.
+
+ Args:
+ sample_format: A key in SAMPLE_FORMAT.
+
+ Returns:The maximum value the sample can hold + 1.
+
+ """
+ size_bits = SAMPLE_FORMATS[sample_format]['size_bytes'] * 8
+ return 1 << (size_bits - 1)
+
+
+class AudioRawDataError(Exception):
+ """Error in AudioRawData."""
+ pass
+
+
+class AudioRawData(object):
+ """The abstraction of audio raw data.
+
+ @property channel: The number of channels.
+ @property channel_data: A list of lists containing samples in each channel.
+ E.g., The third sample in the second channel is
+ channel_data[1][2].
+ @property sample_format: The sample format which should be one of the keys
+ in audio_data.SAMPLE_FORMATS.
+ """
+
+ def __init__(self, binary, channel, sample_format):
+ """Initializes an AudioRawData.
+
+ Args:
+ binary: A string containing binary data. If binary is not None,
+ The samples in binary will be parsed and be filled into
+ channel_data.
+ channel: The number of channels.
+ sample_format: One of the keys in audio_data.SAMPLE_FORMATS.
+ """
+ self.channel = channel
+ self.channel_data = [[] for _ in range(self.channel)]
+ self.sample_format = sample_format
+ if binary:
+ self.read_binary(binary)
+
+ def read_binary(self, binary):
+ """Reads samples from binary and fills channel_data.
+
+ Reads samples of fixed width from binary string into a numpy array
+ and shapes them into each channel.
+
+ Args:
+ binary: A string containing binary data.
+ """
+ sample_format_dict = SAMPLE_FORMATS[self.sample_format]
+
+ # The data type used in numpy fromstring function. For example,
+ # <i4 for 32-bit signed int.
+ np_dtype = '%s%d' % (sample_format_dict['dtype_str'],
+ sample_format_dict['size_bytes'])
+
+ # Reads data from a string into 1-D array.
+ np_array = numpy.fromstring(binary, dtype=np_dtype)
+
+ n_frames = len(np_array) / self.channel
+ # Reshape np_array into an array of shape (n_frames, channel).
+ np_array = np_array.reshape(int(n_frames), self.channel)
+ # Transpose np_arrya so it becomes of shape (channel, n_frames).
+ self.channel_data = np_array.transpose()
diff --git a/acts/framework/acts/test_utils/audio_analysis_lib/audio_quality_measurement.py b/acts/framework/acts/test_utils/audio_analysis_lib/audio_quality_measurement.py
new file mode 100644
index 0000000..4087fe4
--- /dev/null
+++ b/acts/framework/acts/test_utils/audio_analysis_lib/audio_quality_measurement.py
@@ -0,0 +1,929 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - 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.
+"""This module provides utilities to detect some artifacts and measure the
+ quality of audio."""
+
+import logging
+import math
+import numpy
+
+import acts.test_utils.audio_analysis_lib.audio_analysis as audio_analysis
+
+# The input signal should be one sine wave with fixed frequency which
+# can have silence before and/or after sine wave.
+# For example:
+# silence sine wave silence
+# -----------|VVVVVVVVVVVVV|-----------
+# (a) (b) (c)
+# This module detects these artifacts:
+# 1. Detect noise in (a) and (c).
+# 2. Detect delay in (b).
+# 3. Detect burst in (b).
+# Assume the transitions between (a)(b) and (b)(c) are smooth and
+# amplitude increases/decreases linearly.
+# This module will detect artifacts in the sine wave.
+# This module also estimates the equivalent noise level by teager operator.
+# This module also detects volume changes in the sine wave. However, volume
+# changes may be affected by delay or burst.
+# Some artifacts may cause each other.
+
+# In this module, amplitude and frequency are derived from Hilbert transform.
+# Both amplitude and frequency are a function of time.
+
+# To detect each artifact, each point will be compared with
+# average amplitude of its block. The block size will be 1.5 ms.
+# Using average amplitude can mitigate the error caused by
+# Hilbert transform and noise.
+# In some case, for more accuracy, the block size may be modified
+# to other values.
+DEFAULT_BLOCK_SIZE_SECS = 0.0015
+
+# If the difference between average frequency of this block and
+# dominant frequency of full signal is less than 0.5 times of
+# dominant frequency, this block is considered to be within the
+# sine wave. In most cases, if there is no sine wave(only noise),
+# average frequency will be much greater than 5 times of
+# dominant frequency.
+# Also, for delay during playback, the frequency will be about 0
+# in perfect situation or much greater than 5 times of dominant
+# frequency if it's noised.
+DEFAULT_FREQUENCY_ERROR = 0.5
+
+# If the amplitude of some sample is less than 0.6 times of the
+# average amplitude of its left/right block, it will be considered
+# as a delay during playing.
+DEFAULT_DELAY_AMPLITUDE_THRESHOLD = 0.6
+
+# If the average amplitude of the block before or after playing
+# is more than 0.5 times to the average amplitude of the wave,
+# it will be considered as a noise artifact.
+DEFAULT_NOISE_AMPLITUDE_THRESHOLD = 0.5
+
+# In the sine wave, if the amplitude is more than 1.4 times of
+# its left side and its right side, it will be considered as
+# a burst.
+DEFAULT_BURST_AMPLITUDE_THRESHOLD = 1.4
+
+# When detecting burst, if the amplitude is lower than 0.5 times
+# average amplitude, we ignore it.
+DEFAULT_BURST_TOO_SMALL = 0.5
+
+# For a signal which is the combination of sine wave with fixed frequency f and
+# amplitude 1 and standard noise with amplitude k, the average teager value is
+# nearly linear to the noise level k.
+# Given frequency f, we simulate a sine wave with default noise level and
+# calculate its average teager value. Then, we can estimate the equivalent
+# noise level of input signal by the average teager value of input signal.
+DEFAULT_STANDARD_NOISE = 0.005
+
+# For delay, burst, volume increasing/decreasing, if two delay(
+# burst, volume increasing/decreasing) happen within
+# DEFAULT_SAME_EVENT_SECS seconds, we consider they are the
+# same event.
+DEFAULT_SAME_EVENT_SECS = 0.001
+
+# When detecting increasing/decreasing volume of signal, if the amplitude
+# is lower than 0.1 times average amplitude, we ignore it.
+DEFAULT_VOLUME_CHANGE_TOO_SMALL = 0.1
+
+# If average amplitude of right block is less/more than average
+# amplitude of left block times DEFAULT_VOLUME_CHANGE_AMPLITUDE, it will be
+# considered as decreasing/increasing on volume.
+DEFAULT_VOLUME_CHANGE_AMPLITUDE = 0.1
+
+# If the increasing/decreasing volume event is too close to the start or the end
+# of sine wave, we consider its volume change as part of rising/falling phase in
+# the start/end.
+NEAR_START_OR_END_SECS = 0.01
+
+# After applying Hilbert transform, the resulting amplitude and frequency may be
+# extremely large in the start and/or the end part. Thus, we will append zeros
+# before and after the whole wave for 0.1 secs.
+APPEND_ZEROS_SECS = 0.1
+
+# If the noise event is too close to the start or the end of the data, we
+# consider its noise as part of artifacts caused by edge effect of Hilbert
+# transform.
+# For example, originally, the data duration is 10 seconds.
+# We append 0.1 seconds of zeros in the beginning and the end of the data, so
+# the data becomes 10.2 seocnds long.
+# Then, we apply Hilbert transform to 10.2 seconds of data.
+# Near 0.1 seconds and 10.1 seconds, there will be edge effect of Hilbert
+# transform. We do not want these be treated as noise.
+# If NEAR_DATA_START_OR_END_SECS is set to 0.01, then the noise happened
+# at [0, 0.11] and [10.09, 10.1] will be ignored.
+NEAR_DATA_START_OR_END_SECS = 0.01
+
+# If the noise event is too close to the start or the end of the sine wave in
+# the data, we consider its noise as part of artifacts caused by edge effect of
+# Hilbert transform.
+# A |-------------|vvvvvvvvvvvvvvvvvvvvvvv|-------------|
+# B |ooooooooo| d | | d |ooooooooo|
+#
+# A is full signal. It contains a sine wave and silence before and after sine
+# wave.
+# In B, |oooo| shows the parts that we are going to check for noise before/after
+# sine wave. | d | is determined by NEAR_SINE_START_OR_END_SECS.
+NEAR_SINE_START_OR_END_SECS = 0.01
+
+
+class SineWaveNotFound(Exception):
+ """Error when there's no sine wave found in the signal"""
+ pass
+
+
+def hilbert(x):
+ """Hilbert transform copied from scipy.
+
+ More information can be found here:
+ http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.hilbert.html
+
+ Args:
+ x: Real signal data to transform.
+
+ Returns:
+ Analytic signal of x, we can further extract amplitude and
+ frequency from it.
+
+ """
+ x = numpy.asarray(x)
+ if numpy.iscomplexobj(x):
+ raise ValueError("x must be real.")
+ axis = -1
+ N = x.shape[axis]
+ if N <= 0:
+ raise ValueError("N must be positive.")
+
+ Xf = numpy.fft.fft(x, N, axis=axis)
+ h = numpy.zeros(N)
+ if N % 2 == 0:
+ h[0] = h[N // 2] = 1
+ h[1:N // 2] = 2
+ else:
+ h[0] = 1
+ h[1:(N + 1) // 2] = 2
+
+ if len(x.shape) > 1:
+ ind = [newaxis] * x.ndim
+ ind[axis] = slice(None)
+ h = h[ind]
+ x = numpy.fft.ifft(Xf * h, axis=axis)
+ return x
+
+
+def noised_sine_wave(frequency, rate, noise_level):
+ """Generates a sine wave of 2 second with specified noise level.
+
+ Args:
+ frequency: Frequency of sine wave.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ noise_level: Required noise level.
+
+ Returns:
+ A sine wave with specified noise level.
+
+ """
+ wave = []
+ for index in range(0, rate * 2):
+ sample = 2.0 * math.pi * frequency * float(index) / float(rate)
+ sine_wave = math.sin(sample)
+ noise = noise_level * numpy.random.standard_normal()
+ wave.append(sine_wave + noise)
+ return wave
+
+
+def average_teager_value(wave, amplitude):
+ """Computes the normalized average teager value.
+
+ After averaging the teager value, we will normalize the value by
+ dividing square of amplitude.
+
+ Args:
+ wave: Wave to apply teager operator.
+ amplitude: Average amplitude of given wave.
+
+ Returns:
+ Average teager value.
+
+ """
+ teager_value, length = 0, len(wave)
+ for i in range(1, length - 1):
+ ith_teager_value = abs(wave[i] * wave[i] - wave[i - 1] * wave[i + 1])
+ ith_teager_value *= max(1, abs(wave[i]))
+ teager_value += ith_teager_value
+ teager_value = (float(teager_value) / length) / (amplitude**2)
+ return teager_value
+
+
+def noise_level(amplitude, frequency, rate, teager_value_of_input):
+ """Computes the noise level compared with standard_noise.
+
+ For a signal which is the combination of sine wave with fixed frequency f
+ and amplitude 1 and standard noise with amplitude k, the average teager
+ value is nearly linear to the noise level k.
+ Thus, we can compute the average teager value of a sine wave with
+ standard_noise. Then, we can estimate the noise level of given input.
+
+ Args:
+ amplitude: Amplitude of input audio.
+ frequency: Dominant frequency of input audio.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ teager_value_of_input: Average teager value of input audio.
+
+ Returns:
+ A float value denotes the audio is equivalent to have how many times of
+ noise compared with its amplitude.For example, 0.02 denotes that the
+ wave has a noise which has standard distribution with standard
+ deviation being 0.02 times the amplitude of the wave.
+
+ """
+ standard_noise = DEFAULT_STANDARD_NOISE
+
+ # Generates the standard sine wave with stdandard_noise level of noise.
+ standard_wave = noised_sine_wave(frequency, rate, standard_noise)
+
+ # Calculates the average teager value.
+ teager_value_of_std_wave = average_teager_value(standard_wave, amplitude)
+
+ return (teager_value_of_input / teager_value_of_std_wave) * standard_noise
+
+
+def error(f1, f2):
+ """Calculates the relative error between f1 and f2.
+
+ Args:
+ f1: Exact value.
+ f2: Test value.
+
+ Returns:
+ Relative error between f1 and f2.
+
+ """
+ return abs(float(f1) - float(f2)) / float(f1)
+
+
+def hilbert_analysis(signal, rate, block_size):
+ """Finds amplitude and frequency of each time of signal by Hilbert transform.
+
+ Args:
+ signal: The wave to analyze.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ block_size: The size of block to transform.
+
+ Returns:
+ A tuple of list: (amplitude, frequency) composed of amplitude and
+ frequency of each time.
+
+ """
+ # To apply Hilbert transform, the wave will be transformed
+ # segment by segment. For each segment, its size will be
+ # block_size and we will only take middle part of it.
+ # Thus, each segment looks like: |-----|=====|=====|-----|.
+ # "=...=" part will be taken while "-...-" part will be ignored.
+ #
+ # The whole size of taken part will be half of block_size
+ # which will be hilbert_block.
+ # The size of each ignored part will be half of hilbert_block
+ # which will be half_hilbert_block.
+ hilbert_block = block_size // 2
+ half_hilbert_block = hilbert_block // 2
+ # As mentioned above, for each block, we will only take middle
+ # part of it. Thus, the whole transformation will be completed as:
+ # |=====|=====|-----| |-----|=====|=====|-----|
+ # |-----|=====|=====|-----| |-----|=====|=====|
+ # |-----|=====|=====|-----|
+ # Specially, beginning and ending part may not have ignored part.
+ length = len(signal)
+ result = []
+ for left_border in range(0, length, hilbert_block):
+ right_border = min(length, left_border + hilbert_block)
+ temp_left_border = max(0, left_border - half_hilbert_block)
+ temp_right_border = min(length, right_border + half_hilbert_block)
+ temp = hilbert(signal[temp_left_border:temp_right_border])
+ for index in range(left_border, right_border):
+ result.append(temp[index - temp_left_border])
+ result = numpy.asarray(result)
+ amplitude = numpy.abs(result)
+ phase = numpy.unwrap(numpy.angle(result))
+ frequency = numpy.diff(phase) / (2.0 * numpy.pi) * rate
+ #frequency.append(frequency[len(frequency)-1])
+ frequecny = numpy.append(frequency, frequency[len(frequency) - 1])
+ return (amplitude, frequency)
+
+
+def find_block_average_value(arr, side_block_size, block_size):
+ """For each index, finds average value of its block, left block, right block.
+
+ It will find average value for each index in the range.
+
+ For each index, the range of its block is
+ [max(0, index - block_size / 2), min(length - 1, index + block_size / 2)]
+ For each index, the range of its left block is
+ [max(0, index - size_block_size), index]
+ For each index, the range of its right block is
+ [index, min(length - 1, index + side_block_size)]
+
+ Args:
+ arr: The array to be computed.
+ side_block_size: the size of the left_block and right_block.
+ block_size: the size of the block.
+
+ Returns:
+ A tuple of lists: (left_block_average_array,
+ right_block_average_array,
+ block_average_array)
+ """
+ length = len(arr)
+ left_border, right_border = 0, 1
+ left_block_sum = arr[0]
+ right_block_sum = arr[0]
+ left_average_array = numpy.zeros(length)
+ right_average_array = numpy.zeros(length)
+ block_average_array = numpy.zeros(length)
+ for index in range(0, length):
+ while left_border < index - side_block_size:
+ left_block_sum -= arr[left_border]
+ left_border += 1
+ while right_border < min(length, index + side_block_size):
+ right_block_sum += arr[right_border]
+ right_border += 1
+
+ left_average_value = float(left_block_sum) / (index - left_border + 1)
+ right_average_value = float(right_block_sum) / (right_border - index)
+ left_average_array[index] = left_average_value
+ right_average_array[index] = right_average_value
+
+ if index + 1 < length:
+ left_block_sum += arr[index + 1]
+ right_block_sum -= arr[index]
+ left_border, right_border = 0, 1
+ block_sum = 0
+ for index in range(0, length):
+ while left_border < index - block_size / 2:
+ block_sum -= arr[left_border]
+ left_border += 1
+ while right_border < min(length, index + block_size / 2):
+ block_sum += arr[right_border]
+ right_border += 1
+
+ average_value = float(block_sum) / (right_border - left_border)
+ block_average_array[index] = average_value
+ return (left_average_array, right_average_array, block_average_array)
+
+
+def find_start_end_index(dominant_frequency, block_frequency_delta, block_size,
+ frequency_error_threshold):
+ """Finds start and end index of sine wave.
+
+ For each block with size of block_size, we check that whether its frequency
+ is close enough to the dominant_frequency. If yes, we will consider this
+ block to be within the sine wave.
+ Then, it will return the start and end index of sine wave indicating that
+ sine wave is between [start_index, end_index)
+ It's okay if the whole signal only contains sine wave.
+
+ Args:
+ dominant_frequency: Dominant frequency of signal.
+ block_frequency_delta: Average absolute difference between dominant
+ frequency and frequency of each block. For
+ each index, its block is
+ [max(0, index - block_size / 2),
+ min(length - 1, index + block_size / 2)]
+ block_size: Block size in samples.
+
+ Returns:
+ A tuple composed of (start_index, end_index)
+
+ """
+ length = len(block_frequency_delta)
+
+ # Finds the start/end time index of playing based on dominant frequency
+ start_index, end_index = length - 1, 0
+ for index in range(0, length):
+ left_border = max(0, index - block_size / 2)
+ right_border = min(length - 1, index + block_size / 2)
+ frequency_error = block_frequency_delta[index] / dominant_frequency
+ if frequency_error < frequency_error_threshold:
+ start_index = min(start_index, left_border)
+ end_index = max(end_index, right_border + 1)
+ return (start_index, end_index)
+
+
+def noise_detection(start_index, end_index, block_amplitude, average_amplitude,
+ rate, noise_amplitude_threshold):
+ """Detects noise before/after sine wave.
+
+ If average amplitude of some sample's block before start of wave or after
+ end of wave is more than average_amplitude times noise_amplitude_threshold,
+ it will be considered as a noise.
+
+ Args:
+ start_index: Start index of sine wave.
+ end_index: End index of sine wave.
+ block_amplitude: An array for average amplitude of each block, where
+ amplitude is computed from Hilbert transform.
+ average_amplitude: Average amplitude of sine wave.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ noise_amplitude_threshold: If the average amplitude of a block is
+ higher than average amplitude of the wave times
+ noise_amplitude_threshold, it will be considered as
+ noise before/after playback.
+
+ Returns:
+ A tuple of lists indicating the time that noise happens:
+ (noise_before_playing, noise_after_playing).
+
+ """
+ length = len(block_amplitude)
+ amplitude_threshold = average_amplitude * noise_amplitude_threshold
+ same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
+
+ # Detects noise before playing.
+ noise_time_point = []
+ last_noise_end_time_point = []
+ previous_noise_index = None
+ times = 0
+ for index in range(0, length):
+ # Ignore noise too close to the beginning or the end of sine wave.
+ # Check the docstring of NEAR_SINE_START_OR_END_SECS.
+ if ((start_index - rate * NEAR_SINE_START_OR_END_SECS) <= index and
+ (index < end_index + rate * NEAR_SINE_START_OR_END_SECS)):
+ continue
+
+ # Ignore noise too close to the beginning or the end of original data.
+ # Check the docstring of NEAR_DATA_START_OR_END_SECS.
+ if (float(index) / rate <=
+ NEAR_DATA_START_OR_END_SECS + APPEND_ZEROS_SECS):
+ continue
+ if (float(length - index) / rate <=
+ NEAR_DATA_START_OR_END_SECS + APPEND_ZEROS_SECS):
+ continue
+ if block_amplitude[index] > amplitude_threshold:
+ same_event = False
+ if previous_noise_index:
+ same_event = (index - previous_noise_index
+ ) < same_event_samples
+ if not same_event:
+ index_start_sec = float(index) / rate - APPEND_ZEROS_SECS
+ index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
+ noise_time_point.append(index_start_sec)
+ last_noise_end_time_point.append(index_end_sec)
+ times += 1
+ index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
+ last_noise_end_time_point[times - 1] = index_end_sec
+ previous_noise_index = index
+
+ noise_before_playing, noise_after_playing = [], []
+ for i in range(times):
+ duration = last_noise_end_time_point[i] - noise_time_point[i]
+ if noise_time_point[i] < float(start_index) / rate - APPEND_ZEROS_SECS:
+ noise_before_playing.append((noise_time_point[i], duration))
+ else:
+ noise_after_playing.append((noise_time_point[i], duration))
+
+ return (noise_before_playing, noise_after_playing)
+
+
+def delay_detection(start_index, end_index, block_amplitude, average_amplitude,
+ dominant_frequency, rate, left_block_amplitude,
+ right_block_amplitude, block_frequency_delta,
+ delay_amplitude_threshold, frequency_error_threshold):
+ """Detects delay during playing.
+
+ For each sample, we will check whether the average amplitude of its block
+ is less than average amplitude of its left block and its right block times
+ delay_amplitude_threshold. Also, we will check whether the frequency of
+ its block is far from the dominant frequency.
+ If at least one constraint fulfilled, it will be considered as a delay.
+
+ Args:
+ start_index: Start index of sine wave.
+ end_index: End index of sine wave.
+ block_amplitude: An array for average amplitude of each block, where
+ amplitude is computed from Hilbert transform.
+ average_amplitude: Average amplitude of sine wave.
+ dominant_frequency: Dominant frequency of signal.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ left_block_amplitude: Average amplitude of left block of each index.
+ Ref to find_block_average_value function.
+ right_block_amplitude: Average amplitude of right block of each index.
+ Ref to find_block_average_value function.
+ block_frequency_delta: Average absolute difference frequency to
+ dominant frequency of block of each index.
+ Ref to find_block_average_value function.
+ delay_amplitude_threshold: If the average amplitude of a block is
+ lower than average amplitude of the wave times
+ delay_amplitude_threshold, it will be considered
+ as delay.
+ frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR
+
+ Returns:
+ List of delay occurrence:
+ [(time_1, duration_1), (time_2, duration_2), ...],
+ where time and duration are in seconds.
+
+ """
+ delay_time_points = []
+ last_delay_end_time_points = []
+ previous_delay_index = None
+ times = 0
+ same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
+ start_time = float(start_index) / rate - APPEND_ZEROS_SECS
+ end_time = float(end_index) / rate - APPEND_ZEROS_SECS
+ for index in range(int(start_index), int(end_index)):
+ if block_amplitude[
+ index] > average_amplitude * delay_amplitude_threshold:
+ continue
+ now_time = float(index) / rate - APPEND_ZEROS_SECS
+ if abs(now_time - start_time) < NEAR_START_OR_END_SECS:
+ continue
+ if abs(now_time - end_time) < NEAR_START_OR_END_SECS:
+ continue
+ # If amplitude less than its left/right side and small enough,
+ # it will be considered as a delay.
+ amp_threshold = average_amplitude * delay_amplitude_threshold
+ left_threshold = delay_amplitude_threshold * left_block_amplitude[
+ index]
+ amp_threshold = min(amp_threshold, left_threshold)
+ right_threshold = delay_amplitude_threshold * right_block_amplitude[
+ index]
+ amp_threshold = min(amp_threshold, right_threshold)
+
+ frequency_error = block_frequency_delta[index] / dominant_frequency
+
+ amplitude_too_small = block_amplitude[index] < amp_threshold
+ frequency_not_match = frequency_error > frequency_error_threshold
+
+ if amplitude_too_small or frequency_not_match:
+ same_event = False
+ if previous_delay_index:
+ same_event = (index - previous_delay_index
+ ) < same_event_samples
+ if not same_event:
+ index_start_sec = float(index) / rate - APPEND_ZEROS_SECS
+ index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
+ delay_time_points.append(index_start_sec)
+ last_delay_end_time_points.append(index_end_sec)
+ times += 1
+ previous_delay_index = index
+ index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
+ last_delay_end_time_points[times - 1] = index_end_sec
+
+ delay_list = []
+ for i in range(len(delay_time_points)):
+ duration = last_delay_end_time_points[i] - delay_time_points[i]
+ delay_list.append((delay_time_points[i], duration))
+ return delay_list
+
+
+def burst_detection(start_index, end_index, block_amplitude, average_amplitude,
+ dominant_frequency, rate, left_block_amplitude,
+ right_block_amplitude, block_frequency_delta,
+ burst_amplitude_threshold, frequency_error_threshold):
+ """Detects burst during playing.
+
+ For each sample, we will check whether the average amplitude of its block is
+ more than average amplitude of its left block and its right block times
+ burst_amplitude_threshold. Also, we will check whether the frequency of
+ its block is not compatible to the dominant frequency.
+ If at least one constraint fulfilled, it will be considered as a burst.
+
+ Args:
+ start_index: Start index of sine wave.
+ end_index: End index of sine wave.
+ block_amplitude: An array for average amplitude of each block, where
+ amplitude is computed from Hilbert transform.
+ average_amplitude: Average amplitude of sine wave.
+ dominant_frequency: Dominant frequency of signal.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ left_block_amplitude: Average amplitude of left block of each index.
+ Ref to find_block_average_value function.
+ right_block_amplitude: Average amplitude of right block of each index.
+ Ref to find_block_average_value function.
+ block_frequency_delta: Average absolute difference frequency to
+ dominant frequency of block of each index.
+ burst_amplitude_threshold: If the amplitude is higher than average
+ amplitude of its left block and its right block
+ times burst_amplitude_threshold. It will be
+ considered as a burst.
+ frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR
+
+ Returns:
+ List of burst occurence: [time_1, time_2, ...],
+ where time is in seconds.
+
+ """
+ burst_time_points = []
+ previous_burst_index = None
+ same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
+ for index in range(int(start_index), int(end_index)):
+ # If amplitude higher than its left/right side and large enough,
+ # it will be considered as a burst.
+ if block_amplitude[
+ index] <= average_amplitude * DEFAULT_BURST_TOO_SMALL:
+ continue
+ if abs(index - start_index) < rate * NEAR_START_OR_END_SECS:
+ continue
+ if abs(index - end_index) < rate * NEAR_START_OR_END_SECS:
+ continue
+ amp_threshold = average_amplitude * DEFAULT_BURST_TOO_SMALL
+ left_threshold = burst_amplitude_threshold * left_block_amplitude[
+ index]
+ amp_threshold = max(amp_threshold, left_threshold)
+ right_threshold = burst_amplitude_threshold * right_block_amplitude[
+ index]
+ amp_threshold = max(amp_threshold, right_threshold)
+
+ frequency_error = block_frequency_delta[index] / dominant_frequency
+
+ amplitude_too_large = block_amplitude[index] > amp_threshold
+ frequency_not_match = frequency_error > frequency_error_threshold
+
+ if amplitude_too_large or frequency_not_match:
+ same_event = False
+ if previous_burst_index:
+ same_event = index - previous_burst_index < same_event_samples
+ if not same_event:
+ burst_time_points.append(
+ float(index) / rate - APPEND_ZEROS_SECS)
+ previous_burst_index = index
+
+ return burst_time_points
+
+
+def changing_volume_detection(start_index, end_index, average_amplitude, rate,
+ left_block_amplitude, right_block_amplitude,
+ volume_changing_amplitude_threshold):
+ """Finds volume changing during playback.
+
+ For each index, we will compare average amplitude of its left block and its
+ right block. If average amplitude of right block is more than average
+ amplitude of left block times (1 + DEFAULT_VOLUME_CHANGE_AMPLITUDE), it will
+ be considered as an increasing volume. If the one of right block is less
+ than that of left block times (1 - DEFAULT_VOLUME_CHANGE_AMPLITUDE), it will
+ be considered as a decreasing volume.
+
+ Args:
+ start_index: Start index of sine wave.
+ end_index: End index of sine wave.
+ average_amplitude: Average amplitude of sine wave.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ left_block_amplitude: Average amplitude of left block of each index.
+ Ref to find_block_average_value function.
+ right_block_amplitude: Average amplitude of right block of each index.
+ Ref to find_block_average_value function.
+ volume_changing_amplitude_threshold: If the average amplitude of right
+ block is higher or lower than
+ that of left one times this
+ value, it will be considered as
+ a volume change.
+ Also refer to
+ DEFAULT_VOLUME_CHANGE_AMPLITUDE
+
+ Returns:
+ List of volume changing composed of 1 for increasing and -1 for
+ decreasing.
+
+ """
+ length = len(left_block_amplitude)
+
+ # Detects rising and/or falling volume.
+ previous_rising_index, previous_falling_index = None, None
+ changing_time = []
+ changing_events = []
+ amplitude_threshold = average_amplitude * DEFAULT_VOLUME_CHANGE_TOO_SMALL
+ same_event_samples = rate * DEFAULT_SAME_EVENT_SECS
+ for index in range(int(start_index), int(end_index)):
+ # Skips if amplitude is too small.
+ if left_block_amplitude[index] < amplitude_threshold:
+ continue
+ if right_block_amplitude[index] < amplitude_threshold:
+ continue
+ # Skips if changing is from start or end time
+ if float(abs(start_index - index)) / rate < NEAR_START_OR_END_SECS:
+ continue
+ if float(abs(end_index - index)) / rate < NEAR_START_OR_END_SECS:
+ continue
+
+ delta_margin = volume_changing_amplitude_threshold
+ if left_block_amplitude[index] > 0:
+ delta_margin *= left_block_amplitude[index]
+
+ increasing_threshold = left_block_amplitude[index] + delta_margin
+ decreasing_threshold = left_block_amplitude[index] - delta_margin
+
+ if right_block_amplitude[index] > increasing_threshold:
+ same_event = False
+ if previous_rising_index:
+ same_event = index - previous_rising_index < same_event_samples
+ if not same_event:
+ changing_time.append(float(index) / rate - APPEND_ZEROS_SECS)
+ changing_events.append(+1)
+ previous_rising_index = index
+ if right_block_amplitude[index] < decreasing_threshold:
+ same_event = False
+ if previous_falling_index:
+ same_event = index - previous_falling_index < same_event_samples
+ if not same_event:
+ changing_time.append(float(index) / rate - APPEND_ZEROS_SECS)
+ changing_events.append(-1)
+ previous_falling_index = index
+
+ # Combines consecutive increasing/decreasing event.
+ combined_changing_events, prev = [], 0
+ for i in range(len(changing_events)):
+ if changing_events[i] == prev:
+ continue
+ combined_changing_events.append((changing_time[i], changing_events[i]))
+ prev = changing_events[i]
+ return combined_changing_events
+
+
+def quality_measurement(
+ signal,
+ rate,
+ dominant_frequency=None,
+ block_size_secs=DEFAULT_BLOCK_SIZE_SECS,
+ frequency_error_threshold=DEFAULT_FREQUENCY_ERROR,
+ delay_amplitude_threshold=DEFAULT_DELAY_AMPLITUDE_THRESHOLD,
+ noise_amplitude_threshold=DEFAULT_NOISE_AMPLITUDE_THRESHOLD,
+ burst_amplitude_threshold=DEFAULT_BURST_AMPLITUDE_THRESHOLD,
+ volume_changing_amplitude_threshold=DEFAULT_VOLUME_CHANGE_AMPLITUDE):
+ """Detects several artifacts and estimates the noise level.
+
+ This method detects artifact before playing, after playing, and delay
+ during playing. Also, it estimates the noise level of the signal.
+ To avoid the influence of noise, it calculates amplitude and frequency
+ block by block.
+
+ Args:
+ signal: A list of numbers for one-channel PCM data. The data should
+ be normalized to [-1,1].
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ dominant_frequency: Dominant frequency of signal. Set None to
+ recalculate the frequency in this function.
+ block_size_secs: Block size in seconds. The measurement will be done
+ block-by-block using average amplitude and frequency
+ in each block to avoid noise.
+ frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR.
+ delay_amplitude_threshold: If the average amplitude of a block is
+ lower than average amplitude of the wave
+ times delay_amplitude_threshold, it will
+ be considered as delay.
+ Also refer to delay_detection and
+ DEFAULT_DELAY_AMPLITUDE_THRESHOLD.
+ noise_amplitude_threshold: If the average amplitude of a block is
+ higher than average amplitude of the wave
+ times noise_amplitude_threshold, it will
+ be considered as noise before/after
+ playback.
+ Also refer to noise_detection and
+ DEFAULT_NOISE_AMPLITUDE_THRESHOLD.
+ burst_amplitude_threshold: If the average amplitude of a block is
+ higher than average amplitude of its left
+ block and its right block times
+ burst_amplitude_threshold. It will be
+ considered as a burst.
+ Also refer to burst_detection and
+ DEFAULT_BURST_AMPLITUDE_THRESHOLD.
+ volume_changing_amplitude_threshold: If the average amplitude of right
+ block is higher or lower than
+ that of left one times this
+ value, it will be considered as
+ a volume change.
+ Also refer to
+ changing_volume_detection and
+ DEFAULT_VOLUME_CHANGE_AMPLITUDE
+
+ Returns:
+ A dictoinary of detection/estimation:
+ {'artifacts':
+ {'noise_before_playback':
+ [(time_1, duration_1), (time_2, duration_2), ...],
+ 'noise_after_playback':
+ [(time_1, duration_1), (time_2, duration_2), ...],
+ 'delay_during_playback':
+ [(time_1, duration_1), (time_2, duration_2), ...],
+ 'burst_during_playback':
+ [time_1, time_2, ...]
+ },
+ 'volume_changes':
+ [(time_1, flag_1), (time_2, flag_2), ...],
+ 'equivalent_noise_level': level
+ }
+ where durations and time points are in seconds. And,
+ equivalence_noise_level is the quotient of noise and wave which
+ refers to DEFAULT_STANDARD_NOISE. volume_changes is a list of
+ tuples containing time stamps and decreasing/increasing flags for
+ volume change events.
+
+ """
+ # Calculates the block size, from seconds to samples.
+ block_size = int(block_size_secs * rate)
+
+ signal = numpy.concatenate(
+ (numpy.zeros(int(rate * APPEND_ZEROS_SECS)), signal,
+ numpy.zeros(int(rate * APPEND_ZEROS_SECS))))
+ signal = numpy.array(signal, dtype=float)
+ length = len(signal)
+
+ # Calculates the amplitude and frequency.
+ amplitude, frequency = hilbert_analysis(signal, rate, block_size)
+
+ # Finds the dominant frequency.
+ if not dominant_frequency:
+ dominant_frequency = audio_analysis.spectral_analysis(signal,
+ rate)[0][0]
+
+ # Finds the array which contains absolute difference between dominant
+ # frequency and frequency at each time point.
+ frequency_delta = abs(frequency - dominant_frequency)
+
+ # Computes average amplitude of each type of block
+ res = find_block_average_value(amplitude, block_size * 2, block_size)
+ left_block_amplitude, right_block_amplitude, block_amplitude = res
+
+ # Computes average absolute difference of frequency and dominant frequency
+ # of the block of each index
+ _, _, block_frequency_delta = find_block_average_value(
+ frequency_delta, block_size * 2, block_size)
+
+ # Finds start and end index of sine wave.
+ start_index, end_index = find_start_end_index(
+ dominant_frequency, block_frequency_delta, block_size,
+ frequency_error_threshold)
+
+ if start_index > end_index:
+ raise SineWaveNotFound('No sine wave found in signal')
+
+ logging.debug('Found sine wave: start: %s, end: %s',
+ float(start_index) / rate - APPEND_ZEROS_SECS,
+ float(end_index) / rate - APPEND_ZEROS_SECS)
+
+ sum_of_amplitude = float(sum(amplitude[int(start_index):int(end_index)]))
+ # Finds average amplitude of sine wave.
+ average_amplitude = sum_of_amplitude / (end_index - start_index)
+
+ # Finds noise before and/or after playback.
+ noise_before_playing, noise_after_playing = noise_detection(
+ start_index, end_index, block_amplitude, average_amplitude, rate,
+ noise_amplitude_threshold)
+
+ # Finds delay during playback.
+ delays = delay_detection(start_index, end_index, block_amplitude,
+ average_amplitude, dominant_frequency, rate,
+ left_block_amplitude, right_block_amplitude,
+ block_frequency_delta, delay_amplitude_threshold,
+ frequency_error_threshold)
+
+ # Finds burst during playback.
+ burst_time_points = burst_detection(
+ start_index, end_index, block_amplitude, average_amplitude,
+ dominant_frequency, rate, left_block_amplitude, right_block_amplitude,
+ block_frequency_delta, burst_amplitude_threshold,
+ frequency_error_threshold)
+
+ # Finds volume changing during playback.
+ volume_changes = changing_volume_detection(
+ start_index, end_index, average_amplitude, rate, left_block_amplitude,
+ right_block_amplitude, volume_changing_amplitude_threshold)
+
+ # Calculates the average teager value.
+ teager_value = average_teager_value(
+ signal[int(start_index):int(end_index)], average_amplitude)
+
+ # Finds out the noise level.
+ noise = noise_level(average_amplitude, dominant_frequency, rate,
+ teager_value)
+
+ return {
+ 'artifacts': {
+ 'noise_before_playback': noise_before_playing,
+ 'noise_after_playback': noise_after_playing,
+ 'delay_during_playback': delays,
+ 'burst_during_playback': burst_time_points
+ },
+ 'volume_changes': volume_changes,
+ 'equivalent_noise_level': noise
+ }
diff --git a/acts/framework/acts/test_utils/audio_analysis_lib/check_quality.py b/acts/framework/acts/test_utils/audio_analysis_lib/check_quality.py
new file mode 100644
index 0000000..15ff09e
--- /dev/null
+++ b/acts/framework/acts/test_utils/audio_analysis_lib/check_quality.py
@@ -0,0 +1,555 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - 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.
+"""Audio Analysis tool to analyze wave file and detect artifacts."""
+
+import argparse
+import collections
+import json
+import logging
+import math
+import numpy
+import os
+import pprint
+import subprocess
+import tempfile
+import wave
+
+import acts.test_utils.audio_analysis_lib.audio_analysis as audio_analysis
+import acts.test_utils.audio_analysis_lib.audio_data as audio_data
+import acts.test_utils.audio_analysis_lib.audio_quality_measurement as \
+ audio_quality_measurement
+
+# Holder for quality parameters used in audio_quality_measurement module.
+QualityParams = collections.namedtuple('QualityParams', [
+ 'block_size_secs', 'frequency_error_threshold',
+ 'delay_amplitude_threshold', 'noise_amplitude_threshold',
+ 'burst_amplitude_threshold'
+])
+
+DEFAULT_QUALITY_BLOCK_SIZE_SECS = 0.0015
+DEFAULT_BURST_AMPLITUDE_THRESHOLD = 1.4
+DEFAULT_DELAY_AMPLITUDE_THRESHOLD = 0.6
+DEFAULT_FREQUENCY_ERROR_THRESHOLD = 0.5
+DEFAULT_NOISE_AMPLITUDE_THRESHOLD = 0.5
+
+
+class WaveFileException(Exception):
+ """Error in WaveFile."""
+ pass
+
+
+class WaveFormatExtensibleException(Exception):
+ """Wave file is in WAVE_FORMAT_EXTENSIBLE format which is not supported."""
+ pass
+
+
+class WaveFile(object):
+ """Class which handles wave file reading.
+
+ Properties:
+ raw_data: audio_data.AudioRawData object for data in wave file.
+ rate: sampling rate.
+
+ """
+
+ def __init__(self, filename):
+ """Inits a wave file.
+
+ Args:
+ filename: file name of the wave file.
+
+ """
+ self.raw_data = None
+ self.rate = None
+
+ self._wave_reader = None
+ self._n_channels = None
+ self._sample_width_bits = None
+ self._n_frames = None
+ self._binary = None
+
+ try:
+ self._read_wave_file(filename)
+ except WaveFormatExtensibleException:
+ logging.warning(
+ 'WAVE_FORMAT_EXTENSIBLE is not supproted. '
+ 'Try command "sox in.wav -t wavpcm out.wav" to convert '
+ 'the file to WAVE_FORMAT_PCM format.')
+ self._convert_and_read_wav_file(filename)
+
+ def _convert_and_read_wav_file(self, filename):
+ """Converts the wav file and read it.
+
+ Converts the file into WAVE_FORMAT_PCM format using sox command and
+ reads its content.
+
+ Args:
+ filename: The wave file to be read.
+
+ Raises:
+ RuntimeError: sox is not installed.
+
+ """
+ # Checks if sox is installed.
+ try:
+ subprocess.check_output(['sox', '--version'])
+ except:
+ raise RuntimeError('sox command is not installed. '
+ 'Try sudo apt-get install sox')
+
+ with tempfile.NamedTemporaryFile(suffix='.wav') as converted_file:
+ command = ['sox', filename, '-t', 'wavpcm', converted_file.name]
+ logging.debug('Convert the file using sox: %s', command)
+ subprocess.check_call(command)
+ self._read_wave_file(converted_file.name)
+
+ def _read_wave_file(self, filename):
+ """Reads wave file header and samples.
+
+ Args:
+ filename: The wave file to be read.
+
+ @raises WaveFormatExtensibleException: Wave file is in
+ WAVE_FORMAT_EXTENSIBLE format.
+ @raises WaveFileException: Wave file format is not supported.
+
+ """
+ try:
+ self._wave_reader = wave.open(filename, 'r')
+ self._read_wave_header()
+ self._read_wave_binary()
+ except wave.Error as e:
+ if 'unknown format: 65534' in str(e):
+ raise WaveFormatExtensibleException()
+ else:
+ logging.exception('Unsupported wave format')
+ raise WaveFileException()
+ finally:
+ if self._wave_reader:
+ self._wave_reader.close()
+
+ def _read_wave_header(self):
+ """Reads wave file header.
+
+ @raises WaveFileException: wave file is compressed.
+
+ """
+ # Header is a tuple of
+ # (nchannels, sampwidth, framerate, nframes, comptype, compname).
+ header = self._wave_reader.getparams()
+ logging.debug('Wave header: %s', header)
+
+ self._n_channels = header[0]
+ self._sample_width_bits = header[1] * 8
+ self.rate = header[2]
+ self._n_frames = header[3]
+ comptype = header[4]
+ compname = header[5]
+
+ if comptype != 'NONE' or compname != 'not compressed':
+ raise WaveFileException('Can not support compressed wav file.')
+
+ def _read_wave_binary(self):
+ """Reads in samples in wave file."""
+ self._binary = self._wave_reader.readframes(self._n_frames)
+ format_str = 'S%d_LE' % self._sample_width_bits
+ self.raw_data = audio_data.AudioRawData(
+ binary=self._binary,
+ channel=self._n_channels,
+ sample_format=format_str)
+
+
+class QualityCheckerError(Exception):
+ """Error in QualityChecker."""
+ pass
+
+
+class CompareFailure(QualityCheckerError):
+ """Exception when frequency comparison fails."""
+ pass
+
+
+class QualityFailure(QualityCheckerError):
+ """Exception when quality check fails."""
+ pass
+
+
+class QualityChecker(object):
+ """Quality checker controls the flow of checking quality of raw data."""
+
+ def __init__(self, raw_data, rate):
+ """Inits a quality checker.
+
+ Args:
+ raw_data: An audio_data.AudioRawData object.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+
+ """
+ self._raw_data = raw_data
+ self._rate = rate
+ self._spectrals = []
+ self._quality_result = []
+
+ def do_spectral_analysis(self, ignore_high_freq, check_quality,
+ quality_params):
+ """Gets the spectral_analysis result.
+
+ Args:
+ ignore_high_freq: Ignore high frequencies above this threshold.
+ check_quality: Check quality of each channel.
+ quality_params: A QualityParams object for quality measurement.
+
+ """
+ self.has_data()
+ for channel_idx in range(self._raw_data.channel):
+ signal = self._raw_data.channel_data[channel_idx]
+ max_abs = max(numpy.abs(signal))
+ logging.debug('Channel %d max abs signal: %f', channel_idx,
+ max_abs)
+ if max_abs == 0:
+ logging.info('No data on channel %d, skip this channel',
+ channel_idx)
+ continue
+
+ saturate_value = audio_data.get_maximum_value_from_sample_format(
+ self._raw_data.sample_format)
+ normalized_signal = audio_analysis.normalize_signal(
+ signal, saturate_value)
+ logging.debug('saturate_value: %f', saturate_value)
+ logging.debug('max signal after normalized: %f',
+ max(normalized_signal))
+ spectral = audio_analysis.spectral_analysis(
+ normalized_signal, self._rate)
+
+ logging.debug('Channel %d spectral:\n%s', channel_idx,
+ pprint.pformat(spectral))
+
+ # Ignore high frequencies above the threshold.
+ spectral = [(f, c) for (f, c) in spectral if f < ignore_high_freq]
+
+ logging.info('Channel %d spectral after ignoring high frequencies '
+ 'above %f:\n%s', channel_idx, ignore_high_freq,
+ pprint.pformat(spectral))
+
+ try:
+ if check_quality:
+ quality = audio_quality_measurement.quality_measurement(
+ signal=normalized_signal,
+ rate=self._rate,
+ dominant_frequency=spectral[0][0],
+ block_size_secs=quality_params.block_size_secs,
+ frequency_error_threshold=quality_params.
+ frequency_error_threshold,
+ delay_amplitude_threshold=quality_params.
+ delay_amplitude_threshold,
+ noise_amplitude_threshold=quality_params.
+ noise_amplitude_threshold,
+ burst_amplitude_threshold=quality_params.
+ burst_amplitude_threshold)
+
+ logging.debug('Channel %d quality:\n%s', channel_idx,
+ pprint.pformat(quality))
+ self._quality_result.append(quality)
+ self._spectrals.append(spectral)
+ except Exception as error:
+ logging.warning(
+ "Failed to analyze channel {} with error: {}".format(
+ channel_idx, error))
+
+ def has_data(self):
+ """Checks if data has been set.
+
+ Raises:
+ QualityCheckerError: if data or rate is not set yet.
+
+ """
+ if not self._raw_data or not self._rate:
+ raise QualityCheckerError('Data and rate is not set yet')
+
+ def check_freqs(self, expected_freqs, freq_threshold):
+ """Checks the dominant frequencies in the channels.
+
+ Args:
+ expected_freq: A list of frequencies. If frequency is 0, it
+ means this channel should be ignored.
+ freq_threshold: The difference threshold to compare two
+ frequencies.
+
+ """
+ logging.debug('expected_freqs: %s', expected_freqs)
+ for idx, expected_freq in enumerate(expected_freqs):
+ if expected_freq == 0:
+ continue
+ if not self._spectrals[idx]:
+ raise CompareFailure(
+ 'Failed at channel %d: no dominant frequency' % idx)
+ dominant_freq = self._spectrals[idx][0][0]
+ if abs(dominant_freq - expected_freq) > freq_threshold:
+ raise CompareFailure(
+ 'Failed at channel %d: %f is too far away from %f' %
+ (idx, dominant_freq, expected_freq))
+
+ def check_quality(self):
+ """Checks the quality measurement results on each channel.
+
+ Raises:
+ QualityFailure when there is artifact.
+
+ """
+ error_msgs = []
+
+ for idx, quality_res in enumerate(self._quality_result):
+ artifacts = quality_res['artifacts']
+ if artifacts['noise_before_playback']:
+ error_msgs.append('Found noise before playback: %s' %
+ (artifacts['noise_before_playback']))
+ if artifacts['noise_after_playback']:
+ error_msgs.append('Found noise after playback: %s' %
+ (artifacts['noise_after_playback']))
+ if artifacts['delay_during_playback']:
+ error_msgs.append('Found delay during playback: %s' %
+ (artifacts['delay_during_playback']))
+ if artifacts['burst_during_playback']:
+ error_msgs.append('Found burst during playback: %s' %
+ (artifacts['burst_during_playback']))
+ if error_msgs:
+ raise QualityFailure('Found bad quality: %s',
+ '\n'.join(error_msgs))
+
+ def dump(self, output_file):
+ """Dumps the result into a file in json format.
+
+ Args:
+ output_file: A file path to dump spectral and quality
+ measurement result of each channel.
+
+ """
+ dump_dict = {
+ 'spectrals': self._spectrals,
+ 'quality_result': self._quality_result
+ }
+ with open(output_file, 'w') as f:
+ json.dump(dump_dict, f)
+
+ def has_data(self):
+ """Checks if data has been set.
+
+ Raises:
+ QualityCheckerError: if data or rate is not set yet.
+
+ """
+ if not self._raw_data or not self._rate:
+ raise QualityCheckerError('Data and rate is not set yet')
+
+ def check_freqs(self, expected_freqs, freq_threshold):
+ """Checks the dominant frequencies in the channels.
+
+ Args:
+ expected_freq: A list of frequencies. If frequency is 0, it
+ means this channel should be ignored.
+ freq_threshold: The difference threshold to compare two
+ frequencies.
+
+ """
+ logging.debug('expected_freqs: %s', expected_freqs)
+ for idx, expected_freq in enumerate(expected_freqs):
+ if expected_freq == 0:
+ continue
+ if not self._spectrals[idx]:
+ raise CompareFailure(
+ 'Failed at channel %d: no dominant frequency' % idx)
+ dominant_freq = self._spectrals[idx][0][0]
+ if abs(dominant_freq - expected_freq) > freq_threshold:
+ raise CompareFailure(
+ 'Failed at channel %d: %f is too far away from %f' %
+ (idx, dominant_freq, expected_freq))
+
+ def check_quality(self):
+ """Checks the quality measurement results on each channel.
+
+ Raises:
+ QualityFailure when there is artifact.
+
+ """
+ error_msgs = []
+
+ for idx, quality_res in enumerate(self._quality_result):
+ artifacts = quality_res['artifacts']
+ if artifacts['noise_before_playback']:
+ error_msgs.append('Found noise before playback: %s' %
+ (artifacts['noise_before_playback']))
+ if artifacts['noise_after_playback']:
+ error_msgs.append('Found noise after playback: %s' %
+ (artifacts['noise_after_playback']))
+ if artifacts['delay_during_playback']:
+ error_msgs.append('Found delay during playback: %s' %
+ (artifacts['delay_during_playback']))
+ if artifacts['burst_during_playback']:
+ error_msgs.append('Found burst during playback: %s' %
+ (artifacts['burst_during_playback']))
+ if error_msgs:
+ raise QualityFailure('Found bad quality: %s',
+ '\n'.join(error_msgs))
+
+ def dump(self, output_file):
+ """Dumps the result into a file in json format.
+
+ Args:
+ output_file: A file path to dump spectral and quality
+ measurement result of each channel.
+
+ """
+ dump_dict = {
+ 'spectrals': self._spectrals,
+ 'quality_result': self._quality_result
+ }
+ with open(output_file, 'w') as f:
+ json.dump(dump_dict, f)
+
+
+class CheckQualityError(Exception):
+ """Error in check_quality main function."""
+ pass
+
+
+def read_audio_file(filename, channel, bit_width, rate):
+ """Reads audio file.
+
+ Args:
+ filename: The wav or raw file to check.
+ channel: For raw file. Number of channels.
+ bit_width: For raw file. Bit width of a sample.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+
+
+ Returns:
+ A tuple (raw_data, rate) where raw_data is audio_data.AudioRawData, rate
+ is sampling rate.
+
+ """
+ if filename.endswith('.wav'):
+ wavefile = WaveFile(filename)
+ raw_data = wavefile.raw_data
+ rate = wavefile.rate
+ elif filename.endswith('.raw'):
+ binary = None
+ with open(filename, 'rb') as f:
+ binary = f.read()
+ raw_data = audio_data.AudioRawData(
+ binary=binary, channel=channel, sample_format='S%d_LE' % bit_width)
+ else:
+ raise CheckQualityError(
+ 'File format for %s is not supported' % filename)
+
+ return raw_data, rate
+
+
+def get_quality_params(
+ quality_block_size_secs, quality_frequency_error_threshold,
+ quality_delay_amplitude_threshold, quality_noise_amplitude_threshold,
+ quality_burst_amplitude_threshold):
+ """Gets quality parameters in arguments.
+
+ Args:
+ quality_block_size_secs: Input block size in seconds.
+ quality_frequency_error_threshold: Input the frequency error
+ threshold.
+ quality_delay_amplitude_threshold: Input the delay aplitutde
+ threshold.
+ quality_noise_amplitude_threshold: Input the noise aplitutde
+ threshold.
+ quality_burst_amplitude_threshold: Input the burst aplitutde
+ threshold.
+
+ Returns:
+ A QualityParams object.
+
+ """
+ quality_params = QualityParams(
+ block_size_secs=quality_block_size_secs,
+ frequency_error_threshold=quality_frequency_error_threshold,
+ delay_amplitude_threshold=quality_delay_amplitude_threshold,
+ noise_amplitude_threshold=quality_noise_amplitude_threshold,
+ burst_amplitude_threshold=quality_burst_amplitude_threshold)
+
+ return quality_params
+
+
+def quality_analysis(
+ filename,
+ output_file,
+ bit_width,
+ rate,
+ channel,
+ freqs=None,
+ freq_threshold=5,
+ ignore_high_freq=5000,
+ spectral_only=False,
+ quality_block_size_secs=DEFAULT_QUALITY_BLOCK_SIZE_SECS,
+ quality_burst_amplitude_threshold=DEFAULT_BURST_AMPLITUDE_THRESHOLD,
+ quality_delay_amplitude_threshold=DEFAULT_DELAY_AMPLITUDE_THRESHOLD,
+ quality_frequency_error_threshold=DEFAULT_FREQUENCY_ERROR_THRESHOLD,
+ quality_noise_amplitude_threshold=DEFAULT_NOISE_AMPLITUDE_THRESHOLD,
+):
+ """ Runs various functions to measure audio quality base on user input.
+
+ Args:
+ filename: The wav or raw file to check.
+ output_file: Output file to dump analysis result in JSON format.
+ bit_width: For raw file. Bit width of a sample.
+ rate: Sampling rate in samples per second. Example inputs: 44100,
+ 48000
+ channel: For raw file. Number of channels.
+ freqs: Expected frequencies in the channels.
+ freq_threshold: Frequency difference threshold in Hz.
+ ignore_high_freq: Frequency threshold in Hz to be ignored for high
+ frequency. Default is 5KHz
+ spectral_only: Only do spectral analysis on each channel.
+ quality_block_size_secs: Input block size in seconds.
+ quality_frequency_error_threshold: Input the frequency error
+ threshold.
+ quality_delay_amplitude_threshold: Input the delay aplitutde
+ threshold.
+ quality_noise_amplitude_threshold: Input the noise aplitutde
+ threshold.
+ quality_burst_amplitude_threshold: Input the burst aplitutde
+ threshold.
+ """
+ format = '%(asctime)-15s:%(levelname)s:%(pathname)s:%(lineno)d: %(message)s'
+ logging.basicConfig(format=format, level=logging.INFO)
+ raw_data, rate = read_audio_file(filename, channel, bit_width, rate)
+
+ checker = QualityChecker(raw_data, rate)
+
+ quality_params = get_quality_params(
+ quality_block_size_secs, quality_frequency_error_threshold,
+ quality_delay_amplitude_threshold, quality_noise_amplitude_threshold,
+ quality_burst_amplitude_threshold)
+
+ checker.do_spectral_analysis(
+ ignore_high_freq=ignore_high_freq,
+ check_quality=(not spectral_only),
+ quality_params=quality_params)
+
+ checker.dump(output_file)
+
+ if freqs:
+ checker.check_freqs(freqs, freq_threshold)
+
+ if not spectral_only:
+ checker.check_quality()
diff --git a/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py b/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py
index cded9f5..21d63c7 100644
--- a/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py
+++ b/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py
@@ -24,11 +24,24 @@
from acts import utils
from acts.base_test import BaseTestClass
from acts.signals import TestSignal
+from acts.utils import create_dir
+from acts.utils import dump_string_to_file
from acts.controllers import android_device
+from acts.libs.proto.proto_utils import compile_import_proto
+from acts.libs.proto.proto_utils import parse_proto_to_ascii
+from acts.test_utils.bt.bt_metrics_utils import get_bluetooth_metrics
+from acts.test_utils.bt.bt_test_utils import get_device_selector_dictionary
from acts.test_utils.bt.bt_test_utils import reset_bluetooth
from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts.test_utils.bt.ble_lib import BleLib
+from acts.test_utils.bt.bta_lib import BtaLib
+from acts.test_utils.bt.config_lib import ConfigLib
+from acts.test_utils.bt.gattc_lib import GattClientLib
+from acts.test_utils.bt.gatts_lib import GattServerLib
+from acts.test_utils.bt.rfcomm_lib import RfcommLib
+from acts.test_utils.bt.shell_commands_lib import ShellCommands
class BluetoothBaseTest(BaseTestClass):
@@ -38,6 +51,38 @@
def __init__(self, controllers):
BaseTestClass.__init__(self, controllers)
+ for ad in self.android_devices:
+ self._setup_bt_libs(ad)
+ if 'preferred_device_order' in self.user_params:
+ prefered_device_order = self.user_params['preferred_device_order']
+ for i, ad in enumerate(self.android_devices):
+ if ad.serial in prefered_device_order:
+ index = prefered_device_order.index(ad.serial)
+ self.android_devices[i], self.android_devices[index] = \
+ self.android_devices[index], self.android_devices[i]
+
+ def collect_bluetooth_manager_metrics_logs(self, ads, test_name):
+ """
+ Collect Bluetooth metrics logs, save an ascii log to disk and return
+ both binary and ascii logs to caller
+ :param ads: list of active Android devices
+ :return: List of binary metrics logs,
+ List of ascii metrics logs
+ """
+ bluetooth_logs = []
+ bluetooth_logs_ascii = []
+ for ad in ads:
+ serial = ad.serial
+ out_name = "{}_{}_{}".format(serial, test_name,
+ "bluetooth_metrics.txt")
+ bluetooth_log = get_bluetooth_metrics(ad,
+ ad.bluetooth_proto_module)
+ bluetooth_log_ascii = parse_proto_to_ascii(bluetooth_log)
+ dump_string_to_file(bluetooth_log_ascii,
+ os.path.join(ad.metrics_path, out_name))
+ bluetooth_logs.append(bluetooth_log)
+ bluetooth_logs_ascii.append(bluetooth_log_ascii)
+ return bluetooth_logs, bluetooth_logs_ascii
# Use for logging in the test cases to facilitate
# faster log lookup and reduce ambiguity in logging.
@@ -86,6 +131,8 @@
return _safe_wrap_test_case
def setup_class(self):
+ self.device_selector = get_device_selector_dictionary(
+ self.android_devices)
if "reboot_between_test_class" in self.user_params:
threads = []
for a in self.android_devices:
@@ -95,7 +142,42 @@
thread.start()
for t in threads:
t.join()
- return setup_multiple_devices_for_bt_test(self.android_devices)
+ if not setup_multiple_devices_for_bt_test(self.android_devices):
+ return False
+ if "bluetooth_proto_path" in self.user_params:
+ from google import protobuf
+
+ self.bluetooth_proto_path = self.user_params[
+ "bluetooth_proto_path"][0]
+ if not os.path.isfile(self.bluetooth_proto_path):
+ try:
+ self.bluetooth_proto_path = "{}/bluetooth.proto".format(
+ os.path.dirname(os.path.realpath(__file__)))
+ except Exception:
+ self.log.error("File not found.")
+ if not os.path.isfile(self.bluetooth_proto_path):
+ self.log.error("Unable to find Bluetooth proto {}.".format(
+ self.bluetooth_proto_path))
+ return False
+ for ad in self.android_devices:
+ ad.metrics_path = os.path.join(ad.log_path, "BluetoothMetrics")
+ create_dir(ad.metrics_path)
+ ad.bluetooth_proto_module = \
+ compile_import_proto(ad.metrics_path, self.bluetooth_proto_path)
+ if not ad.bluetooth_proto_module:
+ self.log.error("Unable to compile bluetooth proto at " +
+ self.bluetooth_proto_path)
+ return False
+ # Clear metrics.
+ get_bluetooth_metrics(ad, ad.bluetooth_proto_module)
+ return True
+
+ def teardown_class(self):
+ if "bluetooth_proto_path" in self.user_params:
+ # Collect metrics here bassed off class name
+ bluetooth_logs, bluetooth_logs_ascii = \
+ self.collect_bluetooth_manager_metrics_logs(
+ self.android_devices, self.__class__.__name__)
def setup_test(self):
self.timer_list = []
@@ -105,12 +187,10 @@
a.droid.wakeUpNow()
return True
- def teardown_test(self):
- return True
-
def on_fail(self, test_name, begin_time):
- self.log.debug("Test {} failed. Gathering bugreport and btsnoop logs".
- format(test_name))
+ self.log.debug(
+ "Test {} failed. Gathering bugreport and btsnoop logs".format(
+ test_name))
take_btsnoop_logs(self.android_devices, self, test_name)
self._take_bug_report(test_name, begin_time)
for _ in range(5):
@@ -142,3 +222,29 @@
self.log.info("Total items in list {}".format(
len(self.timer_list)))
self.timer_list = []
+
+ def _setup_bt_libs(self, android_device):
+ # Bluetooth Low Energy library.
+ setattr(android_device, "ble", BleLib(
+ log=self.log, dut=android_device))
+ # Bluetooth Adapter library.
+ setattr(android_device, "bta", BtaLib(
+ log=self.log, dut=android_device))
+ # Bluetooth stack config library.
+ setattr(android_device, "config",
+ ConfigLib(log=self.log, dut=android_device))
+ # GATT Client library.
+ setattr(android_device, "gattc",
+ GattClientLib(log=self.log, dut=android_device))
+ # GATT Server library.
+ setattr(android_device, "gatts",
+ GattServerLib(log=self.log, dut=android_device))
+ # RFCOMM library.
+ setattr(android_device, "rfcomm",
+ RfcommLib(log=self.log, dut=android_device))
+ # Shell command library
+ setattr(android_device, "shell",
+ ShellCommands(log=self.log, dut=android_device))
+ # Setup Android Device feature list
+ setattr(android_device, "features",
+ android_device.adb.shell("pm list features").split("\n"))
diff --git a/acts/framework/acts/test_utils/bt/BtFunhausBaseTest.py b/acts/framework/acts/test_utils/bt/BtFunhausBaseTest.py
index 5fb302a..7d0085b 100644
--- a/acts/framework/acts/test_utils/bt/BtFunhausBaseTest.py
+++ b/acts/framework/acts/test_utils/bt/BtFunhausBaseTest.py
@@ -43,8 +43,46 @@
def __init__(self, controllers):
BtMetricsBaseTest.__init__(self, controllers)
self.ad = self.android_devices[0]
+ self.dongle = self.relay_devices[0]
+
+ def _pair_devices(self):
+ self.ad.droid.bluetoothStartPairingHelper(False)
+ self.dongle.enter_pairing_mode()
+
+ self.ad.droid.bluetoothBond(self.dongle.mac_address)
+
+ end_time = time.time() + 20
+ self.ad.log.info("Verifying devices are bonded")
+ while time.time() < end_time:
+ bonded_devices = self.ad.droid.bluetoothGetBondedDevices()
+
+ for d in bonded_devices:
+ if d['address'] == self.dongle.mac_address:
+ self.ad.log.info("Successfully bonded to device.")
+ self.log.info("Bonded devices:\n{}".format(bonded_devices))
+ return True
+ self.ad.log.info("Failed to bond devices.")
+ return False
+
+ def setup_test(self):
+ super(BtFunhausBaseTest, self).setup_test()
+ self.dongle.setup()
+ tries = 5
+ # Since we are not concerned with pairing in this test, try 5 times.
+ while tries > 0:
+ if self._pair_devices():
+ return True
+ else:
+ tries -= 1
+ return False
+
+ def teardown_test(self):
+ super(BtFunhausBaseTest, self).teardown_test()
+ self.dongle.clean_up()
+ return True
def on_fail(self, test_name, begin_time):
+ self.dongle.clean_up()
self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
super(BtFunhausBaseTest, self).on_fail(test_name, begin_time)
@@ -80,8 +118,8 @@
music_path = os.path.join(self.user_params[Config.key_config_path],
music_path)
if not os.path.isdir(music_path):
- self.log.error("Unable to find music directory {}.".format(
- music_path))
+ self.log.error(
+ "Unable to find music directory {}.".format(music_path))
return False
if type(music_path) is list:
for item in music_path:
@@ -144,7 +182,7 @@
while time.time() < end_time:
if not self.ad.droid.bluetoothCheckState():
self.ad.log.error("Device {}'s Bluetooth state is off.".format(
- serial))
+ self.ad.serial))
return False
if self.ad.droid.bluetoothGetConnectedDevices() == 0:
self.ad.log.error(
diff --git a/acts/framework/acts/test_utils/bt/BtMetricsBaseTest.py b/acts/framework/acts/test_utils/bt/BtMetricsBaseTest.py
index 3964ca4..3528447 100644
--- a/acts/framework/acts/test_utils/bt/BtMetricsBaseTest.py
+++ b/acts/framework/acts/test_utils/bt/BtMetricsBaseTest.py
@@ -28,7 +28,6 @@
def __init__(self, controllers):
BluetoothBaseTest.__init__(self, controllers)
self.bluetooth_proto_path = None
- self.dongle = self.relay_devices[0]
self.ad = self.android_devices[0]
def setup_class(self):
@@ -46,8 +45,8 @@
except Exception:
self.log.error("File not found.")
if not os.path.isfile(self.bluetooth_proto_path):
- self.log.error("Unable to find Bluetooth proto {}."
- .format(self.bluetooth_proto_path))
+ self.log.error("Unable to find Bluetooth proto {}.".format(
+ self.bluetooth_proto_path))
return False
for ad in self.android_devices:
ad.metrics_path = os.path.join(ad.log_path, "BluetoothMetrics")
@@ -70,40 +69,8 @@
# Clear all metrics
for ad in self.android_devices:
get_bluetooth_metrics(ad, ad.bluetooth_proto_module)
- self.dongle.setup()
- tries = 5
- # Since we are not concerned with pairing in this test, try 5 times.
- while tries > 0:
- if self._pair_devices():
- return True
- else:
- tries -= 1
- return False
-
- def teardown_test(self):
- super(BtMetricsBaseTest, self).teardown_test()
- self.dongle.clean_up()
return True
- def _pair_devices(self):
- self.ad.droid.bluetoothStartPairingHelper(False)
- self.dongle.enter_pairing_mode()
-
- self.ad.droid.bluetoothBond(self.dongle.mac_address)
-
- end_time = time.time() + 20
- self.ad.log.info("Verifying devices are bonded")
- while time.time() < end_time:
- bonded_devices = self.ad.droid.bluetoothGetBondedDevices()
-
- for d in bonded_devices:
- if d['address'] == self.dongle.mac_address:
- self.ad.log.info("Successfully bonded to device.")
- self.log.info("Bonded devices:\n{}".format(bonded_devices))
- return True
- self.ad.log.info("Failed to bond devices.")
- return False
-
def collect_bluetooth_manager_metrics_logs(self, ads):
"""
Collect Bluetooth metrics logs, save an ascii log to disk and return
diff --git a/acts/tests/google/bt/pts/ble_lib.py b/acts/framework/acts/test_utils/bt/ble_lib.py
similarity index 73%
rename from acts/tests/google/bt/pts/ble_lib.py
rename to acts/framework/acts/test_utils/bt/ble_lib.py
index f8ff402..3bbaede 100644
--- a/acts/tests/google/bt/pts/ble_lib.py
+++ b/acts/framework/acts/test_utils/bt/ble_lib.py
@@ -21,10 +21,10 @@
from acts.test_utils.bt.bt_constants import ble_advertise_settings_tx_powers
from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
from acts.test_utils.bt.bt_constants import small_timeout
-from acts.test_utils.bt.bt_test_utils import adv_fail
+from acts.test_utils.bt.bt_constants import adv_fail
from acts.test_utils.bt.bt_constants import adv_succ
-from acts.test_utils.bt.bt_test_utils import advertising_set_on_own_address_read
-from acts.test_utils.bt.bt_test_utils import advertising_set_started
+from acts.test_utils.bt.bt_constants import advertising_set_on_own_address_read
+from acts.test_utils.bt.bt_constants import advertising_set_started
from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
import time
@@ -32,11 +32,10 @@
class BleLib():
- def __init__(self, log, mac_addr, dut):
+ def __init__(self, log, dut):
self.advertisement_list = []
self.dut = dut
self.log = log
- self.mac_addr = mac_addr
self.default_timeout = 5
self.set_advertisement_list = []
self.generic_uuid = "0000{}-0000-1000-8000-00805f9b34fb"
@@ -76,22 +75,25 @@
self.dut.droid.bleStartBleAdvertising(
advertise_callback, advertise_data, advertise_settings)
if self._verify_ble_adv_started(advertise_callback):
- self.log.info("Tracking Callback ID: {}".format(
- advertise_callback))
+ self.log.info(
+ "Tracking Callback ID: {}".format(advertise_callback))
self.advertisement_list.append(advertise_callback)
self.log.info(self.advertisement_list)
def start_connectable_advertisement_set(self, line):
"""Start Connectable Advertisement Set"""
adv_callback = self.dut.droid.bleAdvSetGenCallback()
- adv_data = {"includeDeviceName": True, }
- self.dut.droid.bleAdvSetStartAdvertisingSet({
- "connectable": True,
- "legacyMode": False,
- "primaryPhy": "PHY_LE_1M",
- "secondaryPhy": "PHY_LE_1M",
- "interval": 320
- }, adv_data, None, None, None, 0, 0, adv_callback)
+ adv_data = {
+ "includeDeviceName": True,
+ }
+ self.dut.droid.bleAdvSetStartAdvertisingSet(
+ {
+ "connectable": True,
+ "legacyMode": False,
+ "primaryPhy": "PHY_LE_1M",
+ "secondaryPhy": "PHY_LE_1M",
+ "interval": 320
+ }, adv_data, None, None, None, 0, 0, adv_callback)
evt = self.dut.ed.pop_event(
advertising_set_started.format(adv_callback), self.default_timeout)
set_id = evt['data']['setId']
@@ -151,8 +153,8 @@
self.dut.droid.bleStartBleAdvertising(
advertise_callback, advertise_data, advertise_settings)
if self._verify_ble_adv_started(advertise_callback):
- self.log.info("Tracking Callback ID: {}".format(
- advertise_callback))
+ self.log.info(
+ "Tracking Callback ID: {}".format(advertise_callback))
self.advertisement_list.append(advertise_callback)
self.log.info(self.advertisement_list)
@@ -175,3 +177,35 @@
return
self.dut.droid.bleStopBleAdvertising(callback_id)
self.advertisement_list.remove(callback_id)
+
+ def start_max_advertisements(self, line):
+ scan_response = None
+ if line:
+ scan_response = bool(line)
+ while (True):
+ try:
+ self.dut.droid.bleSetAdvertiseSettingsAdvertiseMode(
+ ble_advertise_settings_modes['low_latency'])
+ self.dut.droid.bleSetAdvertiseSettingsIsConnectable(True)
+ advertise_callback, advertise_data, advertise_settings = (
+ generate_ble_advertise_objects(self.dut.droid))
+ if scan_response:
+ self.dut.droid.bleStartBleAdvertisingWithScanResponse(
+ advertise_callback, advertise_data, advertise_settings,
+ advertise_data)
+ else:
+ self.dut.droid.bleStartBleAdvertising(
+ advertise_callback, advertise_data, advertise_settings)
+ if self._verify_ble_adv_started(advertise_callback):
+ self.log.info(
+ "Tracking Callback ID: {}".format(advertise_callback))
+ self.advertisement_list.append(advertise_callback)
+ self.log.info(self.advertisement_list)
+ else:
+ self.log.info("Advertisements active: {}".format(
+ len(self.advertisement_list)))
+ return False
+ except Exception as err:
+ self.log.info("Advertisements active: {}".format(
+ len(self.advertisement_list)))
+ return True
diff --git a/acts/framework/acts/test_utils/bt/bluetooth.proto b/acts/framework/acts/test_utils/bt/bluetooth.proto
index a43ff47..4a07e59 100644
--- a/acts/framework/acts/test_utils/bt/bluetooth.proto
+++ b/acts/framework/acts/test_utils/bt/bluetooth.proto
@@ -114,6 +114,15 @@
optional int32 tx_bytes = 2;
}
+enum A2dpSourceCodec {
+ A2DP_SOURCE_CODEC_UNKNOWN = 0;
+ A2DP_SOURCE_CODEC_SBC = 1;
+ A2DP_SOURCE_CODEC_AAC = 2;
+ A2DP_SOURCE_CODEC_APTX = 3;
+ A2DP_SOURCE_CODEC_APTX_HD = 4;
+ A2DP_SOURCE_CODEC_LDAC = 5;
+}
+
// Session information that gets logged for A2DP session.
message A2DPSession {
// Media timer in milliseconds.
@@ -139,6 +148,12 @@
// Total audio time in this A2DP session
optional int64 audio_duration_millis = 8;
+
+ // Audio codec used in this A2DP session in A2DP source role
+ optional A2dpSourceCodec source_codec = 9;
+
+ // Whether A2DP offload is enabled in this A2DP session
+ optional bool is_a2dp_offload = 10;
}
message PairEvent {
diff --git a/acts/framework/acts/test_utils/bt/bt_carkit_lib.py b/acts/framework/acts/test_utils/bt/bt_carkit_lib.py
new file mode 100644
index 0000000..f1fa9fa
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/bt_carkit_lib.py
@@ -0,0 +1,830 @@
+#/usr/bin/env python3.4
+#
+# 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.
+
+import time
+import os
+
+from acts.keys import Config
+from acts.utils import rand_ascii_str
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_constants import logcat_strings
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_BLUETOOTH
+from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
+from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts.test_utils.tel.tel_test_utils import get_phone_number
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts.test_utils.tel.tel_test_utils import num_active_calls
+from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts.test_utils.tel.tel_voice_utils import get_audio_route
+from acts.test_utils.tel.tel_voice_utils import set_audio_route
+from acts.test_utils.tel.tel_voice_utils import swap_calls
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts.utils import exe_cmd
+from acts.utils import get_current_epoch_time
+
+KEYCODE_VOLUME_UP = "input keyevent 24"
+KEYCODE_VOLUME_DOWN = "input keyevent 25"
+KEYCODE_EVENT_PLAY_PAUSE = "input keyevent 85"
+KEYCODE_MEDIA_STOP = "input keyevent 86"
+KEYCODE_EVENT_NEXT = "input keyevent 87"
+KEYCODE_EVENT_PREVIOUS = "input keyevent 88"
+KEYCODE_MEDIA_REWIND = "input keyevent 89"
+KEYCODE_MEDIA_FAST_FORWARD = "input keyevent 90"
+KEYCODE_MUTE = "input keyevent 91"
+
+default_timeout = 10
+
+
+class E2eBtCarkitLib():
+
+ android_devices = []
+ short_timeout = 3
+ active_call_id = None
+ hold_call_id = None
+ log = None
+ mac_address = None
+
+ def __init__(self, log, target_mac_address=None):
+ self.log = log
+ self.target_mac_address = target_mac_address
+
+ def connect_hsp_helper(self, ad):
+ end_time = time.time() + default_timeout + 10
+ connected_hsp_devices = len(ad.droid.bluetoothHspGetConnectedDevices())
+ while connected_hsp_devices != 1 and time.time() < end_time:
+ try:
+ ad.droid.bluetoothHspConnect(self.target_mac_address)
+ time.sleep(3)
+ if len(ad.droid.bluetoothHspGetConnectedDevices() == 1):
+ break
+ except Exception:
+ self.log.debug("Failed to connect hsp trying again...")
+ try:
+ ad.droid.bluetoothConnectBonded(self.target_mac_address)
+ except Exception:
+ self.log.info("Failed to connect to bonded device...")
+ connected_hsp_devices = len(
+ ad.droid.bluetoothHspGetConnectedDevices())
+ if connected_hsp_devices != 1:
+ self.log.error("Failed to reconnect to HSP service...")
+ return False
+ self.log.info("Connected to HSP service...")
+ return True
+
+ def setup_multi_call(self, caller0, caller1, callee):
+ outgoing_num = get_phone_number(self.log, callee)
+ if not initiate_call(self.log, caller0, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, callee):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ if not initiate_call(self.log, caller1, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, callee):
+ self.log.error("Failed to answer call.")
+ return False
+ return True
+
+ def process_tests(self, tests):
+ for test in tests:
+ try:
+ test()
+ except Exception as err:
+ self.log.error(err)
+
+ def run_suite_hfp_tests(self):
+ tests = [
+ self.outgoing_call_private_number,
+ self.outgoing_call_unknown_contact,
+ self.incomming_call_private_number,
+ self.incomming_call_unknown_contact,
+ self.outgoing_call_multiple_iterations,
+ self.outgoing_call_hsp_disabled_then_enabled_during_call,
+ self.call_audio_routes,
+ self.sms_during_incomming_call,
+ self.multi_incomming_call,
+ self.multi_call_audio_routing,
+ self.multi_call_swap_multiple_times,
+ self.outgoing_call_a2dp_play_before_and_after,
+ ]
+ _process_tests(tests)
+
+ def run_suite_hfp_conf_tests(self):
+ tests = [
+ self.multi_call_join_conference_call,
+ self.multi_call_join_conference_call_hangup_conf_call,
+ self.outgoing_multi_call_join_conference_call,
+ self.multi_call_join_conference_call_audio_routes,
+ ]
+ _process_tests(tests)
+
+ def run_suite_map_tests(self):
+ tests = [
+ self.sms_receive_different_sizes,
+ self.sms_receive_multiple,
+ self.sms_send_outgoing_texts,
+ ]
+ _process_tests(tests)
+
+ def run_suite_avrcp_tests(self):
+ tests = [
+ self.avrcp_play_pause,
+ self.avrcp_next_previous_song,
+ self.avrcp_next_previous,
+ self.avrcp_next_repetative,
+ ]
+ _process_tests(tests)
+
+ def disconnect_reconnect_multiple_iterations(self, pri_dut):
+ iteration_count = 5
+ self.log.info(
+ "Test disconnect-reconnect scenario from phone {} times.".format(
+ iteration_count))
+ self.log.info(
+ "This test will prompt for user interaction after each reconnect.")
+ input("Press enter to execute this testcase...")
+ #Assumes only one devices connected
+ grace_timeout = 4 #disconnect and reconnect timeout
+ for n in range(iteration_count):
+ self.log.info("Test iteration {}.".format(n + 1))
+ self.log.info("Disconnecting device {}...".format(
+ self.target_mac_address))
+ pri_dut.droid.bluetoothDisconnectConnected(self.target_mac_address)
+ # May have to do a longer sleep for carkits.... need to test
+ time.sleep(grace_timeout)
+ self.log.info("Connecting device {}...".format(
+ self.target_mac_address))
+ pri_dut.droid.bluetoothConnectBonded(self.target_mac_address)
+ if not self.connect_hsp_helper(pri_dut):
+ return False
+ start_time = time.time()
+ connected_devices = pri_dut.droid.bluetoothGetConnectedDevices()
+ self.log.info(
+ "Waiting up to 10 seconds for device to reconnect...")
+ while time.time() < start_time + 10 and len(connected_devices) != 1:
+ connected_devices = pri_dut.droid.bluetoothGetConnectedDevices(
+ )
+ time.sleep(1)
+ if len(connected_devices) != 1:
+ self.log.error(
+ "Failed to reconnect at iteration {}... continuing".format(
+ n))
+ return False
+ input("Continue to next iteration?")
+ return True
+
+ def disconnect_a2dp_only_then_reconnect(self, pri_dut):
+ self.log.info(
+ "Test disconnect-reconnect a2dp only scenario from phone.")
+ input("Press enter to execute this testcase...")
+ if not pri_dut.droid.bluetoothA2dpDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect A2DP service...")
+ return False
+ time.sleep(self.short_timeout)
+ result = input("Confirm A2DP disconnected? (Y/n) ")
+ if result == "n":
+ self.log.error(
+ "Tester confirmed that A2DP did not disconnect. Failing test.")
+ return False
+ if len(pri_dut.droid.bluetoothA2dpGetConnectedDevices()) != 0:
+ self.log.error("Failed to disconnect from A2DP service")
+ return False
+ pri_dut.droid.bluetoothA2dpConnect(self.target_mac_address)
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothA2dpGetConnectedDevices()) != 1:
+ self.log.error("Failed to reconnect to A2DP service...")
+ return False
+ return True
+
+ def disconnect_hsp_only_then_reconnect(self, pri_dut):
+ self.log.info(
+ "Test disconnect-reconnect hsp only scenario from phone.")
+ input("Press enter to execute this testcase...")
+ if not pri_dut.droid.bluetoothHspDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect HSP service...")
+ return False
+ time.sleep(self.short_timeout)
+ result = input("Confirm HFP disconnected? (Y/n) ")
+ pri_dut.droid.bluetoothHspConnect(self.target_mac_address)
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothHspGetConnectedDevices()) != 1:
+ self.log.error("Failed to connect from HSP service")
+ return False
+ return True
+
+ def disconnect_both_hsp_and_a2dp_then_reconnect(self, pri_dut):
+ self.log.info(
+ "Test disconnect-reconnect hsp and a2dp scenario from phone.")
+ input("Press enter to execute this testcase...")
+ if not pri_dut.droid.bluetoothA2dpDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect A2DP service...")
+ return False
+ if not pri_dut.droid.bluetoothHspDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect HSP service...")
+ return False
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothA2dpGetConnectedDevices()) != 0:
+ self.log.error("Failed to disconnect from A2DP service")
+ return False
+ if len(pri_dut.droid.bluetoothHspGetConnectedDevices()) != 0:
+ self.log.error("Failed to disconnect from HSP service")
+ return False
+ result = input("Confirm HFP and A2DP disconnected? (Y/n) ")
+ pri_dut.droid.bluetoothConnectBonded(self.target_mac_address)
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothA2dpGetConnectedDevices()) != 1:
+ self.log.error("Failed to reconnect to A2DP service...")
+ return False
+ if not self.connect_hsp_helper(pri_dut):
+ return False
+ return True
+
+ def outgoing_call_private_number(self, pri_dut, ter_dut):
+ self.log.info(
+ "Test outgoing call scenario from phone to private number")
+ input("Press enter to execute this testcase...")
+ outgoing_num = "*67" + get_phone_number(self.log, ter_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, ter_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def outgoing_call_a2dp_play_before_and_after(self, pri_dut, sec_dut):
+ self.log.info(
+ "Test outgoing call scenario while playing music. Music should resume after call."
+ )
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in a play state..."
+ )
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, sec_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter when music continues to play.")
+ self.log.info("Pausing Music...")
+ pri_dut.adb.shell(KEYCODE_EVENT_PLAY_PAUSE)
+ return True
+
+ def outgoing_call_unknown_contact(self, pri_dut, ter_dut):
+ self.log.info(
+ "Test outgoing call scenario from phone to unknow contact")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, ter_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, ter_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def incomming_call_private_number(self, pri_dut, ter_dut):
+ self.log.info(
+ "Test incomming call scenario to phone from private number")
+ input("Press enter to execute this testcase...")
+ outgoing_num = "*67" + get_phone_number(self.log, pri_dut)
+ if not initiate_call(self.log, ter_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, pri_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+
+ return True
+
+ def incomming_call_unknown_contact(self, pri_dut, ter_dut):
+ self.log.info(
+ "Test incomming call scenario to phone from unknown contact")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, pri_dut)
+ if not initiate_call(self.log, ter_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, pri_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def outgoing_call_multiple_iterations(self, pri_dut, sec_dut):
+ iteration_count = 3
+ self.log.info(
+ "Test outgoing call scenario from phone {} times from known contact".
+ format(iteration_count))
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ for _ in range(iteration_count):
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, sec_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def outgoing_call_hsp_disabled_then_enabled_during_call(
+ self, pri_dut, sec_dut):
+ self.log.info(
+ "Test outgoing call hsp disabled then enable during call.")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ if not pri_dut.droid.bluetoothHspDisconnect(self.target_mac_address):
+ self.log.error("Failed to disconnect HSP service...")
+ return False
+ time.sleep(self.short_timeout)
+ if len(pri_dut.droid.bluetoothHspGetConnectedDevices()) != 0:
+ self.log.error("Failed to disconnect from HSP service")
+ return False
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ time.sleep(default_timeout)
+ pri_dut.droid.bluetoothConnectBonded(self.target_mac_address)
+ time.sleep(self.short_timeout)
+ test_result = True
+ if len(pri_dut.droid.bluetoothHspGetConnectedDevices()) != 1:
+ self.log.error("Failed to reconnect to HSP service...")
+ return
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return test_result
+
+ def call_audio_routes(self, pri_dut, sec_dut):
+ self.log.info("Test various audio routes scenario from phone.")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, sec_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ call_id = pri_dut.droid.telecomCallGetCallIds()[0]
+ pri_dut.droid.telecomCallPlayDtmfTone(call_id, "9")
+ input("Press enter to switch to speaker...")
+ self.log.info("Switching to speaker.")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_SPEAKER)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_SPEAKER:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_SPEAKER))
+ return False
+ input("Press enter to switch to earpiece...")
+ self.log.info("Switching to earpiece.")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_EARPIECE)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_EARPIECE:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_EARPIECE))
+ return False
+ input("Press enter to switch to Bluetooth...")
+ self.log.info("Switching to Bluetooth...")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_BLUETOOTH)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_BLUETOOTH:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_BLUETOOTH))
+ return False
+ input("Press enter to hangup call...")
+ self.log.info("Hanging up call...")
+ pri_dut.droid.telecomCallStopDtmfTone(call_id)
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def sms_receive_different_sizes(self, pri_dut, sec_dut):
+ self.log.info("Test recieve sms.")
+ input("Press enter to execute this testcase...")
+ msg = [rand_ascii_str(50), rand_ascii_str(1), rand_ascii_str(500)]
+ if not sms_send_receive_verify(self.log, sec_dut, pri_dut, msg):
+ return False
+ else:
+ self.log.info("Successfully sent sms. Please verify on carkit.")
+ return True
+
+ def sms_receive_multiple(self, pri_dut, sec_dut):
+ text_count = 10
+ self.log.info(
+ "Test sending {} sms messages to phone.".format(text_count))
+ input("Press enter to execute this testcase...")
+ for _ in range(text_count):
+ msg = [rand_ascii_str(50)]
+ if not sms_send_receive_verify(self.log, sec_dut, pri_dut, msg):
+ return False
+ else:
+ self.log.info(
+ "Successfully sent sms. Please verify on carkit.")
+ return True
+
+ def sms_send_outgoing_texts(self, pri_dut, sec_dut):
+ self.log.info("Test send sms of different sizes.")
+ input("Press enter to execute this testcase...")
+ msg = [rand_ascii_str(50), rand_ascii_str(1), rand_ascii_str(500)]
+ if not sms_send_receive_verify(self.log, pri_dut, sec_dut, msg):
+ return False
+ else:
+ self.log.info("Successfully sent sms. Please verify on carkit.")
+ return True
+
+ def sms_during_incomming_call(self, pri_dut, sec_dut):
+ self.log.info(
+ "Test incomming call scenario to phone from unknown contact")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, pri_dut)
+ if not initiate_call(self.log, sec_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, pri_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ msg = [rand_ascii_str(10)]
+ if not sms_send_receive_verify(self.log, sec_dut, pri_dut, msg):
+ return False
+ else:
+ self.log.info("Successfully sent sms. Please verify on carkit.")
+ input("Press enter to hangup call...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_incomming_call(self, pri_dut, sec_dut, ter_dut):
+ self.log.info("Test 2 incomming calls scenario to phone.")
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_audio_routing(self, pri_dut, sec_dut, ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone, then test audio routing."
+ )
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to switch to earpiece...")
+ self.log.info("Switching to earpiece.")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_EARPIECE)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_EARPIECE:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_EARPIECE))
+ return False
+ input("Press enter to switch to Bluetooth...")
+ self.log.info("Switching to Bluetooth...")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_BLUETOOTH)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_BLUETOOTH:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_BLUETOOTH))
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_swap_multiple_times(self, pri_dut, sec_dut, ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone, then test audio routing."
+ )
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to swap active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ if not swap_calls(self.log, [pri_dut, sec_dut, ter_dut], calls[0],
+ calls[1], 5):
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_join_conference_call(self, pri_dut, sec_dut, ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone then join the calls.")
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to join active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ pri_dut.droid.telecomCallJoinCallsInConf(calls[0], calls[1])
+ time.sleep(WAIT_TIME_IN_CALL)
+ if num_active_calls(self.log, pri_dut) != 4:
+ self.log.error("Total number of call ids in {} is not 4.".format(
+ pri_dut.serial))
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_join_conference_call_hangup_conf_call(
+ self, pri_dut, sec_dut, ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone then join the calls, then terminate the call from the primary dut."
+ )
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to join active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ pri_dut.droid.telecomCallJoinCallsInConf(calls[0], calls[1])
+ time.sleep(WAIT_TIME_IN_CALL)
+ if num_active_calls(self.log, pri_dut) != 4:
+ self.log.error("Total number of call ids in {} is not 4.".format(
+ pri_dut.serial))
+ return False
+ input("Press enter to hangup conf call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def outgoing_multi_call_join_conference_call(self, pri_dut, sec_dut,
+ ter_dut):
+ self.log.info(
+ "Test 2 outgoing calls scenario from phone then join the calls.")
+ input("Press enter to execute this testcase...")
+ outgoing_num = get_phone_number(self.log, sec_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, sec_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ time.sleep(self.short_timeout)
+ outgoing_num = get_phone_number(self.log, ter_dut)
+ if not initiate_call(self.log, pri_dut, outgoing_num):
+ self.log.error("Failed to initiate call")
+ return False
+ if not wait_and_answer_call(self.log, ter_dut):
+ self.log.error("Failed to answer call.")
+ return False
+ input("Press enter to join active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ pri_dut.droid.telecomCallJoinCallsInConf(calls[0], calls[1])
+ time.sleep(WAIT_TIME_IN_CALL)
+ if num_active_calls(self.log, pri_dut) != 4:
+ self.log.error("Total number of call ids in {} is not 4.".format(
+ pri_dut.serial))
+ return False
+ input("Press enter to hangup call 1...")
+ if not hangup_call(self.log, sec_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ input("Press enter to hangup call 2...")
+ if not hangup_call(self.log, ter_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def multi_call_join_conference_call_audio_routes(self, pri_dut, sec_dut,
+ ter_dut):
+ self.log.info(
+ "Test 2 incomming calls scenario to phone then join the calls, then test different audio routes."
+ )
+ input("Press enter to execute this testcase...")
+ if not self.setup_multi_call(sec_dut, ter_dut, pri_dut):
+ return False
+ input("Press enter to join active calls...")
+ calls = pri_dut.droid.telecomCallGetCallIds()
+ pri_dut.droid.telecomCallJoinCallsInConf(calls[0], calls[1])
+ time.sleep(WAIT_TIME_IN_CALL)
+ if num_active_calls(self.log, pri_dut) != 4:
+ self.log.error("Total number of call ids in {} is not 4.".format(
+ pri_dut.serial))
+ return False
+ input("Press enter to switch to phone speaker...")
+ self.log.info("Switching to earpiece.")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_EARPIECE)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_EARPIECE:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_EARPIECE))
+ return False
+ input("Press enter to switch to Bluetooth...")
+ self.log.info("Switching to Bluetooth...")
+ set_audio_route(self.log, pri_dut, AUDIO_ROUTE_BLUETOOTH)
+ time.sleep(self.short_timeout)
+ if get_audio_route(self.log, pri_dut) != AUDIO_ROUTE_BLUETOOTH:
+ self.log.error(
+ "Audio Route not set to {}".format(AUDIO_ROUTE_BLUETOOTH))
+ return False
+ input("Press enter to hangup conf call...")
+ if not hangup_call(self.log, pri_dut):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def avrcp_play_pause(self, pri_dut):
+ play_pause_count = 5
+ self.log.info(
+ "Test AVRCP play/pause {} times.".format(play_pause_count))
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in the play state..."
+ )
+ for i in range(play_pause_count):
+ input("Execute iteration {}?".format(i + 1))
+ pri_dut.adb.shell(KEYCODE_EVENT_PLAY_PAUSE)
+ self.log.info("Test should end in a paused state.")
+ return True
+
+ def avrcp_next_previous_song(self, pri_dut):
+ self.log.info("Test AVRCP go to the next song then the previous song.")
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in the play state..."
+ )
+ self.log.info("Hitting Next input event...")
+ pri_dut.adb.shell(KEYCODE_EVENT_NEXT)
+ input("Press enter to go to the previous song")
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ self.log.info("Test should end on original song.")
+ return True
+
+ def avrcp_next_previous(self, pri_dut):
+ self.log.info(
+ "Test AVRCP go to the next song then the press previous after a few seconds."
+ )
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in the play state..."
+ )
+ self.log.info("Hitting Next input event...")
+ pri_dut.adb.shell(KEYCODE_EVENT_NEXT)
+ time.sleep(5)
+ self.log.info("Hitting Previous input event...")
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ self.log.info("Test should end on \"next\" song.")
+ return True
+
+ def avrcp_next_repetative(self, pri_dut):
+ iterations = 10
+ self.log.info("Test AVRCP go to the next {} times".format(iterations))
+ pri_dut.adb.shell(KEYCODE_EVENT_PREVIOUS)
+ input(
+ "Press enter to execute this testcase when music is in the play state..."
+ )
+ for i in range(iterations):
+ self.log.info(
+ "Hitting Next input event, iteration {}...".format(i + 1))
+ pri_dut.adb.shell(KEYCODE_EVENT_NEXT)
+ # Allow time for the carkit to update.
+ time.sleep(1)
+ return True
+
+ def _cycle_aboslute_volume_control_helper(self, volume_step,
+ android_volume_steps, pri_dut):
+ begin_time = get_current_epoch_time()
+ pri_dut.droid.setMediaVolume(volume_step)
+ percentage_to_set = int((volume_step / android_volume_steps) * 100)
+ self.log.info("Setting phone volume to {}%".format(percentage_to_set))
+ volume_info_logcat = pri_dut.search_logcat(
+ logcat_strings['media_playback_vol_changed'], begin_time)
+ if len(volume_info_logcat) > 1:
+ self.log.info("Instant response detected.")
+ carkit_response = volume_info_logcat[-1]['log_message'].split(',')
+ for item in carkit_response:
+ if " volume=" in item:
+ carkit_vol_response = int((
+ int(item.split("=")[-1]) / android_volume_steps) * 100)
+ self.log.info(
+ "Carkit set volume to {}%".format(carkit_vol_response))
+ result = input(
+ "Did volume change reflect properly on carkit and phone? (Y/n) "
+ ).lower()
+
+ def cycle_absolute_volume_control(self, pri_dut):
+ result = input(
+ "Does carkit support Absolute Volume Control? (Y/n) ").lower()
+ if result is "n":
+ return True
+ android_volume_steps = 25
+ for i in range(android_volume_steps):
+ self._cycle_aboslute_volume_control_helper(i, android_volume_steps,
+ pri_dut)
+ for i in reversed(range(android_volume_steps)):
+ self._cycle_aboslute_volume_control_helper(i, android_volume_steps,
+ pri_dut)
+ return True
+
+ def cycle_battery_level(self, pri_dut):
+ for i in range(11):
+ level = i * 10
+ pri_dut.shell.set_battery_level(level)
+ question = "Phone battery level {}. Has the carkit indicator " \
+ "changed? (Y/n) "
+ result = input(question.format(level)).lower()
+
+ def test_voice_recognition_from_phone(self, pri_dut):
+ result = input(
+ "Does carkit support voice recognition (BVRA)? (Y/n) ").lower()
+ if result is "n":
+ return True
+ input("Press enter to start voice recognition from phone.")
+ self.pri_dut.droid.bluetoothHspStartVoiceRecognition(
+ self.target_mac_address)
+ input("Press enter to stop voice recognition from phone.")
+ self.pri_dut.droid.bluetoothHspStopVoiceRecognition(
+ self.target_mac_address)
+
+ def test_audio_and_voice_recognition_from_phone(self, pri_dut):
+ result = input(
+ "Does carkit support voice recognition (BVRA)? (Y/n) ").lower()
+ if result is "n":
+ return True
+ # Start playing music here
+ input("Press enter to start voice recognition from phone.")
+ self.pri_dut.droid.bluetoothHspStartVoiceRecognition(
+ self.target_mac_address)
+ input("Press enter to stop voice recognition from phone.")
+ self.pri_dut.droid.bluetoothHspStopVoiceRecognition(
+ self.target_mac_address)
+ time.sleep(2)
+ result = input("Did carkit continue music playback after? (Y/n) ")
diff --git a/acts/framework/acts/test_utils/bt/bt_coc_test_utils.py b/acts/framework/acts/test_utils/bt/bt_coc_test_utils.py
new file mode 100644
index 0000000..306291f
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/bt_coc_test_utils.py
@@ -0,0 +1,291 @@
+#/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import logging
+import time
+from acts import utils
+
+from acts.test_utils.bt.bt_constants import bt_default_timeout
+from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
+from acts.test_utils.bt.bt_constants import default_le_connection_interval_ms
+from acts.test_utils.bt.bt_constants import default_le_data_length
+from acts.test_utils.bt.bt_constants import gatt_phy
+from acts.test_utils.bt.bt_constants import gatt_transport
+from acts.test_utils.bt.bt_constants import l2cap_coc_header_size
+from acts.test_utils.bt.bt_constants import le_connection_event_time_step_ms
+from acts.test_utils.bt.bt_constants import le_connection_interval_time_step_ms
+from acts.test_utils.bt.bt_constants import le_default_supervision_timeout
+from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
+from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+
+log = logging
+
+
+class BtCoCTestUtilsError(Exception):
+ pass
+
+
+def do_multi_connection_throughput(client_ad, list_server_ad,
+ list_client_conn_id, num_iterations,
+ number_buffers, buffer_size):
+ """Throughput measurements from one client to one-or-many servers.
+
+ Args:
+ client_ad: the Android device to perform the write.
+ list_server_ad: the list of Android server devices connected to this client.
+ list_client_conn_id: list of client connection IDs
+ num_iterations: the number of test repetitions.
+ number_buffers: the total number of data buffers to transmit per test.
+ buffer_size: the number of bytes per L2CAP data buffer.
+
+ Returns:
+ Throughput in terms of bytes per second, 0 if test failed.
+ """
+
+ total_num_bytes = 0
+ start_write_time = time.perf_counter()
+ client_ad.log.info(
+ "do_multi_connection_throughput: Before write. Start Time={:f}, "
+ "num_iterations={}, number_buffers={}, buffer_size={}, "
+ "number_buffers*buffer_size={}, num_servers={}".format(
+ start_write_time, num_iterations, number_buffers, buffer_size,
+ number_buffers * buffer_size, len(list_server_ad)))
+
+ if (len(list_server_ad) != len(list_client_conn_id)):
+ client_ad.log.error("do_multi_connection_throughput: invalid "
+ "parameters. Num of list_server_ad({}) != "
+ "list_client_conn({})".format(
+ len(list_server_ad), len(list_client_conn_id)))
+ return 0
+
+ try:
+ for _, client_conn_id in enumerate(list_client_conn_id):
+ client_ad.log.info("do_multi_connection_throughput: "
+ "client_conn_id={}".format(client_conn_id))
+ # Plumb the tx data queue with the first set of data buffers.
+ client_ad.droid.bluetoothConnectionThroughputSend(
+ number_buffers, buffer_size, client_conn_id)
+ except Exception as err:
+ client_ad.log.error("Failed to write data: {}".format(err))
+ return 0
+
+ # Each Loop iteration will write and read one set of buffers.
+ for _ in range(0, (num_iterations - 1)):
+ try:
+ for _, client_conn_id in enumerate(list_client_conn_id):
+ client_ad.droid.bluetoothConnectionThroughputSend(
+ number_buffers, buffer_size, client_conn_id)
+ except Exception as err:
+ client_ad.log.error("Failed to write data: {}".format(err))
+ return 0
+
+ for _, server_ad in enumerate(list_server_ad):
+ try:
+ server_ad.droid.bluetoothConnectionThroughputRead(
+ number_buffers, buffer_size)
+ total_num_bytes += number_buffers * buffer_size
+ except Exception as err:
+ server_ad.log.error("Failed to read data: {}".format(err))
+ return 0
+
+ for _, server_ad in enumerate(list_server_ad):
+ try:
+ server_ad.droid.bluetoothConnectionThroughputRead(
+ number_buffers, buffer_size)
+ total_num_bytes += number_buffers * buffer_size
+ except Exception as err:
+ server_ad.log.error("Failed to read data: {}".format(err))
+ return 0
+
+ end_read_time = time.perf_counter()
+
+ test_time = (end_read_time - start_write_time)
+ if (test_time == 0):
+ client_ad.log.error("Buffer transmits cannot take zero time")
+ return 0
+ data_rate = (1.000 * total_num_bytes) / test_time
+ log.info(
+ "Calculated using total write and read times: total_num_bytes={}, "
+ "test_time={}, data rate={:08.0f} bytes/sec, {:08.0f} bits/sec".format(
+ total_num_bytes, test_time, data_rate, (data_rate * 8)))
+ return data_rate
+
+
+def orchestrate_coc_connection(
+ client_ad,
+ server_ad,
+ is_ble,
+ secured_conn=False,
+ le_connection_interval=0,
+ le_tx_data_length=default_le_data_length,
+ accept_timeout_ms=default_bluetooth_socket_timeout_ms,
+ le_min_ce_len=0,
+ le_max_ce_len=0):
+ """Sets up the CoC connection between two Android devices.
+
+ Args:
+ client_ad: the Android device performing the connection.
+ server_ad: the Android device accepting the connection.
+ is_ble: using LE transport.
+ secured_conn: using secured connection
+ le_connection_interval: LE Connection interval. 0 means use default.
+ le_tx_data_length: LE Data Length used by BT Controller to transmit.
+ accept_timeout_ms: timeout while waiting for incoming connection.
+ Returns:
+ True if connection was successful or false if unsuccessful,
+ client connection ID,
+ and server connection ID
+ """
+ server_ad.droid.bluetoothStartPairingHelper()
+ client_ad.droid.bluetoothStartPairingHelper()
+
+ adv_callback = None
+ mac_address = None
+ if is_ble:
+ try:
+ # This will start advertising and scanning. Will fail if it could
+ # not find the advertisements from server_ad
+ client_ad.log.info(
+ "Orchestrate_coc_connection: Start BLE advertisement and"
+ "scanning. Secured Connection={}".format(secured_conn))
+ mac_address, adv_callback, scan_callback = (
+ get_mac_address_of_generic_advertisement(client_ad, server_ad))
+ except BtTestUtilsError as err:
+ raise BtCoCTestUtilsError(
+ "Orchestrate_coc_connection: Error in getting mac address: {}".
+ format(err))
+ else:
+ mac_address = server_ad.droid.bluetoothGetLocalAddress()
+ adv_callback = None
+
+ # Adjust the Connection Interval (if necessary)
+ bluetooth_gatt_1 = -1
+ gatt_callback_1 = -1
+ gatt_connected = False
+ if is_ble and (le_connection_interval != 0 or le_min_ce_len != 0 or le_max_ce_len != 0):
+ client_ad.log.info(
+ "Adjusting connection interval={}, le_min_ce_len={}, le_max_ce_len={}"
+ .format(le_connection_interval, le_min_ce_len, le_max_ce_len))
+ try:
+ bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection(
+ client_ad,
+ mac_address,
+ False,
+ transport=gatt_transport['le'],
+ opportunistic=False)
+ client_ad.droid.bleStopBleScan(scan_callback)
+ except GattTestUtilsError as err:
+ client_ad.log.error(err)
+ if (adv_callback != None):
+ server_ad.droid.bleStopBleAdvertising(adv_callback)
+ return False, None, None
+ client_ad.log.info("setup_gatt_connection returns success")
+ if (le_connection_interval != 0):
+ minInterval = le_connection_interval / le_connection_interval_time_step_ms
+ maxInterval = le_connection_interval / le_connection_interval_time_step_ms
+ else:
+ minInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+ maxInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+ if (le_min_ce_len != 0):
+ le_min_ce_len = le_min_ce_len / le_connection_event_time_step_ms
+ if (le_max_ce_len != 0):
+ le_max_ce_len = le_max_ce_len / le_connection_event_time_step_ms
+
+ return_status = client_ad.droid.gattClientRequestLeConnectionParameters(
+ bluetooth_gatt_1, minInterval, maxInterval, 0,
+ le_default_supervision_timeout, le_min_ce_len, le_max_ce_len)
+ if not return_status:
+ client_ad.log.error(
+ "gattClientRequestLeConnectionParameters returns failure")
+ if (adv_callback != None):
+ server_ad.droid.bleStopBleAdvertising(adv_callback)
+ return False, None, None
+ client_ad.log.info(
+ "gattClientRequestLeConnectionParameters returns success. Interval={}"
+ .format(minInterval))
+ gatt_connected = True
+ # For now, we will only test with 1 Mbit Phy.
+ # TODO: Add explicit tests with 2 MBit Phy.
+ client_ad.droid.gattClientSetPreferredPhy(
+ bluetooth_gatt_1, gatt_phy['1m'], gatt_phy['1m'], 0)
+
+ server_ad.droid.bluetoothSocketConnBeginAcceptThreadPsm(
+ accept_timeout_ms, is_ble, secured_conn)
+
+ psm_value = server_ad.droid.bluetoothSocketConnGetPsm()
+ client_ad.log.info("Assigned PSM value={}".format(psm_value))
+
+ client_ad.droid.bluetoothSocketConnBeginConnectThreadPsm(
+ mac_address, is_ble, psm_value, secured_conn)
+
+ if (le_tx_data_length != default_le_data_length) and is_ble:
+ client_ad.log.info("orchestrate_coc_connection: call "
+ "bluetoothSocketRequestMaximumTxDataLength")
+ client_ad.droid.bluetoothSocketRequestMaximumTxDataLength()
+
+ end_time = time.time() + bt_default_timeout
+ test_result = False
+ while time.time() < end_time:
+ if len(server_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
+ server_ad.log.info("CoC Server Connection Active")
+ if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
+ client_ad.log.info("CoC Client Connection Active")
+ test_result = True
+ break
+ time.sleep(1)
+
+ if (adv_callback != None):
+ server_ad.droid.bleStopBleAdvertising(adv_callback)
+
+ if not test_result:
+ client_ad.log.error("Failed to establish an CoC connection")
+ return False, None, None
+
+ if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
+ server_ad.log.info(
+ "CoC client_ad Connection Active, num=%d",
+ len(client_ad.droid.bluetoothSocketConnActiveConnections()))
+ else:
+ server_ad.log.info("Error CoC client_ad Connection Inactive")
+ client_ad.log.info("Error CoC client_ad Connection Inactive")
+
+ # Wait for the client to be ready
+ client_conn_id = None
+ while (client_conn_id == None):
+ client_conn_id = client_ad.droid.bluetoothGetLastConnId()
+ if (client_conn_id != None):
+ break
+ time.sleep(1)
+
+ # Wait for the server to be ready
+ server_conn_id = None
+ while (server_conn_id == None):
+ server_conn_id = server_ad.droid.bluetoothGetLastConnId()
+ if (server_conn_id != None):
+ break
+ time.sleep(1)
+
+ client_ad.log.info(
+ "orchestrate_coc_connection: client conn id={}, server conn id={}".
+ format(client_conn_id, server_conn_id))
+
+ if gatt_connected:
+ disconnect_gatt_connection(client_ad, bluetooth_gatt_1,
+ gatt_callback_1)
+ client_ad.droid.gattClientClose(bluetooth_gatt_1)
+
+ return True, client_conn_id, server_conn_id
diff --git a/acts/framework/acts/test_utils/bt/bt_constants.py b/acts/framework/acts/test_utils/bt/bt_constants.py
index 7a55aa4..5be4c79 100644
--- a/acts/framework/acts/test_utils/bt/bt_constants.py
+++ b/acts/framework/acts/test_utils/bt/bt_constants.py
@@ -18,10 +18,31 @@
bt_default_timeout = 15
default_rfcomm_timeout_ms = 10000
+default_bluetooth_socket_timeout_ms = 10000
pan_connect_timeout = 5
bt_discovery_timeout = 3
small_timeout = 0.0001
+# Time delay (in seconds) at the end of each LE CoC Test to give sufficient time
+# for the ACL LE link to be disconnected. The ACL link stays connected after
+# L2CAP disconnects. An example of the timeout is L2CAP_LINK_INACTIVITY_TOUT.
+# This delay must be greater than the maximum of these timeouts.
+# TODO: Investigate the use of broadcast intent
+# BluetoothDevice.ACTION_ACL_DISCONNECTED to replace this delay method.
+l2cap_max_inactivity_delay_after_disconnect = 5
+
+# LE specifications related constants
+le_connection_interval_time_step_ms = 1.25
+le_default_supervision_timeout = 2000
+default_le_data_length = 23
+default_le_connection_interval_ms = 30
+le_connection_event_time_step_ms = 0.625
+
+# Headers of LE L2CAP Connection-oriented Channels. See section 3.4, Vol 3, Part A, Version 5.0.
+l2cap_header_size = 4
+l2cap_coc_sdu_length_field_size = 2
+l2cap_coc_header_size = l2cap_header_size + l2cap_coc_sdu_length_field_size
+
java_integer = {"min": -2147483648, "max": 2147483647}
btsnoop_log_path_on_device = "/data/misc/bluetooth/logs/btsnoop_hci.log"
@@ -41,7 +62,6 @@
advertising_set_started = "AdvertisingSet{}onAdvertisingSetStarted"
advertising_set_stopped = "AdvertisingSet{}onAdvertisingSetStopped"
advertising_set_on_own_address_read = "AdvertisingSet{}onOwnAddressRead"
-advertising_set_stopped = "AdvertisingSet{}onAdvertisingSetStopped"
advertising_set_enabled = "AdvertisingSet{}onAdvertisingEnabled"
advertising_set_data_set = "AdvertisingSet{}onAdvertisingDataSet"
advertising_set_scan_response_set = "AdvertisingSet{}onScanResponseDataSet"
@@ -72,6 +92,9 @@
rfcomm_secure_uuid = "fa87c0d0-afac-11de-8a39-0800200c9a66"
rfcomm_insecure_uuid = "8ce255c0-200a-11e0-ac64-0800200c9a66"
+# bluetooth socket connection test uuid
+bluetooth_socket_conn_test_uuid = "12345678-1234-5678-9abc-123456789abc"
+
# Bluetooth Adapter Scan Mode Types
bt_scan_mode_types = {
"state_off": -1,
@@ -159,6 +182,20 @@
"undefined": -1
}
+# Bluetooth HID constants.
+hid_connection_timeout = 5
+
+# Bluetooth HID EventFacade constants.
+hid_on_set_report_event = "onSetReport"
+hid_on_get_report_event = "onGetReport"
+hid_on_set_protocol_event = "onSetProtocol"
+hid_on_intr_data_event = "onInterruptData"
+hid_on_virtual_cable_unplug_event = "onVirtualCableUnplug"
+hid_id_keyboard = 1
+hid_id_mouse = 2
+hid_default_event_timeout = 15
+hid_default_set_report_payload = "Haha"
+
### Bluetooth Constants End ###
### Bluetooth Low Energy Constants Begin ###
@@ -238,28 +275,40 @@
gatt_cb_err = {
"char_write_req_err":
"Characteristic Write Request event not found. Expected {}",
- "char_write_err": "Characteristic Write event not found. Expected {}",
+ "char_write_err":
+ "Characteristic Write event not found. Expected {}",
"desc_write_req_err":
"Descriptor Write Request event not found. Expected {}",
- "desc_write_err": "Descriptor Write event not found. Expected {}",
- "char_read_err": "Characteristic Read event not found. Expected {}",
- "char_read_req_err": "Characteristic Read Request not found. Expected {}",
- "desc_read_err": "Descriptor Read event not found. Expected {}",
+ "desc_write_err":
+ "Descriptor Write event not found. Expected {}",
+ "char_read_err":
+ "Characteristic Read event not found. Expected {}",
+ "char_read_req_err":
+ "Characteristic Read Request not found. Expected {}",
+ "desc_read_err":
+ "Descriptor Read event not found. Expected {}",
"desc_read_req_err":
"Descriptor Read Request event not found. Expected {}",
- "rd_remote_rssi_err": "Read Remote RSSI event not found. Expected {}",
+ "rd_remote_rssi_err":
+ "Read Remote RSSI event not found. Expected {}",
"gatt_serv_disc_err":
"GATT Services Discovered event not found. Expected {}",
- "serv_added_err": "Service Added event not found. Expected {}",
- "mtu_changed_err": "MTU Changed event not found. Expected {}",
- "mtu_serv_changed_err": "MTU Server Changed event not found. Expected {}",
+ "serv_added_err":
+ "Service Added event not found. Expected {}",
+ "mtu_changed_err":
+ "MTU Changed event not found. Expected {}",
+ "mtu_serv_changed_err":
+ "MTU Server Changed event not found. Expected {}",
"gatt_conn_changed_err":
"GATT Connection Changed event not found. Expected {}",
"char_change_err":
"GATT Characteristic Changed event not fond. Expected {}",
- "phy_read_err": "Phy Read event not fond. Expected {}",
- "phy_update_err": "Phy Update event not fond. Expected {}",
- "exec_write_err": "GATT Execute Write event not found. Expected {}"
+ "phy_read_err":
+ "Phy Read event not fond. Expected {}",
+ "phy_update_err":
+ "Phy Update event not fond. Expected {}",
+ "exec_write_err":
+ "GATT Execute Write event not found. Expected {}"
}
# GATT callback strings as defined in GattClientFacade.java and
@@ -511,3 +560,137 @@
}
### Bluetooth GATT Constants End ###
+
+### Chameleon Constants Begin ###
+
+# Chameleon audio bits per sample.
+audio_bits_per_sample_16 = 16
+audio_bits_per_sample_24 = 24
+audio_bits_per_sample_32 = 32
+
+# Chameleon audio sample rates.
+audio_sample_rate_44100 = 44100
+audio_sample_rate_48000 = 48000
+audio_sample_rate_88200 = 88200
+audio_sample_rate_96000 = 96000
+
+# Chameleon audio channel modes.
+audio_channel_mode_mono = 1
+audio_channel_mode_stereo = 2
+audio_channel_mode_8 = 8
+
+# Chameleon time delays.
+delay_after_binding_seconds = 0.5
+delay_before_record_seconds = 0.5
+silence_wait_seconds = 5
+
+# Chameleon bus endpoints.
+fpga_linein_bus_endpoint = 'Chameleon FPGA line-in'
+headphone_bus_endpoint = 'Cros device headphone'
+
+### Chameleon Constants End ###
+
+### Begin logcat strings dict"""
+logcat_strings = {
+ "media_playback_vol_changed": "onRouteVolumeChanged",
+}
+
+### End logcat strings dict"""
+
+### Begin Service Discovery UUIDS ###
+### Values match the Bluetooth SIG defined values: """
+""" https://www.bluetooth.com/specifications/assigned-numbers/service-discovery """
+sig_uuid_constants = {
+ "BASE_UUID": "0000{}-0000-1000-8000-00805F9B34FB",
+ "SDP": "0001",
+ "UDP": "0002",
+ "RFCOMM": "0003",
+ "TCP": "0004",
+ "TCS-BIN": "0005",
+ "TCS-AT": "0006",
+ "ATT": "0007",
+ "OBEX": "0008",
+ "IP": "0009",
+ "FTP": "000A",
+ "HTTP": "000C",
+ "WSP": "000E",
+ "BNEP": "000F",
+ "UPNP": "0010",
+ "HIDP": "0011",
+ "HardcopyControlChannel": "0012",
+ "HardcopyDataChannel": "0014",
+ "HardcopyNotification": "0016",
+ "AVCTP": "0017",
+ "AVDTP": "0019",
+ "CMTP": "001B",
+ "MCAPControlChannel": "001E",
+ "MCAPDataChannel": "001F",
+ "L2CAP": "0100",
+ "ServiceDiscoveryServerServiceClassID": "1000",
+ "BrowseGroupDescriptorServiceClassID": "1001",
+ "SerialPort": "1101",
+ "LANAccessUsingPPP": "1102",
+ "DialupNetworking": "1103",
+ "IrMCSync": "1104",
+ "OBEXObjectPush": "1105",
+ "OBEXFileTransfer": "1106",
+ "IrMCSyncCommand": "1107",
+ "Headset": "1108",
+ "CordlessTelephony": "1109",
+ "AudioSource": "110A",
+ "AudioSink": "110B",
+ "A/V_RemoteControlTarget": "110C",
+ "AdvancedAudioDistribution": "110D",
+ "A/V_RemoteControl": "110E",
+ "A/V_RemoteControlController": "110F",
+ "Intercom": "1110",
+ "Fax": "1111",
+ "Headset - Audio Gateway (AG)": "1112",
+ "WAP": "1113",
+ "WAP_CLIENT": "1114",
+ "PANU": "1115",
+ "NAP": "1116",
+ "GN": "1117",
+ "DirectPrinting": "1118",
+ "ReferencePrinting": "1119",
+ "ImagingResponder": "111B",
+ "ImagingAutomaticArchive": "111C",
+ "ImagingReferencedObjects": "111D",
+ "Handsfree": "111E",
+ "HandsfreeAudioGateway": "111F",
+ "DirectPrintingReferenceObjectsService": "1120",
+ "ReflectedUI": "1121",
+ "BasicPrinting": "1122",
+ "PrintingStatus": "1123",
+ "HumanInterfaceDeviceService": "1124",
+ "HardcopyCableReplacement": "1125",
+ "HCR_Print": "1126",
+ "HCR_Scan": "1127",
+ "Common_ISDN_Access": "1128",
+ "SIM_Access": "112D",
+ "Phonebook Access - PCE": "112E",
+ "Phonebook Access - PSE": "112F",
+ "Phonebook Access": "1130",
+ "Headset - HS": "1131",
+ "Message Access Server": "1132",
+ "Message Notification Server": "1133",
+ "Message Access Profile": "1134",
+ "GNSS": "1135",
+ "GNSS_Server": "1136",
+ "PnPInformation": "1200",
+ "GenericNetworking": "1201",
+ "GenericFileTransfer": "1202",
+ "GenericAudio": "1203",
+ "GenericTelephony": "1204",
+ "UPNP_Service": "1205",
+ "UPNP_IP_Service": "1206",
+ "ESDP_UPNP_IP_PAN": "1300",
+ "ESDP_UPNP_IP_LAP": "1301",
+ "ESDP_UPNP_L2CAP": "1302",
+ "VideoSource": "1303",
+ "VideoSink": "1304",
+ "VideoDistribution": "1305",
+ "HDP": "1400"
+}
+
+### End Service Discovery UUIDS ###
diff --git a/acts/framework/acts/test_utils/bt/bt_contacts_utils.py b/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
index ccebdbf..70eac5e 100644
--- a/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+# /usr/bin/env python3.4
#
# Copyright (C) 2016 The Android Open Source Project
#
@@ -24,7 +24,7 @@
import re
import random
import string
-
+import time
from acts.utils import exe_cmd
import queue
@@ -274,7 +274,7 @@
return len(contact_list)
-def import_device_contacts_from_vcf(device, destination_path, vcf_file):
+def import_device_contacts_from_vcf(device, destination_path, vcf_file, timeout=10):
"""Uploads and import vcf file to device.
"""
number_count = phone_number_count(destination_path, vcf_file)
@@ -283,6 +283,15 @@
phone_phonebook_path = "{}{}".format(STORAGE_PATH, vcf_file)
device.adb.push("{} {}".format(local_phonebook_path, phone_phonebook_path))
device.droid.importVcf("file://{}{}".format(STORAGE_PATH, vcf_file))
+ start_time = time.time()
+ while time.time() < start_time + timeout:
+ #TODO: use unattended way to bypass contact import module instead of keyevent
+ if "ImportVCardActivity" in device.get_my_current_focus_window():
+ # keyevent to allow contacts import from vcf file
+ for key in ["DPAD_RIGHT", "DPAD_RIGHT", "ENTER"]:
+ device.adb.shell("input keyevent KEYCODE_{}".format(key))
+ break
+ time.sleep(1)
if wait_for_phone_number_update_complete(device, number_count):
return number_count
else:
@@ -299,6 +308,15 @@
return True
+def delete_vcf_files(device):
+ """Deletes all files with .vcf extension
+ """
+ files = device.adb.shell("ls {}".format(STORAGE_PATH))
+ for file_name in files.split():
+ if ".vcf" in file_name:
+ device.adb.shell("rm -f {}{}".format(STORAGE_PATH, file_name))
+
+
def erase_contacts(device):
"""Erase all contacts out of devices contact database.
"""
@@ -379,8 +397,8 @@
for i in range(len(pse_call_log)):
# Compare the phone number
if normalize_phonenumber(pse_call_log[i][
- "number"]) != normalize_phonenumber(pce_call_log[i][
- "number"]):
+ "number"]) != normalize_phonenumber(pce_call_log[i][
+ "number"]):
log.warning("Call Log numbers differ")
call_logs_match = False
@@ -391,7 +409,7 @@
# Compare time to truncated second.
if int(pse_call_log[i]["date"]) // 1000 != int(pce_call_log[i][
- "date"]) // 1000:
+ "date"]) // 1000:
log.warning("Call log times don't match, check timezone.")
call_logs_match = False
diff --git a/acts/framework/acts/test_utils/bt/bt_gatt_utils.py b/acts/framework/acts/test_utils/bt/bt_gatt_utils.py
index 3f80d68..e3d410e 100644
--- a/acts/framework/acts/test_utils/bt/bt_gatt_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_gatt_utils.py
@@ -52,18 +52,18 @@
event = cen_ad.ed.pop_event(expected_event, default_timeout)
except Empty:
close_gatt_client(cen_ad, bluetooth_gatt)
- raise GattTestUtilsError("Could not establish a connection to "
- "peripheral. Expected event: {}".format(
- expected_event))
+ raise GattTestUtilsError(
+ "Could not establish a connection to "
+ "peripheral. Expected event: {}".format(expected_event))
if event['data']['State'] != gatt_connection_state['connected']:
close_gatt_client(cen_ad, bluetooth_gatt)
try:
cen_ad.droid.gattClientClose(bluetooth_gatt)
except Exception:
self.log.debug("Failed to close gatt client.")
- raise GattTestUtilsError(
- "Could not establish a connection to "
- "peripheral. Expected event: {}".format(expected_event))
+ raise GattTestUtilsError("Could not establish a connection to "
+ "peripheral. Event Details: {}".format(
+ pprint.pformat(event)))
return bluetooth_gatt, gatt_callback
@@ -85,8 +85,8 @@
try:
event = cen_ad.ed.pop_event(expected_event, default_timeout)
except Empty:
- raise GattTestUtilsError(gatt_cb_err['gatt_conn_change_err'].format(
- expected_event))
+ raise GattTestUtilsError(
+ gatt_cb_err['gatt_conn_change_err'].format(expected_event))
found_state = event['data']['State']
expected_state = gatt_connection_state['disconnected']
if found_state != expected_state:
@@ -106,7 +106,7 @@
if mac_address is None:
if transport == gatt_transport['le']:
try:
- mac_address, adv_callback = (
+ mac_address, adv_callback, scan_callback = (
get_mac_address_of_generic_advertisement(cen_ad, per_ad))
except BtTestUtilsError as err:
raise GattTestUtilsError(
@@ -119,17 +119,23 @@
return bluetooth_gatt, gatt_callback, adv_callback
-def run_continuous_write_descriptor(cen_droid, cen_ed, per_droid, per_ed,
- gatt_server, gatt_server_callback,
- bluetooth_gatt, services_count,
- discovered_services_index):
+def run_continuous_write_descriptor(cen_droid,
+ cen_ed,
+ per_droid,
+ per_ed,
+ gatt_server,
+ gatt_server_callback,
+ bluetooth_gatt,
+ services_count,
+ discovered_services_index,
+ number_of_iterations=100000):
log.info("Starting continuous write")
bt_device_id = 0
status = 1
offset = 1
- test_value = "1,2,3,4,5,6,7"
- test_value_return = "1,2,3"
- for x in range(100000):
+ test_value = [1, 2, 3, 4, 5, 6, 7]
+ test_value_return = [1, 2, 3]
+ for _ in range(number_of_iterations):
try:
for i in range(services_count):
characteristic_uuids = (
@@ -142,8 +148,6 @@
discovered_services_index, i, characteristic))
log.info(descriptor_uuids)
for descriptor in descriptor_uuids:
- log.info("descriptor to be written {}".format(
- descriptor))
cen_droid.gattClientDescriptorSetValue(
bluetooth_gatt, discovered_services_index, i,
characteristic, descriptor, test_value)
@@ -175,7 +179,7 @@
except Empty:
log.error(gatt_cb_strings['desc_write_err'].format(
expected_event))
- return False
+ raise Exception("Thread ended prematurely.")
except Exception as err:
log.error("Continuing but found exception: {}".format(err))
@@ -183,32 +187,45 @@
def setup_characteristics_and_descriptors(droid):
characteristic_input = [
{
- 'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
- 'property': gatt_characteristic['property_write'] |
- gatt_characteristic['property_write_no_response'],
- 'permission': gatt_characteristic['permission_write']
+ 'uuid':
+ "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+ 'property':
+ gatt_characteristic['property_write']
+ | gatt_characteristic['property_write_no_response'],
+ 'permission':
+ gatt_characteristic['permission_write']
},
{
- 'uuid': "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
- 'property': gatt_characteristic['property_notify'] |
- gatt_characteristic['property_read'],
- 'permission': gatt_characteristic['permission_read']
+ 'uuid':
+ "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
+ 'property':
+ gatt_characteristic['property_notify']
+ | gatt_characteristic['property_read'],
+ 'permission':
+ gatt_characteristic['permission_read']
},
{
- 'uuid': "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
- 'property': gatt_characteristic['property_notify'] |
- gatt_characteristic['property_read'],
- 'permission': gatt_characteristic['permission_read']
+ 'uuid':
+ "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
+ 'property':
+ gatt_characteristic['property_notify']
+ | gatt_characteristic['property_read'],
+ 'permission':
+ gatt_characteristic['permission_read']
},
]
descriptor_input = [{
- 'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
- 'property': gatt_descriptor['permission_read'] |
- gatt_descriptor['permission_write'],
+ 'uuid':
+ "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+ 'property':
+ gatt_descriptor['permission_read']
+ | gatt_descriptor['permission_write'],
}, {
- 'uuid': "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
- 'property': gatt_descriptor['permission_read'] |
- gatt_characteristic['permission_write'],
+ 'uuid':
+ "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
+ 'property':
+ gatt_descriptor['permission_read']
+ | gatt_characteristic['permission_write'],
}]
characteristic_list = setup_gatt_characteristics(droid,
characteristic_input)
@@ -241,8 +258,8 @@
per_ed.pop_event(expected_event, default_timeout)
except Empty:
per_ad.droid.gattServerClose(gatt_server)
- raise GattTestUtilsError(gatt_cb_strings['serv_added_err'].format(
- expected_event))
+ raise GattTestUtilsError(
+ gatt_cb_strings['serv_added_err'].format(expected_event))
for characteristic in characteristic_list:
per_droid.gattServerAddCharacteristicToService(gattService2,
characteristic)
@@ -251,8 +268,8 @@
per_ed.pop_event(expected_event, default_timeout)
except Empty:
per_ad.droid.gattServerClose(gatt_server)
- raise GattTestUtilsError(gatt_cb_strings['serv_added_err'].format(
- expected_event))
+ raise GattTestUtilsError(
+ gatt_cb_strings['serv_added_err'].format(expected_event))
for characteristic in characteristic_list:
per_droid.gattServerAddCharacteristicToService(gattService3,
characteristic)
@@ -261,40 +278,53 @@
per_ed.pop_event(expected_event, default_timeout)
except Empty:
per_ad.droid.gattServerClose(gatt_server)
- raise GattTestUtilsError(gatt_cb_strings['serv_added_err'].format(
- expected_event))
+ raise GattTestUtilsError(
+ gatt_cb_strings['serv_added_err'].format(expected_event))
return gatt_server_callback, gatt_server
def setup_characteristics_and_descriptors(droid):
characteristic_input = [
{
- 'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
- 'property': gatt_characteristic['property_write'] |
- gatt_characteristic['property_write_no_response'],
- 'permission': gatt_characteristic['property_write']
+ 'uuid':
+ "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+ 'property':
+ gatt_characteristic['property_write']
+ | gatt_characteristic['property_write_no_response'],
+ 'permission':
+ gatt_characteristic['property_write']
},
{
- 'uuid': "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
- 'property': gatt_characteristic['property_notify'] |
- gatt_characteristic['property_read'],
- 'permission': gatt_characteristic['permission_read']
+ 'uuid':
+ "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
+ 'property':
+ gatt_characteristic['property_notify']
+ | gatt_characteristic['property_read'],
+ 'permission':
+ gatt_characteristic['permission_read']
},
{
- 'uuid': "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
- 'property': gatt_characteristic['property_notify'] |
- gatt_characteristic['property_read'],
- 'permission': gatt_characteristic['permission_read']
+ 'uuid':
+ "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
+ 'property':
+ gatt_characteristic['property_notify']
+ | gatt_characteristic['property_read'],
+ 'permission':
+ gatt_characteristic['permission_read']
},
]
descriptor_input = [{
- 'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
- 'property': gatt_descriptor['permission_read'] |
- gatt_descriptor['permission_write'],
+ 'uuid':
+ "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+ 'property':
+ gatt_descriptor['permission_read']
+ | gatt_descriptor['permission_write'],
}, {
- 'uuid': "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
- 'property': gatt_descriptor['permission_read'] |
- gatt_characteristic['permission_write'],
+ 'uuid':
+ "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
+ 'property':
+ gatt_descriptor['permission_read']
+ | gatt_characteristic['permission_write'],
}]
characteristic_list = setup_gatt_characteristics(droid,
characteristic_input)
@@ -316,7 +346,8 @@
for item in input:
index = droid.gattServerCreateBluetoothGattDescriptor(
item['uuid'],
- item['property'], )
+ item['property'],
+ )
descriptor_list.append(index)
log.info("setup descriptor list: {}".format(descriptor_list))
return descriptor_list
@@ -344,8 +375,8 @@
mtu_event = cen_ad.ed.pop_event(expected_event, default_timeout)
mtu_size_found = mtu_event['data']['MTU']
if mtu_size_found != mtu:
- log.error("MTU size found: {}, expected: {}".format(mtu_size_found,
- mtu))
+ log.error("MTU size found: {}, expected: {}".format(
+ mtu_size_found, mtu))
return False
except Empty:
log.error(gatt_cb_err['mtu_changed_err'].format(expected_event))
diff --git a/acts/framework/acts/test_utils/bt/bt_metrics_utils.py b/acts/framework/acts/test_utils/bt/bt_metrics_utils.py
index ba9a1b6..3ed6f11 100644
--- a/acts/framework/acts/test_utils/bt/bt_metrics_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_metrics_utils.py
@@ -31,9 +31,5 @@
proto_native_str_64 = \
ad.adb.shell("/system/bin/dumpsys bluetooth_manager --proto-bin")
proto_native_str = base64.b64decode(proto_native_str_64)
- proto_java_str_64 = \
- ad.adb.shell("/system/bin/dumpsys bluetooth_manager --proto-java-bin")
- proto_java_str = base64.b64decode(proto_java_str_64)
bluetooth_log.MergeFromString(proto_native_str)
- bluetooth_log.MergeFromString(proto_java_str)
return bluetooth_log
diff --git a/acts/framework/acts/test_utils/bt/bt_test_utils.py b/acts/framework/acts/test_utils/bt/bt_test_utils.py
index 5658408..58f1a3c 100644
--- a/acts/framework/acts/test_utils/bt/bt_test_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_test_utils.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+# /usr/bin/env python3.4
#
# Copyright (C) 2016 The Android Open Source Project
#
@@ -53,16 +53,21 @@
from acts.test_utils.bt.bt_constants import bt_profile_states
from acts.test_utils.bt.bt_constants import bt_profile_constants
from acts.test_utils.bt.bt_constants import bt_rfcomm_uuids
+from acts.test_utils.bt.bt_constants import bluetooth_socket_conn_test_uuid
from acts.test_utils.bt.bt_constants import bt_scan_mode_types
from acts.test_utils.bt.bt_constants import btsnoop_last_log_path_on_device
from acts.test_utils.bt.bt_constants import btsnoop_log_path_on_device
from acts.test_utils.bt.bt_constants import default_rfcomm_timeout_ms
+from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
from acts.test_utils.bt.bt_constants import mtu_changed
from acts.test_utils.bt.bt_constants import pairing_variant_passkey_confirmation
from acts.test_utils.bt.bt_constants import pan_connect_timeout
from acts.test_utils.bt.bt_constants import small_timeout
from acts.test_utils.bt.bt_constants import scan_result
from acts.test_utils.bt.bt_constants import scan_failed
+from acts.test_utils.bt.bt_constants import sig_uuid_constants
+from acts.test_utils.bt.bt_constants import hid_id_keyboard
+
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
from acts.test_utils.tel.tel_test_utils import verify_http_connection
from acts.utils import exe_cmd
@@ -103,8 +108,8 @@
event = scn_ad.ed.pop_event(
scan_result.format(scan_callback), bt_default_timeout)
except Empty as error:
- raise BtTestUtilsError("Failed to find scan event: {}".format(
- error))
+ raise BtTestUtilsError(
+ "Failed to find scan event: {}".format(error))
address = event['data']['Result']['deviceInfo']['address']
if address not in address_list:
address_list.append(address)
@@ -141,8 +146,8 @@
adv_ad.log.info("Advertisement {} started.".format(i + 1))
except Empty as error:
adv_ad.log.error("Advertisement {} failed to start.".format(i + 1))
- raise BtTestUtilsError("Test failed with Empty error: {}".format(
- error))
+ raise BtTestUtilsError(
+ "Test failed with Empty error: {}".format(error))
return advertise_callback_list
@@ -219,7 +224,8 @@
threads = []
try:
for a in android_devices:
- thread = threading.Thread(target=reset_bluetooth, args=([[a]]))
+ thread = threading.Thread(
+ target=factory_reset_bluetooth, args=([[a]]))
threads.append(thread)
thread.start()
for t in threads:
@@ -229,7 +235,7 @@
d = a.droid
# TODO: Create specific RPC command to instantiate
# BluetoothConnectionFacade. This is just a workaround.
- d.bluetoothStartConnectionStateChangeMonitor("");
+ d.bluetoothStartConnectionStateChangeMonitor("")
setup_result = d.bluetoothSetLocalName(generate_id_by_size(4))
if not setup_result:
a.log.error("Failed to set device name.")
@@ -279,6 +285,71 @@
return True
+def wait_for_bluetooth_manager_state(droid,
+ state=None,
+ timeout=10,
+ threshold=5):
+ """ Waits for BlueTooth normalized state or normalized explicit state
+ args:
+ droid: droid device object
+ state: expected BlueTooth state
+ timeout: max timeout threshold
+ threshold: list len of bt state
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ all_states = []
+ get_state = lambda: droid.bluetoothGetLeState()
+ start_time = time.time()
+ while time.time() < start_time + timeout:
+ all_states.append(get_state())
+ if len(all_states) >= threshold:
+ # for any normalized state
+ if state is None:
+ if len(set(all_states[-threshold:])) == 1:
+ log.info("State normalized {}".format(
+ set(all_states[-threshold:])))
+ return True
+ else:
+ # explicit check against normalized state
+ if set([state]).issubset(all_states[-threshold:]):
+ return True
+ time.sleep(0.5)
+ log.error(
+ "Bluetooth state fails to normalize" if state is None else
+ "Failed to match bluetooth state, current state {} expected state {}".
+ format(get_state(), state))
+ return False
+
+
+def factory_reset_bluetooth(android_devices):
+ """Clears Bluetooth stack of input Android device list.
+
+ Args:
+ android_devices: The Android device list to reset Bluetooth
+
+ Returns:
+ True if successful, false if unsuccessful.
+ """
+ for a in android_devices:
+ droid, ed = a.droid, a.ed
+ a.log.info("Reset state of bluetooth on device.")
+ if not bluetooth_enabled_check(a):
+ return False
+ # TODO: remove device unbond b/79418045
+ # Temporary solution to ensure all devices are unbonded
+ bonded_devices = droid.bluetoothGetBondedDevices()
+ for b in bonded_devices:
+ a.log.info("Removing bond for device {}".format(b['address']))
+ droid.bluetoothUnbond(b['address'])
+
+ droid.bluetoothFactoryReset()
+ wait_for_bluetooth_manager_state(droid)
+ if not enable_bluetooth(droid, ed):
+ return False
+ return True
+
+
def reset_bluetooth(android_devices):
"""Resets Bluetooth state of input Android device list.
@@ -321,6 +392,7 @@
"Determining number of maximum concurrent advertisements...")
advertisement_count = 0
bt_enabled = False
+ expected_bluetooth_on_event_name = bluetooth_on
if not android_device.droid.bluetoothCheckState():
android_device.droid.bluetoothToggleState(True)
try:
@@ -350,14 +422,14 @@
regex = "(" + adv_succ.format(
advertise_callback) + "|" + adv_fail.format(
- advertise_callback) + ")"
+ advertise_callback) + ")"
# wait for either success or failure event
evt = android_device.ed.pop_events(regex, bt_default_timeout,
small_timeout)
if evt[0]["name"] == adv_succ.format(advertise_callback):
advertisement_count += 1
- android_device.log.info("Advertisement {} started.".format(
- advertisement_count))
+ android_device.log.info(
+ "Advertisement {} started.".format(advertisement_count))
else:
error = evt[0]["data"]["Error"]
if error == "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS":
@@ -403,10 +475,11 @@
else:
max_advertisements = determine_max_advertisements(a)
max_tries = 3
- #Retry to calculate max advertisements
+ # Retry to calculate max advertisements
while max_advertisements == -1 and max_tries > 0:
- a.log.info("Attempts left to determine max advertisements: {}".
- format(max_tries))
+ a.log.info(
+ "Attempts left to determine max advertisements: {}".format(
+ max_tries))
max_advertisements = determine_max_advertisements(a)
max_tries -= 1
advertisements_to_devices[model] = max_advertisements
@@ -425,7 +498,7 @@
def generate_id_by_size(
size,
chars=(
- string.ascii_lowercase + string.ascii_uppercase + string.digits)):
+ string.ascii_lowercase + string.ascii_uppercase + string.digits)):
"""Generate random ascii characters of input size and input char types
Args:
@@ -465,7 +538,7 @@
except Exception as err:
adv_android_device.log.debug(
"Failed to stop LE advertisement... reseting Bluetooth. Error {}".
- format(err))
+ format(err))
reset_bluetooth([adv_android_device])
@@ -508,11 +581,10 @@
event = scan_ad.ed.pop_event(
"BleScan{}onScanResults".format(scan_callback), bt_default_timeout)
except Empty as err:
- raise BtTestUtilsError("Scanner did not find advertisement {}".format(
- err))
+ raise BtTestUtilsError(
+ "Scanner did not find advertisement {}".format(err))
mac_address = event['data']['Result']['deviceInfo']['address']
- scan_ad.droid.bleStopBleScan(scan_callback)
- return mac_address, advertise_callback
+ return mac_address, advertise_callback, scan_callback
def enable_bluetooth(droid, ed):
@@ -647,8 +719,7 @@
"""Sets the priority of said profile(s) on host_ad for client_ad"""
for profile in profiles:
host_ad.log.info("Profile {} on {} for {} set to priority {}".format(
- profile,
- host_ad.droid.bluetoothGetLocalName(),
+ profile, host_ad.droid.bluetoothGetLocalName(),
client_ad.droid.bluetoothGetLocalAddress(), priority.value))
if bt_profile_constants['a2dp_sink'] == profile:
host_ad.droid.bluetoothA2dpSinkSetPriority(
@@ -712,8 +783,8 @@
timeout=bt_default_timeout)
pri_variant = pri_pairing_req["data"]["PairingVariant"]
pri_pin = pri_pairing_req["data"]["Pin"]
- pri_ad.log.info("Primary device received Pin: {}, Variant: {}"
- .format(pri_pin, pri_variant))
+ pri_ad.log.info("Primary device received Pin: {}, Variant: {}".format(
+ pri_pin, pri_variant))
sec_pairing_req = sec_ad.ed.pop_event(
event_name="BluetoothActionPairingRequest",
timeout=bt_default_timeout)
@@ -723,8 +794,8 @@
.format(sec_pin, sec_variant))
except Empty as err:
log.error("Wait for pin error: {}".format(err))
- log.error("Pairing request state, Primary: {}, Secondary: {}"
- .format(pri_pairing_req, sec_pairing_req))
+ log.error("Pairing request state, Primary: {}, Secondary: {}".format(
+ pri_pairing_req, sec_pairing_req))
return False
if pri_variant == sec_variant == pairing_variant_passkey_confirmation:
confirmation = pri_pin == sec_pin
@@ -832,7 +903,7 @@
paired = False
for paired_device in pri_ad.droid.bluetoothGetBondedDevices():
if paired_device['address'] == \
- sec_ad.droid.bluetoothGetLocalAddress():
+ sec_ad.droid.bluetoothGetLocalAddress():
paired = True
break
@@ -842,34 +913,34 @@
# Now try to connect them, the following call will try to initiate all
# connections.
- pri_ad.droid.bluetoothConnectBonded(sec_ad.droid.bluetoothGetLocalAddress(
- ))
+ pri_ad.droid.bluetoothConnectBonded(
+ sec_ad.droid.bluetoothGetLocalAddress())
end_time = time.time() + 10
profile_connected = set()
sec_addr = sec_ad.droid.bluetoothGetLocalAddress()
pri_ad.log.info("Profiles to be connected {}".format(profiles_set))
# First use APIs to check profile connection state
- while (time.time() < end_time and
- not profile_connected.issuperset(profiles_set)):
- if (bt_profile_constants['headset_client'] not in profile_connected and
- bt_profile_constants['headset_client'] in profiles_set):
+ while (time.time() < end_time
+ and not profile_connected.issuperset(profiles_set)):
+ if (bt_profile_constants['headset_client'] not in profile_connected
+ and bt_profile_constants['headset_client'] in profiles_set):
if is_hfp_client_device_connected(pri_ad, sec_addr):
profile_connected.add(bt_profile_constants['headset_client'])
- if (bt_profile_constants['a2dp'] not in profile_connected and
- bt_profile_constants['a2dp'] in profiles_set):
+ if (bt_profile_constants['a2dp'] not in profile_connected
+ and bt_profile_constants['a2dp'] in profiles_set):
if is_a2dp_src_device_connected(pri_ad, sec_addr):
profile_connected.add(bt_profile_constants['a2dp'])
- if (bt_profile_constants['a2dp_sink'] not in profile_connected and
- bt_profile_constants['a2dp_sink'] in profiles_set):
+ if (bt_profile_constants['a2dp_sink'] not in profile_connected
+ and bt_profile_constants['a2dp_sink'] in profiles_set):
if is_a2dp_snk_device_connected(pri_ad, sec_addr):
profile_connected.add(bt_profile_constants['a2dp_sink'])
- if (bt_profile_constants['map_mce'] not in profile_connected and
- bt_profile_constants['map_mce'] in profiles_set):
+ if (bt_profile_constants['map_mce'] not in profile_connected
+ and bt_profile_constants['map_mce'] in profiles_set):
if is_map_mce_device_connected(pri_ad, sec_addr):
profile_connected.add(bt_profile_constants['map_mce'])
- if (bt_profile_constants['map'] not in profile_connected and
- bt_profile_constants['map'] in profiles_set):
+ if (bt_profile_constants['map'] not in profile_connected
+ and bt_profile_constants['map'] in profiles_set):
if is_map_mse_device_connected(pri_ad, sec_addr):
profile_connected.add(bt_profile_constants['map'])
time.sleep(0.1)
@@ -890,10 +961,10 @@
device_addr = profile_event['data']['addr']
if state == bt_profile_states['connected'] and \
- device_addr == sec_ad.droid.bluetoothGetLocalAddress():
+ device_addr == sec_ad.droid.bluetoothGetLocalAddress():
profile_connected.add(profile)
- pri_ad.log.info("Profiles connected until now {}".format(
- profile_connected))
+ pri_ad.log.info(
+ "Profiles connected until now {}".format(profile_connected))
# Failure happens inside the while loop. If we came here then we already
# connected.
return True
@@ -938,10 +1009,11 @@
while not profile_disconnected.issuperset(profiles_list):
try:
profile_event = pri_ad.ed.pop_event(
- bluetooth_profile_connection_state_changed, default_timeout)
+ bluetooth_profile_connection_state_changed, bt_default_timeout)
pri_ad.log.info("Got event {}".format(profile_event))
- except Exception:
- pri_ad.log.error("Did not disconnect from Profiles")
+ except Exception as e:
+ pri_ad.log.error(
+ "Did not disconnect from Profiles. Reason {}".format(e))
return False
profile = profile_event['data']['profile']
@@ -949,10 +1021,10 @@
device_addr = profile_event['data']['addr']
if state == bt_profile_states['disconnected'] and \
- device_addr == sec_ad.droid.bluetoothGetLocalAddress():
+ device_addr == sec_ad.droid.bluetoothGetLocalAddress():
profile_disconnected.add(profile)
- pri_ad.log.info("Profiles disconnected so far {}".format(
- profile_disconnected))
+ pri_ad.log.info(
+ "Profiles disconnected so far {}".format(profile_disconnected))
return True
@@ -998,8 +1070,8 @@
snoop_path + '/' + out_name, ".btsnoop_hci.log.last"))
exe_cmd(cmd)
except Exception as err:
- testcase.log.info("File does not exist {}".format(
- btsnoop_last_log_path_on_device))
+ testcase.log.info(
+ "File does not exist {}".format(btsnoop_last_log_path_on_device))
def kill_bluetooth_process(ad):
@@ -1026,32 +1098,50 @@
Returns:
True if connection was successful, false if unsuccessful.
"""
+ result = orchestrate_bluetooth_socket_connection(
+ client_ad, server_ad, accept_timeout_ms,
+ (bt_rfcomm_uuids['default_uuid'] if uuid is None else uuid))
+
+ return result
+
+
+def orchestrate_bluetooth_socket_connection(
+ client_ad,
+ server_ad,
+ accept_timeout_ms=default_bluetooth_socket_timeout_ms,
+ uuid=None):
+ """Sets up the Bluetooth Socket connection between two Android devices.
+
+ Args:
+ client_ad: the Android device performing the connection.
+ server_ad: the Android device accepting the connection.
+ Returns:
+ True if connection was successful, false if unsuccessful.
+ """
server_ad.droid.bluetoothStartPairingHelper()
client_ad.droid.bluetoothStartPairingHelper()
- if not uuid:
- server_ad.droid.bluetoothRfcommBeginAcceptThread(
- bt_rfcomm_uuids['default_uuid'], accept_timeout_ms)
- client_ad.droid.bluetoothRfcommBeginConnectThread(
- server_ad.droid.bluetoothGetLocalAddress(),
- bt_rfcomm_uuids['default_uuid'])
- else:
- server_ad.droid.bluetoothRfcommBeginAcceptThread(uuid,
- accept_timeout_ms)
- client_ad.droid.bluetoothRfcommBeginConnectThread(
- server_ad.droid.bluetoothGetLocalAddress(), uuid)
+
+ server_ad.droid.bluetoothSocketConnBeginAcceptThreadUuid(
+ (bluetooth_socket_conn_test_uuid
+ if uuid is None else uuid), accept_timeout_ms)
+ client_ad.droid.bluetoothSocketConnBeginConnectThreadUuid(
+ server_ad.droid.bluetoothGetLocalAddress(),
+ (bluetooth_socket_conn_test_uuid if uuid is None else uuid))
+
end_time = time.time() + bt_default_timeout
result = False
test_result = True
while time.time() < end_time:
- if len(client_ad.droid.bluetoothRfcommActiveConnections()) > 0:
+ if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
test_result = True
- client_ad.log.info("RFCOMM Client Connection Active")
+ client_ad.log.info("Bluetooth socket Client Connection Active")
break
else:
test_result = False
time.sleep(1)
if not test_result:
- client_ad.log.error("Failed to establish an RFCOMM connection")
+ client_ad.log.error(
+ "Failed to establish a Bluetooth socket connection")
return False
return True
@@ -1071,19 +1161,19 @@
client_ad.log.info("Write message.")
try:
if binary:
- client_ad.droid.bluetoothRfcommWriteBinary(msg)
+ client_ad.droid.bluetoothSocketConnWriteBinary(msg)
else:
- client_ad.droid.bluetoothRfcommWrite(msg)
+ client_ad.droid.bluetoothSocketConnWrite(msg)
except Exception as err:
client_ad.log.error("Failed to write data: {}".format(err))
return False
server_ad.log.info("Read message.")
try:
if binary:
- read_msg = server_ad.droid.bluetoothRfcommReadBinary().rstrip(
+ read_msg = server_ad.droid.bluetoothSocketConnReadBinary().rstrip(
"\r\n")
else:
- read_msg = server_ad.droid.bluetoothRfcommRead()
+ read_msg = server_ad.droid.bluetoothSocketConnRead()
except Exception as err:
server_ad.log.error("Failed to read data: {}".format(err))
return False
@@ -1127,13 +1217,13 @@
false if unsuccessful.
"""
test_result = True
- if len(server_ad.droid.bluetoothRfcommActiveConnections()) == 0:
+ if len(server_ad.droid.bluetoothSocketConnActiveConnections()) == 0:
if log:
- server_ad.log.error("No rfcomm connections found on server.")
+ server_ad.log.error("No socket connections found on server.")
test_result = False
- if len(client_ad.droid.bluetoothRfcommActiveConnections()) == 0:
+ if len(client_ad.droid.bluetoothSocketConnActiveConnections()) == 0:
if log:
- client_ad.log.error("No rfcomm connections found on client.")
+ client_ad.log.error("No socket connections found on client.")
test_result = False
return test_result
@@ -1260,3 +1350,128 @@
return True
return False
+
+def hid_keyboard_report(key, modifier="00"):
+ """Get the HID keyboard report for the given key
+
+ Args:
+ key: the key we want
+ modifier: HID keyboard modifier bytes
+ Returns:
+ The byte array for the HID report.
+ """
+ return str(
+ bytearray.fromhex(" ".join(
+ [modifier, "00", key, "00", "00", "00", "00", "00"])), "utf-8")
+
+
+def hid_device_send_key_data_report(host_id, device_ad, key, interval=1):
+ """Send a HID report simulating a 1-second keyboard press from host_ad to
+ device_ad
+
+ Args:
+ host_id: the Bluetooth MAC address or name of the HID host
+ device_ad: HID device
+ key: the key we want to send
+ interval: the interval between key press and key release
+ """
+ device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard,
+ hid_keyboard_report(key))
+ time.sleep(interval)
+ device_ad.droid.bluetoothHidDeviceSendReport(host_id, hid_id_keyboard,
+ hid_keyboard_report("00"))
+
+
+def is_a2dp_connected(sink, source):
+ """
+ Convenience Function to see if the 2 devices are connected on
+ A2dp.
+ Args:
+ sink: Audio Sink
+ source: Audio Source
+ Returns:
+ True if Connected
+ False if Not connected
+ """
+
+ devices = sink.droid.bluetoothA2dpSinkGetConnectedDevices()
+ for device in devices:
+ sink.log.info("A2dp Connected device {}".format(device["name"]))
+ if (device["address"] == source.droid.bluetoothGetLocalAddress()):
+ return True
+ return False
+
+
+def get_device_selector_dictionary(android_device_list):
+ """Create a dictionary of Bluetooth features vs Android devices.
+
+ Args:
+ android_device_list: The list of Android devices.
+ Returns:
+ A dictionary of profiles/features to Android devices.
+ """
+ selector_dict = {}
+ for ad in android_device_list:
+ uuids = ad.droid.bluetoothGetLocalUuids()
+
+ for profile, uuid_const in sig_uuid_constants.items():
+ uuid_check = sig_uuid_constants['BASE_UUID'].format(
+ uuid_const).lower()
+ if uuid_check in uuids:
+ if profile in selector_dict:
+ selector_dict[profile].append(ad)
+ else:
+ selector_dict[profile] = [ad]
+
+ # Various services may not be active during BT startup.
+ # If the device can be identified through adb shell pm list features
+ # then try to add them to the appropriate profiles / features.
+
+ # Android TV.
+ if "feature:com.google.android.tv.installed" in ad.features:
+ ad.log.info("Android TV device found.")
+ supported_profiles = ['AudioSink']
+ _add_android_device_to_dictionary(ad, supported_profiles,
+ selector_dict)
+
+ # Android Auto
+ elif "feature:android.hardware.type.automotive" in ad.features:
+ ad.log.info("Android Auto device found.")
+ # Add: AudioSink , A/V_RemoteControl,
+ supported_profiles = [
+ 'AudioSink', 'A/V_RemoteControl', 'Message Notification Server'
+ ]
+ _add_android_device_to_dictionary(ad, supported_profiles,
+ selector_dict)
+ # Android Wear
+ elif "feature:android.hardware.type.watch" in ad.features:
+ ad.log.info("Android Wear device found.")
+ supported_profiles = []
+ _add_android_device_to_dictionary(ad, supported_profiles,
+ selector_dict)
+ # Android Phone
+ elif "feature:android.hardware.telephony" in ad.features:
+ ad.log.info("Android Phone device found.")
+ # Add: AudioSink
+ supported_profiles = [
+ 'AudioSource', 'A/V_RemoteControlTarget',
+ 'Message Access Server'
+ ]
+ _add_android_device_to_dictionary(ad, supported_profiles,
+ selector_dict)
+ return selector_dict
+
+
+def _add_android_device_to_dictionary(android_device, profile_list,
+ selector_dict):
+ """Adds the AndroidDevice and supported features to the selector dictionary
+
+ Args:
+ android_device: The Android device.
+ profile_list: The list of profiles the Android device supports.
+ """
+ for profile in profile_list:
+ if profile in selector_dict and android_device not in selector_dict[profile]:
+ selector_dict[profile].append(android_device)
+ else:
+ selector_dict[profile] = [android_device]
diff --git a/acts/tests/google/bt/pts/bta_lib.py b/acts/framework/acts/test_utils/bt/bta_lib.py
similarity index 79%
rename from acts/tests/google/bt/pts/bta_lib.py
rename to acts/framework/acts/test_utils/bt/bta_lib.py
index f75ff6b..661a7e8 100644
--- a/acts/tests/google/bt/pts/bta_lib.py
+++ b/acts/framework/acts/test_utils/bt/bta_lib.py
@@ -24,18 +24,18 @@
class BtaLib():
- def __init__(self, log, mac_addr, dut):
+ def __init__(self, log, dut, target_mac_address=None):
self.advertisement_list = []
self.dut = dut
self.log = log
- self.mac_addr = mac_addr
+ self.target_mac_addr = target_mac_address
+
+ def set_target_mac_addr(self, mac_addr):
+ self.target_mac_addr = mac_addr
def set_scan_mode(self, scan_mode):
"""Set the Scan mode of the Bluetooth Adapter"""
- for mode in bt_scan_mode_types:
- if scan_mode == mode.name:
- set_bt_scan_mode(self.dut, mode.value)
- return
+ set_bt_scan_mode(self.dut, bt_scan_mode_types[scan_mode])
def set_device_name(self, line):
"""Set Bluetooth Adapter Name"""
@@ -51,7 +51,7 @@
def init_bond(self):
"""Initiate bond to PTS device"""
- self.dut.droid.bluetoothDiscoverAndBond(self.mac_addr)
+ self.dut.droid.bluetoothDiscoverAndBond(self.target_mac_addr)
def start_discovery(self):
"""Start BR/EDR Discovery"""
@@ -70,15 +70,15 @@
def bond(self):
"""Bond to PTS device"""
- self.dut.droid.bluetoothBond(self.mac_addr)
+ self.dut.droid.bluetoothBond(self.target_mac_addr)
def disconnect(self):
"""BTA disconnect"""
- self.dut.droid.bluetoothDisconnectConnected(self.mac_addr)
+ self.dut.droid.bluetoothDisconnectConnected(self.target_mac_addr)
def unbond(self):
"""Unbond from PTS device"""
- self.dut.droid.bluetoothUnbond(self.mac_addr)
+ self.dut.droid.bluetoothUnbond(self.target_mac_addr)
def start_pairing_helper(self, line):
"""Start or stop Bluetooth Pairing Helper"""
@@ -99,12 +99,13 @@
def fetch_uuids_with_sdp(self):
"""BTA fetch UUIDS with SDP"""
- self.log.info(self.dut.droid.bluetoothFetchUuidsWithSdp(self.mac_addr))
+ self.log.info(
+ self.dut.droid.bluetoothFetchUuidsWithSdp(self.target_mac_addr))
def connect_profiles(self):
"""Connect available profiles"""
- self.dut.droid.bluetoothConnectBonded(self.mac_addr)
+ self.dut.droid.bluetoothConnectBonded(self.target_mac_addr)
def tts_speak(self):
"""Open audio channel by speaking characters"""
- self.dut.droid.ttsSpeak(self.mac_addr)
+ self.dut.droid.ttsSpeak(self.target_mac_addr)
diff --git a/acts/tests/google/bt/pts/config_lib.py b/acts/framework/acts/test_utils/bt/config_lib.py
similarity index 100%
rename from acts/tests/google/bt/pts/config_lib.py
rename to acts/framework/acts/test_utils/bt/config_lib.py
diff --git a/acts/tests/google/bt/pts/configs/bt_stack.conf b/acts/framework/acts/test_utils/bt/configs/bt_stack.conf
similarity index 100%
rename from acts/tests/google/bt/pts/configs/bt_stack.conf
rename to acts/framework/acts/test_utils/bt/configs/bt_stack.conf
diff --git a/acts/tests/google/bt/pts/configs/dis_mitm_bt_stack.conf b/acts/framework/acts/test_utils/bt/configs/dis_mitm_bt_stack.conf
similarity index 100%
rename from acts/tests/google/bt/pts/configs/dis_mitm_bt_stack.conf
rename to acts/framework/acts/test_utils/bt/configs/dis_mitm_bt_stack.conf
diff --git a/acts/tests/google/bt/pts/configs/non_bond_bt_stack.conf b/acts/framework/acts/test_utils/bt/configs/non_bond_bt_stack.conf
similarity index 100%
rename from acts/tests/google/bt/pts/configs/non_bond_bt_stack.conf
rename to acts/framework/acts/test_utils/bt/configs/non_bond_bt_stack.conf
diff --git a/acts/tests/google/bt/pts/gatt_test_database.py b/acts/framework/acts/test_utils/bt/gatt_test_database.py
similarity index 100%
rename from acts/tests/google/bt/pts/gatt_test_database.py
rename to acts/framework/acts/test_utils/bt/gatt_test_database.py
diff --git a/acts/tests/google/bt/pts/gattc_lib.py b/acts/framework/acts/test_utils/bt/gattc_lib.py
similarity index 76%
rename from acts/tests/google/bt/pts/gattc_lib.py
rename to acts/framework/acts/test_utils/bt/gattc_lib.py
index 4bcb0f4..349c9ee 100644
--- a/acts/tests/google/bt/pts/gattc_lib.py
+++ b/acts/framework/acts/test_utils/bt/gattc_lib.py
@@ -17,13 +17,19 @@
GATT Client Libraries
"""
+from acts.test_utils.bt.bt_constants import default_le_connection_interval_ms
+from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
from acts.test_utils.bt.bt_gatt_utils import setup_gatt_mtu
+from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
from acts.test_utils.bt.bt_constants import gatt_cb_strings
from acts.test_utils.bt.bt_constants import gatt_char_desc_uuids
from acts.test_utils.bt.bt_constants import gatt_descriptor
from acts.test_utils.bt.bt_constants import gatt_transport
+from acts.test_utils.bt.bt_constants import le_default_supervision_timeout
+from acts.test_utils.bt.bt_constants import le_connection_interval_time_step_ms
+from acts.test_utils.bt.bt_constants import scan_result
from acts.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
import time
@@ -31,20 +37,47 @@
class GattClientLib():
- def __init__(self, log, mac_addr, dut):
+ def __init__(self, log, dut, target_mac_addr=None):
self.dut = dut
self.log = log
- self.mac_addr = mac_addr
self.gatt_callback = None
self.bluetooth_gatt = None
self.discovered_services_index = None
+ self.target_mac_addr = target_mac_addr
self.generic_uuid = "0000{}-0000-1000-8000-00805f9b34fb"
+ def set_target_mac_addr(self, mac_addr):
+ self.target_mac_addr = mac_addr
+
+ def connect_over_le_based_off_name(self, autoconnect, name):
+ """Perform GATT connection over LE"""
+ self.dut.droid.bleSetScanSettingsScanMode(
+ ble_scan_settings_modes['low_latency'])
+ filter_list = self.dut.droid.bleGenFilterList()
+ scan_settings = self.dut.droid.bleBuildScanSetting()
+ scan_callback = self.dut.droid.bleGenScanCallback()
+ event_name = scan_result.format(scan_callback)
+ self.dut.droid.bleSetScanFilterDeviceName("BLE Rect")
+ self.dut.droid.bleBuildScanFilter(filter_list)
+ self.dut.droid.bleStartBleScan(filter_list, scan_settings,
+ scan_callback)
+
+ try:
+ event = self.dut.ed.pop_event(event_name, 10)
+ self.log.info("Found scan result: {}".format(event))
+ except Exception:
+ self.log.info("Didn't find any scan results.")
+ mac_addr = event['data']['Result']['deviceInfo']['address']
+ self.bluetooth_gatt, self.gatt_callback = setup_gatt_connection(
+ self.dut, mac_addr, autoconnect, transport=gatt_transport['le'])
+ self.dut.droid.bleStopBleScan(scan_callback)
+ self.discovered_services_index = None
+
def connect_over_le(self, autoconnect):
"""Perform GATT connection over LE"""
self.bluetooth_gatt, self.gatt_callback = setup_gatt_connection(
self.dut,
- self.mac_addr,
+ self.target_mac_addr,
autoconnect,
transport=gatt_transport['le'])
self.discovered_services_index = None
@@ -52,10 +85,14 @@
def connect_over_bredr(self):
"""Perform GATT connection over BREDR"""
self.bluetooth_gatt, self.gatt_callback = setup_gatt_connection(
- self.dut, self.mac_addr, False, transport=gatt_transport['bredr'])
+ self.dut,
+ self.target_mac_addr,
+ False,
+ transport=gatt_transport['bredr'])
def disconnect(self):
"""Perform GATT disconnect"""
+ cmd = "Disconnect GATT connection"
try:
disconnect_gatt_connection(self.dut, self.bluetooth_gatt,
self.gatt_callback)
@@ -130,6 +167,19 @@
self.bluetooth_gatt, self.discovered_services_index,
int(instance_id, 16), write_value)
+ def write_char_by_instance_id_value(self, line):
+ """GATT Client Write to Characteristic by instance ID"""
+ args = line.split()
+ if len(args) != 2:
+ self.log.info("2 Arguments required: [InstanceId] [Size]")
+ return
+ instance_id = args[0]
+ write_value = args[1]
+ self._setup_discovered_services_index()
+ self.dut.droid.gattClientWriteCharacteristicByInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index,
+ int(instance_id, 16), [int(write_value)])
+
def mod_write_char_by_instance_id(self, line):
"""GATT Client Write to Char that doesn't have write permission"""
args = line.split()
@@ -283,15 +333,6 @@
self._setup_discovered_services_index()
services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
self.discovered_services_index)
- """
- self.log.info(
- CMD_LOG.format(
- cmd,
- self.dut.droid.gattClientWriteDescriptorByInstanceId(
- self.bluetooth_gatt, self.discovered_services_index,
- int(instance_id, 16),
- gatt_descriptor['enable_notification_value'])))
- """
for i in range(services_count):
characteristic_uuids = (
self.dut.droid.gattClientGetDiscoveredCharacteristicUuids(
@@ -319,6 +360,39 @@
self.bluetooth_gatt,
self.discovered_services_index, i, j, True)
+ def enable_indication_desc_by_instance_id(self, line):
+ """GATT Client Enable indication on Descriptor by instance ID"""
+ instance_id = line
+ self._setup_discovered_services_index()
+ services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
+ self.discovered_services_index)
+ for i in range(services_count):
+ characteristic_uuids = (
+ self.dut.droid.gattClientGetDiscoveredCharacteristicUuids(
+ self.discovered_services_index, i))
+ for j in range(len(characteristic_uuids)):
+ descriptor_uuids = (
+ self.dut.droid.
+ gattClientGetDiscoveredDescriptorUuidsByIndex(
+ self.discovered_services_index, i, j))
+ for k in range(len(descriptor_uuids)):
+ desc_inst_id = self.dut.droid.gattClientGetDescriptorInstanceId(
+ self.bluetooth_gatt, self.discovered_services_index, i,
+ j, k)
+ if desc_inst_id == int(instance_id, 16):
+ self.dut.droid.gattClientDescriptorSetValueByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, k,
+ gatt_descriptor['enable_indication_value'])
+ time.sleep(2) #Necessary for PTS
+ self.dut.droid.gattClientWriteDescriptorByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, k)
+ time.sleep(2) #Necessary for PTS
+ self.dut.droid.gattClientSetCharacteristicNotificationByIndex(
+ self.bluetooth_gatt,
+ self.discovered_services_index, i, j, True)
+
def char_enable_all_notifications(self):
self._setup_discovered_services_index()
services_count = self.dut.droid.gattClientGetDiscoveredServicesCount(
@@ -393,8 +467,9 @@
self.bluetooth_gatt,
self.discovered_services_index, i, j, k)
except Exception as err:
- self.log.info("Failed to read to descriptor: {}".
- format(descriptor_uuids[k]))
+ self.log.info(
+ "Failed to read to descriptor: {}".format(
+ descriptor_uuids[k]))
def write_all_char(self, line):
"""Write to every Characteristic on the GATT server"""
@@ -423,8 +498,9 @@
j)
time.sleep(1)
except Exception as err:
- self.log.info("Failed to write to characteristic: {}".
- format(characteristic_uuids[j]))
+ self.log.info(
+ "Failed to write to characteristic: {}".format(
+ characteristic_uuids[j]))
def write_all_desc(self, line):
""" Write to every Descriptor on the GATT server """
@@ -460,8 +536,9 @@
self.bluetooth_gatt,
self.discovered_services_index, i, j, k)
except Exception as err:
- self.log.info("Failed to write to descriptor: {}".
- format(descriptor_uuids[k]))
+ self.log.info(
+ "Failed to write to descriptor: {}".format(
+ descriptor_uuids[k]))
def discover_service_by_uuid(self, line):
""" Discover service by UUID """
@@ -470,3 +547,30 @@
uuid = self.generic_uuid.format(line)
self.dut.droid.gattClientDiscoverServiceByUuid(self.bluetooth_gatt,
uuid)
+
+ def request_le_connection_parameters(self):
+ le_min_ce_len = 0
+ le_max_ce_len = 0
+ le_connection_interval = 0
+ minInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+ maxInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+ return_status = self.dut.droid.gattClientRequestLeConnectionParameters(
+ self.bluetooth_gatt, minInterval, maxInterval, 0,
+ le_default_supervision_timeout, le_min_ce_len, le_max_ce_len)
+ self.log.info(
+ "Result of request le connection param: {}".format(return_status))
+
+ def socket_conn_begin_connect_thread_psm(self, line):
+ args = line.split()
+ is_ble = bool(int(args[0]))
+ secured_conn = bool(int(args[1]))
+ psm_value = int(args[2]) # 1
+ self.dut.droid.bluetoothSocketConnBeginConnectThreadPsm(
+ self.target_mac_addr, is_ble, psm_value, secured_conn)
+
+ def socket_conn_begin_accept_thread_psm(self, line):
+ accept_timeout_ms = default_bluetooth_socket_timeout_ms
+ is_ble = True
+ secured_conn = False
+ self.dut.droid.bluetoothSocketConnBeginAcceptThreadPsm(
+ accept_timeout_ms, is_ble, secured_conn)
diff --git a/acts/tests/google/bt/pts/gatts_lib.py b/acts/framework/acts/test_utils/bt/gatts_lib.py
similarity index 87%
rename from acts/tests/google/bt/pts/gatts_lib.py
rename to acts/framework/acts/test_utils/bt/gatts_lib.py
index 45a7a8d..cbffb18 100644
--- a/acts/tests/google/bt/pts/gatts_lib.py
+++ b/acts/framework/acts/test_utils/bt/gatts_lib.py
@@ -28,8 +28,8 @@
from acts.test_utils.bt.bt_constants import gatt_server_responses
from acts.test_utils.bt.bt_constants import gatt_service_types
from acts.test_utils.bt.bt_constants import small_timeout
+from acts.test_utils.bt.gatt_test_database import STRING_512BYTES
-from gatt_test_database import STRING_512BYTES
from acts.utils import exe_cmd
from math import ceil
@@ -44,27 +44,30 @@
gatt_server_callback = None
gatt_server_list = []
log = None
- mac_addr = None
service_list = []
write_mapping = {}
- def __init__(self, log, mac_addr, dut):
+ def __init__(self, log, dut):
self.dut = dut
self.log = log
- self.mac_addr = mac_addr
def list_all_uuids(self):
"""From the GATT Client, discover services and list all services,
chars and descriptors.
"""
- self.log.info("Listing Characteristics")
+ self.log.info("Service List:")
+ for service in self.dut.droid.gattGetServiceUuidList(self.gatt_server):
+ self.dut.log.info("GATT Server service uuid: {}".format(service))
+ self.log.info("Characteristics List:")
for characteristic in self.characteristic_list:
instance_id = self.dut.droid.gattServerGetCharacteristicInstanceId(
characteristic)
uuid = self.dut.droid.gattServerGetCharacteristicUuid(
characteristic)
- self.dut.log.info("GATT Server characteristic handle uuid: {} {}".
- format(hex(instance_id), uuid))
+ self.dut.log.info(
+ "GATT Server characteristic handle uuid: {} {}".format(
+ hex(instance_id), uuid))
+ # TODO: add getting insance ids and uuids from each descriptor.
def open(self):
"""Open an empty GATT Server instance"""
@@ -84,8 +87,8 @@
for btgs in self.gatt_server_list:
self.dut.droid.gattServerClose(btgs)
except Exception as err:
- self.log.error("Failed to close Bluetooth GATT Servers: {}".format(
- err))
+ self.log.error(
+ "Failed to close Bluetooth GATT Servers: {}".format(err))
self.characteristic_list = []
self.descriptor_list = []
self.gatt_server_list = []
@@ -114,12 +117,15 @@
self.gatt_server_callback)
char_read = gatt_event['char_read_req']['evt'].format(
self.gatt_server_callback)
+ char_write_req = gatt_event['char_write_req']['evt'].format(
+ self.gatt_server_callback)
char_write = gatt_event['char_write']['evt'].format(
self.gatt_server_callback)
execute_write = gatt_event['exec_write']['evt'].format(
self.gatt_server_callback)
- regex = "({}|{}|{}|{}|{})".format(desc_read, desc_write, char_read,
- char_write, execute_write)
+ regex = "({}|{}|{}|{}|{}|{})".format(desc_read, desc_write, char_read,
+ char_write, execute_write,
+ char_write_req)
events = self.dut.ed.pop_events(regex, 5, small_timeout)
status = 0
if user_input:
@@ -128,8 +134,8 @@
self.log.debug("Found event: {}.".format(event))
request_id = event['data']['requestId']
if event['name'] == execute_write:
- if ('execute' in event['data'] and
- event['data']['execute'] == True):
+ if ('execute' in event['data']
+ and event['data']['execute'] == True):
for key in self.write_mapping:
value = self.write_mapping[key]
self.log.info("Writing key, value: {}, {}".format(
@@ -144,13 +150,14 @@
continue
offset = event['data']['offset']
instance_id = event['data']['instanceId']
- if (event['name'] == desc_write or event['name'] == char_write):
- if ('preparedWrite' in event['data'] and
- event['data']['preparedWrite'] == True):
+ if (event['name'] == desc_write or event['name'] == char_write
+ or event['name'] == char_write_req):
+ if ('preparedWrite' in event['data']
+ and event['data']['preparedWrite'] == True):
value = event['data']['value']
if instance_id in self.write_mapping.keys():
- self.write_mapping[instance_id] = self.write_mapping[
- instance_id] + value
+ self.write_mapping[
+ instance_id] = self.write_mapping[instance_id] + value
self.log.info(
"New Prepared Write Value for {}: {}".format(
instance_id, self.write_mapping[instance_id]))
@@ -183,8 +190,8 @@
self.gatt_server, 0, request_id, status, offset, data)
def _setup_service(self, serv):
- service = self.dut.droid.gattServerCreateService(serv['uuid'],
- serv['type'])
+ service = self.dut.droid.gattServerCreateService(
+ serv['uuid'], serv['type'])
if 'handles' in serv:
self.dut.droid.gattServerServiceSetHandlesToReserve(
service, serv['handles'])
@@ -224,8 +231,8 @@
descriptor = self.dut.droid.gattServerCreateBluetoothGattDescriptor(
desc['uuid'], desc['permissions'])
if 'value' in desc:
- self.dut.droid.gattServerDescriptorSetByteValue(descriptor,
- desc['value'])
+ self.dut.droid.gattServerDescriptorSetByteValue(
+ descriptor, desc['value'])
if 'instance_id' in desc:
self.dut.droid.gattServerDescriptorSetInstanceId(
descriptor, desc['instance_id'])
@@ -257,6 +264,7 @@
expected_event = gatt_cb_strings['serv_added'].format(
self.gatt_server_callback)
self.dut.ed.pop_event(expected_event, 10)
+ return self.gatt_server, self.gatt_server_callback
def send_continuous_response(self, user_input):
"""Send the same response"""
@@ -268,7 +276,7 @@
self.gatt_server_callback)
char_write = gatt_event['char_write']['evt'].format(
self.gatt_server_callback)
- execute_write = gatt_event['char_exec_write']['evt'].format(
+ execute_write = gatt_event['exec_write']['evt'].format(
self.gatt_server_callback)
regex = "({}|{}|{}|{}|{})".format(desc_read, desc_write, char_read,
char_write, execute_write)
@@ -327,8 +335,8 @@
self.log.info(event)
request_id = event['data']['requestId']
if event['name'] == execute_write:
- if ('execute' in event['data'] and
- event['data']['execute'] == True):
+ if ('execute' in event['data']
+ and event['data']['execute'] == True):
for key in self.write_mapping:
value = self.write_mapping[key]
self.log.debug("Writing key, value: {}, {}".format(
@@ -341,15 +349,14 @@
continue
offset = event['data']['offset']
instance_id = event['data']['instanceId']
- if (event['name'] == desc_write or
- event['name'] == char_write):
- if ('preparedWrite' in event['data'] and
- event['data']['preparedWrite'] == True):
+ if (event['name'] == desc_write
+ or event['name'] == char_write):
+ if ('preparedWrite' in event['data']
+ and event['data']['preparedWrite'] == True):
value = event['data']['value']
if instance_id in self.write_mapping:
self.write_mapping[
- instance_id] = self.write_mapping[
- instance_id] + value
+ instance_id] = self.write_mapping[instance_id] + value
else:
self.write_mapping[instance_id] = value
else:
diff --git a/acts/tests/google/bt/pts/rfcomm_lib.py b/acts/framework/acts/test_utils/bt/rfcomm_lib.py
similarity index 73%
rename from acts/tests/google/bt/pts/rfcomm_lib.py
rename to acts/framework/acts/test_utils/bt/rfcomm_lib.py
index 257c929..ee1fd04 100644
--- a/acts/tests/google/bt/pts/rfcomm_lib.py
+++ b/acts/framework/acts/test_utils/bt/rfcomm_lib.py
@@ -24,11 +24,14 @@
class RfcommLib():
- def __init__(self, log, mac_addr, dut):
+ def __init__(self, log, dut, target_mac_addr=None):
self.advertisement_list = []
self.dut = dut
self.log = log
- self.mac_addr = mac_addr
+ self.target_mac_addr = target_mac_addr
+
+ def set_target_mac_addr(self, mac_addr):
+ self.target_mac_addr = mac_addr
def connect(self, line):
"""Perform an RFCOMM connect"""
@@ -36,18 +39,19 @@
if len(line) > 0:
uuid = line
if uuid:
- self.dut.droid.bluetoothRfcommBeginConnectThread(self.mac_addr,
- uuid)
+ self.dut.droid.bluetoothRfcommBeginConnectThread(
+ self.target_mac_addr, uuid)
else:
- self.dut.droid.bluetoothRfcommBeginConnectThread(self.mac_addr)
+ self.dut.droid.bluetoothRfcommBeginConnectThread(
+ self.target_mac_addr)
def open_rfcomm_socket(self):
"""Open rfcomm socket"""
- self.dut.droid.rfcommCreateRfcommSocket(self.mac_addr, 1)
+ self.dut.droid.rfcommCreateRfcommSocket(self.target_mac_addr, 1)
def open_l2cap_socket(self):
"""Open L2CAP socket"""
- self.dut.droid.rfcommCreateL2capSocket(self.mac_addr, 1)
+ self.dut.droid.rfcommCreateL2capSocket(self.target_mac_addr, 1)
def write(self, line):
"""Write String data over an RFCOMM connection"""
@@ -69,8 +73,8 @@
if uuid:
self.dut.droid.bluetoothRfcommBeginAcceptThread(uuid)
else:
- self.dut.droid.bluetoothRfcommBeginAcceptThread(bt_rfcomm_uuids[
- 'base_uuid'])
+ self.dut.droid.bluetoothRfcommBeginAcceptThread(
+ bt_rfcomm_uuids['base_uuid'])
def stop(self):
"""Stop RFCOMM Connection"""
@@ -78,4 +82,4 @@
def open_l2cap_socket(self):
"""Open L2CAP socket"""
- self.dut.droid.rfcommCreateL2capSocket(self.mac_addr, 1)
+ self.dut.droid.rfcommCreateL2capSocket(self.target_mac_addr, 1)
diff --git a/acts/framework/acts/test_utils/bt/shell_commands_lib.py b/acts/framework/acts/test_utils/bt/shell_commands_lib.py
new file mode 100644
index 0000000..f44e123
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/shell_commands_lib.py
@@ -0,0 +1,44 @@
+#/usr/bin/env python3.4
+#
+# 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.
+"""
+Shell command library.
+"""
+
+
+class ShellCommands():
+ def __init__(self, log, dut):
+ self.dut = dut
+ self.log = log
+
+ def set_battery_level(self, level):
+ """Set the battery level via ADB shell
+ Args:
+ level: the percent level to set
+ """
+ self.dut.adb.shell("dumpsys battery set level {}".format(level))
+
+ def disable_ble_scanning(self):
+ """Disable BLE scanning via ADB shell"""
+ self.dut.adb.shell("settings put global ble_scan_always_enabled 0")
+
+ def enable_ble_scanning(self):
+ """Enable BLE scanning via ADB shell"""
+ self.dut.adb.shell("settings put global ble_scan_always_enabled 1")
+
+ def consume_cpu_core(self):
+ """Consume a CPU core on the Android device via ADB shell"""
+ self.dut.adb.shell("echo $$ > /dev/cpuset/top-app/tasks")
+ self.dut.adb.shell("cat /dev/urandom > /dev/null &")
diff --git a/acts/framework/acts/test_utils/car/car_media_utils.py b/acts/framework/acts/test_utils/car/car_media_utils.py
index aa1efd7..bc2225e 100644
--- a/acts/framework/acts/test_utils/car/car_media_utils.py
+++ b/acts/framework/acts/test_utils/car/car_media_utils.py
@@ -84,26 +84,6 @@
return verifyEventReceived(log, toDevice, expctEvent, timeout)
-def is_a2dp_connected(log, sink, source):
- """
- Convenience Function to see if the 2 devices are connected on
- A2dp.
- ToDo: Move to bt_test_utils if used in more places.
- Args:
- sink: Audio Sink
- source: Audio Source
- Returns:
- True if Connected
- False if Not connected
- """
- devices = sink.droid.bluetoothA2dpSinkGetConnectedDevices()
- for device in devices:
- log.info("A2dp Connected device {}".format(device["name"]))
- if (device["address"] == source.droid.bluetoothGetLocalAddress()):
- return True
- return False
-
-
def log_metadata(log, metadata):
"""
Log the Metadata to the console.
@@ -154,7 +134,7 @@
return False
if not (metadata1[MEDIA_KEY_NUM_TRACKS] == metadata2[MEDIA_KEY_NUM_TRACKS]
- ):
+ ):
log.info("Song Num Tracks do not match")
return False
diff --git a/acts/framework/acts/test_utils/coex/CoexBaseTest.py b/acts/framework/acts/test_utils/coex/CoexBaseTest.py
new file mode 100644
index 0000000..e2c5372
--- /dev/null
+++ b/acts/framework/acts/test_utils/coex/CoexBaseTest.py
@@ -0,0 +1,321 @@
+#/usr/bin/env python3.4
+#
+# 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.
+
+import os
+import threading
+import time
+
+from acts.base_test import BaseTestClass
+from acts.controllers.iperf_client import IPerfClient
+from acts.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts.test_utils.coex.coex_test_utils import configure_and_start_ap
+from acts.test_utils.coex.coex_test_utils import iperf_result
+from acts.test_utils.coex.coex_test_utils import get_phone_ip
+from acts.test_utils.coex.coex_test_utils import wifi_connection_check
+from acts.test_utils.coex.coex_test_utils import xlsheet
+from acts.test_utils.wifi.wifi_test_utils import reset_wifi
+from acts.test_utils.wifi.wifi_test_utils import wifi_connect
+from acts.test_utils.wifi.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi.wifi_test_utils import wifi_toggle_state
+from acts.utils import create_dir
+from acts.utils import start_standing_subprocess
+from acts.utils import stop_standing_subprocess
+
+TEST_CASE_TOKEN = "[Test Case]"
+RESULT_LINE_TEMPLATE = TEST_CASE_TOKEN + " %s %s"
+IPERF_SERVER_WAIT_TIME = 5
+
+
+class CoexBaseTest(BaseTestClass):
+
+ def __init__(self, controllers):
+ BaseTestClass.__init__(self, controllers)
+ self.pri_ad = self.android_devices[0]
+ if len(self.android_devices) == 2:
+ self.sec_ad = self.android_devices[1]
+ elif len(self.android_devices) == 3:
+ self.third_ad = self.android_devices[2]
+
+ def setup_class(self):
+ self.tag = 0
+ self.iperf_result = {}
+ self.thread_list = []
+ if not setup_multiple_devices_for_bt_test(self.android_devices):
+ self.log.error("Failed to setup devices for bluetooth test")
+ return False
+ req_params = ["network", "iperf"]
+ self.unpack_userparams(req_params)
+ if "RelayDevice" in self.user_params:
+ self.audio_receiver = self.relay_devices[0]
+ else:
+ self.log.warning("Missing Relay config file.")
+ if "music_file" in self.user_params:
+ self.push_music_to_android_device(self.pri_ad)
+ self.path = self.pri_ad.log_path
+ if "AccessPoints" in self.user_params:
+ self.ap = self.access_points[0]
+ configure_and_start_ap(self.ap, self.network)
+ wifi_test_device_init(self.pri_ad)
+ wifi_connect(self.pri_ad, self.network)
+
+ def setup_test(self):
+ self.received = []
+ for a in self.android_devices:
+ a.ed.clear_all_events()
+ if not wifi_connection_check(self.pri_ad, self.network["SSID"]):
+ self.log.error("Wifi connection does not exist")
+ return False
+ if not enable_bluetooth(self.pri_ad.droid, self.pri_ad.ed):
+ self.log.error("Failed to enable bluetooth")
+ return False
+
+ def teardown_test(self):
+ if not disable_bluetooth(self.pri_ad.droid):
+ self.log.info("Failed to disable bluetooth")
+ return False
+ self.teardown_thread()
+
+ def teardown_class(self):
+ if "AccessPoints" in self.user_params:
+ self.ap.close()
+ reset_wifi(self.pri_ad)
+ wifi_toggle_state(self.pri_ad, False)
+ json_result = self.results.json_str()
+ xlsheet(self.pri_ad, json_result)
+
+ def start_iperf_server_on_shell(self, server_port):
+ """Starts iperf server on android device with specified.
+
+ Args:
+ server_port: Port in which server should be started.
+ """
+ log_path = os.path.join(self.pri_ad.log_path, "iPerf{}".format(
+ server_port))
+ iperf_server = "iperf3 -s -p {} -J".format(server_port)
+ log_files = []
+ create_dir(log_path)
+ out_file_name = "IPerfServer,{},{},{}.log".format(
+ server_port, self.tag, log_files)
+ self.tag = self.tag + 1
+ full_out_path = os.path.join(log_path, out_file_name)
+ cmd = "adb -s {} shell {} > {}".format(
+ self.pri_ad.serial, iperf_server, full_out_path)
+ self.iperf_process.append(start_standing_subprocess(cmd))
+ log_files.append(full_out_path)
+ time.sleep(IPERF_SERVER_WAIT_TIME)
+
+ def stop_iperf_server_on_shell(self):
+ """Stops all the instances of iperf server on shell."""
+ try:
+ for process in self.iperf_process:
+ stop_standing_subprocess(process)
+ except Exception:
+ self.log.info("Iperf server already killed")
+
+ def run_iperf_and_get_result(self):
+ """Frames iperf command based on test and starts iperf client on
+ host machine.
+ """
+ self.flag_list = []
+ self.iperf_process = []
+ test_params = self.current_test_name.split("_")
+
+ self.protocol = test_params[-2:-1]
+ self.stream = test_params[-1:]
+
+ if self.protocol[0] == "tcp":
+ self.iperf_args = "-t {} -p {} {} -J".format(
+ self.iperf["duration"], self.iperf["port_1"],
+ self.iperf["tcp_window_size"])
+ else:
+ self.iperf_args = ("-t {} -p {} -u {} --get-server-output -J"
+ .format(self.iperf["duration"],
+ self.iperf["port_1"],
+ self.iperf["udp_bandwidth"]))
+
+ if self.stream[0] == "ul":
+ self.iperf_args += " -R"
+
+ if self.protocol[0] == "tcp" and self.stream[0] == "bidirectional":
+ self.bidirectional_args = "-t {} -p {} {} -R -J".format(
+ self.iperf["duration"], self.iperf["port_2"],
+ self.iperf["tcp_window_size"])
+ else:
+ self.bidirectional_args = ("-t {} -p {} -u {} --get-server-output"
+ " -J".format(self.iperf["duration"],
+ self.iperf["port_2"],
+ self.iperf["udp_bandwidth"]
+ ))
+
+ if self.stream[0] == "bidirectional":
+ self.start_iperf_server_on_shell(self.iperf["port_2"])
+ self.start_iperf_server_on_shell(self.iperf["port_1"])
+
+ if self.stream[0] == "bidirectional":
+ args = [
+ lambda: self.run_iperf(self.iperf_args, self.iperf["port_1"]),
+ lambda: self.run_iperf(self.bidirectional_args,
+ self.iperf["port_2"])
+ ]
+ self.run_thread(args)
+ else:
+ args = [
+ lambda: self.run_iperf(self.iperf_args, self.iperf["port_1"])
+ ]
+ self.run_thread(args)
+
+ def run_iperf(self, iperf_args, server_port):
+ """Gets android device ip and start iperf client from host machine to
+ that ip and parses the iperf result.
+
+ Args:
+ iperf_args: Iperf parameters to run traffic.
+ server_port: Iperf port to start client.
+ """
+ ip = get_phone_ip(self.pri_ad)
+ iperf_client = IPerfClient(server_port, ip, self.pri_ad.log_path)
+ result = iperf_client.start(iperf_args)
+ try:
+ sent, received = iperf_result(result, self.stream)
+ self.received.append(str(round(received, 2)) + "Mb/s")
+ self.log.info("Sent: {} Mb/s, Received: {} Mb/s".format(
+ sent, received))
+ self.flag_list.append(True)
+
+ except TypeError:
+ self.log.error("Iperf failed/stopped.")
+ self.flag_list.append(False)
+ self.received.append("Iperf Failed")
+
+ self.iperf_result[self.current_test_name] = self.received
+
+ def on_fail(self, record, test_name, begin_time):
+ self.log.info(
+ "Test {} failed, Fetching Btsnoop logs and bugreport".format(
+ test_name))
+ take_btsnoop_logs(self.android_devices, self, test_name)
+ self._take_bug_report(test_name, begin_time)
+ record.extras = self.received
+
+ def _on_fail(self, record):
+ """Proxy function to guarantee the base implementation of on_fail is
+ called.
+
+ Args:
+ record: The records.TestResultRecord object for the failed test
+ case.
+ """
+ if record.details:
+ self.log.error(record.details)
+ self.log.info(RESULT_LINE_TEMPLATE, record.test_name, record.result)
+ self.on_fail(record, record.test_name, record.log_begin_time)
+
+ def _on_pass(self, record):
+ """Proxy function to guarantee the base implementation of on_pass is
+ called.
+
+ Args:
+ record: The records.TestResultRecord object for the passed test
+ case.
+ """
+ msg = record.details
+ if msg:
+ self.log.info(msg)
+ self.log.info(RESULT_LINE_TEMPLATE, record.test_name, record.result)
+ record.extras = self.received
+
+ def run_thread(self, kwargs):
+ """Convenience function to start thread.
+
+ Args:
+ kwargs: Function object to start in thread.
+ """
+ for function in kwargs:
+ self.thread = threading.Thread(target=function)
+ self.thread_list.append(self.thread)
+ self.thread.start()
+
+ def teardown_result(self):
+ """Convenience function to join thread and fetch iperf result."""
+ for thread_id in self.thread_list:
+ if thread_id.is_alive():
+ thread_id.join()
+ self.stop_iperf_server_on_shell()
+ if False in self.flag_list:
+ return False
+ return True
+
+ def teardown_thread(self):
+ """Convenience function to join thread."""
+ for thread_id in self.thread_list:
+ if thread_id.is_alive():
+ thread_id.join()
+ self.stop_iperf_server_on_shell()
+
+ def push_music_to_android_device(self, ad):
+ """Add music to Android device as specified by the test config
+
+ Args:
+ ad: Android device
+
+ Returns:
+ True on success, False on failure
+ """
+ self.log.info("Pushing music to the Android device")
+ music_path_str = "music_file"
+ android_music_path = "/sdcard/Music/"
+ if music_path_str not in self.user_params:
+ self.log.error("Need music for audio testcases")
+ return False
+ music_path = self.user_params[music_path_str]
+ if type(music_path) is list:
+ self.log.info("Media ready to push as is.")
+ if type(music_path) is list:
+ for item in music_path:
+ self.music_file_to_play = item
+ ad.adb.push("{} {}".format(item, android_music_path))
+ return True
+
+ def avrcp_actions(self):
+ """Performs avrcp controls like volume up, volume down, skip next and
+ skip previous.
+
+ Returns: True if successful, otherwise False.
+ """
+ #TODO: Validate the success state of functionalities performed.
+ self.audio_receiver.volume_up()
+ time.sleep(2)
+ self.audio_receiver.volume_down()
+ time.sleep(2)
+ self.audio_receiver.skip_next()
+ time.sleep(2)
+ self.audio_receiver.skip_previous()
+ time.sleep(2)
+ return True
+
+ def change_volume(self):
+ """Changes volume with HFP call.
+
+ Returns: True if successful, otherwise False.
+ """
+ self.audio_receiver.volume_up()
+ time.sleep(2)
+ self.audio_receiver.volume_down()
+ time.sleep(2)
+ return True
diff --git a/acts/framework/acts/test_utils/coex/bluez_test_utils.py b/acts/framework/acts/test_utils/coex/bluez_test_utils.py
new file mode 100644
index 0000000..5a49a77
--- /dev/null
+++ b/acts/framework/acts/test_utils/coex/bluez_test_utils.py
@@ -0,0 +1,513 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import dbus
+import dbus.mainloop.glib
+import dbus.service
+import logging
+import time
+
+from acts.test_utils.coex.coex_constants import ADAPTER_INTERFACE
+from acts.test_utils.coex.coex_constants import CALL_MANAGER
+from acts.test_utils.coex.coex_constants import DBUS_INTERFACE
+from acts.test_utils.coex.coex_constants import DEVICE_INTERFACE
+from acts.test_utils.coex.coex_constants import DISCOVERY_TIME
+from acts.test_utils.coex.coex_constants import MEDIA_CONTROL_INTERACE
+from acts.test_utils.coex.coex_constants import MEDIA_PLAY_INTERFACE
+from acts.test_utils.coex.coex_constants import OBJECT_MANGER
+from acts.test_utils.coex.coex_constants import OFONO_MANAGER
+from acts.test_utils.coex.coex_constants import PROPERTIES
+from acts.test_utils.coex.coex_constants import PROPERTIES_CHANGED
+from acts.test_utils.coex.coex_constants import SERVICE_NAME
+from acts.test_utils.coex.coex_constants import VOICE_CALL
+from acts.test_utils.coex.coex_constants import WAIT_TIME
+from gi.repository import GObject
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+class BluezUtils():
+
+ def __init__(self):
+ devices = {}
+ self.device_interface = False
+ self.mainloop = 0
+ self.property_changed = False
+ self.bd_address = None
+ self.bus = dbus.SystemBus()
+ self.bus.add_signal_receiver(self.properties_changed,
+ dbus_interface=DBUS_INTERFACE,
+ signal_name=PROPERTIES_CHANGED,
+ arg0=DEVICE_INTERFACE,
+ path_keyword="path")
+ self.om = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, "/"), OBJECT_MANGER)
+ objects = self.om.GetManagedObjects()
+ for path, interfaces in objects.items():
+ if ADAPTER_INTERFACE in interfaces:
+ devices[path] = interfaces[ADAPTER_INTERFACE]
+ self.adapter = self.find_adapter(0)
+
+ def register_signal(self):
+ """Start signal_dispatcher"""
+ self.mainloop = GObject.MainLoop()
+ self.mainloop.run()
+
+ def unregister_signal(self):
+ """Stops signal_dispatcher"""
+ self.mainloop.quit()
+
+ def get_properties(self,props, path, check):
+ """Return's status for parameter check .
+
+ Args:
+ props:dbus interface
+ path:path for getting status
+ check:String for which status need to be checked
+ """
+ return props.Get(path,check)
+
+ def properties_changed(self, interface, changed, invalidated, path):
+ """
+ Function to be executed when specified signal is caught
+ """
+ if path == "/org/bluez/hci0/dev_" + (self.bd_address).replace(":", "_"):
+ self.unregister_signal()
+ return
+
+ def get_managed_objects(self):
+ """Gets the instance of all the objects in dbus.
+
+ Returns:
+ Dictionary containing path and interface of
+ all the instance in dbus.
+ """
+ manager = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, "/"), OBJECT_MANGER)
+ return manager.GetManagedObjects()
+
+ def find_adapter(self, pattern=None):
+ """Gets the adapter interface with specified pattern in dbus.
+
+ Args:
+ pattern: Adapter name pattern to be found out.
+
+ Returns:
+ Adapter interface with specified pattern.
+ """
+ return self.find_adapter_in_objects(self.get_managed_objects(), pattern)
+
+ def find_adapter_in_objects(self, objects, pattern=None):
+ """Gets the adapter interface with specified pattern in dbus.
+
+ Args:
+ objects: Dictionary containing path and interface of
+ all the instance in dbus.
+ pattern: Adapter name pattern to be found out.
+
+ Returns:
+ Adapter interface if successful else raises an exception.
+ """
+ for path, ifaces in objects.items():
+ adapter = ifaces.get(ADAPTER_INTERFACE)
+ if adapter is None:
+ continue
+ if not pattern or pattern == adapter["Address"] or \
+ path.endswith(pattern):
+ adapter_obj = self.bus.get_object(SERVICE_NAME, path)
+ return dbus.Interface(adapter_obj, ADAPTER_INTERFACE)
+ raise Exception("Bluetooth adapter not found")
+
+ def find_device_in_objects(self,
+ objects,
+ device_address,
+ adapter_pattern=None):
+ """Gets the device interface in objects with specified device
+ address and pattern.
+
+ Args:
+ objects: Dictionary containing path and interface of
+ all the instance in dbus.
+ device_address: Bluetooth interface MAC address of the device
+ which is to be found out.
+ adapter_pattern: Adapter name pattern to be found out.
+
+ Returns:
+ Device interface if successful else raises an exception.
+ """
+ path_prefix = ""
+ if adapter_pattern:
+ adapter = self.find_adapter_in_objects(objects, adapter_pattern)
+ path_prefix = adapter.object_path
+ for path, ifaces in objects.items():
+ device = ifaces.get(DEVICE_INTERFACE)
+ if device is None:
+ continue
+ if (device["Address"] == device_address and
+ path.startswith(path_prefix)):
+ device_obj = self.bus.get_object(SERVICE_NAME, path)
+ return dbus.Interface(device_obj, DEVICE_INTERFACE)
+ raise Exception("Bluetooth device not found")
+
+ def get_bluetooth_adapter_address(self):
+ """Gets the bluetooth adapter address.
+
+ Returns:
+ Address of bluetooth adapter.
+ """
+ path = self.adapter.object_path
+ props = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, path), PROPERTIES)
+ address = props.Get(ADAPTER_INTERFACE, "Address")
+ return address
+
+ def find_device(self, device_address):
+ """Discovers the DUT and returns its dbus interface.
+
+ Args:
+ Device_address: Bluetooth interface MAC address of the device.
+
+ Returns:
+ Dbus interface of the device.
+ """
+ addr = "dev_" + str(device_address).replace(":", "_")
+ device_path = "org/bluez/hci0/" + addr
+ self.adapter.StartDiscovery()
+ time.sleep(DISCOVERY_TIME)
+ objects = self.om.GetManagedObjects()
+ for path, interfaces in objects.items():
+ if device_path in path:
+ obj = self.bus.get_object(SERVICE_NAME, path)
+ self.device_interface = dbus.Interface(obj, DEVICE_INTERFACE)
+ self.adapter.StopDiscovery()
+ if not self.device_interface:
+ self.adapter.StopDiscovery()
+ return False
+ return True
+
+ def media_control_iface(self, device_address):
+ """Gets the dbus media control interface for the device
+ and returns it.
+
+ Args:
+ device_address: Bluetooth interface MAC address of the device.
+
+ Returns:
+ Dbus media control interface of the device.
+ """
+ control_iface = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, '/org/bluez/hci0/dev_' +
+ device_address.replace(":", "_")),
+ MEDIA_CONTROL_INTERACE)
+ return control_iface
+
+ def get_a2dp_interface(self, device_address):
+ """Gets the dbus media interface for the device.
+
+ Args:
+ device_address: Bluetooth interface MAC address of the device.
+
+ Returns:
+ Dbus media interface of the device.
+ """
+ a2dp_interface = dbus.Interface(
+ self.bus.get_object(
+ SERVICE_NAME, '/org/bluez/hci0/dev_' +
+ device_address.replace(":", "_") + '/player0'),
+ MEDIA_PLAY_INTERFACE)
+ return a2dp_interface
+
+ def ofo_iface(self):
+ """Gets dbus hfp interface for the device.
+
+ Returns:
+ Dbus hfp interface of the device.
+ """
+ manager = dbus.Interface(
+ self.bus.get_object('org.ofono', '/'), OFONO_MANAGER)
+ modems = manager.GetModems()
+ return modems
+
+ def call_manager(self, path):
+ """Gets Ofono(HFP) interface for the device.
+
+ Args:
+ path: Ofono interface path of the device.
+
+ Returns:
+ Ofono interface for the device.
+ """
+ vcm = dbus.Interface(
+ self.bus.get_object('org.ofono', path), CALL_MANAGER)
+ return vcm
+
+ def answer_call_interface(self, path):
+ """Gets the voice call interface for the device.
+
+ Args
+ path: Voice call path of the device.
+
+ Returns:
+ Interface for the voice call.
+ """
+ call = dbus.Interface(
+ self.bus.get_object('org.ofono', path), VOICE_CALL)
+ return call
+
+ def pair_bluetooth_device(self):
+ """Pairs the bluez machine with DUT.
+
+ Returns:
+ True if pairing is successful else False.
+ """
+ self.device_interface.Pair()
+ path = self.device_interface.object_path
+ props = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, path), PROPERTIES)
+ paired = self.get_properties(props, DEVICE_INTERFACE, "Paired")
+ return paired
+
+ def connect_bluetooth_device(self, *args):
+ """Connects the bluez machine to DUT with the specified
+ profile.
+
+ Args:
+ uuid: Profile UUID which is to be connected.
+
+ Returns:
+ True if connection is successful else False.
+ """
+
+ self.register_signal()
+ for uuid in args:
+ self.device_interface.ConnectProfile(uuid)
+ path = self.device_interface.object_path
+ props = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, path), PROPERTIES)
+ connect = self.get_properties(props, DEVICE_INTERFACE, "Connected")
+ return connect
+
+ def disconnect_bluetooth_profile(self, uuid, pri_ad):
+ """Disconnects the DUT for the specified profile.
+
+ Args:
+ uuid: Profile UUID which is to be disconnected.
+ pri_ad: An android device object.
+
+ Returns:
+ True if disconnection of profile is successful else False.
+ """
+
+ self.register_signal()
+ self.device_interface.DisconnectProfile(uuid)
+ time.sleep(6)
+ connected_devices = pri_ad.droid.bluetoothGetConnectedDevices()
+ if len(connected_devices) > 0:
+ return False
+ return True
+
+ def play_media(self, address):
+ """Initiate media play for the specified device.
+
+ Args:
+ address: Bluetooth interface MAC address of the device.
+
+ Returns:
+ "playing" if successful else "stopped" or "paused".
+ """
+ self.register_signal()
+ a2dp = self.media_control_iface(address)
+ time.sleep(WAIT_TIME)
+ a2dp.Play()
+ play_pause = self.get_a2dp_interface(address)
+ path = play_pause.object_path
+ time.sleep(WAIT_TIME)
+ props = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, path), PROPERTIES)
+ status = self.get_properties(props, MEDIA_PLAY_INTERFACE, "Status")
+ return status
+
+ def pause_media(self, address):
+ """Pauses the media palyer for the specified device.
+
+ Args:
+ address: Bluetooth interface MAC address of the device.
+
+ Return:
+ "paused" or "stopped" if successful else "playing".
+ """
+ self.register_signal()
+ a2dp = self.get_a2dp_interface(address)
+ time.sleep(WAIT_TIME)
+ a2dp.Pause()
+ path = a2dp.object_path
+ props = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, path), PROPERTIES)
+ status = self.get_properties(props, MEDIA_PLAY_INTERFACE, "Status")
+ return status
+
+ def remove_bluetooth_device(self, address):
+ """Removes the device from the paired list.
+
+ Args:
+ address: Bluetooth interface MAC address of the device.
+
+ Returns:
+ True if removing of device is successful else False.
+ """
+ managed_objects = self.get_managed_objects()
+ adapter = self.find_adapter_in_objects(managed_objects)
+ try:
+ dev = self.find_device_in_objects(managed_objects, address)
+ path = dev.object_path
+ except:
+ return False
+
+ adapter.RemoveDevice(path)
+ return True
+
+ def stop_media(self, address):
+ """Stops the media player for the specified device.
+
+ Args:
+ address: Bluetooth interface MAC address of the device.
+
+ Returns:
+ "paused" or "stopped" if successful else "playing".
+ """
+ self.register_signal()
+ a2dp = self.get_a2dp_interface(address)
+ time.sleep(WAIT_TIME)
+ a2dp.Stop()
+ path = a2dp.object_path
+ props = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, path), PROPERTIES)
+ status = self.get_properties(props, MEDIA_PLAY_INTERFACE, "Status")
+ return status
+
+ def skip_next(self, address):
+ """Skips to Next track in media player.
+
+ Args:
+ address: Bluetooth interface MAC address of the device.
+
+ Returns:
+ True if the media track change is successful else False.
+ """
+ self.register_signal()
+ a2dp = self.get_a2dp_interface(address)
+ time.sleep(WAIT_TIME)
+ path = a2dp.object_path
+ props = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, path), PROPERTIES)
+ track = self.get_properties(props, MEDIA_PLAY_INTERFACE, "Track")
+ Title = track['Title']
+ a2dp.Next()
+ time.sleep(WAIT_TIME)
+ track = self.get_properties(props, MEDIA_PLAY_INTERFACE, "Track")
+ if Title == track['Title']:
+ return False
+ return True
+
+ def skip_previous(self, address):
+ """Skips to previous track in media player.
+
+ Args:
+ address: Buetooth interface MAC address of the device.
+
+ Returns:
+ True if media track change is successful else False.
+ """
+ a2dp = self.get_a2dp_interface(address)
+ time.sleep(WAIT_TIME)
+ path = a2dp.object_path
+ props = dbus.Interface(
+ self.bus.get_object(SERVICE_NAME, path), PROPERTIES)
+ track = self.get_properties(props, MEDIA_PLAY_INTERFACE, "Track")
+ Title = track['Title']
+ a2dp.Previous()
+ a2dp.Previous()
+ time.sleep(WAIT_TIME)
+ track = self.get_properties(props,MEDIA_PLAY_INTERFACE, "Track")
+ if Title == track['Title']:
+ return False
+ return True
+
+ def avrcp_actions(self, address):
+ """Performs AVRCP actions for the device
+
+ Args:
+ address: Bluetooth interface MAC address of the device.
+
+ Returns:
+ True if avrcp actions are performed else False.
+ """
+ if not self.skip_next(address):
+ logging.info("skip Next failed")
+ return False
+ time.sleep(WAIT_TIME)
+
+ if not self.skip_previous(address):
+ logging.info("skip previous failed")
+ return False
+ time.sleep(WAIT_TIME)
+ return True
+
+ def initiate_and_disconnect_call_from_hf(self, phone_no, duration):
+ """Initiates the call from bluez for the specified phone number.
+
+ Args:
+ phone_no: Phone number to which the call should be made.
+ duration: Time till which the call should be active.
+
+ Returns:
+ True if the call is initiated and disconnected else False.
+ """
+ modems = self.ofo_iface()
+ modem = modems[0][0]
+ hide_callerid = "default"
+ vcm = self.call_manager(modem)
+ time.sleep(WAIT_TIME)
+ path = vcm.Dial(phone_no, hide_callerid)
+ if 'voicecall' not in path:
+ return False
+ time.sleep(duration)
+ vcm.HangupAll()
+ return True
+
+ def answer_call(self, duration):
+ """Answers the incoming call from bluez.
+
+ Args:
+ duration: Time till which the call should be active.
+
+ Returns:
+ True if call is answered else False.
+ """
+ modems = self.ofo_iface()
+ for path, properties in modems:
+ if CALL_MANAGER not in properties["Interfaces"]:
+ continue
+ mgr = self.call_manager(path)
+ calls = mgr.GetCalls()
+ for path, properties in calls:
+ state = properties["State"]
+ if state != "incoming":
+ continue
+ call = self.answer_call_interface(path)
+ call.Answer()
+ time.sleep(duration)
+ call.Hangup()
+ return True
diff --git a/acts/framework/acts/test_utils/coex/coex_constants.py b/acts/framework/acts/test_utils/coex/coex_constants.py
new file mode 100644
index 0000000..22f478a
--- /dev/null
+++ b/acts/framework/acts/test_utils/coex/coex_constants.py
@@ -0,0 +1,41 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+AUDIO_ROUTE_SPEAKER = "SPEAKER"
+AUDIO_ROUTE_BLUETOOTH = "BLUETOOTH"
+
+CALL_WAIT_TIME = 10
+DISCOVERY_TIME = 13
+WAIT_TIME = 3
+
+OBJECT_MANGER = "org.freedesktop.DBus.ObjectManager"
+PROPERTIES = "org.freedesktop.DBus.Properties"
+PROPERTIES_CHANGED = "PropertiesChanged"
+SERVICE_NAME = "org.bluez"
+CALL_MANAGER = "org.ofono.VoiceCallManager"
+VOICE_CALL = "org.ofono.VoiceCall"
+OFONO_MANAGER = "org.ofono.Manager"
+
+ADAPTER_INTERFACE = SERVICE_NAME + ".Adapter1"
+DBUS_INTERFACE = "org.freedesktop.DBus.Properties"
+DEVICE_INTERFACE = SERVICE_NAME + ".Device1"
+MEDIA_CONTROL_INTERACE = SERVICE_NAME +".MediaControl1"
+MEDIA_PLAY_INTERFACE = SERVICE_NAME + ".MediaPlayer1"
+
+bluetooth_profiles = {
+ "A2DP_SRC": "0000110a-0000-1000-8000-00805f9b34fb",
+ "HFP_AG": "0000111f-0000-1000-8000-00805f9b34fb"
+}
diff --git a/acts/framework/acts/test_utils/coex/coex_test_utils.py b/acts/framework/acts/test_utils/coex/coex_test_utils.py
new file mode 100644
index 0000000..5ac3515
--- /dev/null
+++ b/acts/framework/acts/test_utils/coex/coex_test_utils.py
@@ -0,0 +1,919 @@
+# /usr/bin/env python3.4
+#
+# 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
+
+import json
+import logging
+import math
+import os
+import re
+import subprocess
+import time
+import xlsxwriter
+
+from acts.controllers.ap_lib import hostapd_config
+from acts.controllers.ap_lib import hostapd_constants
+from acts.controllers.ap_lib import hostapd_security
+from acts.test_utils.bt.bt_constants import \
+ bluetooth_profile_connection_state_changed
+from acts.test_utils.bt.bt_constants import bt_default_timeout
+from acts.test_utils.bt.bt_constants import bt_profile_constants
+from acts.test_utils.bt.bt_constants import bt_profile_states
+from acts.test_utils.bt.bt_constants import bt_scan_mode_types
+from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
+from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
+from acts.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts.test_utils.bt.bt_test_utils import is_a2dp_src_device_connected
+from acts.test_utils.bt.bt_test_utils import is_a2dp_snk_device_connected
+from acts.test_utils.bt.bt_test_utils import is_hfp_client_device_connected
+from acts.test_utils.bt.bt_test_utils import is_map_mce_device_connected
+from acts.test_utils.bt.bt_test_utils import is_map_mse_device_connected
+from acts.test_utils.car.car_telecom_utils import wait_for_active
+from acts.test_utils.car.car_telecom_utils import wait_for_dialing
+from acts.test_utils.car.car_telecom_utils import wait_for_not_in_call
+from acts.test_utils.car.car_telecom_utils import wait_for_ringing
+from acts.test_utils.tel.tel_test_utils import get_phone_number
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts.test_utils.tel.tel_test_utils import run_multithread_func
+from acts.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts.test_utils.wifi.wifi_test_utils import reset_wifi
+from acts.test_utils.wifi.wifi_test_utils import wifi_connect
+from acts.test_utils.wifi.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi.wifi_test_utils import wifi_toggle_state
+from acts.utils import exe_cmd, create_dir
+
+THROUGHPUT_THRESHOLD = 100
+AP_START_TIME = 10
+DISCOVERY_TIME = 10
+BLUETOOTH_WAIT_TIME = 2
+
+
+def a2dp_dumpsys_parser(file_path):
+ """Convenience function to parse a2dp dumpsys logs.
+
+ Args:
+ file_path: Path of dumpsys logs.
+
+ Returns:
+ True if parsing is successful, False otherwise.
+ """
+ a2dp_dumpsys_info = []
+ with open(file_path) as dumpsys_file:
+ for line in dumpsys_file:
+ if "A2DP State:" in line:
+ a2dp_dumpsys_info.append(line)
+ elif "Counts (max dropped)" not in line and len(
+ a2dp_dumpsys_info) > 0:
+ a2dp_dumpsys_info.append(line)
+ elif "Counts (max dropped)" in line:
+ a2dp_dumpsys_info = ''.join(a2dp_dumpsys_info)
+ logging.info(a2dp_dumpsys_info)
+ return True
+ logging.error("failed to get A2DP state")
+ return False
+
+
+def connect_ble(pri_ad, sec_ad):
+ """Connect BLE device from DUT.
+
+ Args:
+ pri_ad: An android device object.
+ sec_ad: An android device object.
+
+ Returns:
+ True if successful, otherwise False.
+ """
+ adv_instances = []
+ gatt_server_list = []
+ bluetooth_gatt_list = []
+ pri_ad.droid.bluetoothEnableBLE()
+ gatt_server_cb = sec_ad.droid.gattServerCreateGattServerCallback()
+ gatt_server = sec_ad.droid.gattServerOpenGattServer(gatt_server_cb)
+ gatt_server_list.append(gatt_server)
+ try:
+ bluetooth_gatt, gatt_callback, adv_callback = (
+ orchestrate_gatt_connection(pri_ad, sec_ad))
+ bluetooth_gatt_list.append(bluetooth_gatt)
+ except GattTestUtilsError as err:
+ logging.error(err)
+ return False
+ adv_instances.append(adv_callback)
+ connected_devices = sec_ad.droid.gattServerGetConnectedDevices(gatt_server)
+ logging.debug("Connected device = {}".format(connected_devices))
+ return True
+
+
+def collect_bluetooth_manager_dumpsys_logs(pri_ad):
+ """Collect "adb shell dumpsys bluetooth_manager" logs.
+
+ Args:
+ pri_ad : An android device.
+
+ Returns:
+ True if dumpsys is successful, False otherwise.
+ """
+ out_file = "{}_{}".format(pri_ad.serial, "bluetooth_dumpsys.txt")
+ dumpsys_path = ''.join((pri_ad.log_path, "/BluetoothDumpsys"))
+ create_dir(dumpsys_path)
+ cmd = ''.join("adb -s {} shell dumpsys bluetooth_manager > {}/{}".format(
+ pri_ad.serial, dumpsys_path, out_file))
+ exe_cmd(cmd)
+ file_path = "{}/{}".format(dumpsys_path, out_file)
+ if not a2dp_dumpsys_parser(file_path):
+ logging.error("Could not parse dumpsys logs")
+ return False
+ return True
+
+
+def configure_and_start_ap(ap, network):
+ """Configure hostapd parameters and starts access point.
+
+ Args:
+ ap: An access point object.
+ network: A dictionary with wifi network details.
+ """
+ hostapd_sec = hostapd_security.Security(
+ security_mode=network["security"], password=network["password"])
+
+ config = hostapd_config.HostapdConfig(
+ n_capabilities=[hostapd_constants.N_CAPABILITY_HT40_MINUS],
+ mode=hostapd_constants.MODE_11N_PURE,
+ channel=network["channel"],
+ ssid=network["SSID"],
+ security=hostapd_sec)
+ ap.start_ap(config)
+ time.sleep(AP_START_TIME)
+
+
+def connect_dev_to_headset(pri_droid, dev_to_connect, profiles_set):
+ supported_profiles = bt_profile_constants.values()
+ for profile in profiles_set:
+ if profile not in supported_profiles:
+ pri_droid.log.info("Profile {} is not supported list {}".format(
+ profile, supported_profiles))
+ return False
+
+ paired = False
+ for paired_device in pri_droid.droid.bluetoothGetBondedDevices():
+ if paired_device['address'] == \
+ dev_to_connect:
+ paired = True
+ break
+
+ if not paired:
+ pri_droid.log.info("{} not paired to {}".format(
+ pri_droid.droid.getBuildSerial(), dev_to_connect))
+ return False
+
+ pri_droid.droid.bluetoothConnectBonded(dev_to_connect)
+
+ end_time = time.time() + 10
+ profile_connected = set()
+ sec_addr = dev_to_connect
+ logging.info("Profiles to be connected {}".format(profiles_set))
+
+ while (time.time() < end_time and
+ not profile_connected.issuperset(profiles_set)):
+ if (bt_profile_constants['headset_client'] not in profile_connected and
+ bt_profile_constants['headset_client'] in profiles_set):
+ if is_hfp_client_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['headset_client'])
+ if (bt_profile_constants['headset'] not in profile_connected and
+ bt_profile_constants['headset'] in profiles_set):
+ profile_connected.add(bt_profile_constants['headset'])
+ if (bt_profile_constants['a2dp'] not in profile_connected and
+ bt_profile_constants['a2dp'] in profiles_set):
+ if is_a2dp_src_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['a2dp'])
+ if (bt_profile_constants['a2dp_sink'] not in profile_connected and
+ bt_profile_constants['a2dp_sink'] in profiles_set):
+ if is_a2dp_snk_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['a2dp_sink'])
+ if (bt_profile_constants['map_mce'] not in profile_connected and
+ bt_profile_constants['map_mce'] in profiles_set):
+ if is_map_mce_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['map_mce'])
+ if (bt_profile_constants['map'] not in profile_connected and
+ bt_profile_constants['map'] in profiles_set):
+ if is_map_mse_device_connected(pri_droid, sec_addr):
+ profile_connected.add(bt_profile_constants['map'])
+ time.sleep(0.1)
+
+ while not profile_connected.issuperset(profiles_set):
+ try:
+ time.sleep(10)
+ profile_event = pri_droid.ed.pop_event(
+ bluetooth_profile_connection_state_changed,
+ bt_default_timeout + 10)
+ logging.info("Got event {}".format(profile_event))
+ except Exception:
+ logging.error("Did not get {} profiles left {}".format(
+ bluetooth_profile_connection_state_changed, profile_connected))
+ return False
+ profile = profile_event['data']['profile']
+ state = profile_event['data']['state']
+ device_addr = profile_event['data']['addr']
+ if state == bt_profile_states['connected'] and \
+ device_addr == dev_to_connect:
+ profile_connected.add(profile)
+ logging.info("Profiles connected until now {}".format(
+ profile_connected))
+ return True
+
+
+def device_discoverable(pri_ad, sec_ad):
+ """Verifies whether the device is discoverable or not.
+
+ Args:
+ pri_ad: An primary android device object.
+ sec_ad: An secondary android device object.
+
+ Returns:
+ True if the device found, False otherwise.
+ """
+ pri_ad.droid.bluetoothMakeDiscoverable()
+ scan_mode = pri_ad.droid.bluetoothGetScanMode()
+ if scan_mode == bt_scan_mode_types['connectable_discoverable']:
+ logging.info("Primary device scan mode is "
+ "SCAN_MODE_CONNECTABLE_DISCOVERABLE.")
+ else:
+ logging.info("Primary device scan mode is not "
+ "SCAN_MODE_CONNECTABLE_DISCOVERABLE.")
+ return False
+ if sec_ad.droid.bluetoothStartDiscovery():
+ time.sleep(DISCOVERY_TIME)
+ droid_name = pri_ad.droid.bluetoothGetLocalName()
+ get_discovered_devices = sec_ad.droid.bluetoothGetDiscoveredDevices()
+ find_flag = False
+
+ if get_discovered_devices:
+ for device in get_discovered_devices:
+ if 'name' in device and device['name'] == droid_name:
+ logging.info("Primary device is in the discovery "
+ "list of secondary device.")
+ find_flag = True
+ break
+ else:
+ logging.info("Secondary device get all the discovered devices "
+ "list is empty")
+ return False
+ else:
+ logging.info("Secondary device start discovery process error.")
+ return False
+ if not find_flag:
+ return False
+ return True
+
+
+def disconnect_headset_from_dev(pri_ad, sec_ad, profiles_list):
+ """
+ Disconnect primary from secondary on a specific set of profiles
+ Args:
+ pri_ad - Primary android_device initiating disconnection
+ sec_ad - Secondary android droid (sl4a interface to keep the
+ method signature the same connect_pri_to_sec above)
+ profiles_list - List of profiles we want to disconnect from
+
+ Returns:
+ True on Success
+ False on Failure
+ """
+ supported_profiles = bt_profile_constants.values()
+ for profile in profiles_list:
+ if profile not in supported_profiles:
+ pri_ad.log.info("Profile {} is not in supported list {}".format(
+ profile, supported_profiles))
+ return False
+
+ pri_ad.log.info(pri_ad.droid.bluetoothGetBondedDevices())
+
+ try:
+ pri_ad.droid.bluetoothDisconnectConnectedProfile(sec_ad, profiles_list)
+ except Exception as err:
+ pri_ad.log.error(
+ "Exception while trying to disconnect profile(s) {}: {}".format(
+ profiles_list, err))
+ return False
+
+ profile_disconnected = set()
+ pri_ad.log.info("Disconnecting from profiles: {}".format(profiles_list))
+
+ while not profile_disconnected.issuperset(profiles_list):
+ try:
+ profile_event = pri_ad.ed.pop_event(
+ bluetooth_profile_connection_state_changed, bt_default_timeout)
+ pri_ad.log.info("Got event {}".format(profile_event))
+ except Exception:
+ pri_ad.log.error("Did not disconnect from Profiles")
+ return False
+
+ profile = profile_event['data']['profile']
+ state = profile_event['data']['state']
+ device_addr = profile_event['data']['addr']
+
+ if state == bt_profile_states['disconnected'] and \
+ device_addr == sec_ad:
+ profile_disconnected.add(profile)
+ pri_ad.log.info("Profiles disconnected so far {}".format(
+ profile_disconnected))
+
+ return True
+
+
+def initiate_disconnect_from_hf(audio_receiver, pri_ad, sec_ad, duration):
+ """Initiates call and disconnect call on primary device.
+ Steps:
+ 1. Initiate call from HF.
+ 2. Wait for dialing state at DUT and wait for ringing at secondary device.
+ 3. Accepts call from secondary device.
+ 4. Wait for call active state at primary and secondary device.
+ 5. Sleeps until given duration.
+ 6. Disconnect call from primary device.
+ 7. Wait for call is not present state.
+
+ Args:
+ pri_ad: An android device to disconnect call.
+ sec_ad: An android device accepting call.
+ duration: Duration of call in seconds.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ audio_receiver.initiate_call_from_hf()
+ time.sleep(2)
+ flag = True
+ flag &= wait_for_dialing(logging, pri_ad)
+ flag &= wait_for_ringing(logging, sec_ad)
+ if not flag:
+ logging.error("Outgoing call did not get established")
+ return False
+
+ if not wait_and_answer_call(logging, sec_ad):
+ logging.error("Failed to answer call in second device.")
+ return False
+ if not wait_for_active(logging, pri_ad):
+ logging.error("AG not in Active state.")
+ return False
+ if not wait_for_active(logging, sec_ad):
+ logging.error("RE not in Active state.")
+ return False
+ time.sleep(duration)
+ if not hangup_call(logging, pri_ad):
+ logging.error("Failed to hangup call.")
+ return False
+ flag = True
+ flag &= wait_for_not_in_call(logging, pri_ad)
+ flag &= wait_for_not_in_call(logging, sec_ad)
+ return flag
+
+
+def initiate_disconnect_call_dut(pri_ad, sec_ad, duration, callee_number):
+ """Initiates call and disconnect call on primary device.
+ Steps:
+ 1. Initiate call from DUT.
+ 2. Wait for dialing state at DUT and wait for ringing at secondary device.
+ 3. Accepts call from secondary device.
+ 4. Wait for call active state at primary and secondary device.
+ 5. Sleeps until given duration.
+ 6. Disconnect call from primary device.
+ 7. Wait for call is not present state.
+
+ Args:
+ pri_ad: An android device to disconnect call.
+ sec_ad: An android device accepting call.
+ duration: Duration of call in seconds.
+ callee_number: Secondary device's phone number.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not initiate_call(logging, pri_ad, callee_number):
+ logging.error("Failed to initiate call")
+ return False
+ time.sleep(2)
+
+ flag = True
+ flag &= wait_for_dialing(logging, pri_ad)
+ flag &= wait_for_ringing(logging, sec_ad)
+ if not flag:
+ logging.error("Outgoing call did not get established")
+ return False
+
+ if not wait_and_answer_call(logging, sec_ad):
+ logging.error("Failed to answer call in second device.")
+ return False
+ # Wait for AG, RE to go into an Active state.
+ if not wait_for_active(logging, pri_ad):
+ logging.error("AG not in Active state.")
+ return False
+ if not wait_for_active(logging, sec_ad):
+ logging.error("RE not in Active state.")
+ return False
+ time.sleep(duration)
+ if not hangup_call(logging, pri_ad):
+ logging.error("Failed to hangup call.")
+ return False
+ flag = True
+ flag &= wait_for_not_in_call(logging, pri_ad)
+ flag &= wait_for_not_in_call(logging, sec_ad)
+
+ return flag
+
+
+def iperf_result(result, stream):
+ """Accepts the iperf result in json format and parse the output to
+ get throughput value.
+
+ Args:
+ result: contains the logs of iperf in json format.
+ stream: string to indicate uplink/downlink traffic.
+
+ Returns:
+ tx_rate: Data sent from client.
+ rx_rate: Data received from client.
+ """
+ try:
+ with open(result, 'r') as iperf_data:
+ time.sleep(1)
+ json_data = json.load(iperf_data)
+ except ValueError:
+ with open(result, 'r') as iperf_data:
+ # Possibly a result from interrupted iperf run, skip first line
+ # and try again.
+ time.sleep(1)
+ lines = iperf_data.readlines()[1:]
+ json_data = json.loads(''.join(lines))
+
+ if "error" in json_data:
+ logging.error(json_data["error"])
+ return False
+
+ protocol = json_data["start"]["test_start"]["protocol"]
+ if protocol == "UDP":
+ if "intervals" in json_data.keys():
+ interval = [
+ interval["sum"]["bits_per_second"] / 1e6
+ for interval in json_data["intervals"]
+ ]
+ tx_rate = math.fsum(interval) / len(interval)
+ else:
+ logging.error("Unable to retrive client results")
+ return False
+ if "server_output_json" in json_data.keys():
+ interval = [
+ interval["sum"]["bits_per_second"] / 1e6
+ for interval in json_data["server_output_json"]["intervals"]
+ ]
+ rx_rate = math.fsum(interval) / len(interval)
+ else:
+ logging.info("unable to retrive server results")
+ return False
+ if not stream == "ul":
+ return tx_rate, rx_rate
+ return rx_rate, tx_rate
+
+ elif protocol == "TCP":
+ tx_rate = json_data['end']['sum_sent']['bits_per_second']
+ rx_rate = json_data['end']['sum_received']['bits_per_second']
+ return tx_rate / 1e6, rx_rate / 1e6
+ else:
+ return False
+
+
+def is_a2dp_connected(pri_ad, headset_mac_address):
+ """Convenience Function to see if the 2 devices are connected on A2DP.
+
+ Args:
+ pri_ad : An android device.
+ headset_mac_address : Mac address of headset.
+
+ Returns:
+ True:If A2DP connection exists, False otherwise.
+ """
+ devices = pri_ad.droid.bluetoothA2dpGetConnectedDevices()
+ for device in devices:
+ logging.debug("A2dp Connected device {}".format(device["name"]))
+ if device["address"] == headset_mac_address:
+ return True
+ return False
+
+
+def media_stream_check(pri_ad, duration, headset_mac_address):
+ """Checks whether A2DP connecion is active or not for given duration of
+ time.
+
+ Args:
+ pri_ad : An android device.
+ duration : No of seconds to check if a2dp streaming is alive.
+ headset_mac_address : Headset mac address.
+
+ Returns:
+ True: If A2dp connection is active for entire duration.
+ False: If A2dp connection is not active.
+ """
+ while time.time() < duration:
+ if not is_a2dp_connected(pri_ad, headset_mac_address):
+ logging.error("A2dp connection not active at {}".format(
+ pri_ad.droid.getBuildSerial()))
+ return False
+ time.sleep(1)
+ return True
+
+
+def multithread_func(log, tasks):
+ """Multi-thread function wrapper.
+
+ Args:
+ log: log object.
+ tasks: tasks to be executed in parallel.
+
+ Returns:
+ List of results of tasks
+ """
+ results = run_multithread_func(log, tasks)
+ for res in results:
+ if not res:
+ return False
+ return True
+
+
+def music_play_and_check(pri_ad, headset_mac_address, music_to_play, duration):
+ """Starts playing media and checks if media plays for n seconds.
+
+ Steps:
+ 1. Starts media player on android device.
+ 2. Checks if music streaming is ongoing for n seconds.
+ 3. Stops media player.
+ 4. Collect dumpsys logs.
+
+ Args:
+ pri_ad: An android device.
+ headset_mac_address: Mac address of third party headset.
+ music_to_play: Indicates the music file to play.
+ duration: Time in secs to indicate music time to play.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not start_media_play(pri_ad, music_to_play):
+ logging.error("Start media play failed.")
+ return False
+ stream_time = time.time() + duration
+ if not media_stream_check(pri_ad, stream_time, headset_mac_address):
+ logging.error("A2DP Connection check failed.")
+ pri_ad.droid.mediaPlayStop()
+ return False
+ pri_ad.droid.mediaPlayStop()
+ if not collect_bluetooth_manager_dumpsys_logs(pri_ad):
+ return False
+ return True
+
+
+def music_play_and_check_via_app(pri_ad, headset_mac_address):
+ """Starts google music player and check for A2DP connection.
+
+ Steps:
+ 1. Starts Google music player on android device.
+ 2. Checks for A2DP connection.
+
+ Args:
+ pri_ad: An android device.
+ headset_mac_address: Mac address of third party headset.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ pri_ad.adb.shell("am start com.google.android.music")
+ time.sleep(3)
+ pri_ad.adb.shell("input keyevent 85")
+
+ if not is_a2dp_connected(pri_ad, headset_mac_address):
+ logging.error("A2dp connection not active at {}".format(
+ pri_ad.droid.getBuildSerial()))
+ return False
+ return True
+
+
+def get_phone_ip(ad):
+ """Get the WiFi IP address of the phone.
+
+ Args:
+ ad: the android device under test
+
+ Returns:
+ Ip address of the phone for WiFi, as a string
+ """
+ return ad.droid.connectivityGetIPv4Addresses('wlan0')[0]
+
+
+def pair_dev_to_headset(pri_ad, dev_to_pair):
+
+ bonded_devices = pri_ad.droid.bluetoothGetBondedDevices()
+ for d in bonded_devices:
+ if d['address'] == dev_to_pair:
+ pri_ad.log.info("Successfully bonded to device".format(
+ dev_to_pair))
+ return True
+ pri_ad.droid.bluetoothStartDiscovery()
+ time.sleep(10)
+ pri_ad.droid.bluetoothCancelDiscovery()
+ logging.info("disovered devices = {}".format(
+ pri_ad.droid.bluetoothGetDiscoveredDevices()))
+ for device in pri_ad.droid.bluetoothGetDiscoveredDevices():
+ if device['address'] == dev_to_pair:
+
+ result = pri_ad.droid.bluetoothDiscoverAndBond(dev_to_pair)
+ pri_ad.log.info(result)
+ end_time = time.time() + bt_default_timeout
+ pri_ad.log.info("Verifying devices are bonded")
+ time.sleep(5)
+ while time.time() < end_time:
+ bonded_devices = pri_ad.droid.bluetoothGetBondedDevices()
+ bonded = False
+ for d in bonded_devices:
+ if d['address'] == dev_to_pair:
+ pri_ad.log.info("Successfully bonded to device".format(
+ dev_to_pair))
+ return True
+ pri_ad.log.info("Failed to bond devices.")
+ return False
+
+def pair_and_connect_headset(pri_ad, headset_mac_address, profile_to_connect):
+ """Pair and connect android device with third party headset.
+
+ Args:
+ pri_ad: An android device.
+ headset_mac_address: Mac address of third party headset.
+ profile_to_connect: Profile to be connected with headset.
+
+ Returns:
+ True if pair and connect to headset successful, False otherwise.
+ """
+ if not pair_dev_to_headset(pri_ad, headset_mac_address):
+ logging.error("Could not pair to headset.")
+ return False
+ # Wait until pairing gets over.
+ time.sleep(2)
+ if not connect_dev_to_headset(pri_ad, headset_mac_address,
+ profile_to_connect):
+ logging.error("Could not connect to headset.")
+ return False
+ return True
+
+
+def perform_classic_discovery(pri_ad):
+ """Convenience function to start and stop device discovery.
+
+ Args:
+ pri_ad: An android device.
+
+ Returns:
+ True start and stop discovery is successful, False otherwise.
+ """
+ if not pri_ad.droid.bluetoothStartDiscovery():
+ logging.info("Failed to start inquiry")
+ return False
+ time.sleep(DISCOVERY_TIME)
+ if not pri_ad.droid.bluetoothCancelDiscovery():
+ logging.info("Failed to cancel inquiry")
+ return False
+ logging.info("Discovered device list {}".format(
+ pri_ad.droid.bluetoothGetDiscoveredDevices()))
+ return True
+
+
+def connect_wlan_profile(pri_ad, network):
+ """Disconnect and Connect to AP.
+
+ Args:
+ pri_ad: An android device.
+ network: Network to which AP to be connected.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ reset_wifi(pri_ad)
+ wifi_toggle_state(pri_ad, False)
+ wifi_test_device_init(pri_ad)
+ wifi_connect(pri_ad, network)
+ if not wifi_connection_check(pri_ad, network["SSID"]):
+ logging.error("Wifi connection does not exist.")
+ return False
+ return True
+
+
+def toggle_bluetooth(pri_ad, iterations):
+ """Toggles bluetooth on/off for N iterations.
+
+ Args:
+ pri_ad: An android device object.
+ iterations: Number of iterations to run.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ for i in range(iterations):
+ logging.info("Bluetooth Turn on/off iteration : {}".format(i))
+ if not enable_bluetooth(pri_ad.droid, pri_ad.ed):
+ logging.error("Failed to enable bluetooth")
+ return False
+ time.sleep(BLUETOOTH_WAIT_TIME)
+ if not disable_bluetooth(pri_ad.droid):
+ logging.error("Failed to turn off bluetooth")
+ return False
+ time.sleep(BLUETOOTH_WAIT_TIME)
+ return True
+
+
+def toggle_screen_state(pri_ad, iterations):
+ """Toggles the screen state to on or off..
+
+ Args:
+ pri_ad: Android device.
+ iterations: Number of times screen on/off should be performed.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ for i in range(iterations):
+ if not pri_ad.ensure_screen_on():
+ logging.error("User window cannot come up")
+ return False
+ if not pri_ad.go_to_sleep():
+ logging.info("Screen off")
+ return True
+
+
+def setup_tel_config(pri_ad, sec_ad, sim_conf_file):
+ """Sets tel properties for primary device and secondary devices
+
+ Args:
+ pri_ad: An android device object.
+ sec_ad: An android device object.
+ sim_conf_file: Sim card map.
+
+ Returns:
+ pri_ad_num: Phone number of primary device.
+ sec_ad_num: Phone number of secondary device.
+ """
+ setup_droid_properties(logging, pri_ad, sim_conf_file)
+ pri_ad_num = get_phone_number(logging, pri_ad)
+ setup_droid_properties(logging, sec_ad, sim_conf_file)
+ sec_ad_num = get_phone_number(logging, sec_ad)
+ return pri_ad_num, sec_ad_num
+
+
+def start_fping(pri_ad, duration):
+ """Starts fping to ping for DUT's ip address.
+
+ Steps:
+ 1. Run fping command to check DUT's IP is alive or not.
+
+ Args:
+ pri_ad: An android device object.
+ duration: Duration of fping in seconds.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ out_file_name = "{}".format("fping.txt")
+ full_out_path = os.path.join(pri_ad.log_path, out_file_name)
+ cmd = "fping {} -D -c {}".format(get_phone_ip(pri_ad), duration)
+ cmd = cmd.split()
+ with open(full_out_path, "w") as f:
+ subprocess.call(cmd, stdout=f)
+ f = open(full_out_path, "r")
+ for lines in f:
+ l = re.split("[:/=]", lines)
+ if l[len(l) - 1] != "0%":
+ logging.error("Packet drop observed")
+ return False
+ return True
+
+
+def start_media_play(pri_ad, music_file_to_play):
+ """Starts media player on device.
+
+ Args:
+ pri_ad : An android device.
+ music_file_to_play : An audio file to play.
+
+ Returns:
+ True:If media player start music, False otherwise.
+ """
+ if not pri_ad.droid.mediaPlayOpen("file:///sdcard/Music/{}"
+ .format(music_file_to_play)):
+ logging.error("Failed to play music")
+ return False
+
+ pri_ad.droid.mediaPlaySetLooping(True)
+ logging.info("Music is now playing on device {}".format(pri_ad.serial))
+ return True
+
+
+def throughput_pass_fail_check(throughput):
+ """Method to check if the throughput is above the defined
+ threshold.
+
+ Args:
+ throughput: Throughput value of test run.
+
+ Returns:
+ Pass if throughput is above threshold.
+ Fail if throughput is below threshold and Iperf Failed.
+ Empty if Iperf value is not present.
+ """
+ iperf_result_list = []
+ if len(throughput) != 0:
+ for i in range(len(throughput)):
+ if throughput[i] != 'Iperf Failed':
+ if float(throughput[i].split('Mb/s')[0]) > \
+ THROUGHPUT_THRESHOLD:
+ iperf_result_list.append("PASS")
+ else:
+ iperf_result_list.append("FAIL")
+ elif throughput[i] == 'Iperf Failed':
+ iperf_result_list.append("FAIL")
+ return "FAIL" if "FAIL" or "Iperf Failed" in iperf_result_list \
+ else "PASS"
+ else:
+ return " "
+
+
+def wifi_connection_check(pri_ad, ssid):
+ """Function to check existence of wifi connection.
+
+ Args:
+ pri_ad : An android device.
+ ssid : wifi ssid to check.
+
+ Returns:
+ True if wifi connection exists, False otherwise.
+ """
+ wifi_info = pri_ad.droid.wifiGetConnectionInfo()
+ if (wifi_info["SSID"] == ssid and
+ wifi_info["supplicant_state"] == "completed"):
+ return True
+ logging.error("Wifi Connection check failed : {}".format(wifi_info))
+ return False
+
+
+def xlsheet(pri_ad, test_result, attenuation_range=range(0, 1, 1)):
+ """Parses the output and writes in spreadsheet.
+
+ Args:
+ pri_ad: An android device.
+ test_result: final output of testrun.
+ attenuation_range: list of attenuation values.
+ """
+ r = 0
+ c = 0
+ i = 0
+ test_result = json.loads(test_result)
+ file_name = '/'.join((pri_ad.log_path,
+ test_result["Results"][0]["Test Class"] + ".xlsx"))
+ workbook = xlsxwriter.Workbook(file_name)
+ worksheet = workbook.add_worksheet()
+ wb_format = workbook.add_format()
+ wb_format.set_text_wrap()
+ worksheet.set_column('A:A', 50)
+ worksheet.set_column('B:B', 10)
+ wb_format = workbook.add_format({'bold': True})
+ worksheet.write(r, c, "Test_case_name", wb_format)
+ worksheet.write(r, c + 1, "Result", wb_format)
+ if len(attenuation_range) > 1:
+ for idx in attenuation_range:
+ c += 1
+ worksheet.write(r, c + 1, str(idx) + "db", wb_format)
+ else:
+ worksheet.write(r, c + 2, "Iperf_throughput", wb_format)
+ c += 1
+
+ worksheet.write(r, (c + 2), "Throughput_Result", wb_format)
+ result = [(i["Test Name"], i["Result"], (i["Extras"]),
+ throughput_pass_fail_check(i["Extras"]))
+ for i in test_result["Results"]]
+
+ for row, line in enumerate(result):
+ for col, cell in enumerate(line):
+ if isinstance(cell, list):
+ for i in range(len(cell)):
+ worksheet.write(row + 1, col, cell[i])
+ col += 1
+ else:
+ worksheet.write(row + 1, col + i, cell)
diff --git a/acts/framework/acts/test_utils/net/arduino_test_utils.py b/acts/framework/acts/test_utils/net/arduino_test_utils.py
new file mode 100644
index 0000000..af0b3da
--- /dev/null
+++ b/acts/framework/acts/test_utils/net/arduino_test_utils.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2018 Google, Inc.
+#
+# 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.
+
+import logging
+import time
+import pprint
+
+from enum import IntEnum
+from queue import Empty
+
+from acts import asserts
+from acts import signals
+from acts import utils
+from acts.controllers import attenuator
+from acts.test_utils.wifi import wifi_constants
+from acts.test_utils.tel import tel_defines
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+ARDUINO = "/root/arduino/arduino-1.8.5/arduino "
+CONNECT_WIFI = "/arduino/connect_wifi/connect_wifi.ino"
+DISCONNECT_WIFI = "/arduino/disconnect_wifi/disconnect_wifi.ino"
+SSID = wutils.WifiEnums.SSID_KEY
+PWD = wutils.WifiEnums.PWD_KEY
+
+def connect_wifi(wd, network=None):
+ """Connect wifi on arduino wifi dongle
+
+ Args:
+ wd - wifi dongle object
+ network - wifi network to connect to
+ """
+ wd.log.info("Flashing connect_wifi.ino onto dongle")
+ cmd = "locate %s" % CONNECT_WIFI
+ file_path = utils.exe_cmd(cmd).decode("utf-8", "ignore").rstrip()
+ write_status = wd.write(ARDUINO, file_path, network)
+ asserts.assert_true(write_status, "Failed to flash connect wifi")
+ wd.log.info("Flashing complete")
+ wifi_status = wd.wifi_status()
+ asserts.assert_true(wifi_status, "Failed to connect to %s" % network)
+ ping_status = wd.ping_status()
+ asserts.assert_true(ping_status, "Failed to connect to internet")
+
+def disconnect_wifi(wd):
+ """Disconnect wifi on arduino wifi dongle
+
+ Args:
+ wd - wifi dongle object
+
+ Returns:
+ True - if wifi is disconnected
+ False - if not
+ """
+ wd.log.info("Flashing disconnect_wifi.ino onto dongle")
+ cmd = "locate %s" % DISCONNECT_WIFI
+ file_path = utils.exe_cmd(cmd).decode("utf-8", "ignore").rstrip()
+ write_status = wd.write(ARDUINO, file_path)
+ asserts.assert_true(write_status, "Failed to flash disconnect wifi")
+ wd.log.info("Flashing complete")
+ wifi_status = wd.wifi_status(False)
+ asserts.assert_true(not wifi_status, "Failed to disconnect wifi")
diff --git a/acts/framework/acts/test_utils/net/connectivity_const.py b/acts/framework/acts/test_utils/net/connectivity_const.py
index 89bdb2c..bea0060 100644
--- a/acts/framework/acts/test_utils/net/connectivity_const.py
+++ b/acts/framework/acts/test_utils/net/connectivity_const.py
@@ -53,6 +53,34 @@
# This is a random value as of now
VPN_TIMEOUT = 15
+# Connectiivty Manager constants
+TYPE_MOBILE = 0
+TYPE_WIFI = 1
+
+# Multipath preference constants
+MULTIPATH_PREFERENCE_NONE = 0
+MULTIPATH_PREFERENCE_HANDOVER = 1 << 0
+MULTIPATH_PREFERENCE_RELIABILITY = 1 << 1
+MULTIPATH_PREFERENCE_PERFORMANCE = 1 << 2
+
+# IpSec constants
+SOCK_STREAM = 1
+SOCK_DGRAM = 2
+AF_INET = 2
+AF_INET6 = 10
+DIRECTION_IN = 0
+DIRECTION_OUT = 1
+MODE_TRANSPORT = 0
+MODE_TUNNEL = 1
+CRYPT_NULL = "ecb(cipher_null)"
+CRYPT_AES_CBC = "cbc(aes)"
+AUTH_HMAC_MD5 = "hmac(md5)"
+AUTH_HMAC_SHA1 = "hmac(sha1)"
+AUTH_HMAC_SHA256 = "hmac(sha256)"
+AUTH_HMAC_SHA384 = "hmac(sha384)"
+AUTH_HMAC_SHA512 = "hmac(sha512)"
+AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))"
+
# Constants for VpnProfile
class VpnProfile(object):
""" This class contains all the possible
diff --git a/acts/framework/acts/test_utils/net/ipsec_test_utils.py b/acts/framework/acts/test_utils/net/ipsec_test_utils.py
new file mode 100644
index 0000000..ff31ea0
--- /dev/null
+++ b/acts/framework/acts/test_utils/net/ipsec_test_utils.py
@@ -0,0 +1,249 @@
+#
+# Copyright 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.
+
+import binascii
+import os
+import random
+import re
+import threading
+import time
+
+from acts.test_utils.net import connectivity_const as cconst
+from acts import asserts
+
+PKTS = 5
+
+def make_key(len_bits):
+ asserts.assert_true(
+ len_bits % 8 == 0, "Unexpected key length. Should be a multiple "
+ "of 8, got %s" % len_bits)
+ return binascii.hexlify(os.urandom(int(len_bits/8))).decode()
+
+def allocate_spis(ad, ip_a, ip_b, in_spi = None, out_spi = None):
+ """ Allocate in and out SPIs for android device
+
+ Args:
+ 1. ad : android device object
+ 2. ip_a : local IP address for In SPI
+ 3. ip_b : remote IP address for Out SPI
+ 4. in_spi : Generate In SPI with this value
+ 5. out_spi : Generate Out SPI with this value
+
+ Returns:
+ List of In and Out SPI
+ """
+ in_spi_key = ad.droid.ipSecAllocateSecurityParameterIndex(ip_a, in_spi)
+ in_spi = ad.droid.ipSecGetSecurityParameterIndex(in_spi_key)
+ ad.log.info("In SPI: %s" % hex(in_spi))
+
+ out_spi_key = ad.droid.ipSecAllocateSecurityParameterIndex(ip_b, out_spi)
+ out_spi = ad.droid.ipSecGetSecurityParameterIndex(out_spi_key)
+ ad.log.info("Out SPI: %s" % hex(out_spi))
+
+ asserts.assert_true(in_spi and out_spi, "Failed to allocate SPIs")
+ return [in_spi_key, out_spi_key]
+
+def release_spis(ad, spis):
+ """ Destroy SPIs
+
+ Args:
+ 1. ad : android device object
+ 2. spis : list of SPI keys to destroy
+ """
+ for spi_key in spis:
+ ad.droid.ipSecReleaseSecurityParameterIndex(spi_key)
+ spi = ad.droid.ipSecGetSecurityParameterIndex(spi_key)
+ asserts.assert_true(not spi, "Failed to release SPI")
+
+def create_transport_mode_transforms(ad,
+ spis,
+ ip_a,
+ ip_b,
+ crypt_algo,
+ crypt_key,
+ auth_algo,
+ auth_key,
+ trunc_bit,
+ udp_encap_sock=None):
+ """ Create transport mode transforms on the device
+
+ Args:
+ 1. ad : android device object
+ 2. spis : spi keys of the SPIs created
+ 3. ip_a : local IP addr
+ 4. ip_b : remote IP addr
+ 5. crypt_key : encryption key
+ 6. auth_key : authentication key
+ 7. udp_encap_sock : set udp encapsulation for ESP packets
+
+ Returns:
+ List of In and Out Transforms
+ """
+ in_transform = ad.droid.ipSecCreateTransportModeTransform(
+ crypt_algo, crypt_key, auth_algo, auth_key, trunc_bit, spis[0],
+ ip_b, udp_encap_sock)
+ ad.log.info("In Transform: %s" % in_transform)
+ out_transform = ad.droid.ipSecCreateTransportModeTransform(
+ crypt_algo, crypt_key, auth_algo, auth_key, trunc_bit, spis[1],
+ ip_a, udp_encap_sock)
+ ad.log.info("Out Transform: %s" % out_transform)
+ asserts.assert_true(in_transform and out_transform,
+ "Failed to create transforms")
+ return [in_transform, out_transform]
+
+def destroy_transport_mode_transforms(ad, transforms):
+ """ Destroy transforms on the device
+
+ Args:
+ 1. ad : android device object
+ 2. transforms : list to transform keys to destroy
+ """
+ for transform in transforms:
+ ad.droid.ipSecDestroyTransportModeTransform(transform)
+ status = ad.droid.ipSecGetTransformStatus(transform)
+ ad.log.info("Transform status: %s" % status)
+ asserts.assert_true(not status, "Failed to destroy transform")
+
+def apply_transport_mode_transforms_file_descriptors(ad, fd, transforms):
+ """ Apply transpot mode transform to FileDescriptor object
+
+ Args:
+ 1. ad - android device object
+ 2. fd - FileDescriptor key
+ 3. transforms - list of in and out transforms
+ """
+ in_transform = ad.droid.ipSecApplyTransportModeTransformFileDescriptor(
+ fd, cconst.DIRECTION_IN, transforms[0])
+ out_transform = ad.droid.ipSecApplyTransportModeTransformFileDescriptor(
+ fd, cconst.DIRECTION_OUT, transforms[1])
+ asserts.assert_true(in_transform and out_transform,
+ "Failed to apply transform")
+ ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
+ ad.log.info("XFRM STATE:\n%s\n" % ip_xfrm_state)
+ ip_xfrm_policy = ad.adb.shell("ip -s xfrm policy")
+ ad.log.info("XFRM POLICY:\n%s\n" % ip_xfrm_policy)
+
+def remove_transport_mode_transforms_file_descriptors(ad, fd):
+ """ Remove transport mode transform from FileDescriptor object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - FileDescriptor key
+ """
+ status = ad.droid.ipSecRemoveTransportModeTransformsFileDescriptor(fd)
+ asserts.assert_true(status, "Failed to remove transform")
+
+def apply_transport_mode_transforms_datagram_socket(ad, socket, transforms):
+ """ Apply transport mode transform to DatagramSocket object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - DatagramSocket object key
+ 3. transforms - list of in and out transforms
+ """
+ in_tfrm_status = ad.droid.ipSecApplyTransportModeTransformDatagramSocket(
+ socket, cconst.DIRECTION_IN, transforms[0])
+ out_tfrm_status = ad.droid.ipSecApplyTransportModeTransformDatagramSocket(
+ socket, cconst.DIRECTION_OUT, transforms[1])
+ asserts.assert_true(in_tfrm_status and out_tfrm_status,
+ "Failed to apply transform")
+
+ ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
+ ad.log.info("XFRM STATE:\n%s\n" % ip_xfrm_state)
+
+def remove_transport_mode_transforms_datagram_socket(ad, socket):
+ """ Remove transport mode transform from DatagramSocket object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - DatagramSocket object key
+ """
+ status = ad.droid.ipSecRemoveTransportModeTransformsDatagramSocket(socket)
+ asserts.assert_true(status, "Failed to remove transform")
+
+def apply_transport_mode_transforms_socket(ad, socket, transforms):
+ """ Apply transport mode transform to Socket object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - Socket object key
+ 3. transforms - list of in and out transforms
+ """
+ in_tfrm_status = ad.droid.ipSecApplyTransportModeTransformSocket(
+ socket, cconst.DIRECTION_IN, transforms[0])
+ out_tfrm_status = ad.droid.ipSecApplyTransportModeTransformSocket(
+ socket, cconst.DIRECTION_OUT, transforms[1])
+ asserts.assert_true(in_tfrm_status and out_tfrm_status,
+ "Failed to apply transform")
+
+ ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
+ ad.log.info("XFRM STATE:\n%s\n" % ip_xfrm_state)
+
+def remove_transport_mode_transforms_socket(ad, socket):
+ """ Remove transport mode transform from Socket object
+
+ Args:
+ 1. ad - android device object
+ 2. socket - Socket object key
+ """
+ status = ad.droid.ipSecRemoveTransportModeTransformsSocket(socket)
+ asserts.assert_true(status, "Failed to remove transform")
+
+def verify_esp_packets(ads):
+ """ Verify that encrypted ESP packets are sent
+
+ Args:
+ 1. ads - Verify ESP packets on all devices
+ """
+ for ad in ads:
+ ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
+ ad.log.info("XFRM STATE on %s:\n%s\n" % (ad.serial, ip_xfrm_state))
+ pattern = re.findall(r'\d+\(packets\)', ip_xfrm_state)
+ esp_pkts = False
+ for _ in pattern:
+ if int(_.split('(')[0]) >= PKTS:
+ esp_pkts = True
+ break
+ asserts.assert_true(esp_pkts, "Could not find ESP pkts")
+
+def generate_random_crypt_auth_combo():
+ """ Generate every possible combination of crypt and auth keys,
+ auth algo, trunc bits supported by IpSecManager
+ """
+ crypt_key_length = [128, 192, 256]
+ auth_method_key = { cconst.AUTH_HMAC_MD5 : 128,
+ cconst.AUTH_HMAC_SHA1 : 160,
+ cconst.AUTH_HMAC_SHA256 : 256,
+ cconst.AUTH_HMAC_SHA384 : 384,
+ cconst.AUTH_HMAC_SHA512 : 512 }
+ auth_method_trunc = { cconst.AUTH_HMAC_MD5 : list(range(96, 136, 8)),
+ cconst.AUTH_HMAC_SHA1 : list(range(96, 168, 8)),
+ cconst.AUTH_HMAC_SHA256 : list(range(96, 264, 8)),
+ cconst.AUTH_HMAC_SHA384 : list(range(192, 392, 8)),
+ cconst.AUTH_HMAC_SHA512 : list(range(256, 520, 8)) }
+ return_list = []
+ for c in crypt_key_length:
+ for k in auth_method_key.keys():
+ auth_key = auth_method_key[k]
+ lst = auth_method_trunc[k]
+ for t in lst:
+ combo = []
+ combo.append(c)
+ combo.append(k)
+ combo.append(auth_key)
+ combo.append(t)
+ return_list.append(combo)
+
+ return return_list
diff --git a/acts/framework/acts/test_utils/net/socket_test_utils.py b/acts/framework/acts/test_utils/net/socket_test_utils.py
new file mode 100644
index 0000000..a8a05fc
--- /dev/null
+++ b/acts/framework/acts/test_utils/net/socket_test_utils.py
@@ -0,0 +1,285 @@
+#
+# Copyright 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.
+
+import queue
+import re
+import threading
+import time
+
+from acts.test_utils.net import connectivity_const as cconst
+from acts import asserts
+
+MSG = "Test message "
+PKTS = 5
+
+""" Methods for android.system.Os based sockets """
+def open_android_socket(ad, domain, sock_type, ip, port):
+ """ Open TCP or UDP using android.system.Os class
+
+ Args:
+ 1. ad - android device object
+ 2. domain - IPv4 or IPv6 type
+ 3. sock_type - UDP or TCP socket
+ 4. ip - IP addr on the device
+ 5. port - open socket on port
+
+ Returns:
+ File descriptor key
+ """
+ fd_key = ad.droid.openSocket(domain, sock_type, ip, port)
+ ad.log.info("File descriptor: %s" % fd_key)
+ asserts.assert_true(fd_key, "Failed to open socket")
+ return fd_key
+
+def close_android_socket(ad, fd_key):
+ """ Close socket
+
+ Args:
+ 1. ad - android device object
+ 2. fd_key - file descriptor key
+ """
+ status = ad.droid.closeSocket(fd_key)
+ asserts.assert_true(status, "Failed to close socket")
+
+def listen_accept_android_socket(client,
+ server,
+ client_fd,
+ server_fd,
+ server_ip,
+ server_port):
+ """ Listen, accept TCP sockets
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_fd : client's socket handle
+ 4. server_fd : server's socket handle
+ 5. server_ip : send data to this IP
+ 6. server_port : send data to this port
+ """
+ server.droid.listenSocket(server_fd)
+ client.droid.connectSocket(client_fd, server_ip, server_port)
+ sock = server.droid.acceptSocket(server_fd)
+ asserts.assert_true(sock, "Failed to accept socket")
+ return sock
+
+def send_recv_data_android_sockets(client,
+ server,
+ client_fd,
+ server_fd,
+ server_ip,
+ server_port):
+ """ Send TCP or UDP data over android os sockets from client to server.
+ Verify that server received the data.
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_fd : client's socket handle
+ 4. server_fd : server's socket handle
+ 5. server_ip : send data to this IP
+ 6. server_port : send data to this port
+ """
+ send_list = []
+ recv_list = []
+
+ for _ in range(1, PKTS+1):
+ msg = MSG + " %s" % _
+ send_list.append(msg)
+ client.log.info("Sending message: %s" % msg)
+ client.droid.sendDataOverSocket(server_ip, server_port, msg, client_fd)
+ recv_msg = server.droid.recvDataOverSocket(server_fd)
+ server.log.info("Received message: %s" % recv_msg)
+ recv_list.append(recv_msg)
+
+ recv_list = [x.rstrip('\x00') if x else x for x in recv_list]
+ asserts.assert_true(send_list and recv_list and send_list == recv_list,
+ "Send and recv information is incorrect")
+
+""" Methods for java.net.DatagramSocket based sockets """
+def open_datagram_socket(ad, ip, port):
+ """ Open datagram socket
+
+ Args:
+ 1. ad : android device object
+ 2. ip : IP addr on the device
+ 3. port : socket port
+
+ Returns:
+ Hash key of the datagram socket
+ """
+ socket_key = ad.droid.openDatagramSocket(ip, port)
+ ad.log.info("Datagram socket: %s" % socket_key)
+ asserts.assert_true(socket_key, "Failed to open datagram socket")
+ return socket_key
+
+def close_datagram_socket(ad, socket_key):
+ """ Close datagram socket
+
+ Args:
+ 1. socket_key : hash key of datagram socket
+ """
+ status = ad.droid.closeDatagramSocket(socket_key)
+ asserts.assert_true(status, "Failed to close datagram socket")
+
+def send_recv_data_datagram_sockets(client,
+ server,
+ client_sock,
+ server_sock,
+ server_ip,
+ server_port):
+ """ Send data over datagram socket from dut_a to dut_b.
+ Verify that dut_b received the data.
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_sock : client's socket handle
+ 4. server_sock : server's socket handle
+ 5. server_ip : send data to this IP
+ 6. server_port : send data to this port
+ """
+ send_list = []
+ recv_list = []
+
+ for _ in range(1, PKTS+1):
+ msg = MSG + " %s" % _
+ send_list.append(msg)
+ client.log.info("Sending message: %s" % msg)
+ client.droid.sendDataOverDatagramSocket(client_sock,
+ msg,
+ server_ip,
+ server_port)
+ recv_msg = server.droid.recvDataOverDatagramSocket(server_sock)
+ server.log.info("Received message: %s" % recv_msg)
+ recv_list.append(recv_msg)
+
+ recv_list = [x.rstrip('\x00') if x else x for x in recv_list]
+ asserts.assert_true(send_list and recv_list and send_list == recv_list,
+ "Send and recv information is incorrect")
+
+""" Utils methods for java.net.Socket based sockets """
+def _accept_socket(server, server_ip, server_port, server_sock, q):
+ sock = server.droid.acceptTcpSocket(server_sock)
+ server.log.info("Server socket: %s" % sock)
+ q.put(sock)
+
+def _client_socket(client, server_ip, server_port, client_ip, client_port, q):
+ time.sleep(0.5)
+ sock = client.droid.openTcpSocket(server_ip,
+ server_port,
+ client_ip,
+ client_port)
+ client.log.info("Client socket: %s" % sock)
+ q.put(sock)
+
+def open_connect_socket(client,
+ server,
+ client_ip,
+ server_ip,
+ client_port,
+ server_port,
+ server_sock):
+ """ Open tcp socket and connect to server
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_ip : client's socket handle
+ 4. server_ip : send data to this IP
+ 5. client_port : port on client socket
+ 6. server_port : port on server socket
+ 7. server_sock : server socket
+
+ Returns:
+ client and server socket from successful connect
+ """
+ sq = queue.Queue()
+ cq = queue.Queue()
+ s = threading.Thread(target = _accept_socket,
+ args = (server, server_ip, server_port, server_sock,
+ sq))
+ c = threading.Thread(target = _client_socket,
+ args = (client, server_ip, server_port, client_ip,
+ client_port, cq))
+ s.start()
+ c.start()
+ c.join()
+ s.join()
+
+ client_sock = cq.get()
+ server_sock = sq.get()
+ asserts.assert_true(client_sock and server_sock, "Failed to open sockets")
+
+ return client_sock, server_sock
+
+def open_server_socket(server, server_ip, server_port):
+ """ Open tcp server socket
+
+ Args:
+ 1. server : ad object for server device
+ 2. server_ip : send data to this IP
+ 3. server_port : send data to this port
+ """
+ sock = server.droid.openTcpServerSocket(server_ip, server_port)
+ server.log.info("Server Socket: %s" % sock)
+ asserts.assert_true(sock, "Failed to open server socket")
+ return sock
+
+def close_socket(ad, socket):
+ """ Close socket
+
+ Args:
+ 1. ad - android device object
+ 2. socket - socket key
+ """
+ status = ad.droid.closeTcpSocket(socket)
+ asserts.assert_true(status, "Failed to socket")
+
+def close_server_socket(ad, socket):
+ """ Close server socket
+
+ Args:
+ 1. ad - android device object
+ 2. socket - server socket key
+ """
+ status = ad.droid.closeTcpServerSocket(socket)
+ asserts.assert_true(status, "Failed to socket")
+
+def send_recv_data_sockets(client, server, client_sock, server_sock):
+ """ Send data over TCP socket from client to server.
+ Verify that server received the data
+
+ Args:
+ 1. client : ad object for client device
+ 2. server : ad object for server device
+ 3. client_sock : client's socket handle
+ 4. server_sock : server's socket handle
+ """
+ send_list = []
+ recv_list = []
+
+ for _ in range(1, PKTS+1):
+ msg = MSG + " %s" % _
+ send_list.append(msg)
+ client.log.info("Sending message: %s" % msg)
+ client.droid.sendDataOverTcpSocket(client_sock, msg)
+ recv_msg = server.droid.recvDataOverTcpSocket(server_sock)
+ server.log.info("Received message: %s" % recv_msg)
+ recv_list.append(recv_msg)
+
+ recv_list = [x.rstrip('\x00') if x else x for x in recv_list]
+ asserts.assert_true(send_list and recv_list and send_list == recv_list,
+ "Send and recv information is incorrect")
diff --git a/acts/framework/acts/test_utils/power/PowerBTBaseTest.py b/acts/framework/acts/test_utils/power/PowerBTBaseTest.py
new file mode 100644
index 0000000..5dfda12
--- /dev/null
+++ b/acts/framework/acts/test_utils/power/PowerBTBaseTest.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import time
+import acts.test_utils.power.PowerBaseTest as PBT
+from acts.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts.test_utils.bt.bt_test_utils import disable_bluetooth
+
+BT_BASE_UUID = '00000000-0000-1000-8000-00805F9B34FB'
+BT_CLASSICAL_DATA = [1, 2, 3]
+BLE_LOCATION_SCAN_ENABLE = 'settings put global ble_scan_always_enabled 1'
+BLE_LOCATION_SCAN_DISABLE = 'settings put global ble_scan_always_enabled 0'
+START_PMC_CMD = 'am start -n com.android.pmc/com.android.pmc.PMCMainActivity'
+PMC_VERBOSE_CMD = 'setprop log.tag.PMC VERBOSE'
+PMC_BASE_SCAN = 'am broadcast -a com.android.pmc.BLESCAN --es ScanMode '
+
+
+class PowerBTBaseTest(PBT.PowerBaseTest):
+ """Base class for BT power related tests.
+
+ Inherited from the PowerBaseTest class
+ """
+
+ def setup_test(self):
+
+ super().setup_test()
+ # Reset BT to factory defaults
+ self.dut.droid.bluetoothFactoryReset()
+ time.sleep(2)
+ # Start PMC app.
+ self.log.info('Start PMC app...')
+ self.dut.adb.shell(START_PMC_CMD)
+ self.dut.adb.shell(PMC_VERBOSE_CMD)
+
+ def teardown_test(self):
+ """Tear down necessary objects after test case is finished.
+
+ Bring down the AP interface, delete the bridge interface, stop the
+ packet sender, and reset the ethernet interface for the packet sender
+ """
+ super().teardown_test()
+ self.dut.droid.bluetoothFactoryReset()
+ self.dut.adb.shell(BLE_LOCATION_SCAN_DISABLE)
+
+ def teardown_class(self):
+ """Clean up the test class after tests finish running
+
+ """
+ super().teardown_class()
+ self.dut.droid.bluetoothFactoryReset()
+
+ def phone_setup_for_BT(self, bt_on, ble_on, screen_status):
+ """Sets the phone and Bluetooth in the desired state
+
+ Args:
+ bt_on: Enable/Disable BT
+ ble_on: Enable/Disable BLE
+ screen_status: screen ON or OFF
+ """
+
+ # Check if we are enabling a background scan
+ # TODO: Turn OFF cellular wihtout having to turn ON airplane mode
+ if bt_on == 'OFF' and ble_on == 'ON':
+ self.dut.adb.shell(BLE_LOCATION_SCAN_ENABLE)
+ self.dut.droid.connectivityToggleAirplaneMode(False)
+ time.sleep(2)
+
+ # Turn ON/OFF BT
+ if bt_on == 'ON':
+ enable_bluetooth(self.dut.droid, self.dut.ed)
+ self.dut.log.info('BT is ON')
+ else:
+ disable_bluetooth(self.dut.droid)
+ self.dut.droid.bluetoothDisableBLE()
+ self.dut.log.info('BT is OFF')
+ time.sleep(2)
+
+ # Turn ON/OFF BLE
+ if ble_on == 'ON':
+ self.dut.droid.bluetoothEnableBLE()
+ self.dut.log.info('BLE is ON')
+ else:
+ self.dut.droid.bluetoothDisableBLE()
+ self.dut.log.info('BLE is OFF')
+ time.sleep(2)
+
+ # Set the desired screen status
+ if screen_status == 'OFF':
+ self.dut.droid.goToSleepNow()
+ self.dut.log.info('Screen is OFF')
+ time.sleep(2)
+
+ def start_pmc_ble_scan(self,
+ scan_mode,
+ offset_start,
+ scan_time,
+ idle_time=None,
+ num_reps=1):
+ """Starts a generic BLE scan via the PMC app
+
+ Args:
+ dut: object of the android device under test
+ scan mode: desired BLE scan type
+ offset_start: Time delay in seconds before scan starts
+ scan_time: active scan time
+ idle_time: iddle time (i.e., no scans occuring)
+ num_reps: Number of repetions of the ative+idle scan sequence
+ """
+ scan_dur = scan_time
+ if not idle_time:
+ idle_time = 0.2 * scan_time
+ scan_dur = 0.8 * scan_time
+
+ first_part_msg = '%s%s --es StartTime %d --es ScanTime %d' % (
+ PMC_BASE_SCAN, scan_mode, offset_start, scan_dur)
+
+ msg = '%s --es NoScanTime %d --es Repetitions %d' % (first_part_msg,
+ idle_time,
+ num_reps)
+
+ self.dut.log.info('Sent BLE scan broadcast message: %s', msg)
+ self.dut.adb.shell(msg)
diff --git a/acts/framework/acts/test_utils/power/PowerBaseTest.py b/acts/framework/acts/test_utils/power/PowerBaseTest.py
new file mode 100644
index 0000000..78c5090
--- /dev/null
+++ b/acts/framework/acts/test_utils/power/PowerBaseTest.py
@@ -0,0 +1,496 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+import acts
+import json
+import logging
+import math
+import os
+import time
+import acts.controllers.iperf_server as ipf
+from acts import asserts
+from acts import base_test
+from acts import utils
+from acts.controllers import monsoon
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi import wifi_power_test_utils as wputils
+
+SETTINGS_PAGE = 'am start -n com.android.settings/.Settings'
+SCROLL_BOTTOM = 'input swipe 0 2000 0 0'
+UNLOCK_SCREEN = 'input keyevent 82'
+SCREENON_USB_DISABLE = 'dumpsys battery unplug'
+RESET_BATTERY_STATS = 'dumpsys batterystats --reset'
+AOD_OFF = 'settings put secure doze_always_on 0'
+MUSIC_IQ_OFF = 'pm disable-user com.google.intelligence.sense'
+# Command to disable gestures
+LIFT = 'settings put secure doze_pulse_on_pick_up 0'
+DOUBLE_TAP = 'settings put secure doze_pulse_on_double_tap 0'
+JUMP_TO_CAMERA = 'settings put secure camera_double_tap_power_gesture_disabled 1'
+RAISE_TO_CAMERA = 'settings put secure camera_lift_trigger_enabled 0'
+FLIP_CAMERA = 'settings put secure camera_double_twist_to_flip_enabled 0'
+ASSIST_GESTURE = 'settings put secure assist_gesture_enabled 0'
+ASSIST_GESTURE_ALERT = 'settings put secure assist_gesture_silence_alerts_enabled 0'
+ASSIST_GESTURE_WAKE = 'settings put secure assist_gesture_wake_enabled 0'
+SYSTEM_NAVI = 'settings put secure system_navigation_keys_enabled 0'
+# End of command to disable gestures
+AUTO_TIME_OFF = 'settings put global auto_time 0'
+AUTO_TIMEZONE_OFF = 'settings put global auto_time_zone 0'
+FORCE_YOUTUBE_STOP = 'am force-stop com.google.android.youtube'
+FORCE_DIALER_STOP = 'am force-stop com.google.android.dialer'
+IPERF_TIMEOUT = 180
+THRESHOLD_TOLERANCE = 0.2
+GET_FROM_PHONE = 'get_from_dut'
+GET_FROM_AP = 'get_from_ap'
+PHONE_BATTERY_VOLTAGE = 4.2
+MONSOON_MAX_CURRENT = 8.0
+MONSOON_RETRY_INTERVAL = 300
+MEASUREMENT_RETRY_COUNT = 3
+RECOVER_MONSOON_RETRY_COUNT = 3
+MIN_PERCENT_SAMPLE = 95
+ENABLED_MODULATED_DTIM = 'gEnableModulatedDTIM='
+MAX_MODULATED_DTIM = 'gMaxLIModulatedDTIM='
+TEMP_FILE = '/sdcard/Download/tmp.log'
+IPERF_DURATION = 'iperf_duration'
+INITIAL_ATTEN = [0, 0, 90, 90]
+
+
+class ObjNew():
+ """Create a random obj with unknown attributes and value.
+
+ """
+
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)
+
+ def __contains__(self, item):
+ """Function to check if one attribute is contained in the object.
+
+ Args:
+ item: the item to check
+ Return:
+ True/False
+ """
+ return hasattr(self, item)
+
+
+class PowerBaseTest(base_test.BaseTestClass):
+ """Base class for all wireless power related tests.
+
+ """
+
+ def __init__(self, controllers):
+
+ base_test.BaseTestClass.__init__(self, controllers)
+
+ def setup_class(self):
+
+ self.log = logging.getLogger()
+ self.tests = self._get_all_test_names()
+
+ # Setup the must have controllers, phone and monsoon
+ self.dut = self.android_devices[0]
+ self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
+ self.mon = self.monsoons[0]
+ self.mon.set_max_current(8.0)
+ self.mon.set_voltage(4.2)
+ self.mon.attach_device(self.dut)
+
+ # Unpack the test/device specific parameters
+ TEST_PARAMS = self.TAG + '_params'
+ req_params = [TEST_PARAMS, 'custom_files']
+ self.unpack_userparams(req_params)
+ # Unpack the custom files based on the test configs
+ for file in self.custom_files:
+ if 'pass_fail_threshold_' + self.dut.model in file:
+ self.threshold_file = file
+ elif 'attenuator_setting' in file:
+ self.attenuation_file = file
+ elif 'network_config' in file:
+ self.network_file = file
+
+ # Unpack test specific configs
+ self.unpack_testparams(getattr(self, TEST_PARAMS))
+ if hasattr(self, 'attenuators'):
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.atten_level = self.unpack_custom_file(self.attenuation_file)
+ self.set_attenuation(INITIAL_ATTEN)
+ self.threshold = self.unpack_custom_file(self.threshold_file)
+ self.mon_info = self.create_monsoon_info()
+
+ # Onetime task for each test class
+ # Temporary fix for b/77873679
+ self.adb_disable_verity()
+ self.dut.adb.shell('mv /vendor/bin/chre /vendor/bin/chre_renamed')
+ self.dut.adb.shell('pkill chre')
+
+ def setup_test(self):
+ """Set up test specific parameters or configs.
+
+ """
+ # Set the device into rockbottom state
+ self.dut_rockbottom()
+ # Wait for extra time if needed for the first test
+ if hasattr(self, 'extra_wait'):
+ self.more_wait_first_test()
+
+ def teardown_test(self):
+ """Tear down necessary objects after test case is finished.
+
+ """
+ self.log.info('Tearing down the test case')
+ self.mon.usb('on')
+
+ def teardown_class(self):
+ """Clean up the test class after tests finish running
+
+ """
+ self.log.info('Tearing down the test class')
+ self.mon.usb('on')
+
+ def unpack_testparams(self, bulk_params):
+ """Unpack all the test specific parameters.
+
+ Args:
+ bulk_params: dict with all test specific params in the config file
+ """
+ for key in bulk_params.keys():
+ setattr(self, key, bulk_params[key])
+
+ def unpack_custom_file(self, file, test_specific=True):
+ """Unpack the pass_fail_thresholds from a common file.
+
+ Args:
+ file: the common file containing pass fail threshold.
+ """
+ with open(file, 'r') as f:
+ params = json.load(f)
+ if test_specific:
+ try:
+ return params[self.TAG]
+ except KeyError:
+ pass
+ else:
+ return params
+
+ def decode_test_configs(self, attrs, indices):
+ """Decode the test config/params from test name.
+
+ Remove redundant function calls when tests are similar.
+ Args:
+ attrs: a list of the attrs of the test config obj
+ indices: a list of the location indices of keyword in the test name.
+ """
+ # Decode test parameters for the current test
+ test_params = self.current_test_name.split('_')
+ values = [test_params[x] for x in indices]
+ config_dict = dict(zip(attrs, values))
+ self.test_configs = ObjNew(**config_dict)
+
+ def more_wait_first_test(self):
+ # For the first test, increase the offset for longer wait time
+ if self.current_test_name == self.tests[0]:
+ self.mon_info.offset = self.mon_offset + self.extra_wait
+ else:
+ self.mon_info.offset = self.mon_offset
+
+ def set_attenuation(self, atten_list):
+ """Function to set the attenuator to desired attenuations.
+
+ Args:
+ atten_list: list containing the attenuation for each attenuator.
+ """
+ if len(atten_list) != self.num_atten:
+ raise Exception('List given does not have the correct length')
+ for i in range(self.num_atten):
+ self.attenuators[i].set_atten(atten_list[i])
+
+ def dut_rockbottom(self):
+ """Set the phone into Rock-bottom state.
+
+ """
+ self.dut.log.info('Now set the device to Rockbottom State')
+ utils.require_sl4a((self.dut, ))
+ self.dut.droid.connectivityToggleAirplaneMode(False)
+ time.sleep(2)
+ self.dut.droid.connectivityToggleAirplaneMode(True)
+ time.sleep(2)
+ utils.set_ambient_display(self.dut, False)
+ utils.set_auto_rotate(self.dut, False)
+ utils.set_adaptive_brightness(self.dut, False)
+ utils.sync_device_time(self.dut)
+ utils.set_location_service(self.dut, False)
+ utils.set_mobile_data_always_on(self.dut, False)
+ utils.disable_doze_light(self.dut)
+ utils.disable_doze(self.dut)
+ wutils.reset_wifi(self.dut)
+ wutils.wifi_toggle_state(self.dut, False)
+ try:
+ self.dut.droid.nfcDisable()
+ except acts.controllers.sl4a_lib.rpc_client.Sl4aApiError:
+ self.dut.log.info('NFC is not available')
+ self.dut.droid.setScreenBrightness(0)
+ self.dut.adb.shell(AOD_OFF)
+ self.dut.droid.setScreenTimeout(2200)
+ self.dut.droid.wakeUpNow()
+ self.dut.adb.shell(LIFT)
+ self.dut.adb.shell(DOUBLE_TAP)
+ self.dut.adb.shell(JUMP_TO_CAMERA)
+ self.dut.adb.shell(RAISE_TO_CAMERA)
+ self.dut.adb.shell(FLIP_CAMERA)
+ self.dut.adb.shell(ASSIST_GESTURE)
+ self.dut.adb.shell(ASSIST_GESTURE_ALERT)
+ self.dut.adb.shell(ASSIST_GESTURE_WAKE)
+ self.dut.adb.shell(SCREENON_USB_DISABLE)
+ self.dut.adb.shell(UNLOCK_SCREEN)
+ self.dut.adb.shell(SETTINGS_PAGE)
+ self.dut.adb.shell(SCROLL_BOTTOM)
+ self.dut.adb.shell(MUSIC_IQ_OFF)
+ self.dut.adb.shell(AUTO_TIME_OFF)
+ self.dut.adb.shell(AUTO_TIMEZONE_OFF)
+ self.dut.adb.shell(FORCE_YOUTUBE_STOP)
+ self.dut.adb.shell(FORCE_DIALER_STOP)
+ self.dut.droid.wifiSetCountryCode('US')
+ self.dut.droid.wakeUpNow()
+ self.dut.log.info('Device has been set to Rockbottom state')
+ self.dut.log.info('Screen is ON')
+
+ def measure_power_and_validate(self):
+ """The actual test flow and result processing and validate.
+
+ """
+ self.collect_power_data()
+ self.pass_fail_check()
+
+ def collect_power_data(self):
+ """Measure power, plot and take log if needed.
+
+ """
+ tag = ''
+ # Collecting current measurement data and plot
+ begin_time = utils.get_current_epoch_time()
+ self.file_path, self.test_result = self.monsoon_data_collect_save()
+ wputils.monsoon_data_plot(self.mon_info, self.file_path, tag=tag)
+ # Take Bugreport
+ if self.bug_report:
+ self.dut.take_bug_report(self.test_name, begin_time)
+
+ def pass_fail_check(self):
+ """Check the test result and decide if it passed or failed.
+
+ The threshold is provided in the config file. In this class, result is
+ current in mA.
+ """
+ current_threshold = self.threshold[self.test_name]
+ if self.test_result:
+ asserts.assert_true(
+ abs(self.test_result - current_threshold) / current_threshold <
+ THRESHOLD_TOLERANCE,
+ ('Measured average current in [{}]: {}, which is '
+ 'more than {} percent off than acceptable threshold {:.2f}mA'
+ ).format(self.test_name, self.test_result,
+ self.pass_fail_tolerance * 100, current_threshold))
+ asserts.explicit_pass('Measurement finished for {}.'.format(
+ self.test_name))
+ else:
+ asserts.fail(
+ 'Something happened, measurement is not complete, test failed')
+
+ def create_monsoon_info(self):
+ """Creates the config dictionary for monsoon
+
+ Returns:
+ mon_info: Dictionary with the monsoon packet config
+ """
+ if hasattr(self, IPERF_DURATION):
+ self.mon_duration = self.iperf_duration - 10
+ mon_info = ObjNew(
+ dut=self.mon,
+ freq=self.mon_freq,
+ duration=self.mon_duration,
+ offset=self.mon_offset,
+ data_path=self.mon_data_path)
+ return mon_info
+
+ def monsoon_recover(self):
+ """Test loop to wait for monsoon recover from unexpected error.
+
+ Wait for a certain time duration, then quit.0
+ Args:
+ mon: monsoon object
+ Returns:
+ True/False
+ """
+ try:
+ self.mon.reconnect_monsoon()
+ time.sleep(2)
+ self.mon.usb('on')
+ logging.info('Monsoon recovered from unexpected error')
+ time.sleep(2)
+ return True
+ except monsoon.MonsoonError:
+ logging.info(self.mon.mon.ser.in_waiting)
+ logging.warning('Unable to recover monsoon from unexpected error')
+ return False
+
+ def monsoon_data_collect_save(self):
+ """Current measurement and save the log file.
+
+ Collect current data using Monsoon box and return the path of the
+ log file. Take bug report if requested.
+
+ Returns:
+ data_path: the absolute path to the log file of monsoon current
+ measurement
+ avg_current: the average current of the test
+ """
+
+ tag = '{}_{}_{}'.format(self.test_name, self.dut.model,
+ self.dut.build_info['build_id'])
+ data_path = os.path.join(self.mon_info.data_path, '{}.txt'.format(tag))
+ total_expected_samples = self.mon_info.freq * (
+ self.mon_info.duration + self.mon_info.offset)
+ min_required_samples = total_expected_samples * MIN_PERCENT_SAMPLE / 100
+ # Retry counter for monsoon data aquisition
+ retry_measure = 1
+ # Indicator that need to re-collect data
+ need_collect_data = 1
+ result = None
+ while retry_measure <= MEASUREMENT_RETRY_COUNT:
+ try:
+ # If need to retake data
+ if need_collect_data == 1:
+ #Resets the battery status right before the test started
+ self.dut.adb.shell(RESET_BATTERY_STATS)
+ self.log.info(
+ 'Starting power measurement with monsoon box, try #{}'.
+ format(retry_measure))
+ #Start the power measurement using monsoon
+ self.mon_info.dut.monsoon_usb_auto()
+ result = self.mon_info.dut.measure_power(
+ self.mon_info.freq,
+ self.mon_info.duration,
+ tag=tag,
+ offset=self.mon_info.offset)
+ self.mon_info.dut.reconnect_dut()
+ # Reconnect to dut
+ else:
+ self.mon_info.dut.reconnect_dut()
+ # Reconnect and return measurement results if no error happens
+ avg_current = result.average_current
+ monsoon.MonsoonData.save_to_text_file([result], data_path)
+ self.log.info('Power measurement done within {} try'.format(
+ retry_measure))
+ return data_path, avg_current
+ # Catch monsoon errors during measurement
+ except monsoon.MonsoonError:
+ self.log.info(self.mon_info.dut.mon.ser.in_waiting)
+ # Break early if it's one count away from limit
+ if retry_measure == MEASUREMENT_RETRY_COUNT:
+ self.log.error(
+ 'Test failed after maximum measurement retry')
+ break
+
+ self.log.warning('Monsoon error happened, now try to recover')
+ # Retry loop to recover monsoon from error
+ retry_monsoon = 1
+ while retry_monsoon <= RECOVER_MONSOON_RETRY_COUNT:
+ mon_status = self.monsoon_recover(self.mon_info.dut)
+ if mon_status:
+ break
+ else:
+ retry_monsoon += 1
+ self.log.warning(
+ 'Wait for {} second then try again'.format(
+ MONSOON_RETRY_INTERVAL))
+ time.sleep(MONSOON_RETRY_INTERVAL)
+
+ # Break the loop to end test if failed to recover monsoon
+ if not mon_status:
+ self.log.error(
+ 'Tried our best, still failed to recover monsoon')
+ break
+ else:
+ # If there is no data, or captured samples are less than min
+ # required, re-take
+ if not result:
+ self.log.warning('No data taken, need to remeasure')
+ elif len(result._data_points) <= min_required_samples:
+ self.log.warning(
+ 'More than {} percent of samples are missing due to monsoon error. Need to remeasure'.
+ format(100 - MIN_PERCENT_SAMPLE))
+ else:
+ need_collect_data = 0
+ self.log.warning(
+ 'Data collected is valid, try reconnect to DUT to finish test'
+ )
+ retry_measure += 1
+
+ if retry_measure > MEASUREMENT_RETRY_COUNT:
+ self.log.error('Test failed after maximum measurement retry')
+
+ def setup_ap_connection(self, network, bandwidth=80, connect=True):
+ """Setup AP and connect DUT to it.
+
+ Args:
+ network: the network config for the AP to be setup
+ bandwidth: bandwidth of the WiFi network to be setup
+ connect: indicator of if connect dut to the network after setup
+ Returns:
+ self.brconfigs: dict for bridge interface configs
+ """
+ wutils.wifi_toggle_state(self.dut, True)
+ if hasattr(self, 'access_points'):
+ self.brconfigs = wputils.ap_setup(
+ self.access_point, network, bandwidth=bandwidth)
+ if connect:
+ wutils.wifi_connect(self.dut, network)
+
+ def process_iperf_results(self):
+ """Get the iperf results and process.
+
+ Returns:
+ throughput: the average throughput during tests.
+ """
+ # Get IPERF results and add this to the plot title
+ RESULTS_DESTINATION = os.path.join(self.iperf_server.log_path,
+ 'iperf_client_output_{}.log'.format(
+ self.current_test_name))
+ PULL_FILE = '{} {}'.format(TEMP_FILE, RESULTS_DESTINATION)
+ self.dut.adb.pull(PULL_FILE)
+ # Calculate the average throughput
+ if self.use_client_output:
+ iperf_file = RESULTS_DESTINATION
+ else:
+ iperf_file = self.iperf_server.log_files[-1]
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+ throughput = (math.fsum(iperf_result.instantaneous_rates[:-1]) /
+ len(iperf_result.instantaneous_rates[:-1])) * 8
+ self.log.info('The average throughput is {}'.format(throughput))
+ except ValueError:
+ self.log.warning('Cannot get iperf result. Setting to 0')
+ throughput = 0
+ return throughput
+
+ # TODO(@qijiang)Merge with tel_test_utils.py
+ def adb_disable_verity(self):
+ """Disable verity on the device.
+
+ """
+ if self.dut.adb.getprop("ro.boot.veritymode") == "enforcing":
+ self.dut.adb.disable_verity()
+ self.dut.reboot()
+ self.dut.adb.root()
+ self.dut.adb.remount()
diff --git a/acts/framework/acts/test_utils/power/PowerCoexBaseTest.py b/acts/framework/acts/test_utils/power/PowerCoexBaseTest.py
new file mode 100644
index 0000000..1976b0f
--- /dev/null
+++ b/acts/framework/acts/test_utils/power/PowerCoexBaseTest.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2018 - The Android Open Source Project
+8
+# 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.
+
+import acts.test_utils.power.PowerBTBaseTest as PBtBT
+import acts.test_utils.power.PowerWiFiBaseTest as PWBT
+from acts import utils
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+
+class PowerCoexBaseTest(PBtBT.PowerBTBaseTest, PWBT.PowerWiFiBaseTest):
+ """Base class for BT power related tests.
+
+ Inherited from the PowerBaseTest class
+ """
+
+ def coex_test_phone_setup(self, Screen_status, WiFi_status, WiFi_band,
+ BT_status, BLE_status, Cellular_status,
+ Celluar_band):
+ """Setup the phone in desired state for coex tests.
+
+ Args:
+ Screen_status: 'ON' or 'OFF'
+ WiFi_status: 'ON', 'Connected', 'Disconnected', or 'OFF'
+ WiFi_band: '2g', '5g' or None, the band of AP
+ BT_status: 'ON' or 'OFF'
+ BLE_status: 'ON' or 'OFF'
+ Cellular_status: 'ON' or 'OFF'
+ Celluar_band: 'Verizon', 'Tmobile', or 'ATT' for live network,
+ actual band for callbox setup; 'None' when celluar is OFF
+ """
+ # Setup WiFi
+ if WiFi_status is 'ON':
+ wutils.wifi_toggle_state(self.dut, True)
+ elif WiFi_status is 'Connected':
+ self.setup_ap_connection(self.main_network[WiFi_band])
+ elif WiFi_status is 'Disconnected':
+ self.setup_ap_connection(
+ self.main_network[WiFi_band], connect=False)
+
+ # Setup BT/BLE
+ self.phone_setup_for_BT(BT_status, BLE_status, Screen_status)
+
+ # Setup Cellular
+ if Cellular_status is 'ON':
+ self.dut.droid.connectivityToggleAirplaneMode(False)
+ utils.set_mobile_data_always_on(self.dut, True)
+
+ def coex_scan_setup(self, WiFi_scan, BLE_scan_mode, wifi_scan_command):
+ """Setup for scan activities on WiFi, BT/BLE, and cellular.
+
+ Args:
+ WiFi_scan: 'ON', 'OFF' or 'PNO'
+ BLE_scan_mode: 'balanced', 'opportunistic', 'low_power', or 'low_latency'
+ """
+ if WiFi_scan is 'ON':
+ self.dut.adb.shell(wifi_scan_command)
+ if WiFi_scan is 'PNO':
+ self.log.info(
+ 'Set attenuation so device loses connection to trigger PNO scans'
+ )
+ # Set to maximum attenuation 95 dB to cut down connection
+ [self.attenuators[i].set_atten(95) for i in range(self.num_atten)]
+ if BLE_scan_mode is not None:
+ self.start_pmc_ble_scan(BLE_scan_mode, self.mon_info.offset,
+ self.mon_info.duration)
diff --git a/acts/framework/acts/test_utils/power/PowerWiFiBaseTest.py b/acts/framework/acts/test_utils/power/PowerWiFiBaseTest.py
new file mode 100644
index 0000000..19702f4
--- /dev/null
+++ b/acts/framework/acts/test_utils/power/PowerWiFiBaseTest.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import acts.test_utils.power.PowerBaseTest as PBT
+from acts.test_utils.wifi import wifi_power_test_utils as wputils
+
+IPERF_DURATION = 'iperf_duration'
+
+
+class PowerWiFiBaseTest(PBT.PowerBaseTest):
+ """Base class for WiFi power related tests.
+
+ Inherited from the PowerBaseTest class
+ """
+
+ def setup_class(self):
+
+ super().setup_class()
+ if hasattr(self, 'access_points'):
+ self.access_point = self.access_points[0]
+ self.access_point_main = self.access_points[0]
+ if len(self.access_points) > 1:
+ self.access_point_aux = self.access_points[1]
+ if hasattr(self, 'network_file'):
+ self.networks = self.unpack_custom_file(self.network_file, False)
+ self.main_network = self.networks['main_network']
+ self.aux_network = self.networks['aux_network']
+ if hasattr(self, 'packet_senders'):
+ self.pkt_sender = self.packet_senders[0]
+ if hasattr(self, 'iperf_servers'):
+ self.iperf_server = self.iperf_servers[0]
+ if hasattr(self, 'iperf_duration'):
+ self.mon_duration = self.iperf_duration - 10
+ self.create_monsoon_info()
+
+ def teardown_test(self):
+ """Tear down necessary objects after test case is finished.
+
+ Bring down the AP interface, delete the bridge interface, stop the
+ packet sender, and reset the ethernet interface for the packet sender
+ """
+ super().teardown_test()
+ if hasattr(self, 'pkt_sender'):
+ self.pkt_sender.stop_sending(ignore_status=True)
+ if hasattr(self, 'brconfigs'):
+ self.access_point.bridge.teardown(self.brconfigs)
+ delattr(self, 'brconfigs')
+ if hasattr(self, 'brconfigs_main'):
+ self.access_point_main.bridge.teardown(self.brconfigs_main)
+ delattr(self, 'brconfigs_main')
+ if hasattr(self, 'brconfigs_aux'):
+ self.access_point_aux.bridge.teardown(self.brconfigs_aux)
+ delattr(self, 'brconfigs_aux')
+ if hasattr(self, 'access_points'):
+ for ap in self.access_points:
+ ap.close()
+ if hasattr(self, 'pkt_sender'):
+ wputils.reset_host_interface(self.pkt_sender.interface)
+ if hasattr(self, 'iperf_server'):
+ self.iperf_server.stop()
+
+ def teardown_class(self):
+ """Clean up the test class after tests finish running
+
+ """
+ super().teardown_class()
+ if hasattr(self, 'access_points'):
+ for ap in self.access_points:
+ ap.close()
+
+ def collect_power_data(self):
+ """Measure power, plot and check pass/fail.
+
+ If IPERF is run, need to pull iperf results and attach it to the plot.
+ """
+ super().collect_power_data()
+ tag = ''
+ if hasattr(self, IPERF_DURATION):
+ throughput = self.process_iperf_results()
+ tag = '_RSSI_{0:d}dBm_Throughput_{1:.2f}Mbps'.format(
+ self.RSSI, throughput)
+ wputils.monsoon_data_plot(self.mon_info, self.file_path, tag=tag)
diff --git a/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
index e8e6ad1..7e8de6a 100644
--- a/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
+++ b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
@@ -17,42 +17,47 @@
Base Class for Defining Common Telephony Test Functionality
"""
+import logging
import os
-import time
-import inspect
+import re
+import shutil
import traceback
import acts.controllers.diag_logger
+from acts import asserts
+from acts import logger as acts_logger
from acts.base_test import BaseTestClass
+from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
from acts.keys import Config
from acts.signals import TestSignal
from acts.signals import TestAbortClass
from acts.signals import TestAbortAll
+from acts.signals import TestBlocked
+from acts import records
from acts import utils
from acts.test_utils.tel.tel_subscription_utils import \
initial_set_up_for_subid_infomation
from acts.test_utils.tel.tel_test_utils import abort_all_tests
-from acts.test_utils.tel.tel_test_utils import check_qxdm_logger_always_on
-from acts.test_utils.tel.tel_test_utils import is_sim_locked
from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
from acts.test_utils.tel.tel_test_utils import print_radio_info
-from acts.test_utils.tel.tel_test_utils import refresh_droid_config
+from acts.test_utils.tel.tel_test_utils import reboot_device
+from acts.test_utils.tel.tel_test_utils import refresh_sl4a_session
+from acts.test_utils.tel.tel_test_utils import run_multithread_func
from acts.test_utils.tel.tel_test_utils import setup_droid_properties
from acts.test_utils.tel.tel_test_utils import set_phone_screen_on
from acts.test_utils.tel.tel_test_utils import set_phone_silent_mode
-from acts.test_utils.tel.tel_test_utils import set_qxdm_logger_always_on
+from acts.test_utils.tel.tel_test_utils import set_qxdm_logger_command
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts.test_utils.tel.tel_test_utils import stop_qxdm_loggers
from acts.test_utils.tel.tel_test_utils import unlock_sim
from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
from acts.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_ENABLED
from acts.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_DISABLED
-from acts.utils import force_airplane_mode
-
-QXDM_LOG_PATH = "/data/vendor/radio/diag_logs/logs/"
class TelephonyBaseTest(BaseTestClass):
@@ -61,21 +66,46 @@
BaseTestClass.__init__(self, controllers)
self.logger_sessions = []
+ self.log_path = getattr(logging, "log_path", None)
+ self.qxdm_log = self.user_params.get("qxdm_log", True)
+ qxdm_log_mask_cfg = self.user_params.get("qxdm_log_mask_cfg", None)
+ if isinstance(qxdm_log_mask_cfg, list):
+ qxdm_log_mask_cfg = qxdm_log_mask_cfg[0]
+ if qxdm_log_mask_cfg and "dev/null" in qxdm_log_mask_cfg:
+ qxdm_log_mask_cfg = None
+ stop_qxdm_loggers(self.log, self.android_devices)
for ad in self.android_devices:
- ad.qxdm_log = True
- if getattr(ad, "qxdm_always_on", False):
- #this is only supported on 2017 devices
- ad.log.info("qxdm_always_on is set in config file")
- mask = getattr(ad, "qxdm_mask", "Radio-general.cfg")
- if not check_qxdm_logger_always_on(ad, mask):
- ad.log.info("qxdm always on is not set, turn it on")
- set_qxdm_logger_always_on(ad, mask)
- else:
- ad.log.info("qxdm always on is already set")
+ try:
+ ad.adb.shell("killall -9 tcpdump")
+ except AdbError:
+ ad.log.warn("Killing existing tcpdump processes failed")
+ if not hasattr(ad, "init_log_path"):
+ ad.init_log_path = ad.log_path
+ ad.log_path = self.log_path
print_radio_info(ad)
if not unlock_sim(ad):
abort_all_tests(ad.log, "unable to unlock SIM")
+ ad.wakeup_screen()
+ ad.adb.shell("input keyevent 82")
+ ad.qxdm_log = getattr(ad, "qxdm_log", self.qxdm_log)
+ if ad.qxdm_log:
+ qxdm_log_mask = getattr(ad, "qxdm_log_mask", None)
+ if qxdm_log_mask_cfg:
+ qxdm_mask_path = DEFAULT_QXDM_LOG_PATH
+ ad.adb.shell("mkdir %s" % qxdm_mask_path)
+ ad.log.info("Push %s to %s", qxdm_log_mask_cfg,
+ qxdm_mask_path)
+ ad.adb.push("%s %s" % (qxdm_log_mask_cfg, qxdm_mask_path))
+ mask_file_name = os.path.split(qxdm_log_mask_cfg)[-1]
+ qxdm_log_mask = os.path.join(qxdm_mask_path,
+ mask_file_name)
+ set_qxdm_logger_command(ad, mask=qxdm_log_mask)
+ ad.adb.pull(
+ "/firmware/image/qdsp6m.qdb %s" % ad.init_log_path,
+ ignore_status=True)
+ start_qxdm_loggers(self.log, self.android_devices,
+ utils.get_current_epoch_time())
self.skip_reset_between_cases = self.user_params.get(
"skip_reset_between_cases", True)
@@ -85,59 +115,54 @@
def tel_test_wrap(fn):
def _safe_wrap_test_case(self, *args, **kwargs):
test_id = "%s:%s:%s" % (self.__class__.__name__, self.test_name,
- self.begin_time.replace(' ', '-'))
+ self.log_begin_time.replace(' ', '-'))
self.test_id = test_id
- log_string = "[Test ID] %s" % test_id
- self.log.info(log_string)
- no_crash = True
- try:
- for ad in self.android_devices:
- if getattr(ad, "droid"):
- ad.droid.logI("Started %s" % log_string)
- # TODO: b/19002120 start QXDM Logging
- result = fn(self, *args, **kwargs)
- for ad in self.android_devices:
- if getattr(ad, "droid"):
- ad.droid.logI("Finished %s" % log_string)
- new_crash = ad.check_crash_report(self.test_name,
- self.begin_time, result)
- if self.user_params.get("check_crash", True) and new_crash:
- ad.log.error("Find new crash reports %s", new_crash)
- no_crash = False
- if not result and self.user_params.get("telephony_auto_rerun"):
+ self.result_detail = ""
+ tries = 2 if self.user_params.get("telephony_auto_rerun") else 1
+ for i in range(tries):
+ result = True
+ log_string = "[Test ID] %s" % test_id
+ if i > 1:
+ log_string = "[Rerun]%s" % log_string
self.teardown_test()
- # re-run only once, if re-run pass, mark as pass
- log_string = "[Rerun Test ID] %s. 1st run failed." % test_id
- self.log.info(log_string)
self.setup_test()
- for ad in self.android_devices:
- if getattr(ad, "droid"):
- ad.droid.logI("Rerun Started %s" % log_string)
+ self.log.info(log_string)
+ for ad in self.android_devices:
+ ad.log_path = self.log_path
+ try:
+ ad.droid.logI("Started %s" % log_string)
+ except Exception as e:
+ ad.log.warning(e)
+ refresh_sl4a_session(ad)
+ try:
result = fn(self, *args, **kwargs)
- if result is True:
- self.log.info("Rerun passed.")
- elif result is False:
- self.log.info("Rerun failed.")
- else:
- # In the event that we have a non-bool or null
- # retval, we want to clearly distinguish this in the
- # logs from an explicit failure, though the test will
- # still be considered a failure for reporting purposes.
- self.log.info("Rerun indeterminate.")
- result = False
- return result and no_crash
- except (TestSignal, TestAbortClass, TestAbortAll):
- raise
- except Exception as e:
- self.log.error(str(e))
- return False
- finally:
- # TODO: b/19002120 stop QXDM Logging
+ except (TestSignal, TestAbortClass, TestAbortAll) as signal:
+ if self.result_detail:
+ signal.details = self.result_detail
+ raise
+ except Exception as e:
+ self.log.error(traceback.format_exc())
+ asserts.fail(self.result_detail)
for ad in self.android_devices:
try:
- ad.adb.wait_for_device()
+ ad.droid.logI("Finished %s" % log_string)
except Exception as e:
- self.log.error(str(e))
+ ad.log.warning(e)
+ refresh_sl4a_session(ad)
+ if result: break
+ if self.user_params.get("check_crash", True):
+ new_crash = ad.check_crash_report(self.test_name,
+ self.begin_time, True)
+ if new_crash:
+ msg = "Find new crash reports %s" % new_crash
+ ad.log.error(msg)
+ self.result_detail = "%s %s %s" % (self.result_detail,
+ ad.serial, msg)
+ result = False
+ if result:
+ asserts.explicit_pass(self.result_detail)
+ else:
+ asserts.fail(self.result_detail)
return _safe_wrap_test_case
@@ -244,30 +269,38 @@
return True
def teardown_class(self):
+ stop_qxdm_loggers(self.log, self.android_devices)
+ ensure_phones_default_state(self.log, self.android_devices)
try:
for ad in self.android_devices:
ad.droid.disableDevicePassword()
if "enable_wifi_verbose_logging" in self.user_params:
ad.droid.wifiEnableVerboseLogging(
WIFI_VERBOSE_LOGGING_DISABLED)
+ if hasattr(ad, "init_log_path"):
+ ad.log_path = ad.init_log_path
return True
except Exception as e:
self.log.error("Failure with %s", e)
def setup_test(self):
+ for ad in self.android_devices:
+ ad.ed.clear_all_events()
+ output = ad.adb.logcat("-t 1")
+ match = re.search(r"\d+-\d+\s\d+:\d+:\d+.\d+", output)
+ if match:
+ ad.test_log_begin_time = match.group(0)
+ if getattr(self, "qxdm_log", True):
+ start_qxdm_loggers(self.log, self.android_devices, self.begin_time)
if getattr(self, "diag_logger", None):
for logger in self.diag_logger:
self.log.info("Starting a diagnostic session %s", logger)
self.logger_sessions.append((logger, logger.start()))
-
if self.skip_reset_between_cases:
ensure_phones_idle(self.log, self.android_devices)
else:
ensure_phones_default_state(self.log, self.android_devices)
- def teardown_test(self):
- return True
-
def on_exception(self, test_name, begin_time):
self._pull_diag_logs(test_name, begin_time)
self._take_bug_report(test_name, begin_time)
@@ -278,6 +311,81 @@
self._take_bug_report(test_name, begin_time)
self._cleanup_logger_sessions()
+ def on_blocked(self, test_name, begin_time):
+ self.on_fail(test_name, begin_time)
+
+ def _ad_take_extra_logs(self, ad, test_name, begin_time):
+ extra_qxdm_logs_in_seconds = self.user_params.get(
+ "extra_qxdm_logs_in_seconds", 60 * 3)
+ result = True
+ if getattr(ad, "qxdm_log", True):
+ # Gather qxdm log modified 3 minutes earlier than test start time
+ if begin_time:
+ qxdm_begin_time = begin_time - 1000 * extra_qxdm_logs_in_seconds
+ else:
+ qxdm_begin_time = None
+ try:
+ ad.get_qxdm_logs(test_name, qxdm_begin_time)
+ except Exception as e:
+ ad.log.error("Failed to get QXDM log for %s with error %s",
+ test_name, e)
+ result = False
+
+ try:
+ ad.check_crash_report(test_name, begin_time, log_crash_report=True)
+ except Exception as e:
+ ad.log.error("Failed to check crash report for %s with error %s",
+ test_name, e)
+ result = False
+
+ log_begin_time = getattr(
+ ad, "test_log_begin_time", None
+ ) or acts_logger.epoch_to_log_line_timestamp(begin_time - 1000 * 60)
+ log_path = os.path.join(self.log_path, test_name,
+ "%s_%s.logcat" % (ad.serial, begin_time))
+ try:
+ ad.adb.logcat(
+ 'b all -d -v year -t "%s" > %s' % (log_begin_time, log_path),
+ timeout=120)
+ except Exception as e:
+ ad.log.error("Failed to get logcat with error %s", e)
+ result = False
+ return result
+
+ def _take_bug_report(self, test_name, begin_time):
+ if self._skip_bug_report():
+ return
+ dev_num = getattr(self, "number_of_devices", None) or len(
+ self.android_devices)
+ tasks = [(self._ad_take_bugreport, (ad, test_name, begin_time))
+ for ad in self.android_devices[:dev_num]]
+ tasks.extend([(self._ad_take_extra_logs, (ad, test_name, begin_time))
+ for ad in self.android_devices[:dev_num]])
+ run_multithread_func(self.log, tasks)
+ for ad in self.android_devices[:dev_num]:
+ if getattr(ad, "reboot_to_recover", False):
+ reboot_device(ad)
+ ad.reboot_to_recover = False
+ if not self.user_params.get("zip_log", False): return
+ src_dir = os.path.join(self.log_path, test_name)
+ file_name = "%s_%s" % (src_dir, begin_time)
+ self.log.info("Zip folder %s to %s.zip", src_dir, file_name)
+ shutil.make_archive(file_name, "zip", src_dir)
+ shutil.rmtree(src_dir)
+
+ def _block_all_test_cases(self, tests):
+ """Over-write _block_all_test_case in BaseTestClass."""
+ for (i, (test_name, test_func)) in enumerate(tests):
+ signal = TestBlocked("Failed class setup")
+ record = records.TestResultRecord(test_name, self.TAG)
+ record.test_begin()
+ # mark all test cases as FAIL
+ record.test_fail(signal)
+ self.results.add_record(record)
+ # only gather bug report for the first test case
+ if i == 0:
+ self.on_fail(test_name, record.begin_time)
+
def on_pass(self, test_name, begin_time):
self._cleanup_logger_sessions()
diff --git a/acts/framework/acts/test_utils/tel/TelephonyLabPowerTest.py b/acts/framework/acts/test_utils/tel/TelephonyLabPowerTest.py
new file mode 100644
index 0000000..501f385
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/TelephonyLabPowerTest.py
@@ -0,0 +1,200 @@
+#/usr/bin/env python3.4
+#
+# Copyright 2016 - 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.
+"""
+Sanity tests for voice tests in telephony
+"""
+import time, os
+
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import BtsBandwidth
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts.test_utils.tel.tel_test_utils import set_phone_screen_on
+from acts.test_utils.tel.tel_test_utils import toggle_volte
+from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.utils import create_dir
+from acts.utils import disable_doze
+from acts.utils import set_adaptive_brightness
+from acts.utils import set_ambient_display
+from acts.utils import set_auto_rotate
+from acts.utils import set_location_service
+
+DEFAULT_CALL_NUMBER = "+11234567891"
+
+# Monsoon output Voltage in V
+MONSOON_OUTPUT_VOLTAGE = 4.2
+# Monsoon output max current in A
+MONSOON_MAX_CURRENT = 7.8
+
+# Sampling rate in Hz
+ACTIVE_CALL_TEST_SAMPLING_RATE = 100
+# Sample duration in seconds
+ACTIVE_CALL_TEST_SAMPLE_TIME = 10
+# Offset time in seconds
+ACTIVE_CALL_TEST_OFFSET_TIME = 10
+
+
+class TelephonyLabPowerTest(TelephonyBaseTest):
+ def __init__(self, controllers):
+ TelephonyBaseTest.__init__(self, controllers)
+ self.ad = self.android_devices[0]
+ self.ad.sim_card = getattr(self.ad, "sim_card", None)
+ self.md8475a_ip_address = self.user_params[
+ "anritsu_md8475a_ip_address"]
+ self.wlan_option = self.user_params.get("anritsu_wlan_option", False)
+
+ def _configure_dut(self):
+ try:
+ self.log.info("Rebooting DUT")
+ self.ad.reboot()
+ self.log.info("DUT rebooted")
+ set_adaptive_brightness(self.ad, False)
+ set_ambient_display(self.ad, False)
+ set_auto_rotate(self.ad, False)
+ set_location_service(self.ad, False)
+ # This is not needed for AOSP build
+ disable_doze(self.ad)
+ set_phone_screen_on(self.log, self.ad, 15)
+ self.ad.droid.telephonyFactoryReset()
+ except Exception as e:
+ self.ad.log.error(e)
+ return False
+ return True
+
+ def _configure_dut_network_mode_for_data_volte(self):
+ self._configure_dut()
+ try:
+ # TODO do what is needed to verify connected for LTE data transfer
+ self.log.info("setting back to LTE")
+ self.ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+ "NETWORK_MODE_LTE_CDMA_EVDO",
+ self.ad.droid.subscriptionGetDefaultSubId())
+ self.ad.adb.shell(
+ "setprop net.lte.ims.volte.provisioned 1", ignore_status=True)
+ except Exception as e:
+ self.ad.log.error(e)
+ return False
+ return True
+
+ def _configure_simulation(self):
+ try:
+ self.anritsu = MD8475A(self.md8475a_ip_address, self.log,
+ self.wlan_option)
+ [lte_bts] = set_system_model_lte(self.anritsu, self.user_params,
+ self.ad.sim_card)
+ self.bts = lte_bts
+ lte_bts.bandwidth = BtsBandwidth.LTE_BANDWIDTH_10MHz
+ set_usim_parameters(self.anritsu, self.ad.sim_card)
+ self.anritsu.start_simulation()
+ self.anritsu.send_command("IMSSTARTVN 1")
+ except AnritsuError:
+ self.log.error("Error in connecting to Anritsu Simulator")
+ return False
+ return True
+
+ def _dut_setup_data_volte(self, ad):
+ ad.droid.telephonyToggleDataConnection(True)
+ toggle_volte(self.log, ad, True)
+ return ensure_network_rat(
+ self.log,
+ ad,
+ NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+ RAT_FAMILY_LTE,
+ toggle_apm_after_setting=True)
+
+ def setup_class(self):
+ # Monsoon setup
+ self.log.info("Starting Monsoon setup")
+ self.mon = self.monsoons[0]
+ self.mon.set_voltage(MONSOON_OUTPUT_VOLTAGE)
+ self.mon.set_max_current(MONSOON_MAX_CURRENT)
+ self.mon.dut = self.ad = self.android_devices[0]
+ self.monsoon_log_path = os.path.join(self.log_path, "MonsoonLog")
+ create_dir(self.monsoon_log_path)
+ self.log.info("Conffiguring MD8475A network simulator")
+ self._configure_simulation()
+ self.log.info("Setting DUT's network mode for data and volte")
+ self._configure_dut_network_mode_for_data_volte()
+ self.log.info("Enabling DUT for data and VoLTE")
+ if not self._dut_setup_data_volte(self.ad):
+ self.log.error("phone_setup_volte failed.")
+ self.log.info("Waiting for DUT to register on MD8475A")
+ self.anritsu.wait_for_registration_state()
+ self.log.info("Waiting for DUT to register with IMS server")
+ if not phone_idle_volte(self.log, self.ad):
+ self.log.error("phone_idle_volte failed.")
+
+ def setup_test(self):
+ self.log.info("Bypassing empty setup_test() in TelephonyLabPowerTest")
+
+ def teardown_class(self):
+ self.log.info("Stopping Simulation and disconnect MD8475A")
+ self.anritsu.stop_simulation()
+ self.anritsu.disconnect()
+ return True
+
+ def _save_logs_for_power_test(self, monsoon_result, bug_report):
+ if monsoon_result and "monsoon_log_for_power_test" in self.user_params:
+ monsoon_result.save_to_text_file(
+ [monsoon_result],
+ os.path.join(self.monsoon_log_path, self.test_id))
+ if bug_report and "bug_report_for_power_test" in self.user_params:
+ self.android_devices[0].take_bug_report(self.test_name,
+ self.begin_time)
+
+ def power_test(self,
+ olvl,
+ rflvl,
+ sch_mode="DYNAMIC",
+ sample_rate=ACTIVE_CALL_TEST_SAMPLING_RATE,
+ sample_time=ACTIVE_CALL_TEST_SAMPLE_TIME,
+ offset_time=ACTIVE_CALL_TEST_OFFSET_TIME):
+ """ Set Output(DL)/InputDL(UL) power and scheduling mode of BTS,
+ and samping parameters of Monsoon
+ Args: ovlv: Output (DL) level in dBm
+ rflvl: Input (UL) level in dBm
+ sch_mode: Scheduling mode, either "STATIC" or "DYNAMIC"
+ sample_rate: Sampling rate in Hz
+ sample_time: Sample duration in seconds
+ offset_time: Offset time in seconds
+ Return: True if no exception
+ """
+ self.bts.output_level = olvl
+ self.bts.input_level = rflvl
+ self.bts.lte_scheduling_mode = sch_mode
+ bug_report = True
+ average_current = 0
+ result = None
+ self.log.info("Test %s" % self.test_name)
+ try:
+ result = self.mon.measure_power(sample_rate, sample_time,
+ self.test_id, offset_time)
+ average_current = result.average_current
+ self._save_logs_for_power_test(result, bug_report)
+ self.log.info("{} Result: {} mA".format(self.test_id,
+ average_current))
+ except Exception as e:
+ self.log.error("Exception during power consumption measurement: " +
+ str(e))
+ return False
+ return True
diff --git a/acts/framework/acts/test_utils/tel/anritsu_utils.py b/acts/framework/acts/test_utils/tel/anritsu_utils.py
index 1755198..73ab66b 100644
--- a/acts/framework/acts/test_utils/tel/anritsu_utils.py
+++ b/acts/framework/acts/test_utils/tel/anritsu_utils.py
@@ -78,8 +78,8 @@
TEST_PLMN_1X_NAME = "MD8475A_1X"
TEST_PLMN_1_MCC = "001"
TEST_PLMN_1_MNC = "01"
-DEFAULT_MCC = "001"
-DEFAULT_MNC = "01"
+DEFAULT_MCC = "310"
+DEFAULT_MNC = "260"
DEFAULT_RAC = 1
DEFAULT_LAC = 1
VzW_MCC = "311"
@@ -103,11 +103,13 @@
UE_IPV4_ADDR_2 = "192.168.1.11"
UE_IPV4_ADDR_3 = "192.168.1.21"
UE_IPV6_ADDR_1 = "2001:0:0:1::1"
-UE_IPV6_ADDR_2 = "2001:0:0:1::11"
-UE_IPV6_ADDR_3 = "2001:0:0:1::21"
-DNS_IPV4_ADDR = "192.168.1.2"
+UE_IPV6_ADDR_2 = "2001:0:0:2::1"
+UE_IPV6_ADDR_3 = "2001:0:0:3::1"
+DNS_IPV4_ADDR = "192.168.1.12"
CSCF_IPV4_ADDR = "192.168.1.2"
CSCF_IPV6_ADDR = "2001:0:0:1::2"
+CSCF_IPV6_ADDR_2 = "2001:0:0:2::2"
+CSCF_IPV6_ADDR_3 = "2001:0:0:3::2"
# GSM BAND constants
GSM_BAND_GSM450 = "GSM450"
@@ -126,9 +128,9 @@
WCDMA_BAND_2 = 2
# Default Cell Parameters
-DEFAULT_OUTPUT_LEVEL = -30
-# apply to LTE & WCDMA only to reduce UE transmit power if path loss
-DEFAULT_INPUT_LEVEL = -10
+DEFAULT_OUTPUT_LEVEL = -20
+DEFAULT_1X_OUTPUT_LEVEL = -35
+DEFAULT_INPUT_LEVEL = 0
DEFAULT_LTE_BAND = [2, 4]
DEFAULT_WCDMA_BAND = 1
DEFAULT_WCDMA_PACKET_RATE = BtsPacketRate.WCDMA_DLHSAUTO_REL7_ULHSAUTO
@@ -140,8 +142,8 @@
DEFAULT_EVDO_BAND = 0
DEFAULT_EVDO_CH = 356
DEFAULT_EVDO_SECTOR_ID = "00000000,00000000,00000000,00000000"
-VzW_CDMA1x_BAND = 0
-VzW_CDMA1x_CH = 384
+VzW_CDMA1x_BAND = 1
+VzW_CDMA1x_CH = 150
VzW_CDMA1X_SID = 26
VzW_CDMA1X_NID = 65535
VzW_EVDO_BAND = 0
@@ -210,7 +212,12 @@
DEFAULT_VNID = 1
NDP_NIC_NAME = '"Intel(R) 82577LM Gigabit Network Connection"'
CSCF_Monitoring_UA_URI = '"sip:+11234567890@test.3gpp.com"'
-TMO_CSCF_Monitoring_UA_URI = '"sip:310260123456789@msg.lab.t-mobile.com"'
+TMO_CSCF_Monitoring_UA_URI = '"sip:001010123456789@msg.lab.t-mobile.com"'
+CSCF_Virtual_UA_URI = '"sip:+11234567891@test.3gpp.com"'
+TMO_CSCF_Virtual_UA_URI = '"sip:0123456789@ims.mnc01.mcc001.3gppnetwork.org"'
+CSCF_HOSTNAME = '"ims.mnc01.mcc001.3gppnetwork.org"'
+TMO_USERLIST_NAME = "310260123456789@msg.lab.t-mobile.com"
+VZW_USERLIST_NAME = "001010123456789@test.3gpp.com"
#Cell Numbers
CELL_1 = 1
@@ -358,6 +365,7 @@
bts.rac = get_gsm_rac(user_params, cell_no)
bts.lac = get_gsm_lac(user_params, cell_no)
bts.output_level = DEFAULT_OUTPUT_LEVEL
+ bts.input_level = DEFAULT_INPUT_LEVEL
def _init_1x_bts(bts, user_params, cell_no, sim_card):
@@ -378,8 +386,7 @@
bts.dl_channel = get_1x_channel(user_params, cell_no, sim_card)
bts.sector1_sid = get_1x_sid(user_params, cell_no, sim_card)
bts.sector1_nid = get_1x_nid(user_params, cell_no, sim_card)
- bts.output_level = DEFAULT_OUTPUT_LEVEL
- bts.input_level = DEFAULT_INPUT_LEVEL
+ bts.output_level = DEFAULT_1X_OUTPUT_LEVEL
def _init_evdo_bts(bts, user_params, cell_no, sim_card):
@@ -398,10 +405,15 @@
bts.band = get_evdo_band(user_params, cell_no, sim_card)
bts.dl_channel = get_evdo_channel(user_params, cell_no, sim_card)
bts.evdo_sid = get_evdo_sid(user_params, cell_no, sim_card)
- bts.output_level = DEFAULT_OUTPUT_LEVEL
+ bts.output_level = DEFAULT_1X_OUTPUT_LEVEL
-def _init_PDN(anritsu_handle, pdn, ipv4, ipv6, ims_binding):
+def _init_PDN(anritsu_handle,
+ pdn,
+ ipv4,
+ ipv6,
+ ims_binding,
+ vnid_number=DEFAULT_VNID):
""" initializes the PDN parameters
All PDN parameters should be set here
@@ -420,14 +432,19 @@
pdn.ue_address_ipv6 = ipv6
if ims_binding:
pdn.pdn_ims = Switch.ENABLE
- pdn.pdn_vnid = DEFAULT_VNID
+ pdn.pdn_vnid = vnid_number
else:
pdn.primary_dns_address_ipv4 = DNS_IPV4_ADDR
pdn.secondary_dns_address_ipv4 = DNS_IPV4_ADDR
pdn.cscf_address_ipv4 = CSCF_IPV4_ADDR
-def _init_IMS(anritsu_handle, vnid, sim_card=None):
+def _init_IMS(anritsu_handle,
+ vnid,
+ sim_card=None,
+ ipv6_address=CSCF_IPV6_ADDR,
+ ip_type="IPV4V6",
+ auth=False):
""" initializes the IMS VNID parameters
All IMS parameters should be set here
@@ -440,11 +457,26 @@
"""
# vnid.sync = Switch.ENABLE # supported in 6.40a release
vnid.cscf_address_ipv4 = CSCF_IPV4_ADDR
- vnid.cscf_address_ipv6 = CSCF_IPV6_ADDR
+ vnid.cscf_address_ipv6 = ipv6_address
+ vnid.imscscf_iptype = ip_type
vnid.dns = Switch.DISABLE
vnid.ndp_nic = NDP_NIC_NAME
+ vnid.ndp_prefix = ipv6_address
if sim_card == P0135Ax:
vnid.cscf_monitoring_ua = TMO_CSCF_Monitoring_UA_URI
+ vnid.cscf_virtual_ua = TMO_CSCF_Virtual_UA_URI
+ vnid.cscf_host_name = CSCF_HOSTNAME
+ vnid.cscf_ims_authentication = "DISABLE"
+ if auth:
+ vnid.cscf_ims_authentication = "ENABLE"
+ vnid.tmo_cscf_userslist_add = TMO_USERLIST_NAME
+ elif sim_card == VzW12349:
+ vnid.cscf_monitoring_ua = CSCF_Monitoring_UA_URI
+ vnid.cscf_virtual_ua = CSCF_Virtual_UA_URI
+ vnid.cscf_ims_authentication = "DISABLE"
+ if auth:
+ vnid.cscf_ims_authentication = "ENABLE"
+ vnid.vzw_cscf_userslist_add = VZW_USERLIST_NAME
else:
vnid.cscf_monitoring_ua = CSCF_Monitoring_UA_URI
vnid.psap = Switch.ENABLE
@@ -475,7 +507,31 @@
_init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
_init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
- _init_IMS(anritsu_handle, vnid1, sim_card)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
return [lte1_bts, lte2_bts]
@@ -526,7 +582,31 @@
_init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
_init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
- _init_IMS(anritsu_handle, vnid1, sim_card)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
return [lte_bts, wcdma_bts]
@@ -554,7 +634,31 @@
_init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
_init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
- _init_IMS(anritsu_handle, vnid1, sim_card)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
return [lte_bts, gsm_bts]
@@ -583,7 +687,31 @@
_init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
_init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
- _init_IMS(anritsu_handle, vnid1, sim_card)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
return [lte_bts, cdma1x_bts]
@@ -611,7 +739,31 @@
_init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
_init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
- _init_IMS(anritsu_handle, vnid1)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
return [lte_bts, evdo_bts]
@@ -681,7 +833,31 @@
_init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
_init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
- _init_IMS(anritsu_handle, vnid1, sim_card)
+ if sim_card == P0135Ax:
+ vnid2 = anritsu_handle.get_IMS(2)
+ vnid3 = anritsu_handle.get_IMS(3)
+ _init_IMS(
+ anritsu_handle,
+ vnid1,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR,
+ auth=True)
+ _init_IMS(
+ anritsu_handle,
+ vnid2,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_2,
+ ip_type="IPV6")
+ _init_IMS(
+ anritsu_handle,
+ vnid3,
+ sim_card,
+ ipv6_address=CSCF_IPV6_ADDR_3,
+ ip_type="IPV6")
+ elif sim_card == VzW12349:
+ _init_IMS(anritsu_handle, vnid1, sim_card, auth=True)
+ else:
+ _init_IMS(anritsu_handle, vnid1, sim_card)
return [lte_bts]
@@ -928,9 +1104,9 @@
def handover_tc(log,
anritsu_handle,
wait_time=0,
- timeout=60,
s_bts=BtsNumber.BTS1,
- t_bts=BtsNumber.BTS2):
+ t_bts=BtsNumber.BTS2,
+ timeout=60):
""" Setup and perform a handover test case in MD8475A
Args:
@@ -943,6 +1119,7 @@
True for success False for failure
"""
log.info("Starting HO test case procedure")
+ log.info("Serving BTS = {}, Target BTS = {}".format(s_bts, t_bts))
time.sleep(wait_time)
ho_tc = anritsu_handle.get_AnritsuTestCases()
ho_tc.procedure = TestProcedure.PROCEDURE_HO
@@ -2124,3 +2301,22 @@
except KeyError:
csfb_type = CsfbType.CSFB_TYPE_REDIRECTION
return csfb_type
+
+
+def set_post_sim_params(anritsu_handle, user_params, sim_card):
+ if sim_card == P0135Ax:
+ anritsu_handle.send_command("PDNCHECKAPN 1,ims")
+ anritsu_handle.send_command("PDNCHECKAPN 2,fast.t-mobile.com")
+ anritsu_handle.send_command("PDNIMS 1,ENABLE")
+ anritsu_handle.send_command("PDNVNID 1,1")
+ anritsu_handle.send_command("PDNIMS 2,ENABLE")
+ anritsu_handle.send_command("PDNVNID 2,2")
+ anritsu_handle.send_command("PDNIMS 3,ENABLE")
+ anritsu_handle.send_command("PDNVNID 3,1")
+ if sim_card == VzW12349:
+ anritsu_handle.send_command("PDNCHECKAPN 1,IMS")
+ anritsu_handle.send_command("PDNCHECKAPN 2,VZWINTERNET")
+ anritsu_handle.send_command("PDNIMS 1,ENABLE")
+ anritsu_handle.send_command("PDNVNID 1,1")
+ anritsu_handle.send_command("PDNIMS 3,ENABLE")
+ anritsu_handle.send_command("PDNVNID 3,1")
diff --git a/acts/framework/acts/test_utils/tel/tel_defines.py b/acts/framework/acts/test_utils/tel/tel_defines.py
index 5863e81..ffc7348 100644
--- a/acts/framework/acts/test_utils/tel/tel_defines.py
+++ b/acts/framework/acts/test_utils/tel/tel_defines.py
@@ -18,7 +18,7 @@
# TIMERS
###############################################
# Max time to wait for phone data/network connection state update
-MAX_WAIT_TIME_CONNECTION_STATE_UPDATE = 20
+MAX_WAIT_TIME_CONNECTION_STATE_UPDATE = 60
# Max time to wait for network reselection
MAX_WAIT_TIME_NW_SELECTION = 180
@@ -29,6 +29,9 @@
# Wait time between state check retry
WAIT_TIME_BETWEEN_STATE_CHECK = 5
+# Max wait time for state change
+MAX_WAIT_TIME_FOR_STATE_CHANGE = 60
+
# Max time to wait after caller make a call and before
# callee start ringing
MAX_WAIT_TIME_CALLEE_RINGING = 90
@@ -39,6 +42,9 @@
"+850", "+81"
]
+# default pin/password
+DEFAULT_DEVICE_PASSWORD = "1111"
+
# Wait time after enterring puk code
WAIT_TIME_SUPPLY_PUK_CODE = 30
@@ -94,7 +100,7 @@
MAX_WAIT_TIME_USER_PLANE_DATA = 20
# Max time to wait for tethering entitlement check
-MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK = 15
+MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK = 60
# Max time to wait for voice mail count report correct result.
MAX_WAIT_TIME_VOICE_MAIL_COUNT = 90
@@ -250,6 +256,7 @@
CARRIER_ORG = 'org'
CARRIER_TEL = 'tel'
CARRIER_TSA = 'tsa'
+CARRIER_USCC = 'uscc'
RAT_FAMILY_CDMA = 'cdma'
RAT_FAMILY_CDMA2000 = 'cdma2000'
@@ -423,6 +430,11 @@
PHONE_TYPE_CDMA = "CDMA"
PHONE_TYPE_SIP = "SIP"
+# Constant for SIM Power State
+CARD_POWER_DOWN = 0
+CARD_POWER_UP = 1
+CARD_POWER_UP_PASS_THROUGH = 2
+
# Constant for SIM State
SIM_STATE_READY = "READY"
SIM_STATE_UNKNOWN = "UNKNOWN"
@@ -433,6 +445,7 @@
SIM_STATE_NOT_READY = "NOT_READY"
SIM_STATE_PERM_DISABLED = "PERM_DISABLED"
SIM_STATE_CARD_IO_ERROR = "CARD_IO_ERROR"
+SIM_STATE_LOADED = "LOADED"
# Constant for Data Connection State
DATA_STATE_CONNECTED = "CONNECTED"
@@ -445,6 +458,10 @@
DATA_ROAMING_ENABLE = 1
DATA_ROAMING_DISABLE = 0
+# Constant for ConnectivityManager Data Connection
+TYPE_MOBILE = 0
+TYPE_WIFI = 1
+
# Constant for Telephony Manager Call State
TELEPHONY_STATE_RINGING = "RINGING"
TELEPHONY_STATE_IDLE = "IDLE"
@@ -464,6 +481,15 @@
SERVICE_STATE_POWER_OFF = "POWER_OFF"
SERVICE_STATE_UNKNOWN = "UNKNOWN"
+# Service State Mapping
+SERVICE_STATE_MAPPING = {
+ "-1": SERVICE_STATE_UNKNOWN,
+ "0": SERVICE_STATE_IN_SERVICE,
+ "1": SERVICE_STATE_OUT_OF_SERVICE,
+ "2": SERVICE_STATE_EMERGENCY_ONLY,
+ "3": SERVICE_STATE_POWER_OFF
+}
+
# Constant for VoLTE Hand-over Service State
VOLTE_SERVICE_STATE_HANDOVER_STARTED = "STARTED"
VOLTE_SERVICE_STATE_HANDOVER_COMPLETED = "COMPLETED"
diff --git a/acts/framework/acts/test_utils/tel/tel_lookup_tables.py b/acts/framework/acts/test_utils/tel/tel_lookup_tables.py
index b07d310..e554b00 100644
--- a/acts/framework/acts/test_utils/tel/tel_lookup_tables.py
+++ b/acts/framework/acts/test_utils/tel/tel_lookup_tables.py
@@ -25,7 +25,7 @@
return _TelTables.technology_tbl[rat_type]['generation']
-def network_preference_for_generaton(generation, operator, phone_type=None):
+def network_preference_for_generation(generation, operator, phone_type=None):
if not phone_type:
return _TelTables.operator_network_tbl[operator][generation][
'network_preference']
@@ -223,6 +223,9 @@
'23432': tel_defines.CARRIER_EEUK, #Virgin Mobile (MVNO)
'23415': tel_defines.CARRIER_VFUK,
+ #USCC
+ '311580': tel_defines.CARRIER_USCC,
+
#Vodafone (Germany)
'26202': tel_defines.CARRIER_GMBH,
'26204': tel_defines.CARRIER_GMBH,
@@ -651,6 +654,11 @@
tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
tel_defines.CAPABILITY_VT
+ ],
+ "default": [
+ tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+ tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
+ tel_defines.CAPABILITY_VT
]
}
@@ -670,5 +678,6 @@
tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE,
tel_defines.CAPABILITY_WFC
],
- tel_defines.CARRIER_VFUK: [tel_defines.CAPABILITY_PHONE]
+ tel_defines.CARRIER_VFUK: [tel_defines.CAPABILITY_PHONE],
+ "default": [tel_defines.CAPABILITY_PHONE]
}
diff --git a/acts/framework/acts/test_utils/tel/tel_test_utils.py b/acts/framework/acts/test_utils/tel/tel_test_utils.py
index 8324c9d..8378f17 100644
--- a/acts/framework/acts/test_utils/tel/tel_test_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_test_utils.py
@@ -25,11 +25,16 @@
import urllib.parse
import time
+from acts import utils
from queue import Empty
from acts.asserts import abort_all
from acts.controllers.adb import AdbError
+from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
+from acts.controllers.android_device import SL4A_APK_NAME
from acts.controllers.sl4a_lib.event_dispatcher import EventDispatcher
from acts.test_utils.tel.tel_defines import AOSP_PREFIX
+from acts.test_utils.tel.tel_defines import CARD_POWER_DOWN
+from acts.test_utils.tel.tel_defines import CARD_POWER_UP
from acts.test_utils.tel.tel_defines import CARRIER_UNKNOWN
from acts.test_utils.tel.tel_defines import COUNTRY_CODE_LIST
from acts.test_utils.tel.tel_defines import DATA_STATE_CONNECTED
@@ -77,11 +82,14 @@
from acts.test_utils.tel.tel_defines import RAT_UNKNOWN
from acts.test_utils.tel.tel_defines import SERVICE_STATE_EMERGENCY_ONLY
from acts.test_utils.tel.tel_defines import SERVICE_STATE_IN_SERVICE
+from acts.test_utils.tel.tel_defines import SERVICE_STATE_MAPPING
from acts.test_utils.tel.tel_defines import SERVICE_STATE_OUT_OF_SERVICE
from acts.test_utils.tel.tel_defines import SERVICE_STATE_POWER_OFF
+from acts.test_utils.tel.tel_defines import SIM_STATE_LOADED
+from acts.test_utils.tel.tel_defines import SIM_STATE_NOT_READY
from acts.test_utils.tel.tel_defines import SIM_STATE_PIN_REQUIRED
from acts.test_utils.tel.tel_defines import SIM_STATE_READY
-from acts.test_utils.tel.tel_defines import WAIT_TIME_SUPPLY_PUK_CODE
+from acts.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_IDLE
from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_OFFHOOK
from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_RINGING
@@ -89,12 +97,15 @@
from acts.test_utils.tel.tel_defines import WAIT_TIME_1XRTT_VOICE_ATTACH
from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
+from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
from acts.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID
from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
from acts.test_utils.tel.tel_defines import WAIT_TIME_LEAVE_VOICE_MAIL
from acts.test_utils.tel.tel_defines import WAIT_TIME_REJECT_CALL
from acts.test_utils.tel.tel_defines import WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE
from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts.test_utils.tel.tel_defines import TYPE_MOBILE
+from acts.test_utils.tel.tel_defines import TYPE_WIFI
from acts.test_utils.tel.tel_defines import EventCallStateChanged
from acts.test_utils.tel.tel_defines import EventConnectivityChanged
from acts.test_utils.tel.tel_defines import EventDataConnectionStateChanged
@@ -119,7 +130,7 @@
from acts.test_utils.tel.tel_lookup_tables import get_voice_mail_check_number
from acts.test_utils.tel.tel_lookup_tables import get_voice_mail_delete_digit
from acts.test_utils.tel.tel_lookup_tables import \
- network_preference_for_generaton
+ network_preference_for_generation
from acts.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
from acts.test_utils.tel.tel_lookup_tables import \
rat_families_for_network_preference
@@ -146,11 +157,13 @@
from acts.logger import epoch_to_log_line_timestamp
from acts.logger import normalize_log_line_timestamp
from acts.utils import get_current_epoch_time
+from acts.utils import exe_cmd
WIFI_SSID_KEY = wifi_test_utils.WifiEnums.SSID_KEY
WIFI_PWD_KEY = wifi_test_utils.WifiEnums.PWD_KEY
WIFI_CONFIG_APBAND_2G = wifi_test_utils.WifiEnums.WIFI_CONFIG_APBAND_2G
WIFI_CONFIG_APBAND_5G = wifi_test_utils.WifiEnums.WIFI_CONFIG_APBAND_5G
+WIFI_CONFIG_APBAND_AUTO = wifi_test_utils.WifiEnums.WIFI_CONFIG_APBAND_AUTO
log = logging
STORY_LINE = "+19523521350"
@@ -247,41 +260,45 @@
log.warning("Failed to load %s!", sim_filename)
if not ad.cfg["subscription"]:
abort_all_tests(ad.log, "No Valid SIMs found in device")
- result = False
+ result = True
+ active_sub_id = get_outgoing_voice_sub_id(ad)
for sub_id, sub_info in ad.cfg["subscription"].items():
sub_info["operator"] = get_operator_name(log, ad, sub_id)
iccid = sub_info["iccid"]
if not iccid:
ad.log.warn("Unable to find ICC-ID for SIM")
continue
- if not sub_info["phone_num"]:
- if iccid in sim_data and sim_data[iccid].get("phone_num"):
- sub_info["phone_num"] = sim_data[iccid]["phone_num"]
- ad.phone_number = sub_info["phone_num"]
- result = True
- else:
- phone_number = get_phone_number_by_secret_code(
- ad, sub_info["sim_operator_name"])
- if phone_number:
+ if sub_info.get("phone_num"):
+ if getattr(ad, "phone_number", None) and check_phone_number_match(
+ sub_info["phone_num"], ad.phone_number):
+ sub_info["phone_num"] = ad.phone_number
+ elif iccid and iccid in sim_data and sim_data[iccid].get(
+ "phone_num"):
+ if check_phone_number_match(sim_data[iccid]["phone_num"],
+ sub_info["phone_num"]):
sub_info["phone_num"] = sim_data[iccid]["phone_num"]
- ad.phone_number = sub_info["phone_num"]
- result = True
else:
- ad.log.error(
- "Unable to retrieve phone number for sub %s iccid %s"
- " from device or testbed config or sim_file %s",
- sim_filename, sub_id, iccid)
- else:
- result = True
- if sim_data.get(iccid) and sim_data[iccid].get("phone_num"):
- if not check_phone_number_match(sub_info["phone_num"],
- sim_data[iccid]["phone_num"]):
ad.log.warning(
- "ICCID %s phone number is %s in %s, does not match "
- "the number %s retrieved from the phone", iccid,
- sim_data[iccid]["phone_num"], sim_filename,
+ "phone_num %s in sim card data file for iccid %s"
+ " do not match phone_num %s in droid subscription",
+ sim_data[iccid]["phone_num"], iccid,
sub_info["phone_num"])
- sub_info["phone_num"] = sim_data[iccid]["phone_num"]
+ elif iccid and iccid in sim_data and sim_data[iccid].get("phone_num"):
+ sub_info["phone_num"] = sim_data[iccid]["phone_num"]
+ elif sub_id == active_sub_id:
+ phone_number = get_phone_number_by_secret_code(
+ ad, sub_info["sim_operator_name"])
+ if phone_number:
+ sub_info["phone_num"] = phone_number
+ elif getattr(ad, "phone_num", None):
+ sub_info["phone_num"] = ad.phone_number
+ if (not sub_info.get("phone_num")) and sub_id == active_sub_id:
+ ad.log.info("sub_id %s sub_info = %s", sub_id, sub_info)
+ ad.log.error(
+ "Unable to retrieve phone number for sub %s with iccid"
+ " %s from device or testbed config or sim card file %s",
+ sub_id, iccid, sim_filename)
+ result = False
if not hasattr(
ad, 'roaming'
) and sub_info["sim_plmn"] != sub_info["network_plmn"] and (
@@ -309,20 +326,23 @@
Returns:
None
"""
- cfg = {"subscription": {}}
+ if hasattr(ad, 'cfg'):
+ cfg = ad.cfg.copy()
+ else:
+ cfg = {"subscription": {}}
droid = ad.droid
sub_info_list = droid.subscriptionGetAllSubInfoList()
for sub_info in sub_info_list:
sub_id = sub_info["subscriptionId"]
sim_slot = sub_info["simSlotIndex"]
if sim_slot != INVALID_SIM_SLOT_INDEX:
- sim_serial = droid.telephonyGetSimSerialNumberForSubscription(
- sub_id)
- if not sim_serial:
- ad.log.error("Unable to find ICC-ID for SIM in slot %s",
- sim_slot)
sim_record = {}
- sim_record["iccid"] = sim_serial
+ if sub_info.get("iccId"):
+ sim_record["iccid"] = sub_info["iccId"]
+ else:
+ sim_record[
+ "iccid"] = droid.telephonyGetSimSerialNumberForSubscription(
+ sub_id)
sim_record["sim_slot"] = sim_slot
try:
sim_record[
@@ -330,9 +350,14 @@
sub_id)
except:
sim_record["phone_type"] = droid.telephonyGetPhoneType()
+ if sub_info.get("mcc"):
+ sim_record["mcc"] = sub_info["mcc"]
+ if sub_info.get("mnc"):
+ sim_record["mnc"] = sub_info["mnc"]
sim_record[
"sim_plmn"] = droid.telephonyGetSimOperatorForSubscription(
sub_id)
+ sim_record["display_name"] = sub_info["displayName"]
sim_record[
"sim_operator_name"] = droid.telephonyGetSimOperatorNameForSubscription(
sub_id)
@@ -348,13 +373,18 @@
sim_record[
"sim_country"] = droid.telephonyGetSimCountryIsoForSubscription(
sub_id)
- phone_number = droid.telephonyGetLine1NumberForSubscription(sub_id)
- if not phone_number and getattr(ad, "phone_number", None):
- phone_number = ad.phone_number
- sim_record["phone_num"] = phone_number_formatter(phone_number)
+ if sub_info.get("number"):
+ sim_record["phone_num"] = sub_info["number"]
+ else:
+ sim_record["phone_num"] = phone_number_formatter(
+ droid.telephonyGetLine1NumberForSubscription(sub_id))
sim_record[
"phone_tag"] = droid.telephonyGetLine1AlphaTagForSubscription(
sub_id)
+ if (not sim_record["phone_num"]
+ ) and cfg["subscription"].get(sub_id):
+ sim_record["phone_num"] = cfg["subscription"][sub_id][
+ "phone_num"]
cfg['subscription'][sub_id] = sim_record
ad.log.debug("SubId %s SIM record: %s", sub_id, sim_record)
setattr(ad, 'cfg', cfg)
@@ -470,6 +500,12 @@
return signal_strength
+def get_wifi_signal_strength(ad):
+ signal_strength = ad.droid.wifiGetConnectionInfo()['rssi']
+ ad.log.info("WiFi Signal Strength is %s" % signal_strength)
+ return signal_strength
+
+
def is_expected_event(event_to_check, events_list):
""" check whether event is present in the event list
@@ -506,6 +542,31 @@
return True
+def is_sim_ready_by_adb(log, ad):
+ state = ad.adb.getprop("gsm.sim.state")
+ return state == SIM_STATE_READY or state == SIM_STATE_LOADED
+
+
+def wait_for_sim_ready_by_adb(log, ad, wait_time=90):
+ return _wait_for_droid_in_state(log, ad, wait_time, is_sim_ready_by_adb)
+
+
+def get_service_state_by_adb(log, ad):
+ output = ad.adb.shell("dumpsys telephony.registry | grep mServiceState")
+ if "mVoiceRegState" in output:
+ result = re.search(r"mVoiceRegState=(\S+)\((\S+)\)", output)
+ if result:
+ ad.log.info("mVoiceRegState is %s %s", result.group(1),
+ result.group(2))
+ return result.group(2)
+ else:
+ result = re.search(r"mServiceState=(\S+)", output)
+ if result:
+ ad.log.info("mServiceState=%s %s", result.group(1),
+ SERVICE_STATE_MAPPING[result.group(1)])
+ return SERVICE_STATE_MAPPING[result.group(1)]
+
+
def _is_expecting_event(event_recv_list):
""" check for more event is expected in event list
@@ -780,7 +841,7 @@
False: for errors
"""
if not event_tracking_started:
- ad.ed.clear_all_events()
+ ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
event_ringing = None
for i in range(retries):
@@ -794,7 +855,7 @@
ad.log.info("callee in ringing state")
break
if i == retries - 1:
- ad.log.error(
+ ad.log.info(
"callee didn't receive ring event or got into ringing state")
return False
if not event_tracking_started:
@@ -835,7 +896,7 @@
False: if call offhook event is not received.
"""
if not event_tracking_started:
- ad.ed.clear_all_events()
+ ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
try:
ad.ed.wait_for_event(
@@ -881,7 +942,7 @@
True: if incoming call is received and answered successfully.
False: for errors
"""
- ad.ed.clear_all_events()
+ ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
try:
if not _wait_for_droid_in_state(
@@ -939,9 +1000,10 @@
True: if incoming call is received and reject successfully.
False: for errors
"""
- return wait_and_reject_call_for_subscription(
- log, ad,
- get_incoming_voice_sub_id(ad), incoming_number, delay_reject, reject)
+ return wait_and_reject_call_for_subscription(log, ad,
+ get_incoming_voice_sub_id(ad),
+ incoming_number, delay_reject,
+ reject)
def wait_and_reject_call_for_subscription(log,
@@ -972,7 +1034,7 @@
ad.log.error("Could not reject a call: phone never rang.")
return False
- ad.ed.clear_all_events()
+ ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
if reject is True:
# Delay between ringing and reject.
@@ -1021,7 +1083,7 @@
# short circuit in case no calls are active
if not ad.droid.telecomIsInCall():
return True
- ad.ed.clear_all_events()
+ ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallState()
ad.log.info("Hangup call.")
ad.droid.telecomEndCall()
@@ -1039,7 +1101,7 @@
return False
finally:
ad.droid.telephonyStopTrackingCallStateChange()
- return True
+ return not ad.droid.telecomIsInCall()
def disconnect_call_by_id(log, ad, call_id):
@@ -1110,8 +1172,7 @@
number1 = phone_number_formatter(number1)
number2 = phone_number_formatter(number2)
# Handle extra country code attachment when matching phone number
- if number1.replace("+", "") in number2 or number2.replace("+",
- "") in number1:
+ if number1[-7:] in number2 or number2[-7:] in number1:
return True
else:
logging.info("phone number1 %s and number2 %s does not match" %
@@ -1137,7 +1198,7 @@
Returns:
result: if phone call is placed successfully.
"""
- ad.ed.clear_all_events()
+ ad.ed.clear_events(EventCallStateChanged)
sub_id = get_outgoing_voice_sub_id(ad)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
@@ -1157,16 +1218,18 @@
for i in range(checking_retries):
if (ad.droid.telecomIsInCall() and
ad.droid.telephonyGetCallState() == TELEPHONY_STATE_OFFHOOK
- and
- ad.droid.telecomGetCallState() == TELEPHONY_STATE_OFFHOOK
- ) or wait_for_call_offhook_event(log, ad, sub_id, True,
- checking_interval):
+ and ad.droid.telecomGetCallState() ==
+ TELEPHONY_STATE_OFFHOOK) or wait_for_call_offhook_event(
+ log, ad, sub_id, True, checking_interval):
return True
ad.log.info(
"Make call to %s fail. telecomIsInCall:%s, Telecom State:%s,"
- " Telephony State:%s", callee_number,
- ad.droid.telecomIsInCall(),
+ " Telephony State:%s", callee_number, ad.droid.telecomIsInCall(),
ad.droid.telephonyGetCallState(), ad.droid.telecomGetCallState())
+ reasons = ad.search_logcat(
+ "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause")
+ if reasons:
+ ad.log.info(reasons[-1]["log_message"])
return False
finally:
ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
@@ -1240,6 +1303,7 @@
try:
# Make a Call
ad.wakeup_screen()
+ ad.send_keycode("MENU")
ad.log.info("Call %s", callee_number)
ad.adb.shell("am start -a com.android.phone.EmergencyDialer.DIAL")
ad.adb.shell(
@@ -1263,7 +1327,7 @@
ad.log.error("initiate emergency call failed with error %s", e)
-def hung_up_call_by_adb(ad):
+def hangup_call_by_adb(ad):
"""Make emergency call by EmergencyDialer.
Args:
@@ -1432,7 +1496,7 @@
# ensure that all internal states are updated in telecom
time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
- ad_callee.ed.clear_all_events()
+ ad_callee.ed.ad.ed.clear_events(EventCallStateChanged)
if verify_caller_func and not verify_caller_func(log, ad_caller):
raise _CallSequenceException("Caller not in correct state!")
@@ -1543,8 +1607,8 @@
# wait for telephonyGetVoiceMailCount to update correct result
remaining_time = MAX_WAIT_TIME_VOICE_MAIL_COUNT
- while ((remaining_time > 0) and
- (ad.droid.telephonyGetVoiceMailCount() != 0)):
+ while ((remaining_time > 0)
+ and (ad.droid.telephonyGetVoiceMailCount() != 0)):
time.sleep(1)
remaining_time -= 1
current_voice_mail_count = ad.droid.telephonyGetVoiceMailCount()
@@ -1645,7 +1709,7 @@
"""
CHECK_INTERVAL = 5
- result = True
+ begin_time = get_current_epoch_time()
caller_number = ad_caller.cfg['subscription'][subid_caller]['phone_num']
callee_number = ad_callee.cfg['subscription'][subid_callee]['phone_num']
@@ -1655,9 +1719,15 @@
if not verify_callee_func:
verify_callee_func = is_phone_in_call
result = True
- ad_caller.log.info("Call from %s to %s with duration %s", caller_number,
- callee_number, wait_time_in_call)
+ msg = "Call from %s to %s" % (caller_number, callee_number)
+ if ad_hangup:
+ msg = "%s for duration of %s seconds" % (msg, wait_time_in_call)
+ ad_caller.log.info(msg)
+ for ad in (ad_caller, ad_callee):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ setattr(ad, "call_ids", call_ids)
+ ad.log.info("Before making call, existing phone calls %s", call_ids)
try:
if not initiate_call(
log,
@@ -1665,7 +1735,6 @@
callee_number,
wait_time_betwn_call_initcheck=extra_sleep):
ad_caller.log.error("Initiate call failed.")
- result = False
return False
else:
ad_caller.log.info("Caller initate call successfully")
@@ -1677,11 +1746,19 @@
caller=ad_caller,
incall_ui_display=incall_ui_display):
ad_callee.log.error("Answer call fail.")
- result = False
return False
else:
ad_callee.log.info("Callee answered the call successfully")
+ for ad in (ad_caller, ad_callee):
+ call_ids = ad.droid.telecomCallGetCallIds()
+ new_call_id = list(set(call_ids) - set(ad.call_ids))[0]
+ if not wait_for_in_call_active(ad, call_id=new_call_id):
+ result = False
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error("Audio is not in call state")
+ result = False
+
elapsed_time = 0
while (elapsed_time < wait_time_in_call):
CHECK_INTERVAL = min(CHECK_INTERVAL,
@@ -1690,29 +1767,33 @@
elapsed_time += CHECK_INTERVAL
time_message = "at <%s>/<%s> second." % (elapsed_time,
wait_time_in_call)
- if not verify_caller_func(log, ad_caller):
- ad_caller.log.error("Caller is NOT in correct %s state at %s",
- verify_caller_func.__name__, time_message)
- result = False
- else:
- ad_caller.log.info("Caller is in correct %s state at %s",
- verify_caller_func.__name__, time_message)
- if not verify_callee_func(log, ad_callee):
- ad_callee.log.error("Callee is NOT in correct %s state at %s",
- verify_callee_func.__name__, time_message)
- result = False
- else:
- ad_callee.log.info("Callee is in correct %s state at %s",
- verify_callee_func.__name__, time_message)
- if not result:
- return result
- return result
- finally:
- if result and ad_hangup and not hangup_call(log, ad_hangup):
+ for ad, call_func in [(ad_caller, verify_caller_func),
+ (ad_callee, verify_callee_func)]:
+ if not call_func(log, ad):
+ ad.log.error("NOT in correct %s state at %s",
+ call_func.__name__, time_message)
+ result = False
+ else:
+ ad.log.info("In correct %s state at %s",
+ call_func.__name__, time_message)
+ if not ad.droid.telecomCallGetAudioState():
+ ad.log.error("Audio is not in call state at %s",
+ time_message)
+ result = False
+ if not result:
+ break
+ if ad_hangup and not hangup_call(log, ad_hangup):
ad_hangup.log.info("Failed to hang up the call")
result = False
+ return result
+ finally:
if not result:
for ad in [ad_caller, ad_callee]:
+ reasons = ad.search_logcat(
+ "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause",
+ begin_time)
+ if reasons:
+ ad.log.info(reasons[-1]["log_message"])
try:
if ad.droid.telecomIsInCall():
ad.droid.telecomEndCall()
@@ -1745,14 +1826,14 @@
if not formatter:
return input_string
# Remove "1" or "+1"from front
- if (len(input_string) == PHONE_NUMBER_STRING_FORMAT_11_DIGIT and
- input_string[0] == "1"):
+ if (len(input_string) == PHONE_NUMBER_STRING_FORMAT_11_DIGIT
+ and input_string[0] == "1"):
input_string = input_string[1:]
- elif (len(input_string) == PHONE_NUMBER_STRING_FORMAT_12_DIGIT and
- input_string[0:2] == "+1"):
+ elif (len(input_string) == PHONE_NUMBER_STRING_FORMAT_12_DIGIT
+ and input_string[0:2] == "+1"):
input_string = input_string[2:]
- elif (len(input_string) == PHONE_NUMBER_STRING_FORMAT_7_DIGIT and
- formatter == PHONE_NUMBER_STRING_FORMAT_7_DIGIT):
+ elif (len(input_string) == PHONE_NUMBER_STRING_FORMAT_7_DIGIT
+ and formatter == PHONE_NUMBER_STRING_FORMAT_7_DIGIT):
return input_string
elif len(input_string) != PHONE_NUMBER_STRING_FORMAT_10_DIGIT:
return None
@@ -1787,9 +1868,10 @@
def verify_http_connection(log,
ad,
- url="http://www.google.com/",
+ url="www.google.com",
retry=5,
- retry_interval=15):
+ retry_interval=15,
+ expected_state=True):
"""Make ping request and return status.
Args:
@@ -1800,23 +1882,26 @@
"""
for i in range(0, retry + 1):
-
- try:
- http_response = ad.droid.httpPing(url)
- except:
- http_response = None
-
+ # b/18899134 httpPing will hang
+ #try:
+ # http_response = ad.droid.httpPing(url)
+ #except:
+ # http_response = None
# If httpPing failed, it may return {} (if phone just turn off APM) or
# None (regular fail)
- # So here use "if http_response" to see if it pass or fail
- if http_response:
- ad.log.info("Verify Internet succeeded")
+ state = ad.droid.pingHost(url)
+ ad.log.info("Connection to %s is %s", url, state)
+ if expected_state == state:
+ ad.log.info("Verify Internet connection state is %s succeeded",
+ str(expected_state))
return True
- else:
- if i < retry:
- time.sleep(retry_interval)
- ad.log.info("Verify Internet retry failed after %s second",
- i * retry_interval)
+ if i < retry:
+ ad.log.info(
+ "Verify Internet connection state=%s failed. Try again",
+ str(expected_state))
+ time.sleep(retry_interval)
+ ad.log.info("Verify Internet state=%s failed after %s second",
+ expected_state, i * retry_interval)
return False
@@ -1859,7 +1944,7 @@
return False
-def active_file_download_task(log, ad, file_name="5MB"):
+def check_curl_availability(ad):
if not hasattr(ad, "curl_capable"):
try:
out = ad.adb.shell("/data/curl --version")
@@ -1871,11 +1956,13 @@
except Exception:
setattr(ad, "curl_capable", False)
ad.log.info("curl is unavailable, use chrome to download file")
+ return ad.curl_capable
+
+def active_file_download_task(log, ad, file_name="5MB", method="chrome"):
# files available for download on the same website:
# 1GB.zip, 512MB.zip, 200MB.zip, 50MB.zip, 20MB.zip, 10MB.zip, 5MB.zip
# download file by adb command, as phone call will use sl4a
- url = "http://146.148.91.8/download/" + file_name + ".zip"
file_map_dict = {
'5MB': 5000000,
'10MB': 10000000,
@@ -1891,16 +1978,24 @@
return False
timeout = min(max(file_size / 100000, 600), 3600)
output_path = "/sdcard/Download/" + file_name + ".zip"
- if not ad.curl_capable:
- return (http_file_download_by_chrome, (ad, url, file_size, True,
- timeout))
- else:
+ url = "http://ipv4.download.thinkbroadband.com/" + file_name + ".zip"
+ if method == "sl4a":
+ return (http_file_download_by_sl4a, (ad, url, output_path, file_size,
+ True, timeout))
+ if method == "curl" and check_curl_availability(ad):
+ url = "http://146.148.91.8/download/" + file_name + ".zip"
return (http_file_download_by_curl, (ad, url, output_path, file_size,
True, timeout))
+ elif method == "sl4a":
+ return (http_file_download_by_sl4a, (ad, url, output_path, file_size,
+ True, timeout))
+ else:
+ return (http_file_download_by_chrome, (ad, url, file_size, True,
+ timeout))
-def active_file_download_test(log, ad, file_name="5MB"):
- task = active_file_download_task(log, ad, file_name)
+def active_file_download_test(log, ad, file_name="5MB", method="chrome"):
+ task = active_file_download_task(log, ad, file_name, method=method)
return task[0](*task[1])
@@ -1914,9 +2009,12 @@
"""
for i in range(retries):
ad.log.info("Verify internet connection - attempt %d", i + 1)
- result = adb_shell_ping(ad, count=5, timeout=60, loss_tolerance=40)
- if result:
- return True
+ dest_to_ping = ["www.google.com", "www.amazon.com", "54.230.144.105"]
+ for dest in dest_to_ping:
+ result = adb_shell_ping(
+ ad, count=5, timeout=60, loss_tolerance=40, dest_ip=dest)
+ if result:
+ return True
return False
@@ -1929,7 +2027,9 @@
limit_rate=None,
omit=10,
ipv6=False,
- rate_dict=None):
+ rate_dict=None,
+ blocking=True,
+ log_file_path=None):
"""Iperf test by adb.
Args:
@@ -1947,7 +2047,16 @@
if ipv6: iperf_option += " -6"
if reverse: iperf_option += " -R"
try:
+ if log_file_path:
+ ad.adb.shell("rm %s" % log_file_path, ignore_status=True)
ad.log.info("Running adb iperf test with server %s", iperf_server)
+ if not blocking:
+ ad.run_iperf_client_nb(
+ iperf_server,
+ iperf_option,
+ timeout=timeout + 60,
+ log_file_path=log_file_path)
+ return True
result, data = ad.run_iperf_client(
iperf_server, iperf_option, timeout=timeout + 60)
ad.log.info("Iperf test result with server %s is %s", iperf_server,
@@ -2000,13 +2109,52 @@
if retry:
curl_cmd += " --retry %s" % retry
curl_cmd += " --url %s > %s" % (url, file_path)
+ accounting_apk = "com.android.server.telecom" #"com.quicinc.cne.CNEService"
+ result = True
try:
+ data_accounting = {
+ "mobile_rx_bytes":
+ ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage":
+ get_mobile_data_usage(ad, None, None),
+ "curl_mobile_data_usage":
+ get_mobile_data_usage(ad, None, accounting_apk)
+ }
+ ad.log.info("Before downloading: %s", data_accounting)
ad.log.info("Download %s to %s by adb shell command %s", url,
file_path, curl_cmd)
ad.adb.shell(curl_cmd, timeout=timeout)
if _check_file_existance(ad, file_path, expected_file_size):
ad.log.info("%s is downloaded to %s successfully", url, file_path)
- return True
+ new_data_accounting = {
+ "mobile_rx_bytes":
+ ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage":
+ get_mobile_data_usage(ad, None, None),
+ "curl_mobile_data_usage":
+ get_mobile_data_usage(ad, None, accounting_apk)
+ }
+ ad.log.info("After downloading: %s", new_data_accounting)
+ accounting_diff = {
+ key: value - data_accounting[key]
+ for key, value in new_data_accounting.items()
+ }
+ ad.log.info("Data accounting difference: %s", accounting_diff)
+ if getattr(ad, "on_mobile_data", False):
+ for key, value in accounting_diff.items():
+ if value < expected_file_size:
+ ad.log.warning("%s diff is %s less than %s", key,
+ value, expected_file_size)
+ ad.data_accounting["%s_failure" % key] += 1
+ else:
+ for key, value in accounting_diff.items():
+ if value >= expected_file_size:
+ ad.log.error("%s diff is %s. File download is "
+ "consuming mobile data", key, value)
+ result = False
+ ad.log.info("data_accounting_failure: %s", dict(
+ ad.data_accounting))
+ return result
else:
ad.log.warning("Fail to download %s", url)
return False
@@ -2039,30 +2187,66 @@
check.
timeout: timeout for file download to complete.
"""
+ chrome_apk = "com.android.chrome"
file_directory, file_name = _generate_file_directory_and_file_name(
url, "/sdcard/Download/")
file_path = os.path.join(file_directory, file_name)
# Remove pre-existing file
- ad.force_stop_apk("com.android.chrome")
+ ad.force_stop_apk(chrome_apk)
file_to_be_delete = os.path.join(file_directory, "*%s*" % file_name)
ad.adb.shell("rm -f %s" % file_to_be_delete)
ad.adb.shell("rm -rf /sdcard/Download/.*")
ad.adb.shell("rm -f /sdcard/Download/.*")
- total_rx_bytes_before = ad.droid.getTotalRxBytes()
- mobile_rx_bytes_before = ad.droid.getMobileRxBytes()
- subscriber_mobile_data_usage_before = get_mobile_data_usage(ad)
+ data_accounting = {
+ "total_rx_bytes": ad.droid.getTotalRxBytes(),
+ "mobile_rx_bytes": ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage": get_mobile_data_usage(ad, None, None),
+ "chrome_mobile_data_usage": get_mobile_data_usage(
+ ad, None, chrome_apk)
+ }
+ ad.log.info("Before downloading: %s", data_accounting)
ad.ensure_screen_on()
ad.log.info("Download %s with timeout %s", url, timeout)
open_url_by_adb(ad, url)
elapse_time = 0
+ result = True
while elapse_time < timeout:
time.sleep(30)
if _check_file_existance(ad, file_path, expected_file_size):
ad.log.info("%s is downloaded successfully", url)
if remove_file_after_check:
ad.log.info("Remove the downloaded file %s", file_path)
- ad.adb.shell("rm %s" % file_path, ignore_status=True)
- return True
+ ad.adb.shell("rm -f %s" % file_to_be_delete)
+ ad.adb.shell("rm -rf /sdcard/Download/.*")
+ ad.adb.shell("rm -f /sdcard/Download/.*")
+ #time.sleep(30)
+ new_data_accounting = {
+ "mobile_rx_bytes":
+ ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage":
+ get_mobile_data_usage(ad, None, None),
+ "chrome_mobile_data_usage":
+ get_mobile_data_usage(ad, None, chrome_apk)
+ }
+ ad.log.info("After downloading: %s", new_data_accounting)
+ accounting_diff = {
+ key: value - data_accounting[key]
+ for key, value in new_data_accounting.items()
+ }
+ ad.log.info("Data accounting difference: %s", accounting_diff)
+ if getattr(ad, "on_mobile_data", False):
+ for key, value in accounting_diff.items():
+ if value < expected_file_size:
+ ad.log.warning("%s diff is %s less than %s", key,
+ value, expected_file_size)
+ ad.data_accounting["%s_failure" % key] += 1
+ else:
+ for key, value in accounting_diff.items():
+ if value >= expected_file_size:
+ ad.log.error("%s diff is %s. File download is "
+ "consuming mobile data", key, value)
+ result = False
+ return result
elif _check_file_existance(ad, "%s.crdownload" % file_path):
ad.log.info("Chrome is downloading %s", url)
elif elapse_time < 60:
@@ -2072,15 +2256,15 @@
ad.log.error("Unable to download file from %s", url)
break
elapse_time += 30
- ad.log.error("Fail to download file from %s", url)
+ ad.log.warning("Fail to download file from %s", url)
ad.force_stop_apk("com.android.chrome")
- ad.adb.shell("rm %s" % file_path, ignore_status=True)
- ad.adb.shell("rm %s.crdownload" % file_path, ignore_status=True)
+ ad.adb.shell("rm -f %s" % file_to_be_delete)
+ ad.adb.shell("rm -rf /sdcard/Download/.*")
+ ad.adb.shell("rm -f /sdcard/Download/.*")
return False
-def http_file_download_by_sl4a(log,
- ad,
+def http_file_download_by_sl4a(ad,
url,
out_path=None,
expected_file_size=None,
@@ -2089,7 +2273,6 @@
"""Download http file by sl4a RPC call.
Args:
- log: log object
ad: Android Device Object.
url: The url that file to be downloaded from".
out_path: Optional. Where to download file to.
@@ -2103,13 +2286,68 @@
file_folder, file_name = _generate_file_directory_and_file_name(
url, out_path)
file_path = os.path.join(file_folder, file_name)
+ ad.adb.shell("rm -f %s" % file_path)
+ accounting_apk = SL4A_APK_NAME
+ result = True
try:
+ if not getattr(ad, "downloading_droid", None):
+ ad.downloading_droid, ad.downloading_ed = ad.get_droid()
+ ad.downloading_ed.start()
+ else:
+ try:
+ if not ad.downloading_droid.is_live:
+ ad.downloading_droid, ad.downloading_ed = ad.get_droid()
+ ad.downloading_ed.start()
+ except Exception as e:
+ ad.log.info(e)
+ ad.downloading_droid, ad.downloading_ed = ad.get_droid()
+ ad.downloading_ed.start()
+ data_accounting = {
+ "mobile_rx_bytes":
+ ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage":
+ get_mobile_data_usage(ad, None, None),
+ "sl4a_mobile_data_usage":
+ get_mobile_data_usage(ad, None, accounting_apk)
+ }
+ ad.log.info("Before downloading: %s", data_accounting)
ad.log.info("Download file from %s to %s by sl4a RPC call", url,
file_path)
- ad.droid.httpDownloadFile(url, file_path, timeout=timeout)
+ try:
+ ad.downloading_droid.httpDownloadFile(
+ url, file_path, timeout=timeout)
+ except Exception as e:
+ ad.log.warning("SL4A file download error: %s", e)
+ return False
if _check_file_existance(ad, file_path, expected_file_size):
ad.log.info("%s is downloaded successfully", url)
- return True
+ new_data_accounting = {
+ "mobile_rx_bytes":
+ ad.droid.getMobileRxBytes(),
+ "subscriber_mobile_data_usage":
+ get_mobile_data_usage(ad, None, None),
+ "sl4a_mobile_data_usage":
+ get_mobile_data_usage(ad, None, accounting_apk)
+ }
+ ad.log.info("After downloading: %s", new_data_accounting)
+ accounting_diff = {
+ key: value - data_accounting[key]
+ for key, value in new_data_accounting.items()
+ }
+ ad.log.info("Data accounting difference: %s", accounting_diff)
+ if getattr(ad, "on_mobile_data", False):
+ for key, value in accounting_diff.items():
+ if value < expected_file_size:
+ ad.log.warning("%s diff is %s less than %s", key,
+ value, expected_file_size)
+ ad.data_accounting["%s_failure"] += 1
+ else:
+ for key, value in accounting_diff.items():
+ if value >= expected_file_size:
+ ad.log.error("%s diff is %s. File download is "
+ "consuming mobile data", key, value)
+ result = False
+ return result
else:
ad.log.warning("Fail to download %s", url)
return False
@@ -2122,13 +2360,84 @@
ad.adb.shell("rm %s" % file_path, ignore_status=True)
-def trigger_modem_crash(log, ad, timeout=10):
+def get_mobile_data_usage(ad, subscriber_id=None, apk=None):
+ if not subscriber_id:
+ subscriber_id = ad.droid.telephonyGetSubscriberId()
+ if not getattr(ad, "data_metering_begin_time", None) or not getattr(
+ ad, "data_metering_end_time", None):
+ current_time = int(time.time() * 1000)
+ setattr(ad, "data_metering_begin_time",
+ current_time - 24 * 60 * 60 * 1000)
+ setattr(ad, "data_metering_end_time",
+ current_time + 30 * 24 * 60 * 60 * 1000)
+ begin_time = ad.data_metering_begin_time
+ end_time = ad.data_metering_end_time
+ if apk:
+ uid = ad.get_apk_uid(apk)
+ try:
+ usage = ad.droid.connectivityQueryDetailsForUid(
+ TYPE_MOBILE, subscriber_id, begin_time, end_time, uid)
+ except Exception:
+ usage = ad.droid.connectivityQueryDetailsForUid(
+ subscriber_id, begin_time, end_time, uid)
+ ad.log.debug("The mobile data usage for apk %s is %s", apk, usage)
+ else:
+ try:
+ usage = ad.droid.connectivityQuerySummaryForDevice(
+ TYPE_MOBILE, subscriber_id, begin_time, end_time)
+ except Exception:
+ usage = ad.droid.connectivityQuerySummaryForDevice(
+ subscriber_id, begin_time, end_time)
+ ad.log.debug("The mobile data usage for subscriber is %s", usage)
+ return usage
+
+
+def set_mobile_data_usage_limit(ad, limit, subscriber_id=None):
+ if not subscriber_id:
+ subscriber_id = ad.droid.telephonyGetSubscriberId()
+ ad.log.info("Set subscriber mobile data usage limit to %s", limit)
+ ad.droid.connectivitySetDataUsageLimit(subscriber_id, str(limit))
+
+
+def remove_mobile_data_usage_limit(ad, subscriber_id=None):
+ if not subscriber_id:
+ subscriber_id = ad.droid.telephonyGetSubscriberId()
+ ad.log.info("Remove subscriber mobile data usage limit")
+ ad.droid.connectivitySetDataUsageLimit(subscriber_id, "-1")
+
+
+def trigger_modem_crash(ad, timeout=120):
cmd = "echo restart > /sys/kernel/debug/msm_subsys/modem"
- ad.log.info("Triggering Modem Crash using adb command %s", cmd)
- ad.adb.shell(cmd, timeout=timeout)
+ ad.log.info("Triggering Modem Crash from kernel using adb command %s", cmd)
+ ad.adb.shell(cmd)
+ time.sleep(timeout)
return True
+def trigger_modem_crash_by_modem(ad, timeout=120):
+ begin_time = get_current_epoch_time()
+ ad.adb.shell(
+ "setprop persist.vendor.sys.modem.diag.mdlog false", ignore_status=True)
+ # Legacy pixels use persist.sys.modem.diag.mdlog.
+ ad.adb.shell(
+ "setprop persist.sys.modem.diag.mdlog false", ignore_status=True)
+ stop_qxdm_logger(ad)
+ cmd = ('am instrument -w -e request "4b 25 03 00" '
+ '"com.google.mdstest/com.google.mdstest.instrument.'
+ 'ModemCommandInstrumentation"')
+ ad.log.info("Crash modem by %s", cmd)
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(timeout) # sleep time for sl4a stability
+ reasons = ad.search_logcat("modem subsystem failure reason", begin_time)
+ if reasons:
+ ad.log.info("Modem crash is triggered successfully")
+ ad.log.info(reasons[-1]["log_message"])
+ return True
+ else:
+ ad.log.info("Modem crash is not triggered successfully")
+ return False
+
+
def _connection_state_change(_event, target_state, connection_type):
if connection_type:
if 'TypeName' not in _event['data']:
@@ -2148,7 +2457,7 @@
def wait_for_cell_data_connection(
- log, ad, state, timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+ log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
"""Wait for data connection status to be expected value for default
data subscription.
@@ -2162,7 +2471,7 @@
If True, it will wait for status to be DATA_STATE_CONNECTED.
If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
timeout_value: wait for cell data timeout value.
- This is optional, default value is EventDispatcher.DEFAULT_TIMEOUT
+ This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
Returns:
True if success.
@@ -2185,7 +2494,11 @@
def wait_for_cell_data_connection_for_subscription(
- log, ad, sub_id, state, timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+ log,
+ ad,
+ sub_id,
+ state,
+ timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
"""Wait for data connection status to be expected value for specified
subscrption id.
@@ -2200,7 +2513,7 @@
If True, it will wait for status to be DATA_STATE_CONNECTED.
If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
timeout_value: wait for cell data timeout value.
- This is optional, default value is EventDispatcher.DEFAULT_TIMEOUT
+ This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
Returns:
True if success.
@@ -2214,7 +2527,7 @@
data_state = ad.droid.telephonyGetDataConnectionState()
if not state and ad.droid.telephonyGetDataConnectionState() == state_str:
return True
- ad.ed.clear_all_events()
+ ad.ed.clear_events(EventDataConnectionStateChanged)
ad.droid.telephonyStartTrackingDataConnectionStateChangeForSubscription(
sub_id)
ad.droid.connectivityStartTrackingConnectivityStateChange()
@@ -2247,8 +2560,7 @@
# The bug is tracked here: b/22612607
# So we use _is_network_connected_state_match.
- if _wait_for_droid_in_state(log, ad,
- MAX_WAIT_TIME_CONNECTION_STATE_UPDATE,
+ if _wait_for_droid_in_state(log, ad, timeout_value,
_is_network_connected_state_match, state):
return _wait_for_nw_data_connection(
log, ad, state, NETWORK_CONNECTION_TYPE_CELL, timeout_value)
@@ -2261,7 +2573,7 @@
def wait_for_wifi_data_connection(
- log, ad, state, timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+ log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
"""Wait for data connection status to be expected value and connection is by WiFi.
Args:
@@ -2271,7 +2583,7 @@
If True, it will wait for status to be DATA_STATE_CONNECTED.
If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
timeout_value: wait for network data timeout value.
- This is optional, default value is EventDispatcher.DEFAULT_TIMEOUT
+ This is optional, default value is MAX_WAIT_TIME_NW_SELECTION
Returns:
True if success.
@@ -2282,10 +2594,8 @@
log, ad, state, NETWORK_CONNECTION_TYPE_WIFI, timeout_value)
-def wait_for_data_connection(log,
- ad,
- state,
- timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+def wait_for_data_connection(
+ log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
"""Wait for data connection status to be expected value.
Wait for the data connection status to be DATA_STATE_CONNECTED
@@ -2298,7 +2608,7 @@
If True, it will wait for status to be DATA_STATE_CONNECTED.
If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
timeout_value: wait for network data timeout value.
- This is optional, default value is EventDispatcher.DEFAULT_TIMEOUT
+ This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
Returns:
True if success.
@@ -2312,7 +2622,7 @@
ad,
is_connected,
connection_type=None,
- timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+ timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
"""Wait for data connection status to be expected value.
Wait for the data connection status to be DATA_STATE_CONNECTED
@@ -2327,13 +2637,13 @@
connection_type: expected connection type.
This is optional, if it is None, then any connection type will return True.
timeout_value: wait for network data timeout value.
- This is optional, default value is EventDispatcher.DEFAULT_TIMEOUT
+ This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
Returns:
True if success.
False if failed.
"""
- ad.ed.clear_all_events()
+ ad.ed.clear_events(EventConnectivityChanged)
ad.droid.connectivityStartTrackingConnectivityStateChange()
try:
cur_data_connection_state = ad.droid.connectivityNetworkIsConnected()
@@ -2368,9 +2678,9 @@
# data connection state.
# Otherwise, the network state will not be correct.
# The bug is tracked here: b/20921915
- if _wait_for_droid_in_state(
- log, ad, MAX_WAIT_TIME_CONNECTION_STATE_UPDATE,
- _is_network_connected_state_match, is_connected):
+ if _wait_for_droid_in_state(log, ad, timeout_value,
+ _is_network_connected_state_match,
+ is_connected):
current_type = get_internet_connection_type(log, ad)
ad.log.info("current data connection type: %s", current_type)
if not connection_type:
@@ -2514,9 +2824,8 @@
Raises:
TelTestUtilsError if platform does not support VoLTE.
"""
- return toggle_volte_for_subscription(log, ad,
- get_outgoing_voice_sub_id(ad),
- new_state)
+ return toggle_volte_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad), new_state)
def toggle_volte_for_subscription(log, ad, sub_id, new_state=None):
@@ -2651,7 +2960,11 @@
log: log object.
ad: android device.
"""
- return ad.droid.telecomIsInCall()
+ try:
+ return ad.droid.telecomIsInCall()
+ except:
+ return "mCallState=2" in ad.adb.shell(
+ "dumpsys telephony.registry | grep mCallState")
def is_phone_not_in_call(log, ad):
@@ -2686,6 +2999,40 @@
return _wait_for_droid_in_state(log, ad, max_time, is_phone_in_call)
+def is_phone_in_call_active(ad, call_id=None):
+ """Return True if phone in active call.
+
+ Args:
+ log: log object.
+ ad: android device.
+ call_id: the call id
+ """
+ if not call_id:
+ call_id = ad.droid.telecomCallGetCallIds()[0]
+ call_state = ad.droid.telecomCallGetCallState(call_id)
+ ad.log.info("%s state is %s", call_id, call_state)
+ return call_state == "ACTIVE"
+
+
+def wait_for_in_call_active(ad, timeout=5, interval=1, call_id=None):
+ """Wait for call reach active state.
+
+ Args:
+ log: log object.
+ ad: android device.
+ call_id: the call id
+ """
+ if not call_id:
+ call_id = ad.droid.telecomCallGetCallIds()[0]
+ args = [ad, call_id]
+ if not wait_for_state(is_phone_in_call_active, True, timeout, interval,
+ *args):
+ ad.log.error("Call did not reach ACTIVE state")
+ return False
+ else:
+ return True
+
+
def wait_for_telecom_ringing(log, ad, max_time=MAX_WAIT_TIME_TELECOM_RINGING):
"""Wait for android to be in telecom ringing state.
@@ -2850,17 +3197,18 @@
Return True if VoLTE feature bit is True and IMS registered.
Return False if VoLTE feature bit is False or IMS not registered.
"""
+ result = True
if not ad.droid.telephonyIsVolteAvailable():
ad.log.info("IsVolteCallingAvailble is False")
- return False
+ result = False
else:
ad.log.info("IsVolteCallingAvailble is True")
- if not is_ims_registered(log, ad):
- ad.log.info("VoLTE is Available, but IMS is not registered.")
- return False
- else:
- ad.log.info("IMS is registered")
- return True
+ if not is_ims_registered(log, ad):
+ ad.log.info("IMS is not registered.")
+ result = False
+ else:
+ ad.log.info("IMS is registered")
+ return result
def is_video_enabled(log, ad):
@@ -3087,9 +3435,8 @@
Return True if 'text' equals to event['data']['Text']
and phone number match.
"""
- return (
- check_phone_number_match(event['data']['Sender'], phonenumber_tx) and
- event['data']['Text'] == text)
+ return (check_phone_number_match(event['data']['Sender'], phonenumber_tx)
+ and event['data']['Text'] == text)
def is_sms_partial_match(event, phonenumber_tx, text):
@@ -3105,12 +3452,15 @@
Return True if 'text' starts with event['data']['Text']
and phone number match.
"""
- return (
- check_phone_number_match(event['data']['Sender'], phonenumber_tx) and
- text.startswith(event['data']['Text']))
+ return (check_phone_number_match(event['data']['Sender'], phonenumber_tx)
+ and text.startswith(event['data']['Text']))
-def sms_send_receive_verify(log, ad_tx, ad_rx, array_message):
+def sms_send_receive_verify(log,
+ ad_tx,
+ ad_rx,
+ array_message,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
"""Send SMS, receive SMS, and verify content and sender's number.
Send (several) SMS from droid_tx to droid_rx.
@@ -3126,14 +3476,16 @@
subid_tx = get_outgoing_message_sub_id(ad_tx)
subid_rx = get_incoming_message_sub_id(ad_rx)
return sms_send_receive_verify_for_subscription(
- log, ad_tx, ad_rx, subid_tx, subid_rx, array_message)
+ log, ad_tx, ad_rx, subid_tx, subid_rx, array_message, max_wait_time)
def wait_for_matching_sms(log,
ad_rx,
phonenumber_tx,
text,
- allow_multi_part_long_sms=True):
+ allow_multi_part_long_sms=True,
+ begin_time=None,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
"""Wait for matching incoming SMS.
Args:
@@ -3149,25 +3501,31 @@
"""
if not allow_multi_part_long_sms:
try:
- ad_rx.ed.wait_for_event(EventSmsReceived, is_sms_match,
- MAX_WAIT_TIME_SMS_RECEIVE, phonenumber_tx,
- text)
+ ad_rx.messaging_ed.wait_for_event(EventSmsReceived, is_sms_match,
+ max_wait_time, phonenumber_tx,
+ text)
return True
except Empty:
ad_rx.log.error("No matched SMS received event.")
+ if begin_time:
+ if sms_mms_receive_logcat_check(ad_rx, "sms", begin_time):
+ ad_rx.log.info("Receivd SMS message is seen in logcat")
return False
else:
try:
received_sms = ''
while (text != ''):
- event = ad_rx.ed.wait_for_event(
- EventSmsReceived, is_sms_partial_match,
- MAX_WAIT_TIME_SMS_RECEIVE, phonenumber_tx, text)
+ event = ad_rx.messaging_ed.wait_for_event(
+ EventSmsReceived, is_sms_partial_match, max_wait_time,
+ phonenumber_tx, text)
text = text[len(event['data']['Text']):]
received_sms += event['data']['Text']
return True
except Empty:
ad_rx.log.error("No matched SMS received event.")
+ if begin_time:
+ if sms_mms_receive_logcat_check(ad_rx, "sms", begin_time):
+ ad_rx.log.info("Receivd SMS message is seen in logcat")
if received_sms != '':
ad_rx.log.error("Only received partial matched SMS: %s",
received_sms)
@@ -3191,7 +3549,12 @@
return True
-def wait_for_matching_mms(log, ad_rx, phonenumber_tx, text):
+def wait_for_matching_mms(log,
+ ad_rx,
+ phonenumber_tx,
+ text,
+ begin_time=None,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
"""Wait for matching incoming SMS.
Args:
@@ -3207,17 +3570,25 @@
"""
try:
#TODO: add mms matching after mms message parser is added in sl4a. b/34276948
- ad_rx.ed.wait_for_event(EventMmsDownloaded, is_mms_match,
- MAX_WAIT_TIME_SMS_RECEIVE, phonenumber_tx,
- text)
+ ad_rx.messaging_ed.wait_for_event(EventMmsDownloaded, is_mms_match,
+ max_wait_time, phonenumber_tx, text)
return True
except Empty:
ad_rx.log.warning("No matched MMS downloaded event.")
+ if begin_time:
+ if sms_mms_receive_logcat_check(ad_rx, "mms", begin_time):
+ return True
return False
-def sms_send_receive_verify_for_subscription(log, ad_tx, ad_rx, subid_tx,
- subid_rx, array_message):
+def sms_send_receive_verify_for_subscription(
+ log,
+ ad_tx,
+ ad_rx,
+ subid_tx,
+ subid_rx,
+ array_message,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
"""Send SMS, receive SMS, and verify content and sender's number.
Send (several) SMS from droid_tx to droid_rx.
@@ -3232,43 +3603,70 @@
subid_rx: Receiver's subsciption ID to be used for SMS
array_message: the array of message to send/receive
"""
-
phonenumber_tx = ad_tx.cfg['subscription'][subid_tx]['phone_num']
phonenumber_rx = ad_rx.cfg['subscription'][subid_rx]['phone_num']
+
+ for ad in (ad_tx, ad_rx):
+ if not getattr(ad, "messaging_droid", None):
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ else:
+ try:
+ if not ad.messaging_droid.is_live:
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+ except Exception as e:
+ ad.log.info(e)
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+
for text in array_message:
+ # set begin_time 300ms before current time to system time discrepency
+ begin_time = get_current_epoch_time() - 300
length = len(text)
ad_tx.log.info("Sending SMS from %s to %s, len: %s, content: %s.",
phonenumber_tx, phonenumber_rx, length, text)
- result = False
- ad_rx.ed.clear_all_events()
- ad_rx.droid.smsStartTrackingIncomingSmsMessage()
- time.sleep(0.1) #sleep 100ms after starting event tracking
try:
- ad_tx.droid.smsSendTextMessage(phonenumber_rx, text, True)
-
+ ad_rx.messaging_ed.clear_events(EventSmsReceived)
+ ad_tx.messaging_ed.clear_events(EventSmsSentSuccess)
+ ad_rx.messaging_droid.smsStartTrackingIncomingSmsMessage()
+ time.sleep(1) #sleep 100ms after starting event tracking
+ ad_tx.messaging_droid.smsSendTextMessage(phonenumber_rx, text,
+ False)
try:
- ad_tx.ed.pop_event(EventSmsSentSuccess,
- MAX_WAIT_TIME_SMS_SENT_SUCCESS)
+ ad_tx.messaging_ed.pop_event(EventSmsSentSuccess,
+ max_wait_time)
except Empty:
ad_tx.log.error("No sent_success event for SMS of length %s.",
length)
- return False
+ # check log message as a work around for the missing sl4a
+ # event dispatcher event
+ if not sms_mms_send_logcat_check(ad_tx, "sms", begin_time):
+ return False
if not wait_for_matching_sms(
log,
ad_rx,
phonenumber_tx,
text,
- allow_multi_part_long_sms=True):
+ allow_multi_part_long_sms=True,
+ begin_time=begin_time):
ad_rx.log.error("No matching received SMS of length %s.",
length)
return False
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
finally:
- ad_rx.droid.smsStopTrackingIncomingSmsMessage()
+ ad_rx.messaging_droid.smsStopTrackingIncomingSmsMessage()
return True
-def mms_send_receive_verify(log, ad_tx, ad_rx, array_message):
+def mms_send_receive_verify(log,
+ ad_tx,
+ ad_rx,
+ array_message,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
"""Send MMS, receive MMS, and verify content and sender's number.
Send (several) MMS from droid_tx to droid_rx.
@@ -3282,14 +3680,60 @@
array_message: the array of message to send/receive
"""
return mms_send_receive_verify_for_subscription(
- log, ad_tx, ad_rx,
- get_outgoing_message_sub_id(ad_tx),
- get_incoming_message_sub_id(ad_rx), array_message)
+ log, ad_tx, ad_rx, get_outgoing_message_sub_id(ad_tx),
+ get_incoming_message_sub_id(ad_rx), array_message, max_wait_time)
+
+
+def sms_mms_send_logcat_check(ad, type, begin_time):
+ type = type.upper()
+ log_results = ad.search_logcat(
+ "%s Message sent successfully" % type, begin_time=begin_time)
+ if log_results:
+ ad.log.info("Found %s sent succeessful log message: %s", type,
+ log_results[-1]["log_message"])
+ return True
+ else:
+ log_results = ad.search_logcat(
+ "ProcessSentMessageAction: Done sending %s message" % type,
+ begin_time=begin_time)
+ if log_results:
+ for log_result in log_results:
+ if "status is SUCCEEDED" in log_result["log_message"]:
+ ad.log.info(
+ "Found BugleDataModel %s send succeed log message: %s",
+ type, log_result["log_message"])
+ return True
+ return False
+
+
+def sms_mms_receive_logcat_check(ad, type, begin_time):
+ type = type.upper()
+ log_results = ad.search_logcat(
+ "New %s Received" % type, begin_time=begin_time) or \
+ ad.search_logcat("New %s Downloaded" % type, begin_time=begin_time)
+ if log_results:
+ ad.log.info("Found SL4A %s received log message: %s", type,
+ log_results[-1]["log_message"])
+ return True
+ else:
+ log_results = ad.search_logcat(
+ "Received %s message" % type, begin_time=begin_time)
+ if log_results:
+ ad.log.info("Found %s received log message: %s", type,
+ log_results[-1]["log_message"])
+ return True
+ return False
#TODO: add mms matching after mms message parser is added in sl4a. b/34276948
-def mms_send_receive_verify_for_subscription(log, ad_tx, ad_rx, subid_tx,
- subid_rx, array_payload):
+def mms_send_receive_verify_for_subscription(
+ log,
+ ad_tx,
+ ad_rx,
+ subid_tx,
+ subid_rx,
+ array_payload,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
"""Send MMS, receive MMS, and verify content and sender's number.
Send (several) MMS from droid_tx to droid_rx.
@@ -3307,31 +3751,50 @@
phonenumber_tx = ad_tx.cfg['subscription'][subid_tx]['phone_num']
phonenumber_rx = ad_rx.cfg['subscription'][subid_rx]['phone_num']
+
+ for ad in (ad_rx, ad_tx):
+ if "Permissive" not in ad.adb.shell("su root getenforce"):
+ ad.adb.shell("su root setenforce 0")
+ if not getattr(ad, "messaging_droid", None):
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
+
for subject, message, filename in array_payload:
+ begin_time = get_current_epoch_time()
+ ad_tx.messaging_ed.clear_events(EventMmsSentSuccess)
+ ad_rx.messaging_ed.clear_events(EventMmsDownloaded)
+ ad_rx.messaging_droid.smsStartTrackingIncomingMmsMessage()
ad_tx.log.info(
"Sending MMS from %s to %s, subject: %s, message: %s, file: %s.",
phonenumber_tx, phonenumber_rx, subject, message, filename)
- result = False
- ad_rx.ed.clear_all_events()
- ad_rx.droid.smsStartTrackingIncomingMmsMessage()
try:
- ad_tx.droid.smsSendMultimediaMessage(
+ ad_tx.messaging_droid.smsSendMultimediaMessage(
phonenumber_rx, subject, message, phonenumber_tx, filename)
try:
- ad_tx.ed.pop_event(EventMmsSentSuccess,
- MAX_WAIT_TIME_SMS_SENT_SUCCESS)
+ ad_tx.messaging_ed.pop_event(EventMmsSentSuccess,
+ max_wait_time)
except Empty:
ad_tx.log.warning("No sent_success event.")
- return False
+ # check log message as a work around for the missing sl4a
+ # event dispatcher event
+ if not sms_mms_send_logcat_check(ad_tx, "mms", begin_time):
+ return False
- if not wait_for_matching_mms(log, ad_rx, phonenumber_tx, message):
+ if not wait_for_matching_mms(
+ log, ad_rx, phonenumber_tx, message,
+ begin_time=begin_time):
return False
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
finally:
ad_rx.droid.smsStopTrackingIncomingMmsMessage()
return True
-def mms_receive_verify_after_call_hangup(log, ad_tx, ad_rx, array_message):
+def mms_receive_verify_after_call_hangup(
+ log, ad_tx, ad_rx, array_message,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
"""Verify the suspanded MMS during call will send out after call release.
Hangup call from droid_tx to droid_rx.
@@ -3345,14 +3808,19 @@
array_message: the array of message to send/receive
"""
return mms_receive_verify_after_call_hangup_for_subscription(
- log, ad_tx, ad_rx,
- get_outgoing_message_sub_id(ad_tx),
- get_incoming_message_sub_id(ad_rx), array_message)
+ log, ad_tx, ad_rx, get_outgoing_message_sub_id(ad_tx),
+ get_incoming_message_sub_id(ad_rx), array_message, max_wait_time)
#TODO: add mms matching after mms message parser is added in sl4a. b/34276948
def mms_receive_verify_after_call_hangup_for_subscription(
- log, ad_tx, ad_rx, subid_tx, subid_rx, array_payload):
+ log,
+ ad_tx,
+ ad_rx,
+ subid_tx,
+ subid_rx,
+ array_payload,
+ max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
"""Verify the suspanded MMS during call will send out after call release.
Hangup call from droid_tx to droid_rx.
@@ -3370,24 +3838,30 @@
phonenumber_tx = ad_tx.cfg['subscription'][subid_tx]['phone_num']
phonenumber_rx = ad_rx.cfg['subscription'][subid_rx]['phone_num']
+ for ad in (ad_tx, ad_rx):
+ if not getattr(ad, "messaging_droid", None):
+ ad.messaging_droid, ad.messaging_ed = ad.get_droid()
+ ad.messaging_ed.start()
for subject, message, filename in array_payload:
+ begin_time = get_current_epoch_time()
ad_rx.log.info(
"Waiting MMS from %s to %s, subject: %s, message: %s, file: %s.",
phonenumber_tx, phonenumber_rx, subject, message, filename)
- result = False
- ad_rx.ed.clear_all_events()
- ad_rx.droid.smsStartTrackingIncomingMmsMessage()
+ ad_rx.messaging_droid.smsStartTrackingIncomingMmsMessage()
time.sleep(5)
try:
hangup_call(log, ad_tx)
hangup_call(log, ad_rx)
try:
- ad_tx.ed.pop_event(EventMmsSentSuccess,
- MAX_WAIT_TIME_SMS_SENT_SUCCESS)
+ ad_tx.messaging_ed.pop_event(EventMmsSentSuccess,
+ max_wait_time)
except Empty:
log.warning("No sent_success event.")
-
- if not wait_for_matching_mms(log, ad_rx, phonenumber_tx, message):
+ if not sms_mms_send_logcat_check(ad_tx, "mms", begin_time):
+ return False
+ if not wait_for_matching_mms(
+ log, ad_rx, phonenumber_tx, message,
+ begin_time=begin_time):
return False
finally:
ad_rx.droid.smsStopTrackingIncomingMmsMessage()
@@ -3404,9 +3878,8 @@
"""Ensure ad's current network is in expected rat_family.
"""
return ensure_network_rat_for_subscription(
- log, ad,
- ad.droid.subscriptionGetDefaultSubId(), network_preference, rat_family,
- voice_or_data, max_wait_time, toggle_apm_after_setting)
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), network_preference,
+ rat_family, voice_or_data, max_wait_time, toggle_apm_after_setting)
def ensure_network_rat_for_subscription(
@@ -3464,8 +3937,7 @@
"""Ensure that current rat is within the device's preferred network rats.
"""
return ensure_network_preference_for_subscription(
- log, ad,
- ad.droid.subscriptionGetDefaultSubId(), network_preference,
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), network_preference,
voice_or_data, max_wait_time, toggle_apm_after_setting)
@@ -3525,9 +3997,8 @@
Wait for ad in expected network type.
"""
return ensure_network_generation_for_subscription(
- log, ad,
- ad.droid.subscriptionGetDefaultSubId(), generation, max_wait_time,
- voice_or_data, toggle_apm_after_setting)
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), generation,
+ max_wait_time, voice_or_data, toggle_apm_after_setting)
def ensure_network_generation_for_subscription(
@@ -3548,16 +4019,13 @@
"RAT network type voice: %s, data: %s",
ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id))
- if is_droid_in_network_generation_for_subscription(
- log, ad, sub_id, generation, voice_or_data):
- return True
try:
ad.log.info("Finding the network preference for generation %s for "
"operator %s phone type %s", generation,
ad.cfg["subscription"][sub_id]["operator"],
ad.cfg["subscription"][sub_id]["phone_type"])
- network_preference = network_preference_for_generaton(
+ network_preference = network_preference_for_generation(
generation, ad.cfg["subscription"][sub_id]["operator"],
ad.cfg["subscription"][sub_id]["phone_type"])
ad.log.info("Network preference for %s is %s", generation,
@@ -3574,14 +4042,29 @@
current_network_preference = \
ad.droid.telephonyGetPreferredNetworkTypesForSubscription(
sub_id)
-
- if (current_network_preference is not network_preference and
- not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
- network_preference, sub_id)):
- ad.log.error(
- "Network preference is %s. Set Preferred Networks to %s failed.",
- current_network_preference, network_preference)
- return False
+ for _ in range(3):
+ if current_network_preference == network_preference:
+ break
+ if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+ network_preference, sub_id):
+ ad.log.info(
+ "Network preference is %s. Set Preferred Networks to %s failed.",
+ current_network_preference, network_preference)
+ reasons = ad.search_logcat(
+ "REQUEST_SET_PREFERRED_NETWORK_TYPE error")
+ if reasons:
+ reason_log = reasons[-1]["log_message"]
+ ad.log.info(reason_log)
+ if "DEVICE_IN_USE" in reason_log:
+ time.sleep(5)
+ else:
+ ad.log.error("Failed to set Preferred Networks to %s",
+ network_preference)
+ return False
+ else:
+ ad.log.error("Failed to set Preferred Networks to %s",
+ network_preference)
+ return False
if is_droid_in_network_generation_for_subscription(
log, ad, sub_id, generation, voice_or_data):
@@ -3607,7 +4090,8 @@
rat_generation_from_rat(
ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
sub_id)))
-
+ if not result:
+ ad.log.info("singal strength = %s", get_telephony_signal_strength(ad))
return result
@@ -3617,9 +4101,8 @@
max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
voice_or_data=None):
return wait_for_network_rat_for_subscription(
- log, ad,
- ad.droid.subscriptionGetDefaultSubId(), rat_family, max_wait_time,
- voice_or_data)
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
+ max_wait_time, voice_or_data)
def wait_for_network_rat_for_subscription(
@@ -3640,9 +4123,8 @@
max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
voice_or_data=None):
return wait_for_not_network_rat_for_subscription(
- log, ad,
- ad.droid.subscriptionGetDefaultSubId(), rat_family, max_wait_time,
- voice_or_data)
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
+ max_wait_time, voice_or_data)
def wait_for_not_network_rat_for_subscription(
@@ -3664,8 +4146,7 @@
max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
voice_or_data=None):
return wait_for_preferred_network_for_subscription(
- log, ad,
- ad.droid.subscriptionGetDefaultSubId(), network_preference,
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), network_preference,
max_wait_time, voice_or_data)
@@ -3689,9 +4170,8 @@
max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
voice_or_data=None):
return wait_for_network_generation_for_subscription(
- log, ad,
- ad.droid.subscriptionGetDefaultSubId(), generation, max_wait_time,
- voice_or_data)
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), generation,
+ max_wait_time, voice_or_data)
def wait_for_network_generation_for_subscription(
@@ -3709,8 +4189,8 @@
def is_droid_in_rat_family(log, ad, rat_family, voice_or_data=None):
return is_droid_in_rat_family_for_subscription(
- log, ad,
- ad.droid.subscriptionGetDefaultSubId(), rat_family, voice_or_data)
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
+ voice_or_data)
def is_droid_in_rat_family_for_subscription(log,
@@ -3724,8 +4204,8 @@
def is_droid_in_rat_familiy_list(log, ad, rat_family_list, voice_or_data=None):
return is_droid_in_rat_family_list_for_subscription(
- log, ad,
- ad.droid.subscriptionGetDefaultSubId(), rat_family_list, voice_or_data)
+ log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family_list,
+ voice_or_data)
def is_droid_in_rat_family_list_for_subscription(log,
@@ -3796,8 +4276,8 @@
return True
else:
ad.log.info("%s network rat %s is %s, does not meet expected %s",
- service, nw_rat,
- rat_generation_from_rat(nw_rat), nw_gen)
+ service, nw_rat, rat_generation_from_rat(nw_rat),
+ nw_gen)
return False
return False
@@ -3928,7 +4408,7 @@
while duration < MAX_WAIT_TIME_NW_SELECTION:
subInfo = ad.droid.subscriptionGetAllSubInfoList()
if subInfo and len(subInfo) >= 1:
- ad.log.info("Find valid subcription %s", subInfo)
+ ad.log.debug("Find valid subcription %s", subInfo)
break
else:
ad.log.info("Did not find a valid subscription")
@@ -3973,6 +4453,7 @@
data_roaming = getattr(ad, 'roaming', False)
if get_cell_data_roaming_state_by_adb(ad) != data_roaming:
set_cell_data_roaming_state_by_adb(ad, data_roaming)
+ remove_mobile_data_usage_limit(ad)
if not wait_for_not_network_rat(
log, ad, RAT_FAMILY_WLAN, voice_or_data=NETWORK_SERVICE_DATA):
ad.log.error("%s still in %s", NETWORK_SERVICE_DATA,
@@ -4028,10 +4509,12 @@
wifi_info = ad.droid.wifiGetConnectionInfo()
if wifi_info["supplicant_state"] == "completed" and wifi_info["SSID"] == wifi_ssid:
ad.log.info("Wifi is connected to %s", wifi_ssid)
+ ad.on_mobile_data = False
return True
else:
ad.log.info("Wifi is not connected to %s", wifi_ssid)
ad.log.debug("Wifi connection_info=%s", wifi_info)
+ ad.on_mobile_data = True
return False
@@ -4085,6 +4568,7 @@
boolean success (True) or failure (False)
"""
if not ad.droid.wifiGetConfiguredNetworks():
+ ad.on_mobile_data = True
return True
try:
old_state = ad.droid.wifiCheckState()
@@ -4093,6 +4577,7 @@
except Exception as e:
log.error("forget_all_wifi_networks with exception: %s", e)
return False
+ ad.on_mobile_data = True
return True
@@ -4127,6 +4612,7 @@
"""
ad.droid.wifiFactoryReset()
ad.droid.wifiToggleState(False)
+ ad.on_mobile_data = True
def wifi_toggle_state(log, ad, state, retries=3):
@@ -4142,6 +4628,7 @@
"""
for i in range(retries):
if wifi_test_utils.wifi_toggle_state(ad, state, assert_on_fail=False):
+ ad.on_mobile_data = not state
return True
time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
return False
@@ -4199,7 +4686,7 @@
try:
if current_preference not in get_allowable_network_preference(
sub_info["operator"], sub_info["phone_type"]):
- network_preference = network_preference_for_generaton(
+ network_preference = network_preference_for_generation(
GEN_4G, sub_info["operator"], sub_info["phone_type"])
ad.droid.telephonySetPreferredNetworkTypesForSubscription(
network_preference, sub_id)
@@ -4222,6 +4709,25 @@
return func(*params)
+def run_multithread_func_async(log, task):
+ """Starts a multi-threaded function asynchronously.
+
+ Args:
+ log: log object.
+ task: a task to be executed in parallel.
+
+ Returns:
+ Future object representing the execution of the task.
+ """
+ executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
+ try:
+ future_object = executor.submit(task_wrapper, task)
+ except Exception as e:
+ log.error("Exception error %s", e)
+ raise
+ return future_object
+
+
def run_multithread_func(log, tasks):
"""Run multi-thread functions and return results.
@@ -4232,18 +4738,20 @@
Returns:
results for tasks.
"""
- MAX_NUMBER_OF_WORKERS = 5
+ MAX_NUMBER_OF_WORKERS = 10
number_of_workers = min(MAX_NUMBER_OF_WORKERS, len(tasks))
executor = concurrent.futures.ThreadPoolExecutor(
max_workers=number_of_workers)
+ if not log: log = logging
try:
results = list(executor.map(task_wrapper, tasks))
except Exception as e:
log.error("Exception error %s", e)
raise
executor.shutdown()
- log.info("multithread_func %s result: %s",
- [task[0].__name__ for task in tasks], results)
+ if log:
+ log.info("multithread_func %s result: %s",
+ [task[0].__name__ for task in tasks], results)
return results
@@ -4322,17 +4830,6 @@
out = ad.adb.shell("settings list system | grep volume")
for attr in re.findall(r"(volume_.*)=\d+", out):
ad.adb.shell("settings put system %s 0" % attr)
- try:
- if not ad.droid.telecomIsInCall():
- ad.droid.telecomCallNumber(STORY_LINE)
- for _ in range(10):
- ad.send_keycode("VOLUME_DOWN")
- time.sleep(1)
- ad.droid.telecomEndCall()
- time.sleep(1)
- except Exception as e:
- ad.log.info("fail to turn down voice call volume %s", e)
-
return silent_mode == ad.droid.checkRingerSilentMode()
@@ -4607,32 +5104,169 @@
return None
-def set_qxdm_logger_always_on(ad, mask_file="Radio-general.cfg"):
+def find_qxdm_log_mask(ad, mask="default.cfg"):
+ """Find QXDM logger mask."""
+ if "/" not in mask:
+ # Call nexuslogger to generate log mask
+ start_nexuslogger(ad)
+ # Find the log mask path
+ for path in (DEFAULT_QXDM_LOG_PATH, "/data/diag_logs",
+ "/vendor/etc/mdlog/"):
+ out = ad.adb.shell(
+ "find %s -type f -iname %s" % (path, mask), ignore_status=True)
+ if out and "No such" not in out and "Permission denied" not in out:
+ if path.startswith("/vendor/"):
+ ad.qxdm_log_path = DEFAULT_QXDM_LOG_PATH
+ else:
+ ad.qxdm_log_path = path
+ return out.split("\n")[0]
+ if mask in ad.adb.shell("ls /vendor/etc/mdlog/"):
+ ad.qxdm_log_path = DEFAULT_QXDM_LOG_PATH
+ return "%s/%s" % ("/vendor/etc/mdlog/", mask)
+ else:
+ out = ad.adb.shell("ls %s" % mask, ignore_status=True)
+ if out and "No such" not in out:
+ ad.qxdm_log_path = "/data/vendor/radio/diag_logs"
+ return mask
+ ad.log.warning("Could NOT find QXDM logger mask path for %s", mask)
+
+
+def set_qxdm_logger_command(ad, mask=None):
"""Set QXDM logger always on.
Args:
ad: android device object.
"""
- ad.adb.shell("setprop persist.sys.modem.diag.mdlog true")
- ad.adb.shell("setprop persist.radio.smlog_switch false")
- ad.adb.shell('echo "diag_mdlog -f /data/vendor/radio/diag_logs/cfg/%s'
- ' -o /data/vendor/radio/diag_logs/logs -s 500 -n 10 -b -c > '
- '/data/vendor/radio/diag_logs/diag.conf"' % mask_file)
- ad.reboot()
+ ## Neet to check if log mask will be generated without starting nexus logger
+ masks = []
+ mask_path = None
+ if mask:
+ masks = [mask]
+ masks.extend(["QC_Default.cfg", "default.cfg"])
+ for mask in masks:
+ mask_path = find_qxdm_log_mask(ad, mask)
+ if mask_path: break
+ if not mask_path:
+ ad.log.error("Cannot find QXDM mask %s", mask)
+ ad.qxdm_logger_command = None
+ return False
+ else:
+ ad.log.info("Use QXDM log mask %s", mask_path)
+ ad.log.debug("qxdm_log_path = %s", ad.qxdm_log_path)
+ output_path = os.path.join(ad.qxdm_log_path, "logs")
+ ad.qxdm_logger_command = ("diag_mdlog -f %s -o %s -s 50 -c" %
+ (mask_path, output_path))
+ conf_path = os.path.join(ad.qxdm_log_path, "diag.conf")
+ # Enable qxdm always on so that after device reboot, qxdm will be
+ # turned on automatically
+ ad.adb.shell('echo "%s" > %s' % (ad.qxdm_logger_command, conf_path))
+ ad.adb.shell(
+ "setprop persist.vendor.sys.modem.diag.mdlog true", ignore_status=True)
+ # Legacy pixels use persist.sys.modem.diag.mdlog.
+ ad.adb.shell(
+ "setprop persist.sys.modem.diag.mdlog true", ignore_status=True)
+ return True
-def check_qxdm_logger_always_on(ad, mask_file="Radio-general.cfg"):
+def stop_qxdm_logger(ad):
+ """Stop QXDM logger."""
+ for cmd in ("diag_mdlog -k", "killall diag_mdlog"):
+ output = ad.adb.shell("ps -ef | grep mdlog") or ""
+ if "diag_mdlog" not in output:
+ break
+ ad.log.debug("Kill the existing qxdm process")
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(5)
+
+
+def start_qxdm_logger(ad, begin_time=None):
+ """Start QXDM logger."""
+ if not getattr(ad, "qxdm_log", True): return
+ # Delete existing QXDM logs 5 minutes earlier than the begin_time
+ if getattr(ad, "qxdm_log_path", None):
+ seconds = None
+ if begin_time:
+ current_time = get_current_epoch_time()
+ seconds = int((current_time - begin_time) / 1000.0) + 10 * 60
+ elif len(ad.get_file_names(ad.qxdm_log_path)) > 50:
+ seconds = 900
+ if seconds:
+ ad.adb.shell(
+ "find %s -type f -iname *.qmdl -not -mtime -%ss -delete" %
+ (ad.qxdm_log_path, seconds))
+ if getattr(ad, "qxdm_logger_command", None):
+ output = ad.adb.shell("ps -ef | grep mdlog") or ""
+ if ad.qxdm_logger_command not in output:
+ ad.log.debug("QXDM logging command %s is not running",
+ ad.qxdm_logger_command)
+ if "diag_mdlog" in output:
+ # Kill the existing diag_mdlog process
+ # Only one diag_mdlog process can be run
+ stop_qxdm_logger(ad)
+ ad.log.info("Start QXDM logger")
+ ad.adb.shell_nb(ad.qxdm_logger_command)
+ elif not ad.get_file_names(ad.qxdm_log_path, 60):
+ ad.log.debug("Existing diag_mdlog is not generating logs")
+ stop_qxdm_logger(ad)
+ ad.adb.shell_nb(ad.qxdm_logger_command)
+ return True
+
+
+def start_qxdm_loggers(log, ads, begin_time=None):
+ tasks = [(start_qxdm_logger, [ad, begin_time]) for ad in ads
+ if getattr(ad, "qxdm_log", True)]
+ if tasks: run_multithread_func(log, tasks)
+
+
+def stop_qxdm_loggers(log, ads):
+ tasks = [(stop_qxdm_logger, [ad]) for ad in ads]
+ run_multithread_func(log, tasks)
+
+
+def start_nexuslogger(ad):
+ """Start Nexus/Pixel Logger Apk."""
+ qxdm_logger_apk = None
+ for apk, activity in (("com.android.nexuslogger", ".MainActivity"),
+ ("com.android.pixellogger",
+ ".ui.main.MainActivity")):
+ if ad.is_apk_installed(apk):
+ qxdm_logger_apk = apk
+ break
+ if not qxdm_logger_apk: return
+ if ad.is_apk_running(qxdm_logger_apk):
+ if "granted=true" in ad.adb.shell(
+ "dumpsys package %s | grep WRITE_EXTERN" % qxdm_logger_apk):
+ return True
+ else:
+ ad.log.info("Kill %s" % qxdm_logger_apk)
+ ad.force_stop_apk(qxdm_logger_apk)
+ time.sleep(5)
+ for perm in ("READ", "WRITE"):
+ ad.adb.shell("pm grant %s android.permission.%s_EXTERNAL_STORAGE" %
+ (qxdm_logger_apk, perm))
+ time.sleep(2)
+ for i in range(3):
+ ad.log.info("Start %s Attempt %d" % (qxdm_logger_apk, i + 1))
+ ad.adb.shell("am start -n %s/%s" % (qxdm_logger_apk, activity))
+ time.sleep(5)
+ if ad.is_apk_running(qxdm_logger_apk):
+ ad.send_keycode("HOME")
+ return True
+ return False
+
+
+def check_qxdm_logger_mask(ad, mask_file="QC_Default.cfg"):
"""Check if QXDM logger always on is set.
Args:
ad: android device object.
"""
- if ad.adb.shell("getprop persist.sys.modem.diag.mdlog") != 'true':
- return False
- if ad.adb.shell("getprop persist.radio.smlog_switch") != 'false':
- return False
+ output = ad.adb.shell(
+ "ls /data/vendor/radio/diag_logs/", ignore_status=True)
+ if not output or "No such" in output:
+ return True
if mask_file not in ad.adb.shell(
"cat /data/vendor/radio/diag_logs/diag.conf", ignore_status=True):
return False
@@ -4648,7 +5282,10 @@
"""
ad.log.debug("Ensuring no tcpdump is running in background")
- ad.adb.shell("killall -9 tcpdump")
+ try:
+ ad.adb.shell("killall -9 tcpdump")
+ except AdbError:
+ ad.log.warn("Killing existing tcpdump processes failed")
out = ad.adb.shell("ls -l /sdcard/tcpdump/")
if "No such file" in out or not out:
ad.adb.shell("mkdir /sdcard/tcpdump")
@@ -4669,11 +5306,10 @@
cmd = "adb -s {} shell tcpdump -i any -s0 -n -p udp port 500 or \
udp port 4500 -w {}".format(ad.serial, file_name)
ad.log.debug("%s" % cmd)
- tcpdump_pid = start_standing_subprocess(cmd, 5)
- return (tcpdump_pid, file_name)
+ return start_standing_subprocess(cmd, 5)
-def stop_adb_tcpdump(ad, tcpdump_pid, tcpdump_file, pull_tcpdump=False):
+def stop_adb_tcpdump(ad, proc=None, pull_tcpdump=False, test_name=""):
"""Stops tcpdump on any iface
Pulls the tcpdump file in the tcpdump dir
@@ -4683,13 +5319,18 @@
tcpdump_file: filename needed to pull out
"""
- ad.log.debug("Stopping and pulling tcpdump if failed")
- stop_standing_subprocess(tcpdump_pid)
+ ad.log.info("Stopping and pulling tcpdump if any")
+ try:
+ if proc is not None:
+ stop_standing_subprocess(proc)
+ except Exception as e:
+ ad.log.warning(e)
if pull_tcpdump:
- tcpdump_path = os.path.join(ad.log_path, "tcpdump")
- create_dir(tcpdump_path)
- ad.adb.pull("{} {}".format(tcpdump_file, tcpdump_path))
- ad.adb.shell("rm -rf {}".format(tcpdump_file))
+ log_path = os.path.join(ad.log_path, test_name,
+ "TCPDUMP_%s" % ad.serial)
+ utils.create_dir(log_path)
+ ad.adb.pull("/sdcard/tcpdump/. %s" % log_path)
+ ad.adb.shell("rm -rf /sdcard/tcpdump/*", ignore_status=True)
return True
@@ -4703,7 +5344,7 @@
"""
status = True
# Pull sl4a apk from device
- out = ad.adb.shell("pm path com.googlecode.android_scripting")
+ out = ad.adb.shell("pm path %s" % SL4A_APK_NAME)
result = re.search(r"package:(.*)", out)
if not result:
ad.log.error("Couldn't find sl4a apk")
@@ -4720,6 +5361,7 @@
except Exception as e:
ad.log.error(e)
status = False
+ time.sleep(30) #sleep time after fastboot wipe
for _ in range(2):
try:
ad.log.info("Reboot in fastboot")
@@ -4729,8 +5371,6 @@
except Exception as e:
ad.log.error("Exception error %s", e)
ad.root_adb()
- if not ad.ensure_screen_on():
- ad.log.error("User window cannot come up")
if result:
# Try to reinstall for three times as the device might not be
# ready to apk install shortly after boot complete.
@@ -4738,12 +5378,41 @@
if ad.is_sl4a_installed():
break
ad.log.info("Re-install sl4a")
- ad.adb.install("-r /tmp/base.apk")
+ ad.adb.install("-r /tmp/base.apk", ignore_status=True)
time.sleep(10)
- ad.start_services(ad.skip_sl4a, skip_setup_wizard=skip_setup_wizard)
+ try:
+ ad.start_adb_logcat()
+ except:
+ ad.log.exception("Failed to start adb logcat!")
+ if skip_setup_wizard:
+ ad.exit_setup_wizard()
+ if ad.skip_sl4a: return status
+ bring_up_sl4a(ad)
+
return status
+def bring_up_sl4a(ad, attemps=3):
+ for i in range(attemps):
+ try:
+ droid, ed = ad.get_droid()
+ ed.start()
+ ad.log.info("Broght up new sl4a session")
+ except Exception as e:
+ if i < attemps - 1:
+ ad.log.info(e)
+ time.sleep(10)
+ else:
+ ad.log.error(e)
+ raise
+
+
+def reboot_device(ad):
+ ad.reboot()
+ ad.ensure_screen_on()
+ unlock_sim(ad)
+
+
def unlocking_device(ad, device_password=None):
"""First unlock device attempt, required after reboot"""
ad.unlock_screen(device_password)
@@ -4769,8 +5438,7 @@
ad.terminate_all_sessions()
ad.ensure_screen_on()
ad.log.info("Open new sl4a connection")
- droid, ed = ad.get_droid()
- ed.start()
+ bring_up_sl4a(ad)
def reset_device_password(ad, device_password=None):
@@ -4778,8 +5446,17 @@
unlock_sim(ad)
screen_lock = ad.is_screen_lock_enabled()
if device_password:
- refresh_sl4a_session(ad)
- ad.droid.setDevicePassword(device_password)
+ try:
+ refresh_sl4a_session(ad)
+ ad.droid.setDevicePassword(device_password)
+ except Exception as e:
+ ad.log.warning("setDevicePassword failed with %s", e)
+ try:
+ ad.droid.setDevicePassword(device_password, "1111")
+ except Exception as e:
+ ad.log.warning(
+ "setDevicePassword providing previous password error: %s",
+ e)
time.sleep(2)
if screen_lock:
# existing password changed
@@ -4797,8 +5474,14 @@
# need to disable the password and log in on the first time
# with unlocking with a swipe
ad.log.info("Disable device password")
+ ad.unlock_screen(password="1111")
refresh_sl4a_session(ad)
- ad.droid.disableDevicePassword()
+ ad.ensure_screen_on()
+ try:
+ ad.droid.disableDevicePassword()
+ except Exception as e:
+ ad.log.warning("disableDevicePassword failed with %s", e)
+ fastboot_wipe(ad)
time.sleep(2)
ad.adb.wait_for_device(timeout=180)
refresh_sl4a_session(ad)
@@ -4806,11 +5489,16 @@
ad.start_adb_logcat()
-def is_sim_locked(ad):
+def get_sim_state(ad):
try:
- return ad.droid.telephonyGetSimState() == SIM_STATE_PIN_REQUIRED
+ state = ad.droid.telephonyGetSimState()
except:
- return ad.adb.getprop("gsm.sim.state") == SIM_STATE_PIN_REQUIRED
+ state = ad.adb.getprop("gsm.sim.state")
+ return state
+
+
+def is_sim_locked(ad):
+ return get_sim_state(ad) == SIM_STATE_PIN_REQUIRED
def unlock_sim(ad):
@@ -4821,14 +5509,16 @@
# "puk_pin": "1234"}]
if not is_sim_locked(ad):
return True
+ else:
+ ad.is_sim_locked = True
puk_pin = getattr(ad, "puk_pin", "1111")
try:
if not hasattr(ad, 'puk'):
ad.log.info("Enter SIM pin code")
- result = ad.droid.telephonySupplyPin(puk_pin)
+ ad.droid.telephonySupplyPin(puk_pin)
else:
ad.log.info("Enter PUK code and pin")
- result = ad.droid.telephonySupplyPuk(ad.puk, puk_pin)
+ ad.droid.telephonySupplyPuk(ad.puk, puk_pin)
except:
# if sl4a is not available, use adb command
ad.unlock_screen(puk_pin)
@@ -4902,7 +5592,6 @@
ad.fastboot.flash("radio %s" % file_path, timeout=300)
except Exception as e:
ad.log.error(e)
- status = False
for _ in range(2):
try:
ad.log.info("Reboot in fastboot")
@@ -4915,10 +5604,143 @@
if not ad.ensure_screen_on():
ad.log.error("User window cannot come up")
ad.start_services(ad.skip_sl4a, skip_setup_wizard=skip_setup_wizard)
+ unlock_sim(ad)
+
+
+def set_preferred_apn_by_adb(ad, pref_apn_name):
+ """Select Pref APN
+ Set Preferred APN on UI using content query/insert
+ It needs apn name as arg, and it will match with plmn id
+ """
+ try:
+ plmn_id = get_plmn_by_adb(ad)
+ out = ad.adb.shell("content query --uri content://telephony/carriers "
+ "--where \"apn='%s' and numeric='%s'\"" %
+ (pref_apn_name, plmn_id))
+ if "No result found" in out:
+ ad.log.warning("Cannot find APN %s on device", pref_apn_name)
+ return False
+ else:
+ apn_id = re.search(r'_id=(\d+)', out).group(1)
+ ad.log.info("APN ID is %s", apn_id)
+ ad.adb.shell("content insert --uri content:"
+ "//telephony/carriers/preferapn --bind apn_id:i:%s" %
+ (apn_id))
+ out = ad.adb.shell("content query --uri "
+ "content://telephony/carriers/preferapn")
+ if "No result found" in out:
+ ad.log.error("Failed to set prefer APN %s", pref_apn_name)
+ return False
+ elif apn_id == re.search(r'_id=(\d+)', out).group(1):
+ ad.log.info("Preferred APN set to %s", pref_apn_name)
+ return True
+ except Exception as e:
+ ad.log.error("Exception while setting pref apn %s", e)
+ return True
+
+
+def check_apm_mode_on_by_serial(ad, serial_id):
+ try:
+ apm_check_cmd = "|".join(("adb -s %s shell dumpsys wifi" % serial_id,
+ "grep -i airplanemodeon", "cut -f2 -d ' '"))
+ output = exe_cmd(apm_check_cmd)
+ if output.decode("utf-8").split("\n")[0] == "true":
+ return True
+ else:
+ return False
+ except Exception as e:
+ ad.log.warning("Exception during check apm mode on %s", e)
+ return True
+
+
+def set_apm_mode_on_by_serial(ad, serial_id):
+ try:
+ cmd1 = "adb -s %s shell settings put global airplane_mode_on 1" % serial_id
+ cmd2 = "adb -s %s shell am broadcast -a android.intent.action.AIRPLANE_MODE" % serial_id
+ exe_cmd(cmd1)
+ exe_cmd(cmd2)
+ except Exception as e:
+ ad.log.warning("Exception during set apm mode on %s", e)
+ return True
def print_radio_info(ad, extra_msg=""):
for prop in ("gsm.version.baseband", "persist.radio.ver_info",
- "persist.radio.cnv.ver_info", "persist.radio.ci_status"):
+ "persist.radio.cnv.ver_info"):
output = ad.adb.getprop(prop)
- if output: ad.log.info("%s%s = %s", extra_msg, prop, output)
+ ad.log.info("%s%s = %s", extra_msg, prop, output)
+
+
+def wait_for_state(state_check_func,
+ state,
+ max_wait_time=MAX_WAIT_TIME_FOR_STATE_CHANGE,
+ checking_interval=WAIT_TIME_BETWEEN_STATE_CHECK,
+ *args,
+ **kwargs):
+ while max_wait_time >= 0:
+ if state_check_func(*args, **kwargs) == state:
+ return True
+ time.sleep(checking_interval)
+ max_wait_time -= checking_interval
+ return False
+
+
+def power_off_sim(ad, sim_slot_id=None):
+ try:
+ if sim_slot_id is None:
+ ad.droid.telephonySetSimPowerState(CARD_POWER_DOWN)
+ verify_func = ad.droid.telephonyGetSimState
+ verify_args = []
+ else:
+ ad.droid.telephonySetSimStateForSlotId(sim_slot_id,
+ CARD_POWER_DOWN)
+ verify_func = ad.droid.telephonyGetSimStateForSlotId
+ verify_args = [sim_slot_id]
+ except Exception as e:
+ ad.log.error(e)
+ return False
+ if wait_for_state(verify_func, SIM_STATE_UNKNOWN,
+ MAX_WAIT_TIME_FOR_STATE_CHANGE,
+ WAIT_TIME_BETWEEN_STATE_CHECK, *verify_args):
+ ad.log.info("SIM slot is powered off, SIM state is UNKNOWN")
+ return True
+ else:
+ ad.log.info("SIM state = %s", verify_func(*verify_args))
+ ad.log.warning("Fail to power off SIM slot")
+ return False
+
+
+def power_on_sim(ad, sim_slot_id=None):
+ try:
+ if sim_slot_id is None:
+ ad.droid.telephonySetSimPowerState(CARD_POWER_UP)
+ verify_func = ad.droid.telephonyGetSimState
+ verify_args = []
+ else:
+ ad.droid.telephonySetSimStateForSlotId(sim_slot_id, CARD_POWER_UP)
+ verify_func = ad.droid.telephonyGetSimStateForSlotId
+ verify_args = [sim_slot_id]
+ except Exception as e:
+ ad.log.error(e)
+ return False
+ if wait_for_state(verify_func, SIM_STATE_READY,
+ MAX_WAIT_TIME_FOR_STATE_CHANGE,
+ WAIT_TIME_BETWEEN_STATE_CHECK, *verify_args):
+ ad.log.info("SIM slot is powered on, SIM state is READY")
+ return True
+ elif verify_func(*verify_args) == SIM_STATE_PIN_REQUIRED:
+ ad.log.info("SIM is pin locked")
+ return True
+ else:
+ ad.log.error("Fail to power on SIM slot")
+ return False
+
+
+def log_screen_shot(ad, test_name):
+ file_name = "/sdcard/Pictures/screencap_%s.png" % (
+ utils.get_current_epoch_time())
+ screen_shot_path = os.path.join(ad.log_path, test_name,
+ "Screenshot_%s" % ad.serial)
+ utils.create_dir(screen_shot_path)
+ ad.adb.shell("screencap -p %s" % file_name)
+ ad.adb.pull("%s %s" % (file_name, screen_shot_path))
diff --git a/acts/framework/acts/test_utils/tel/tel_video_utils.py b/acts/framework/acts/test_utils/tel/tel_video_utils.py
index 48ab6ee..c96f490 100644
--- a/acts/framework/acts/test_utils/tel/tel_video_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_video_utils.py
@@ -30,6 +30,10 @@
from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
from acts.test_utils.tel.tel_defines import GEN_4G
+from acts.test_utils.tel.tel_defines import RAT_1XRTT
+from acts.test_utils.tel.tel_defines import RAT_IWLAN
+from acts.test_utils.tel.tel_defines import RAT_LTE
+from acts.test_utils.tel.tel_defines import RAT_UMTS
from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_OFFHOOK
from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_RINGING
from acts.test_utils.tel.tel_defines import VT_STATE_AUDIO_ONLY
@@ -66,6 +70,8 @@
from acts.test_utils.tel.tel_test_utils import wait_for_ringing_call
from acts.test_utils.tel.tel_test_utils import wait_for_telecom_ringing
from acts.test_utils.tel.tel_test_utils import wait_for_video_enabled
+from acts.test_utils.tel.tel_test_utils import get_network_rat
+from acts.test_utils.tel.tel_test_utils import is_wfc_enabled
from acts.test_utils.tel.tel_voice_utils import is_call_hd
@@ -107,8 +113,8 @@
toggle_airplane_mode(log, ad, False)
if not set_wfc_mode(log, ad, wfc_mode):
- log.error(
- "{} WFC mode failed to be set to {}.".format(ad.serial, wfc_mode))
+ log.error("{} WFC mode failed to be set to {}.".format(
+ ad.serial, wfc_mode))
return False
toggle_volte(log, ad, True)
@@ -151,8 +157,9 @@
return False
if not wait_for_video_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED):
- log.error("{} failed to <report video calling enabled> within {}s.".
- format(ad.serial, MAX_WAIT_TIME_VOLTE_ENABLED))
+ log.error(
+ "{} failed to <report video calling enabled> within {}s.".format(
+ ad.serial, MAX_WAIT_TIME_VOLTE_ENABLED))
return False
return True
@@ -191,8 +198,8 @@
"""
if video_state is None:
- log.info(
- "Verify if {}(subid {}) in video call.".format(ad.serial, sub_id))
+ log.info("Verify if {}(subid {}) in video call.".format(
+ ad.serial, sub_id))
if not ad.droid.telecomIsInCall():
log.error("{} not in call.".format(ad.serial))
return False
@@ -219,6 +226,58 @@
return False
+def is_phone_in_call_viwifi_for_subscription(log, ad, sub_id,
+ video_state=None):
+ """Return if ad (for sub_id) is in a viwifi call (in expected video state).
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: device sub_id
+ video_state: Expected Video call state.
+ This is optional, if it's None,
+ then TX_ENABLED/RX_ENABLED/BIDIRECTIONAL video call state will
+ return True.
+
+ Returns:
+ True if ad is in a video call (in expected video state).
+ """
+
+ if video_state is None:
+ log.info("Verify if {}(subid {}) in video call.".format(
+ ad.serial, sub_id))
+ if not ad.droid.telecomIsInCall():
+ log.error("{} not in call.".format(ad.serial))
+ return False
+ nw_type = get_network_rat(log, ad, NETWORK_SERVICE_DATA)
+ if nw_type != RAT_IWLAN:
+ ad.log.error("Data rat on: %s. Expected: iwlan", nw_type)
+ return False
+ if not is_wfc_enabled(log, ad):
+ ad.log.error("WiFi Calling feature bit is False.")
+ return False
+ call_list = ad.droid.telecomCallGetCallIds()
+ for call in call_list:
+ state = ad.droid.telecomCallVideoGetState(call)
+ if video_state is None:
+ if {
+ VT_STATE_AUDIO_ONLY: False,
+ VT_STATE_TX_ENABLED: True,
+ VT_STATE_TX_PAUSED: True,
+ VT_STATE_RX_ENABLED: True,
+ VT_STATE_RX_PAUSED: True,
+ VT_STATE_BIDIRECTIONAL: True,
+ VT_STATE_BIDIRECTIONAL_PAUSED: True,
+ VT_STATE_STATE_INVALID: False
+ }[state]:
+ return True
+ else:
+ if state == video_state:
+ return True
+ ad.log.info("Non-Video-State: %s", state)
+ ad.log.error("Phone not in video call. Call list: %s", call_list)
+ return False
+
+
def is_phone_in_call_video_bidirectional(log, ad):
"""Return if phone in bi-directional video call.
@@ -250,6 +309,36 @@
VT_STATE_BIDIRECTIONAL)
+def is_phone_in_call_viwifi_bidirectional(log, ad):
+ """Return if phone in bi-directional viwifi call.
+
+ Args:
+ log: log object.
+ ad: android device object
+
+ Returns:
+ True if phone in bi-directional viwifi call.
+ """
+ return is_phone_in_call_viwifi_bidirectional_for_subscription(
+ log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def is_phone_in_call_viwifi_bidirectional_for_subscription(log, ad, sub_id):
+ """Return if phone in bi-directional viwifi call for subscription id.
+
+ Args:
+ log: log object.
+ ad: android device object
+ sub_id: subscription id.
+
+ Returns:
+ True if phone in bi-directional viwifi call.
+ """
+ ad.log.info("Verify if subid %s in bi-directional video call.", sub_id)
+ return is_phone_in_call_viwifi_for_subscription(log, ad, sub_id,
+ VT_STATE_BIDIRECTIONAL)
+
+
def is_phone_in_call_video_tx_enabled(log, ad):
"""Return if phone in tx_enabled video call.
@@ -337,8 +426,8 @@
Returns:
True if phone in hd voice call.
"""
- log.info(
- "Verify if {}(subid {}) in hd voice call.".format(ad.serial, sub_id))
+ log.info("Verify if {}(subid {}) in hd voice call.".format(
+ ad.serial, sub_id))
if not ad.droid.telecomIsInCall():
log.error("{} not in call.".format(ad.serial))
return False
@@ -398,8 +487,7 @@
False: for errors
"""
return wait_and_answer_video_call_for_subscription(
- log, ad,
- get_outgoing_voice_sub_id(ad), incoming_number, video_state,
+ log, ad, get_outgoing_voice_sub_id(ad), incoming_number, video_state,
incall_ui_display)
@@ -430,8 +518,9 @@
"""
if not wait_for_ringing_call(log, ad, incoming_number):
- log.error("Video call could not be established: <{}> never rang.".
- format(ad.serial))
+ log.error(
+ "Video call could not be established: <{}> never rang.".format(
+ ad.serial))
return False
ad.ed.clear_all_events()
@@ -505,8 +594,7 @@
"""
return video_call_setup_teardown_for_subscription(
- log, ad_caller, ad_callee,
- get_outgoing_voice_sub_id(ad_caller),
+ log, ad_caller, ad_callee, get_outgoing_voice_sub_id(ad_caller),
get_incoming_voice_sub_id(ad_callee), ad_hangup, video_state,
verify_caller_func, verify_callee_func, wait_time_in_call,
incall_ui_display)
@@ -614,8 +702,8 @@
callee_state_result = verify_callee_func(log, ad_callee)
if not callee_state_result:
raise _CallSequenceException(
- "Callee not in correct state at <{}>/<{}> seconds"
- .format(elapsed_time, wait_time_in_call))
+ "Callee not in correct state at <{}>/<{}> seconds".format(
+ elapsed_time, wait_time_in_call))
if not verify_caller_func:
caller_state_result = ad_caller.droid.telecomIsInCall()
@@ -623,8 +711,8 @@
caller_state_result = verify_caller_func(log, ad_caller)
if not caller_state_result:
raise _CallSequenceException(
- "Caller not in correct state at <{}>/<{}> seconds"
- .format(elapsed_time, wait_time_in_call))
+ "Caller not in correct state at <{}>/<{}> seconds".format(
+ elapsed_time, wait_time_in_call))
if not ad_hangup:
return True
@@ -672,8 +760,7 @@
"""
return video_call_setup_for_subscription(
- log, ad_caller, ad_callee,
- get_outgoing_voice_sub_id(ad_caller),
+ log, ad_caller, ad_callee, get_outgoing_voice_sub_id(ad_caller),
get_incoming_voice_sub_id(ad_callee), video_state, incall_ui_display)
@@ -784,8 +871,8 @@
cur_video_state = ad_requester.droid.telecomCallVideoGetState(
call_id_requester)
- log.info("State change request from {} to {} requested"
- .format(cur_video_state, video_state_request))
+ log.info("State change request from {} to {} requested".format(
+ cur_video_state, video_state_request))
if cur_video_state == video_state_request:
return True
@@ -811,8 +898,8 @@
ad_responder.droid.telecomCallVideoStopListeningForEvent(
call_id_responder, EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED)
- if (verify_func_between_request_and_response and
- not verify_func_between_request_and_response()):
+ if (verify_func_between_request_and_response
+ and not verify_func_between_request_and_response()):
log.error("verify_func_between_request_and_response failed.")
return False
@@ -916,12 +1003,12 @@
current_video_state_requester = ad_requester.droid.telecomCallVideoGetState(
call_id_requester)
if video_state_request is None:
- if (current_video_state_requester == VT_STATE_BIDIRECTIONAL or
- current_video_state_requester ==
+ if (current_video_state_requester == VT_STATE_BIDIRECTIONAL
+ or current_video_state_requester ==
VT_STATE_BIDIRECTIONAL_PAUSED):
video_state_request = VT_STATE_RX_ENABLED
- elif (current_video_state_requester == VT_STATE_TX_ENABLED or
- current_video_state_requester == VT_STATE_TX_PAUSED):
+ elif (current_video_state_requester == VT_STATE_TX_ENABLED
+ or current_video_state_requester == VT_STATE_TX_PAUSED):
video_state_request = VT_STATE_AUDIO_ONLY
else:
log.error("Can Not Downgrade. ad: {}, current state {}".format(
@@ -966,10 +1053,11 @@
return False
if (expected_video_state_responder !=
ad_responder.droid.telecomCallVideoGetState(call_id_responder)):
- log.error("responder not in correct state. expected:{}, current:{}".
- format(expected_video_state_responder,
- ad_responder.droid.telecomCallVideoGetState(
- call_id_responder)))
+ log.error(
+ "responder not in correct state. expected:{}, current:{}".format(
+ expected_video_state_responder,
+ ad_responder.droid.telecomCallVideoGetState(
+ call_id_responder)))
return False
return True
diff --git a/acts/framework/acts/test_utils/tel/tel_voice_utils.py b/acts/framework/acts/test_utils/tel/tel_voice_utils.py
index cf155ae..8afd6c8 100644
--- a/acts/framework/acts/test_utils/tel/tel_voice_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_voice_utils.py
@@ -64,6 +64,7 @@
from acts.test_utils.tel.tel_test_utils import \
reset_preferred_network_type_to_allowable_range
from acts.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts.test_utils.tel.tel_test_utils import set_wifi_to_default
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
from acts.test_utils.tel.tel_test_utils import toggle_volte
from acts.test_utils.tel.tel_test_utils import toggle_volte_for_subscription
@@ -435,6 +436,7 @@
True if success, False if fail.
"""
toggle_airplane_mode(log, ad, False, strict_checking=False)
+ set_wifi_to_default(log, ad)
if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
ad.log.error("Disable WFC failed.")
return False
@@ -771,6 +773,7 @@
def phone_setup_rat_for_subscription(log, ad, sub_id, network_preference,
rat_family):
toggle_airplane_mode(log, ad, False, strict_checking=False)
+ set_wifi_to_default(log, ad)
if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
ad.log.error("Disable WFC failed.")
return False
@@ -978,6 +981,7 @@
return wait_for_network_generation_for_subscription(
log, ad, sub_id, GEN_2G, voice_or_data=NETWORK_SERVICE_VOICE)
+
def get_current_voice_rat(log, ad):
"""Return current Voice RAT
@@ -987,6 +991,7 @@
return get_current_voice_rat_for_subscription(
log, ad, get_outgoing_voice_sub_id(ad))
+
def get_current_voice_rat_for_subscription(log, ad, sub_id):
"""Return current Voice RAT for subscription id.
@@ -995,7 +1000,8 @@
sub_id: subscription id.
"""
return get_network_rat_for_subscription(log, ad, sub_id,
- NETWORK_SERVICE_VOICE)
+ NETWORK_SERVICE_VOICE)
+
def is_phone_in_call_volte(log, ad):
"""Return if phone is in VoLTE call.
diff --git a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
index a048906..3c5c92c 100755
--- a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
+++ b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
@@ -37,12 +37,13 @@
class WifiBaseTest(BaseTestClass):
def __init__(self, controllers):
BaseTestClass.__init__(self, controllers)
- if self.attenuators:
+ if hasattr(self, 'attenuators') and self.attenuators:
for attenuator in self.attenuators:
attenuator.set_atten(0)
def get_wpa2_network(
self,
+ hidden=False,
ap_count=1,
ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
@@ -74,17 +75,33 @@
ref_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
ref_5g_passphrase = utils.rand_ascii_str(passphrase_length_5g)
- network_dict_2g = {
- "SSID": ref_2g_ssid,
- "security": ref_2g_security,
- "password": ref_2g_passphrase
- }
+ if hidden:
+ network_dict_2g = {
+ "SSID": ref_2g_ssid,
+ "security": ref_2g_security,
+ "password": ref_2g_passphrase,
+ "hiddenSSID": True
+ }
- network_dict_5g = {
- "SSID": ref_5g_ssid,
- "security": ref_5g_security,
- "password": ref_5g_passphrase
- }
+ network_dict_5g = {
+ "SSID": ref_5g_ssid,
+ "security": ref_5g_security,
+ "password": ref_5g_passphrase,
+ "hiddenSSID": True
+ }
+ else:
+ network_dict_2g = {
+ "SSID": ref_2g_ssid,
+ "security": ref_2g_security,
+ "password": ref_2g_passphrase
+ }
+
+ network_dict_5g = {
+ "SSID": ref_5g_ssid,
+ "security": ref_5g_security,
+ "password": ref_5g_passphrase
+ }
+
ap = 0
for ap in range(ap_count):
self.user_params["reference_networks"].append({
@@ -97,6 +114,7 @@
return {"2g": network_dict_2g, "5g": network_dict_5g}
def get_open_network(self,
+ hidden=False,
ap_count=1,
ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G):
@@ -116,8 +134,29 @@
self.user_params["open_network"] = []
open_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g)
open_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
- network_dict_2g = {"SSID": open_2g_ssid, "security": 'none'}
- network_dict_5g = {"SSID": open_5g_ssid, "security": 'none'}
+ if hidden:
+ network_dict_2g = {
+ "SSID": open_2g_ssid,
+ "security": 'none',
+ "hiddenSSID": True
+ }
+
+ network_dict_5g = {
+ "SSID": open_5g_ssid,
+ "security": 'none',
+ "hiddenSSID": True
+ }
+ else:
+ network_dict_2g = {
+ "SSID": open_2g_ssid,
+ "security": 'none'
+ }
+
+ network_dict_5g = {
+ "SSID": open_5g_ssid,
+ "security": 'none'
+ }
+
ap = 0
for ap in range(ap_count):
self.user_params["open_network"].append({
@@ -166,6 +205,7 @@
ap_passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
ap_ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
ap_passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G,
+ hidden=False,
ap_count=1):
asserts.assert_true(
len(self.user_params["AccessPoint"]) == 2,
@@ -180,14 +220,16 @@
if "reference_networks" in self.user_params:
pass
else:
- networks_dict = self.get_wpa2_network(ap_count=ap_count)
+ networks_dict = self.get_wpa2_network(hidden=hidden,
+ ap_count=ap_count)
network_list_2g.append(networks_dict["2g"])
network_list_5g.append(networks_dict["5g"])
if "open_network" in self.user_params:
pass
else:
- networks_dict = self.get_open_network(ap_count=ap_count)
+ networks_dict = self.get_open_network(hidden=hidden,
+ ap_count=ap_count)
network_list_2g.append(networks_dict["2g"])
network_list_5g.append(networks_dict["5g"])
@@ -214,7 +256,16 @@
# build config based on the bss_Settings alone.
hostapd_config_settings = network_list.pop(0)
for network in network_list:
- if "password" in network:
+ if "password" in network and "hiddenSSID" in network:
+ bss_settings.append(
+ hostapd_bss_settings.BssSettings(
+ name=network["SSID"],
+ ssid=network["SSID"],
+ hidden=True,
+ security=hostapd_security.Security(
+ security_mode=network["security"],
+ password=network["password"])))
+ elif "password" in network and not "hiddenSSID" in network:
bss_settings.append(
hostapd_bss_settings.BssSettings(
name=network["SSID"],
@@ -222,10 +273,17 @@
security=hostapd_security.Security(
security_mode=network["security"],
password=network["password"])))
- else:
+ elif not "password" in network and "hiddenSSID" in network:
bss_settings.append(
hostapd_bss_settings.BssSettings(
- name=network["SSID"], ssid=network["SSID"]))
+ name=network["SSID"],
+ ssid=network["SSID"],
+ hidden=True))
+ elif not "password" in network and not "hiddenSSID" in network:
+ bss_settings.append(
+ hostapd_bss_settings.BssSettings(
+ name=network["SSID"],
+ ssid=network["SSID"]))
if "password" in hostapd_config_settings:
config = hostapd_ap_preset.create_ap_preset(
channel=ap_settings["channel"],
diff --git a/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py b/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py
index 0b9192d..5bba0a7 100644
--- a/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py
+++ b/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py
@@ -15,6 +15,7 @@
# limitations under the License.
from acts import asserts
+from acts import utils
from acts.base_test import BaseTestClass
from acts.test_utils.wifi import wifi_test_utils as wutils
from acts.test_utils.wifi.aware import aware_const as aconsts
@@ -23,7 +24,7 @@
class AwareBaseTest(BaseTestClass):
def __init__(self, controllers):
- BaseTestClass.__init__(self, controllers)
+ super(AwareBaseTest, self).__init__(controllers)
# message ID counter to make sure all uses are unique
msg_id = 0
@@ -43,6 +44,7 @@
"Device under test does not support Wi-Fi Aware - skipping test")
wutils.wifi_toggle_state(ad, True)
ad.droid.wifiP2pClose()
+ utils.set_location_service(ad, True)
aware_avail = ad.droid.wifiIsAwareAvailable()
if not aware_avail:
self.log.info('Aware not available. Waiting ...')
@@ -52,7 +54,11 @@
self.reset_device_parameters(ad)
self.reset_device_statistics(ad)
self.set_power_mode_parameters(ad)
-
+ ad.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+ autils.configure_ndp_allow_any_override(ad, True)
+ # set randomization interval to 0 (disable) to reduce likelihood of
+ # interference in tests
+ autils.configure_mac_random_interval(ad, 0)
def teardown_test(self):
for ad in self.android_devices:
@@ -85,9 +91,9 @@
"""Set the power configuration DW parameters for the device based on any
configuration overrides (if provided)"""
if self.aware_default_power_mode == "INTERACTIVE":
- autils.config_dw_high_power(ad)
+ autils.config_settings_high_power(ad)
elif self.aware_default_power_mode == "NON_INTERACTIVE":
- autils.config_dw_low_power(ad)
+ autils.config_settings_low_power(ad)
else:
asserts.assert_false(
"The 'aware_default_power_mode' configuration must be INTERACTIVE or "
@@ -102,3 +108,8 @@
"""
self.msg_id = self.msg_id + 1
return self.msg_id
+
+ def on_fail(self, test_name, begin_time):
+ for ad in self.android_devices:
+ ad.take_bug_report(test_name, begin_time)
+ ad.cat_adb_log(test_name, begin_time)
diff --git a/acts/framework/acts/test_utils/wifi/aware/aware_const.py b/acts/framework/acts/test_utils/wifi/aware/aware_const.py
index a7574a4..2f1d3ed 100644
--- a/acts/framework/acts/test_utils/wifi/aware/aware_const.py
+++ b/acts/framework/acts/test_utils/wifi/aware/aware_const.py
@@ -15,14 +15,21 @@
# limitations under the License.
######################################################
-# Aware DW (Discovery Window) power mode values
+# Aware power settings values for interactive (high power) and
+# non-interactive (low power) modes
######################################################
-DW_24_INTERACTIVE = 1
-DW_5_INTERACTIVE = 1
+POWER_DW_24_INTERACTIVE = 1
+POWER_DW_5_INTERACTIVE = 1
+POWER_DISC_BEACON_INTERVAL_INTERACTIVE = 0
+POWER_NUM_SS_IN_DISC_INTERACTIVE = 0
+POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE = 0
-DW_24_NON_INTERACTIVE = 4
-DW_5_NON_INTERACTIVE = 0
+POWER_DW_24_NON_INTERACTIVE = 4
+POWER_DW_5_NON_INTERACTIVE = 0
+POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE = 0
+POWER_NUM_SS_IN_DISC_NON_INTERACTIVE = 0
+POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE = 0
######################################################
# Broadcast events
@@ -51,6 +58,9 @@
DISCOVERY_KEY_DISCOVERY_TYPE = "DiscoveryType"
DISCOVERY_KEY_TTL = "TtlSec"
DISCOVERY_KEY_TERM_CB_ENABLED = "TerminateNotificationEnabled"
+DISCOVERY_KEY_RANGING_ENABLED = "RangingEnabled"
+DISCOVERY_KEY_MIN_DISTANCE_MM = "MinDistanceMm"
+DISCOVERY_KEY_MAX_DISTANCE_MM = "MaxDistanceMm"
PUBLISH_TYPE_UNSOLICITED = 0
PUBLISH_TYPE_SOLICITED = 1
@@ -90,7 +100,7 @@
# WifiAwareDiscoverySessionCallback events keys
SESSION_CB_KEY_CB_ID = "callbackId"
-SESSION_CB_KEY_SESSION_ID = "sessionId"
+SESSION_CB_KEY_SESSION_ID = "discoverySessionId"
SESSION_CB_KEY_REASON = "reason"
SESSION_CB_KEY_PEER_ID = "peerId"
SESSION_CB_KEY_SERVICE_SPECIFIC_INFO = "serviceSpecificInfo"
@@ -101,6 +111,7 @@
SESSION_CB_KEY_MESSAGE_AS_STRING = "messageAsString"
SESSION_CB_KEY_LATENCY_MS = "latencyMs"
SESSION_CB_KEY_TIMESTAMP_MS = "timestampMs"
+SESSION_CB_KEY_DISTANCE_MM = "distanceMm"
######################################################
# WifiAwareRangingListener events (RttManager.RttListener)
diff --git a/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py b/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py
index 4438064..eada097 100644
--- a/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py
@@ -310,21 +310,78 @@
data_min = min(data)
data_max = max(data)
data_mean = statistics.mean(data)
+ data_cdf = extract_cdf(data)
+ data_cdf_decile = extract_cdf_decile(data_cdf)
results['%smin' % key_prefix] = data_min
results['%smax' % key_prefix] = data_max
results['%smean' % key_prefix] = data_mean
+ results['%scdf' % key_prefix] = data_cdf
+ results['%scdf_decile' % key_prefix] = data_cdf_decile
results['%sraw_data' % key_prefix] = data
if num_samples > 1:
data_stdev = statistics.stdev(data)
results['%sstdev' % key_prefix] = data_stdev
- ad.log.info('%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, stdev=%.2f',
- log_prefix, num_samples, data_min, data_max, data_mean,
- data_stdev)
+ ad.log.info(
+ '%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, stdev=%.2f, cdf_decile=%s',
+ log_prefix, num_samples, data_min, data_max, data_mean, data_stdev,
+ data_cdf_decile)
else:
- ad.log.info('%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f', log_prefix,
- num_samples, data_min, data_max, data_mean)
+ ad.log.info(
+ '%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, cdf_decile=%s',
+ log_prefix, num_samples, data_min, data_max, data_mean, data_cdf_decile)
+
+def extract_cdf_decile(cdf):
+ """Extracts the 10%, 20%, ..., 90% points from the CDF and returns their
+ value (a list of 9 values).
+
+ Since CDF may not (will not) have exact x% value picks the value >= x%.
+
+ Args:
+ cdf: a list of 2 lists, the X and Y of the CDF.
+ """
+ decades = []
+ next_decade = 10
+ for x, y in zip(cdf[0], cdf[1]):
+ while 100*y >= next_decade:
+ decades.append(x)
+ next_decade = next_decade + 10
+ if next_decade == 100:
+ break
+ return decades
+
+def extract_cdf(data):
+ """Calculates the Cumulative Distribution Function (CDF) of the data.
+
+ Args:
+ data: A list containing data (does not have to be sorted).
+
+ Returns: a list of 2 lists: the X and Y axis of the CDF.
+ """
+ x = []
+ cdf = []
+ if not data:
+ return (x, cdf)
+
+ all_values = sorted(data)
+ for val in all_values:
+ if not x:
+ x.append(val)
+ cdf.append(1)
+ else:
+ if x[-1] == val:
+ cdf[-1] += 1
+ else:
+ x.append(val)
+ cdf.append(cdf[-1] + 1)
+
+ scale = 1.0 / len(all_values)
+ for i in range(len(cdf)):
+ cdf[i] = cdf[i] * scale
+
+ return (x, cdf)
+
def get_mac_addr(device, interface):
"""Get the MAC address of the specified interface. Uses ifconfig and parses
@@ -392,74 +449,134 @@
return dut.droid.wifiAwareCreateNetworkSpecifierOob(
id, dev_type, peer_mac, None, sec)
-def configure_dw(device, is_default, is_24_band, value):
- """Use the command-line API to configure the DW (discovery window) setting
+def configure_power_setting(device, mode, name, value):
+ """Use the command-line API to configure the power setting
Args:
device: Device on which to perform configuration
- is_default: True for the default setting, False for the non-interactive
- setting
- is_24_band: True for 2.4GHz band, False for 5GHz band
- value: An integer 0 to 5
+ mode: The power mode being set, should be "default", "inactive", or "idle"
+ name: One of the power settings from 'wifiaware set-power'.
+ value: An integer.
"""
- variable = 'dw_%s_%sghz' % ('default' if is_default else 'on_inactive', '24'
- if is_24_band else '5')
- device.adb.shell("cmd wifiaware native_api set %s %d" % (variable, value))
+ device.adb.shell(
+ "cmd wifiaware native_api set-power %s %s %d" % (mode, name, value))
-def config_dw_high_power(device):
- """Configure device's discovery window (DW) values to high power mode -
+def configure_mac_random_interval(device, interval_sec):
+ """Use the command-line API to configure the MAC address randomization
+ interval.
+
+ Args:
+ device: Device on which to perform configuration
+ interval_sec: The MAC randomization interval in seconds. A value of 0
+ disables all randomization.
+ """
+ device.adb.shell(
+ "cmd wifiaware native_api set mac_random_interval_sec %d" % interval_sec)
+
+def configure_ndp_allow_any_override(device, override_api_check):
+ """Use the command-line API to configure whether an NDP Responder may be
+ configured to accept an NDP request from ANY peer.
+
+ By default the target API level of the requesting app determines whether such
+ configuration is permitted. This allows overriding the API check and allowing
+ it.
+
+ Args:
+ device: Device on which to perform configuration.
+ override_api_check: True to allow a Responder to ANY configuration, False to
+ perform the API level check.
+ """
+ device.adb.shell("cmd wifiaware state_mgr allow_ndp_any %s" % (
+ "true" if override_api_check else "false"))
+
+def config_settings_high_power(device):
+ """Configure device's power settings values to high power mode -
whether device is in interactive or non-interactive modes"""
- configure_dw(
- device, is_default=True, is_24_band=True, value=aconsts.DW_24_INTERACTIVE)
- configure_dw(
- device, is_default=True, is_24_band=False, value=aconsts.DW_5_INTERACTIVE)
- configure_dw(
- device,
- is_default=False,
- is_24_band=True,
- value=aconsts.DW_24_INTERACTIVE)
- configure_dw(
- device,
- is_default=False,
- is_24_band=False,
- value=aconsts.DW_5_INTERACTIVE)
+ configure_power_setting(device, "default", "dw_24ghz",
+ aconsts.POWER_DW_24_INTERACTIVE)
+ configure_power_setting(device, "default", "dw_5ghz",
+ aconsts.POWER_DW_5_INTERACTIVE)
+ configure_power_setting(device, "default", "disc_beacon_interval_ms",
+ aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE)
+ configure_power_setting(device, "default", "num_ss_in_discovery",
+ aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE)
+ configure_power_setting(device, "default", "enable_dw_early_term",
+ aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE)
-def config_dw_low_power(device):
- """Configure device's discovery window (DW) values to low power mode - whether
+ configure_power_setting(device, "inactive", "dw_24ghz",
+ aconsts.POWER_DW_24_INTERACTIVE)
+ configure_power_setting(device, "inactive", "dw_5ghz",
+ aconsts.POWER_DW_5_INTERACTIVE)
+ configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
+ aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE)
+ configure_power_setting(device, "inactive", "num_ss_in_discovery",
+ aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE)
+ configure_power_setting(device, "inactive", "enable_dw_early_term",
+ aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE)
+
+def config_settings_low_power(device):
+ """Configure device's power settings values to low power mode - whether
device is in interactive or non-interactive modes"""
- configure_dw(
- device,
- is_default=True,
- is_24_band=True,
- value=aconsts.DW_24_NON_INTERACTIVE)
- configure_dw(
- device,
- is_default=True,
- is_24_band=False,
- value=aconsts.DW_5_NON_INTERACTIVE)
- configure_dw(
- device,
- is_default=False,
- is_24_band=True,
- value=aconsts.DW_24_NON_INTERACTIVE)
- configure_dw(
- device,
- is_default=False,
- is_24_band=False,
- value=aconsts.DW_5_NON_INTERACTIVE)
+ configure_power_setting(device, "default", "dw_24ghz",
+ aconsts.POWER_DW_24_NON_INTERACTIVE)
+ configure_power_setting(device, "default", "dw_5ghz",
+ aconsts.POWER_DW_5_NON_INTERACTIVE)
+ configure_power_setting(device, "default", "disc_beacon_interval_ms",
+ aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE)
+ configure_power_setting(device, "default", "num_ss_in_discovery",
+ aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE)
+ configure_power_setting(device, "default", "enable_dw_early_term",
+ aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE)
-def config_dw_all_modes(device, dw_24ghz, dw_5ghz):
+ configure_power_setting(device, "inactive", "dw_24ghz",
+ aconsts.POWER_DW_24_NON_INTERACTIVE)
+ configure_power_setting(device, "inactive", "dw_5ghz",
+ aconsts.POWER_DW_5_NON_INTERACTIVE)
+ configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
+ aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE)
+ configure_power_setting(device, "inactive", "num_ss_in_discovery",
+ aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE)
+ configure_power_setting(device, "inactive", "enable_dw_early_term",
+ aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE)
+
+
+def config_power_settings(device, dw_24ghz, dw_5ghz, disc_beacon_interval=None,
+ num_ss_in_disc=None, enable_dw_early_term=None):
"""Configure device's discovery window (DW) values to the specified values -
whether the device is in interactive or non-interactive mode.
Args:
dw_24ghz: DW interval in the 2.4GHz band.
dw_5ghz: DW interval in the 5GHz band.
+ disc_beacon_interval: The discovery beacon interval (in ms). If None then
+ not set.
+ num_ss_in_disc: Number of spatial streams to use for discovery. If None then
+ not set.
+ enable_dw_early_term: If True then enable early termination of the DW. If
+ None then not set.
"""
- configure_dw(device, is_default=True, is_24_band=True, value=dw_24ghz)
- configure_dw(device, is_default=True, is_24_band=False, value=dw_5ghz)
- configure_dw(device, is_default=False, is_24_band=True, value=dw_24ghz)
- configure_dw(device, is_default=False, is_24_band=False, value=dw_5ghz)
+ configure_power_setting(device, "default", "dw_24ghz", dw_24ghz)
+ configure_power_setting(device, "default", "dw_5ghz", dw_5ghz)
+ configure_power_setting(device, "inactive", "dw_24ghz", dw_24ghz)
+ configure_power_setting(device, "inactive", "dw_5ghz", dw_5ghz)
+
+ if disc_beacon_interval is not None:
+ configure_power_setting(device, "default", "disc_beacon_interval_ms",
+ disc_beacon_interval)
+ configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
+ disc_beacon_interval)
+
+ if num_ss_in_disc is not None:
+ configure_power_setting(device, "default", "num_ss_in_discovery",
+ num_ss_in_disc)
+ configure_power_setting(device, "inactive", "num_ss_in_discovery",
+ num_ss_in_disc)
+
+ if enable_dw_early_term is not None:
+ configure_power_setting(device, "default", "enable_dw_early_term",
+ enable_dw_early_term)
+ configure_power_setting(device, "inactive", "enable_dw_early_term",
+ enable_dw_early_term)
def create_discovery_config(service_name,
d_type,
@@ -495,6 +612,36 @@
config[aconsts.DISCOVERY_KEY_TERM_CB_ENABLED] = term_cb_enable
return config
+def add_ranging_to_pub(p_config, enable_ranging):
+ """Add ranging enabled configuration to a publish configuration (only relevant
+ for publish configuration).
+
+ Args:
+ p_config: The Publish discovery configuration.
+ enable_ranging: True to enable ranging, False to disable.
+ Returns:
+ The modified publish configuration.
+ """
+ p_config[aconsts.DISCOVERY_KEY_RANGING_ENABLED] = enable_ranging
+ return p_config
+
+def add_ranging_to_sub(s_config, min_distance_mm, max_distance_mm):
+ """Add ranging distance configuration to a subscribe configuration (only
+ relevant to a subscribe configuration).
+
+ Args:
+ s_config: The Subscribe discovery configuration.
+ min_distance_mm, max_distance_mm: The min and max distance specification.
+ Used if not None.
+ Returns:
+ The modified subscribe configuration.
+ """
+ if min_distance_mm is not None:
+ s_config[aconsts.DISCOVERY_KEY_MIN_DISTANCE_MM] = min_distance_mm
+ if max_distance_mm is not None:
+ s_config[aconsts.DISCOVERY_KEY_MAX_DISTANCE_MM] = max_distance_mm
+ return s_config
+
def attach_with_identity(dut):
"""Start an Aware session (attach) and wait for confirmation and identity
information (mac address).
diff --git a/acts/framework/acts/test_utils/wifi/rtt/RttBaseTest.py b/acts/framework/acts/test_utils/wifi/rtt/RttBaseTest.py
new file mode 100644
index 0000000..2182780
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/RttBaseTest.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - Google
+#
+# 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.
+
+from acts import asserts
+from acts import utils
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+
+
+class RttBaseTest(BaseTestClass):
+
+ def __init__(self, controllers):
+ super(RttBaseTest, self).__init__(controllers)
+
+ def setup_test(self):
+ required_params = ("lci_reference", "lcr_reference",
+ "rtt_reference_distance_mm",
+ "stress_test_min_iteration_count",
+ "stress_test_target_run_time_sec")
+ self.unpack_userparams(required_params)
+
+ # can be moved to JSON config file
+ self.rtt_reference_distance_margin_mm = 1000
+ self.rtt_max_failure_rate_two_sided_rtt_percentage = 10
+ self.rtt_max_failure_rate_one_sided_rtt_percentage = 50
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage = 10
+ self.rtt_max_margin_exceeded_rate_one_sided_rtt_percentage = 50
+ self.rtt_min_expected_rssi_dbm = -100
+
+ for ad in self.android_devices:
+ utils.set_location_service(ad, True)
+ asserts.skip_if(
+ not ad.droid.doesDeviceSupportWifiRttFeature(),
+ "Device under test does not support Wi-Fi RTT - skipping test")
+ wutils.wifi_toggle_state(ad, True)
+ rtt_avail = ad.droid.wifiIsRttAvailable()
+ if not rtt_avail:
+ self.log.info('RTT not available. Waiting ...')
+ rutils.wait_for_event(ad, rconsts.BROADCAST_WIFI_RTT_AVAILABLE)
+ ad.ed.clear_all_events()
+ rutils.config_privilege_override(ad, False)
+ ad.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+
+ def teardown_test(self):
+ for ad in self.android_devices:
+ if not ad.droid.doesDeviceSupportWifiRttFeature():
+ return
+
+ # clean-up queue from the System Service UID
+ ad.droid.wifiRttCancelRanging([1000])
+
+ def on_fail(self, test_name, begin_time):
+ for ad in self.android_devices:
+ ad.take_bug_report(test_name, begin_time)
+ ad.cat_adb_log(test_name, begin_time)
diff --git a/acts/framework/acts/test_utils/wifi/rtt/__init__.py b/acts/framework/acts/test_utils/wifi/rtt/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/__init__.py
diff --git a/acts/framework/acts/test_utils/wifi/rtt/rtt_const.py b/acts/framework/acts/test_utils/wifi/rtt/rtt_const.py
new file mode 100644
index 0000000..ddf29e5
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/rtt_const.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - Google
+#
+# 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.
+
+######################################################
+# Broadcast events
+######################################################
+BROADCAST_WIFI_RTT_AVAILABLE = "WifiRttAvailable"
+BROADCAST_WIFI_RTT_NOT_AVAILABLE = "WifiRttNotAvailable"
+
+######################################################
+# RangingResultCallback events
+######################################################
+EVENT_CB_RANGING_ON_FAIL = "WifiRttRangingFailure"
+EVENT_CB_RANGING_ON_RESULT = "WifiRttRangingResults"
+
+EVENT_CB_RANGING_KEY_RESULTS = "Results"
+
+EVENT_CB_RANGING_KEY_STATUS = "status"
+EVENT_CB_RANGING_KEY_DISTANCE_MM = "distanceMm"
+EVENT_CB_RANGING_KEY_DISTANCE_STD_DEV_MM = "distanceStdDevMm"
+EVENT_CB_RANGING_KEY_RSSI = "rssi"
+EVENT_CB_RANGING_KEY_NUM_ATTEMPTED_MEASUREMENTS = "numAttemptedMeasurements"
+EVENT_CB_RANGING_KEY_NUM_SUCCESSFUL_MEASUREMENTS = "numSuccessfulMeasurements"
+EVENT_CB_RANGING_KEY_LCI = "lci"
+EVENT_CB_RANGING_KEY_LCR = "lcr"
+EVENT_CB_RANGING_KEY_TIMESTAMP = "timestamp"
+EVENT_CB_RANGING_KEY_MAC = "mac"
+EVENT_CB_RANGING_KEY_PEER_ID = "peerId"
+EVENT_CB_RANGING_KEY_MAC_AS_STRING = "macAsString"
+
+EVENT_CB_RANGING_STATUS_SUCCESS = 0
+EVENT_CB_RANGING_STATUS_FAIL = 1
+EVENT_CB_RANGING_STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2
+
+######################################################
+# status codes
+######################################################
+
+RANGING_FAIL_CODE_GENERIC = 1
+RANGING_FAIL_CODE_RTT_NOT_AVAILABLE = 2
+
+######################################################
+# ScanResults keys
+######################################################
+
+SCAN_RESULT_KEY_RTT_RESPONDER = "is80211McRTTResponder"
\ No newline at end of file
diff --git a/acts/framework/acts/test_utils/wifi/rtt/rtt_test_utils.py b/acts/framework/acts/test_utils/wifi/rtt/rtt_test_utils.py
new file mode 100644
index 0000000..c24b406
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/rtt_test_utils.py
@@ -0,0 +1,463 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - Google
+#
+# 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.
+
+import queue
+import statistics
+import time
+
+from acts import asserts
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+
+# arbitrary timeout for events
+EVENT_TIMEOUT = 10
+
+
+def decorate_event(event_name, id):
+ return '%s_%d' % (event_name, id)
+
+
+def wait_for_event(ad, event_name, timeout=EVENT_TIMEOUT):
+ """Wait for the specified event or timeout.
+
+ Args:
+ ad: The android device
+ event_name: The event to wait on
+ timeout: Number of seconds to wait
+ Returns:
+ The event (if available)
+ """
+ prefix = ''
+ if hasattr(ad, 'pretty_name'):
+ prefix = '[%s] ' % ad.pretty_name
+ try:
+ event = ad.ed.pop_event(event_name, timeout)
+ ad.log.info('%s%s: %s', prefix, event_name, event['data'])
+ return event
+ except queue.Empty:
+ ad.log.info('%sTimed out while waiting for %s', prefix, event_name)
+ asserts.fail(event_name)
+
+def fail_on_event(ad, event_name, timeout=EVENT_TIMEOUT):
+ """Wait for a timeout period and looks for the specified event - fails if it
+ is observed.
+
+ Args:
+ ad: The android device
+ event_name: The event to wait for (and fail on its appearance)
+ """
+ prefix = ''
+ if hasattr(ad, 'pretty_name'):
+ prefix = '[%s] ' % ad.pretty_name
+ try:
+ event = ad.ed.pop_event(event_name, timeout)
+ ad.log.info('%sReceived unwanted %s: %s', prefix, event_name, event['data'])
+ asserts.fail(event_name, extras=event)
+ except queue.Empty:
+ ad.log.info('%s%s not seen (as expected)', prefix, event_name)
+ return
+
+
+def config_privilege_override(dut, override_to_no_privilege):
+ """Configure the device to override the permission check and to disallow any
+ privileged RTT operations, e.g. disallow one-sided RTT to Responders (APs)
+ which do not support IEEE 802.11mc.
+
+ Args:
+ dut: Device to configure.
+ override_to_no_privilege: True to indicate no privileged ops, False for
+ default (which will allow privileged ops).
+ """
+ dut.adb.shell("cmd wifirtt set override_assume_no_privilege %d" % (
+ 1 if override_to_no_privilege else 0))
+
+
+def get_rtt_constrained_results(scanned_networks, support_rtt):
+ """Filter the input list and only return those networks which either support
+ or do not support RTT (IEEE 802.11mc.)
+
+ Args:
+ scanned_networks: A list of networks from scan results.
+ support_rtt: True - only return those APs which support RTT, False - only
+ return those APs which do not support RTT.
+
+ Returns: a sub-set of the scanned_networks per support_rtt constraint.
+ """
+ matching_networks = []
+ for network in scanned_networks:
+ if support_rtt:
+ if (rconsts.SCAN_RESULT_KEY_RTT_RESPONDER in network and
+ network[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER]):
+ matching_networks.append(network)
+ else:
+ if (rconsts.SCAN_RESULT_KEY_RTT_RESPONDER not in network or
+ not network[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER]):
+ matching_networks.append(network)
+
+ return matching_networks
+
+
+def scan_networks(dut):
+ """Perform a scan and return scan results.
+
+ Args:
+ dut: Device under test.
+
+ Returns: an array of scan results.
+ """
+ wutils.start_wifi_connection_scan(dut)
+ return dut.droid.wifiGetScanResults()
+
+
+def scan_with_rtt_support_constraint(dut, support_rtt, repeat=0):
+ """Perform a scan and return scan results of APs: only those that support or
+ do not support RTT (IEEE 802.11mc) - per the support_rtt parameter.
+
+ Args:
+ dut: Device under test.
+ support_rtt: True - only return those APs which support RTT, False - only
+ return those APs which do not support RTT.
+ repeat: Re-scan this many times to find an RTT supporting network.
+
+ Returns: an array of scan results.
+ """
+ for i in range(repeat + 1):
+ scan_results = scan_networks(dut)
+ aps = get_rtt_constrained_results(scan_results, support_rtt)
+ if len(aps) != 0:
+ return aps
+
+ return []
+
+
+def select_best_scan_results(scans, select_count, lowest_rssi=-80):
+ """Select the strongest 'select_count' scans in the input list based on
+ highest RSSI. Exclude all very weak signals, even if results in a shorter
+ list.
+
+ Args:
+ scans: List of scan results.
+ select_count: An integer specifying how many scans to return at most.
+ lowest_rssi: The lowest RSSI to accept into the output.
+ Returns: a list of the strongest 'select_count' scan results from the scans
+ list.
+ """
+ def takeRssi(element):
+ return element['level']
+
+ result = []
+ scans.sort(key=takeRssi, reverse=True)
+ for scan in scans:
+ if len(result) == select_count:
+ break
+ if scan['level'] < lowest_rssi:
+ break # rest are lower since we're sorted
+ result.append(scan)
+
+ return result
+
+
+def validate_ap_result(scan_result, range_result):
+ """Validate the range results:
+ - Successful if AP (per scan result) support 802.11mc (allowed to fail
+ otherwise)
+ - MAC of result matches the BSSID
+
+ Args:
+ scan_result: Scan result for the AP
+ range_result: Range result returned by the RTT API
+ """
+ asserts.assert_equal(scan_result[wutils.WifiEnums.BSSID_KEY], range_result[
+ rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING_BSSID], 'MAC/BSSID mismatch')
+ if (rconsts.SCAN_RESULT_KEY_RTT_RESPONDER in scan_result and
+ scan_result[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER]):
+ asserts.assert_true(range_result[rconsts.EVENT_CB_RANGING_KEY_STATUS] ==
+ rconsts.EVENT_CB_RANGING_STATUS_SUCCESS,
+ 'Ranging failed for an AP which supports 802.11mc!')
+
+
+def validate_ap_results(scan_results, range_results):
+ """Validate an array of ranging results against the scan results used to
+ trigger the range. The assumption is that the results are returned in the
+ same order as the request (which were the scan results).
+
+ Args:
+ scan_results: Scans results used to trigger the range request
+ range_results: Range results returned by the RTT API
+ """
+ asserts.assert_equal(
+ len(scan_results),
+ len(range_results),
+ 'Mismatch in length of scan results and range results')
+
+ # sort first based on BSSID/MAC
+ scan_results.sort(key=lambda x: x[wutils.WifiEnums.BSSID_KEY])
+ range_results.sort(
+ key=lambda x: x[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING_BSSID])
+
+ for i in range(len(scan_results)):
+ validate_ap_result(scan_results[i], range_results[i])
+
+
+def validate_aware_mac_result(range_result, mac, description):
+ """Validate the range result for an Aware peer specified with a MAC address:
+ - Correct MAC address.
+
+ The MAC addresses may contain ":" (which are ignored for the comparison) and
+ may be in any case (which is ignored for the comparison).
+
+ Args:
+ range_result: Range result returned by the RTT API
+ mac: MAC address of the peer
+ description: Additional content to print on failure
+ """
+ mac1 = mac.replace(':', '').lower()
+ mac2 = range_result[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING].replace(':',
+ '').lower()
+ asserts.assert_equal(mac1, mac2,
+ '%s: MAC mismatch' % description)
+
+def validate_aware_peer_id_result(range_result, peer_id, description):
+ """Validate the range result for An Aware peer specified with a Peer ID:
+ - Correct Peer ID
+ - MAC address information not available
+
+ Args:
+ range_result: Range result returned by the RTT API
+ peer_id: Peer ID of the peer
+ description: Additional content to print on failure
+ """
+ asserts.assert_equal(peer_id,
+ range_result[rconsts.EVENT_CB_RANGING_KEY_PEER_ID],
+ '%s: Peer Id mismatch' % description)
+ asserts.assert_false(rconsts.EVENT_CB_RANGING_KEY_MAC in range_result,
+ '%s: MAC Address not empty!' % description)
+
+
+def extract_stats(results, range_reference_mm, range_margin_mm, min_rssi,
+ reference_lci=[], reference_lcr=[], summary_only=False):
+ """Extract statistics from a list of RTT results. Returns a dictionary
+ with results:
+ - num_results (success or fails)
+ - num_success_results
+ - num_no_results (e.g. timeout)
+ - num_failures
+ - num_range_out_of_margin (only for successes)
+ - num_invalid_rssi (only for successes)
+ - distances: extracted list of distances
+ - distance_std_devs: extracted list of distance standard-deviations
+ - rssis: extracted list of RSSI
+ - distance_mean
+ - distance_std_dev (based on distance - ignoring the individual std-devs)
+ - rssi_mean
+ - rssi_std_dev
+ - status_codes
+ - lcis: extracted list of all of the individual LCI
+ - lcrs: extracted list of all of the individual LCR
+ - any_lci_mismatch: True/False - checks if all LCI results are identical to
+ the reference LCI.
+ - any_lcr_mismatch: True/False - checks if all LCR results are identical to
+ the reference LCR.
+ - num_attempted_measurements: extracted list of all of the individual
+ number of attempted measurements.
+ - num_successful_measurements: extracted list of all of the individual
+ number of successful measurements.
+ - invalid_num_attempted: True/False - checks if number of attempted
+ measurements is non-zero for successful results.
+ - invalid_num_successful: True/False - checks if number of successful
+ measurements is non-zero for successful results.
+
+ Args:
+ results: List of RTT results.
+ range_reference_mm: Reference value for the distance (in mm)
+ range_margin_mm: Acceptable absolute margin for distance (in mm)
+ min_rssi: Acceptable minimum RSSI value.
+ reference_lci, reference_lcr: Reference values for LCI and LCR.
+ summary_only: Only include summary keys (reduce size).
+
+ Returns: A dictionary of stats.
+ """
+ stats = {}
+ stats['num_results'] = 0
+ stats['num_success_results'] = 0
+ stats['num_no_results'] = 0
+ stats['num_failures'] = 0
+ stats['num_range_out_of_margin'] = 0
+ stats['num_invalid_rssi'] = 0
+ stats['any_lci_mismatch'] = False
+ stats['any_lcr_mismatch'] = False
+ stats['invalid_num_attempted'] = False
+ stats['invalid_num_successful'] = False
+
+ range_max_mm = range_reference_mm + range_margin_mm
+ range_min_mm = range_reference_mm - range_margin_mm
+
+ distances = []
+ distance_std_devs = []
+ rssis = []
+ num_attempted_measurements = []
+ num_successful_measurements = []
+ status_codes = []
+ lcis = []
+ lcrs = []
+
+ for i in range(len(results)):
+ result = results[i]
+
+ if result is None: # None -> timeout waiting for RTT result
+ stats['num_no_results'] = stats['num_no_results'] + 1
+ continue
+ stats['num_results'] = stats['num_results'] + 1
+
+ status_codes.append(result[rconsts.EVENT_CB_RANGING_KEY_STATUS])
+ if status_codes[-1] != rconsts.EVENT_CB_RANGING_STATUS_SUCCESS:
+ stats['num_failures'] = stats['num_failures'] + 1
+ continue
+ stats['num_success_results'] = stats['num_success_results'] + 1
+
+ distance_mm = result[rconsts.EVENT_CB_RANGING_KEY_DISTANCE_MM]
+ distances.append(distance_mm)
+ if not range_min_mm <= distance_mm <= range_max_mm:
+ stats['num_range_out_of_margin'] = stats['num_range_out_of_margin'] + 1
+ distance_std_devs.append(
+ result[rconsts.EVENT_CB_RANGING_KEY_DISTANCE_STD_DEV_MM])
+
+ rssi = result[rconsts.EVENT_CB_RANGING_KEY_RSSI]
+ rssis.append(rssi)
+ if not min_rssi <= rssi <= 0:
+ stats['num_invalid_rssi'] = stats['num_invalid_rssi'] + 1
+
+ num_attempted = result[
+ rconsts.EVENT_CB_RANGING_KEY_NUM_ATTEMPTED_MEASUREMENTS]
+ num_attempted_measurements.append(num_attempted)
+ if num_attempted == 0:
+ stats['invalid_num_attempted'] = True
+
+ num_successful = result[
+ rconsts.EVENT_CB_RANGING_KEY_NUM_SUCCESSFUL_MEASUREMENTS]
+ num_successful_measurements.append(num_successful)
+ if num_successful == 0:
+ stats['invalid_num_successful'] = True
+
+ lcis.append(result[rconsts.EVENT_CB_RANGING_KEY_LCI])
+ if (result[rconsts.EVENT_CB_RANGING_KEY_LCI] != reference_lci):
+ stats['any_lci_mismatch'] = True
+ lcrs.append(result[rconsts.EVENT_CB_RANGING_KEY_LCR])
+ if (result[rconsts.EVENT_CB_RANGING_KEY_LCR] != reference_lcr):
+ stats['any_lcr_mismatch'] = True
+
+ if len(distances) > 0:
+ stats['distance_mean'] = statistics.mean(distances)
+ if len(distances) > 1:
+ stats['distance_std_dev'] = statistics.stdev(distances)
+ if len(rssis) > 0:
+ stats['rssi_mean'] = statistics.mean(rssis)
+ if len(rssis) > 1:
+ stats['rssi_std_dev'] = statistics.stdev(rssis)
+ if not summary_only:
+ stats['distances'] = distances
+ stats['distance_std_devs'] = distance_std_devs
+ stats['rssis'] = rssis
+ stats['num_attempted_measurements'] = num_attempted_measurements
+ stats['num_successful_measurements'] = num_successful_measurements
+ stats['status_codes'] = status_codes
+ stats['lcis'] = lcis
+ stats['lcrs'] = lcrs
+
+ return stats
+
+
+def run_ranging(dut, aps, iter_count, time_between_iterations,
+ target_run_time_sec=0):
+ """Executing ranging to the set of APs.
+
+ Will execute a minimum of 'iter_count' iterations. Will continue to run
+ until execution time (just) exceeds 'target_run_time_sec'.
+
+ Args:
+ dut: Device under test
+ aps: A list of APs (Access Points) to range to.
+ iter_count: (Minimum) Number of measurements to perform.
+ time_between_iterations: Number of seconds to wait between iterations.
+ target_run_time_sec: The target run time in seconds.
+
+ Returns: a list of the events containing the RTT results (or None for a
+ failed measurement).
+ """
+ max_peers = dut.droid.wifiRttMaxPeersInRequest()
+
+ asserts.assert_true(len(aps) > 0, "Need at least one AP!")
+ if len(aps) > max_peers:
+ aps = aps[0:max_peers]
+
+ events = {} # need to keep track per BSSID!
+ for ap in aps:
+ events[ap["BSSID"]] = []
+
+ start_clock = time.time()
+ iterations_done = 0
+ run_time = 0
+ while iterations_done < iter_count or (
+ target_run_time_sec != 0 and run_time < target_run_time_sec):
+ if iterations_done != 0 and time_between_iterations != 0:
+ time.sleep(time_between_iterations)
+
+ id = dut.droid.wifiRttStartRangingToAccessPoints(aps)
+ try:
+ event = dut.ed.pop_event(
+ decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT, id), EVENT_TIMEOUT)
+ range_results = event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS]
+ asserts.assert_equal(
+ len(aps),
+ len(range_results),
+ 'Mismatch in length of scan results and range results')
+ for result in range_results:
+ bssid = result[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING]
+ asserts.assert_true(bssid in events,
+ "Result BSSID %s not in requested AP!?" % bssid)
+ asserts.assert_equal(len(events[bssid]), iterations_done,
+ "Duplicate results for BSSID %s!?" % bssid)
+ events[bssid].append(result)
+ except queue.Empty:
+ for ap in aps:
+ events[ap["BSSID"]].append(None)
+
+ iterations_done = iterations_done + 1
+ run_time = time.time() - start_clock
+
+ return events
+
+
+def analyze_results(all_aps_events, rtt_reference_distance_mm,
+ distance_margin_mm, min_expected_rssi, lci_reference, lcr_reference,
+ summary_only=False):
+ """Verifies the results of the RTT experiment.
+
+ Args:
+ all_aps_events: Dictionary of APs, each a list of RTT result events.
+ rtt_reference_distance_mm: Expected distance to the AP (source of truth).
+ distance_margin_mm: Accepted error marging in distance measurement.
+ min_expected_rssi: Minimum acceptable RSSI value
+ lci_reference, lcr_reference: Expected LCI/LCR values (arrays of bytes).
+ summary_only: Only include summary keys (reduce size).
+ """
+ all_stats = {}
+ for bssid, events in all_aps_events.items():
+ stats = extract_stats(events, rtt_reference_distance_mm,
+ distance_margin_mm, min_expected_rssi,
+ lci_reference, lcr_reference, summary_only)
+ all_stats[bssid] = stats
+ return all_stats
diff --git a/acts/framework/acts/test_utils/wifi/wifi_constants.py b/acts/framework/acts/test_utils/wifi/wifi_constants.py
index 1ded7db..9494057 100644
--- a/acts/framework/acts/test_utils/wifi/wifi_constants.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_constants.py
@@ -18,6 +18,7 @@
WIFI_CONNECTED = "WifiNetworkConnected"
WIFI_DISCONNECTED = "WifiNetworkDisconnected"
SUPPLICANT_CON_CHANGED = "SupplicantConnectionChanged"
+WIFI_STATE_CHANGED = "WifiStateChanged"
WIFI_FORGET_NW_SUCCESS = "WifiManagerForgetNetworkOnSuccess"
# These constants will be used by the ACTS wifi tests.
diff --git a/acts/framework/acts/test_utils/wifi/wifi_power_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_power_test_utils.py
index 7ed63cd..b7b9fe8 100644
--- a/acts/framework/acts/test_utils/wifi/wifi_power_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_power_test_utils.py
@@ -14,12 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import acts
-import json
import logging
-import os
import time
-from acts import asserts
from acts import utils
from acts.controllers import monsoon
from acts.libs.proc import job
@@ -32,256 +28,15 @@
from bokeh.plotting import figure, output_file, save
from acts.controllers.ap_lib import hostapd_security
from acts.controllers.ap_lib import hostapd_ap_preset
-from acts.test_utils.bt.bt_test_utils import enable_bluetooth
-from acts.test_utils.bt.bt_test_utils import disable_bluetooth
# http://www.secdev.org/projects/scapy/
# On ubuntu, sudo pip3 install scapy-python3
import scapy.all as scapy
-SETTINGS_PAGE = "am start -n com.android.settings/.Settings"
-SCROLL_BOTTOM = "input swipe 0 2000 0 0"
-UNLOCK_SCREEN = "input keyevent 82"
-SCREENON_USB_DISABLE = "dumpsys battery unplug"
-RESET_BATTERY_STATS = "dumpsys batterystats --reset"
-AOD_OFF = "settings put secure doze_always_on 0"
-MUSIC_IQ_OFF = "pm disable-user com.google.intelligence.sense"
-# Command to disable gestures
-LIFT = "settings put secure doze_pulse_on_pick_up 0"
-DOUBLE_TAP = "settings put secure doze_pulse_on_double_tap 0"
-JUMP_TO_CAMERA = "settings put secure camera_double_tap_power_gesture_disabled 1"
-RAISE_TO_CAMERA = "settings put secure camera_lift_trigger_enabled 0"
-FLIP_CAMERA = "settings put secure camera_double_twist_to_flip_enabled 0"
-ASSIST_GESTURE = "settings put secure assist_gesture_enabled 0"
-ASSIST_GESTURE_ALERT = "settings put secure assist_gesture_silence_alerts_enabled 0"
-ASSIST_GESTURE_WAKE = "settings put secure assist_gesture_wake_enabled 0"
-SYSTEM_NAVI = "settings put secure system_navigation_keys_enabled 0"
-# End of command to disable gestures
-AUTO_TIME_OFF = "settings put global auto_time 0"
-AUTO_TIMEZONE_OFF = "settings put global auto_time_zone 0"
-FORCE_YOUTUBE_STOP = "am force-stop com.google.android.youtube"
-FORCE_DIALER_STOP = "am force-stop com.google.android.dialer"
-IPERF_TIMEOUT = 180
-THRESHOLD_TOLERANCE = 0.2
GET_FROM_PHONE = 'get_from_dut'
GET_FROM_AP = 'get_from_ap'
-PHONE_BATTERY_VOLTAGE = 4.2
-MONSOON_MAX_CURRENT = 8.0
-MONSOON_RETRY_INTERVAL = 300
-MEASUREMENT_RETRY_COUNT = 3
-RECOVER_MONSOON_RETRY_COUNT = 3
-MIN_PERCENT_SAMPLE = 95
-
-
-def dut_rockbottom(ad):
- """Set the phone into Rock-bottom state.
-
- Args:
- ad: the target android device, AndroidDevice object
-
- """
- ad.log.info("Now set the device to Rockbottom State")
- utils.require_sl4a((ad, ))
- ad.droid.connectivityToggleAirplaneMode(False)
- time.sleep(5)
- ad.droid.connectivityToggleAirplaneMode(True)
- utils.set_ambient_display(ad, False)
- utils.set_auto_rotate(ad, False)
- utils.set_adaptive_brightness(ad, False)
- utils.sync_device_time(ad)
- utils.set_location_service(ad, False)
- utils.set_mobile_data_always_on(ad, False)
- utils.disable_doze_light(ad)
- utils.disable_doze(ad)
- wutils.reset_wifi(ad)
- wutils.wifi_toggle_state(ad, False)
- try:
- ad.droid.nfcDisable()
- except acts.controllers.sl4a_lib.rpc_client.Sl4aApiError:
- ad.log.info('NFC is not available')
- ad.droid.setScreenBrightness(0)
- ad.adb.shell(AOD_OFF)
- ad.droid.setScreenTimeout(2200)
- ad.droid.wakeUpNow()
- ad.adb.shell(LIFT)
- ad.adb.shell(DOUBLE_TAP)
- ad.adb.shell(JUMP_TO_CAMERA)
- ad.adb.shell(RAISE_TO_CAMERA)
- ad.adb.shell(FLIP_CAMERA)
- ad.adb.shell(ASSIST_GESTURE)
- ad.adb.shell(ASSIST_GESTURE_ALERT)
- ad.adb.shell(ASSIST_GESTURE_WAKE)
- ad.adb.shell(SCREENON_USB_DISABLE)
- ad.adb.shell(UNLOCK_SCREEN)
- ad.adb.shell(SETTINGS_PAGE)
- ad.adb.shell(SCROLL_BOTTOM)
- ad.adb.shell(MUSIC_IQ_OFF)
- ad.adb.shell(AUTO_TIME_OFF)
- ad.adb.shell(AUTO_TIMEZONE_OFF)
- ad.adb.shell(FORCE_YOUTUBE_STOP)
- ad.adb.shell(FORCE_DIALER_STOP)
- ad.droid.wakeUpNow()
- ad.log.info('Device has been set to Rockbottom state')
- ad.log.info('Screen is ON')
-
-
-def unpack_custom_file(file, test_class=None):
- """Unpack the pass_fail_thresholds from a common file.
-
- Args:
- file: the common file containing pass fail threshold.
- """
- with open(file, 'r') as f:
- params = json.load(f)
- if test_class:
- return params[test_class]
- else:
- return params
-
-
-def pass_fail_check(test_class, test_result):
- """Check the test result and decide if it passed or failed.
- The threshold is provided in the config file
-
- Args:
- test_class: the specific test class where test is running
- test_result: the average current as the test result
- """
- test_name = test_class.current_test_name
- current_threshold = test_class.threshold[test_name]
- if test_result:
- asserts.assert_true(
- abs(test_result - current_threshold) / current_threshold <
- THRESHOLD_TOLERANCE,
- ("Measured average current in [%s]: %s, which is "
- "more than %d percent off than acceptable threshold %.2fmA") %
- (test_name, test_result, test_class.pass_fail_tolerance * 100,
- current_threshold))
- asserts.explicit_pass("Measurement finished for %s." % test_name)
- else:
- asserts.fail(
- "Something happened, measurement is not complete, test failed")
-
-
-def monsoon_recover(mon):
- """Test loop to wait for monsoon recover from unexpected error.
-
- Wait for a certain time duration, then quit.0
- Args:
- mon: monsoon object
- """
- try:
- mon.reconnect_monsoon()
- time.sleep(2)
- mon.usb('on')
- logging.info("Monsoon recovered from unexpected error")
- time.sleep(2)
- return True
- except monsoon.MonsoonError:
- logging.info(mon.mon.ser.in_waiting)
- logging.warning("Unable to recover monsoon from unexpected error")
- return False
-
-
-def monsoon_data_collect_save(ad, mon_info, test_name):
- """Current measurement and save the log file.
-
- Collect current data using Monsoon box and return the path of the
- log file. Take bug report if requested.
-
- Args:
- ad: the android device under test
- mon_info: dict with information of monsoon measurement, including
- monsoon device object, measurement frequency, duration and
- offset etc.
- test_name: current test name, used to contruct the result file name
- bug_report: indicator to take bug report or not, 0 or 1
- Returns:
- data_path: the absolute path to the log file of monsoon current
- measurement
- avg_current: the average current of the test
- """
-
- log = logging.getLogger()
- tag = (test_name + '_' + ad.model + '_' + ad.build_info['build_id'])
- data_path = os.path.join(mon_info['data_path'], "%s.txt" % tag)
- total_expected_samples = mon_info['freq'] * (
- mon_info['duration'] + mon_info['offset'])
- min_required_samples = total_expected_samples * MIN_PERCENT_SAMPLE / 100
- # Retry counter for monsoon data aquisition
- retry_measure = 1
- # Indicator that need to re-collect data
- need_collect_data = 1
- result = None
- while retry_measure <= MEASUREMENT_RETRY_COUNT:
- try:
- # If need to retake data
- if need_collect_data == 1:
- #Resets the battery status right before the test started
- ad.adb.shell(RESET_BATTERY_STATS)
- log.info(
- "Starting power measurement with monsoon box, try #{}".
- format(retry_measure))
- #Start the power measurement using monsoon
- mon_info['dut'].monsoon_usb_auto()
- result = mon_info['dut'].measure_power(
- mon_info['freq'],
- mon_info['duration'],
- tag=tag,
- offset=mon_info['offset'])
- mon_info['dut'].reconnect_dut()
- # Reconnect to dut
- else:
- mon_info['dut'].reconnect_dut()
- # Reconnect and return measurement results if no error happens
- avg_current = result.average_current
- monsoon.MonsoonData.save_to_text_file([result], data_path)
- log.info(
- "Power measurement done within {} try".format(retry_measure))
- return data_path, avg_current
- # Catch monsoon errors during measurement
- except monsoon.MonsoonError:
- log.info(mon_info['dut'].mon.ser.in_waiting)
- # Break early if it's one count away from limit
- if retry_measure == MEASUREMENT_RETRY_COUNT:
- log.error('Test failed after maximum measurement retry')
- break
-
- log.warning('Monsoon error happened, now try to recover')
- # Retry loop to recover monsoon from error
- retry_monsoon = 1
- while retry_monsoon <= RECOVER_MONSOON_RETRY_COUNT:
- mon_status = monsoon_recover(mon_info['dut'])
- if mon_status:
- break
- else:
- retry_monsoon += 1
- log.warning('Wait for {} second then try again'.format(
- MONSOON_RETRY_INTERVAL))
- time.sleep(MONSOON_RETRY_INTERVAL)
-
- # Break the loop to end test if failed to recover monsoon
- if not mon_status:
- log.error('Tried our best, still failed to recover monsoon')
- break
- else:
- # If there is no data or captured samples are less than min
- # required, re-take
- if not result:
- log.warning('No data taken, need to remeasure')
- elif len(result._data_points) <= min_required_samples:
- log.warning(
- 'More than {} percent of samples are missing due to monsoon error. Need to remeasure'.
- format(100 - MIN_PERCENT_SAMPLE))
- else:
- need_collect_data = 0
- log.warning(
- 'Data collected is valid, try reconnect to DUT to finish test'
- )
- retry_measure += 1
-
- if retry_measure > MEASUREMENT_RETRY_COUNT:
- log.error('Test failed after maximum measurement retry')
+ENABLED_MODULATED_DTIM = 'gEnableModulatedDTIM='
+MAX_MODULATED_DTIM = 'gMaxLIModulatedDTIM='
def monsoon_data_plot(mon_info, file_path, tag=""):
@@ -294,7 +49,7 @@
https://drive.google.com/open?id=0Bwp8Cq841VnpT2dGUUxLYWZvVjA
Args:
- mon_info: dict with information of monsoon measurement, including
+ mon_info: obj with information of monsoon measurement, including
monsoon device object, measurement frequency, duration and
offset etc.
file_path: the path to the monsoon log file with current data
@@ -316,7 +71,7 @@
voltage = results[0].voltage
[current_data.extend(x.data_points) for x in results]
[timestamps.extend(x.timestamps) for x in results]
- period = 1 / float(mon_info['freq'])
+ period = 1 / float(mon_info.freq)
time_relative = [x * period for x in range(len(current_data))]
#Calculate the average current for the test
current_data = [x * 1000 for x in current_data]
@@ -328,11 +83,11 @@
data=dict(x0=time_relative, y0=current_data, color=color))
s2 = ColumnDataSource(
data=dict(
- z0=[mon_info['duration']],
+ z0=[mon_info.duration],
y0=[round(avg_current, 2)],
x0=[round(avg_current * voltage, 2)],
- z1=[round(avg_current * voltage * mon_info['duration'], 2)],
- z2=[round(avg_current * mon_info['duration'], 2)]))
+ z1=[round(avg_current * voltage * mon_info.duration, 2)],
+ z2=[round(avg_current * mon_info.duration, 2)]))
#Setting up data table for the output
columns = [
TableColumn(field='z0', title='Total Duration (s)'),
@@ -345,7 +100,7 @@
source=s2, columns=columns, width=1300, height=60, editable=True)
plot_title = file_path[file_path.rfind('/') + 1:-4] + tag
- output_file("%s/%s.html" % (mon_info['data_path'], plot_title))
+ output_file("%s/%s.html" % (mon_info.data_path, plot_title))
TOOLS = ('box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save')
# Create a new plot with the datatable above
plot = figure(
@@ -415,52 +170,66 @@
gEnableModulatedDTIM: Modulated DTIM, int
gMaxLIModulatedDTIM: Maximum modulated DTIM, int
"""
- serial = ad.serial
- ini_file_phone = 'vendor/firmware/wlan/qca_cld/WCNSS_qcom_cfg.ini'
- ini_file_local = 'local_ini_file.ini'
- ini_pull_cmd = 'adb -s %s pull %s %s' % (serial, ini_file_phone,
- ini_file_local)
- ini_push_cmd = 'adb -s %s push %s %s' % (serial, ini_file_local,
- ini_file_phone)
- utils.exe_cmd(ini_pull_cmd)
+ # First trying to find the ini file with DTIM settings
+ ini_file_phone = ad.adb.shell('ls /vendor/firmware/wlan/*/*.ini')
+ ini_file_local = ini_file_phone.split('/')[-1]
+
+ # Pull the file and change the DTIM to desired value
+ ad.adb.pull('{} {}'.format(ini_file_phone, ini_file_local))
with open(ini_file_local, 'r') as fin:
for line in fin:
- if 'gEnableModulatedDTIM=' in line:
- gEDTIM_old = line.strip('gEnableModulatedDTIM=').strip('\n')
- if 'gMaxLIModulatedDTIM=' in line:
- gMDTIM_old = line.strip('gMaxLIModulatedDTIM=').strip('\n')
+ if ENABLED_MODULATED_DTIM in line:
+ gE_old = line.strip('\n')
+ gEDTIM_old = line.strip(ENABLED_MODULATED_DTIM).strip('\n')
+ if MAX_MODULATED_DTIM in line:
+ gM_old = line.strip('\n')
+ gMDTIM_old = line.strip(MAX_MODULATED_DTIM).strip('\n')
+ fin.close()
if int(gEDTIM_old) == gEnableModulatedDTIM and int(
gMDTIM_old) == gMaxLIModulatedDTIM:
ad.log.info('Current DTIM is already the desired value,'
'no need to reset it')
- return
+ return 0
- gE_old = 'gEnableModulatedDTIM=' + gEDTIM_old
- gM_old = 'gMaxLIModulatedDTIM=' + gMDTIM_old
- gE_new = 'gEnableModulatedDTIM=' + str(gEnableModulatedDTIM)
- gM_new = 'gMaxLIModulatedDTIM=' + str(gMaxLIModulatedDTIM)
+ gE_new = ENABLED_MODULATED_DTIM + str(gEnableModulatedDTIM)
+ gM_new = MAX_MODULATED_DTIM + str(gMaxLIModulatedDTIM)
- sed_gE = 'sed -i \'s/%s/%s/g\' %s' % (gE_old, gE_new, ini_file_local)
- sed_gM = 'sed -i \'s/%s/%s/g\' %s' % (gM_old, gM_new, ini_file_local)
- utils.exe_cmd(sed_gE)
- utils.exe_cmd(sed_gM)
+ sed_gE = 'sed -i \'s/{}/{}/g\' {}'.format(gE_old, gE_new, ini_file_local)
+ sed_gM = 'sed -i \'s/{}/{}/g\' {}'.format(gM_old, gM_new, ini_file_local)
+ job.run(sed_gE)
+ job.run(sed_gM)
- utils.exe_cmd('adb -s {} root'.format(serial))
- cmd_out = utils.exe_cmd('adb -s {} remount'.format(serial))
- if ("Permission denied").encode() in cmd_out:
+ # Push the file to the phone
+ push_file_to_phone(ad, ini_file_local, ini_file_phone)
+ ad.log.info('DTIM changes checked in and rebooting...')
+ ad.reboot()
+ # Wait for auto-wifi feature to start
+ time.sleep(20)
+ ad.log.info('DTIM updated and device back from reboot')
+ return 1
+
+
+def push_file_to_phone(ad, file_local, file_phone):
+ """Function to push local file to android phone.
+
+ Args:
+ ad: the target android device
+ file_local: the locla file to push
+ file_phone: the file/directory on the phone to be pushed
+ """
+ ad.adb.root()
+ cmd_out = ad.adb.remount()
+ if 'Permission denied' in cmd_out:
ad.log.info('Need to disable verity first and reboot')
- utils.exe_cmd('adb -s {} disable-verity'.format(serial))
+ ad.adb.disable_verity()
time.sleep(1)
ad.reboot()
ad.log.info('Verity disabled and device back from reboot')
- utils.exe_cmd('adb -s {} root'.format(serial))
- utils.exe_cmd('adb -s {} remount'.format(serial))
+ ad.adb.root()
+ ad.adb.remount()
time.sleep(1)
- utils.exe_cmd(ini_push_cmd)
- ad.log.info('ini file changes checked in and rebooting...')
- ad.reboot()
- ad.log.info('DTIM updated and device back from reboot')
+ ad.adb.push('{} {}'.format(file_local, file_phone))
def ap_setup(ap, network, bandwidth=80):
@@ -544,9 +313,9 @@
index_now = legends.index(legend)
color = colors[index_now % len(colors)]
plot.line(
- x_data, y_data, legend=str(legend), line_width=3, color=color)
+ x_data, y_data, legend=str(legend), line_width=fig_property['linewidth'], color=color)
plot.circle(
- x_data, y_data, size=10, legend=str(legend), fill_color=color)
+ x_data, y_data, size=fig_property['markersize'], legend=str(legend), fill_color=color)
#Plot properties
plot.xaxis.axis_label = fig_property['x_label']
@@ -734,87 +503,3 @@
'gw_ipv4': ipv4_gw
}
return pkt_gen_config
-
-
-def create_monsoon_info(test_class):
- """Creates the config dictionary for monsoon
-
- Args:
- test_class: object with all the parameters
-
- Returns:
- Dictionary with the monsoon packet config
- """
- mon_info = {
- 'dut': test_class.mon,
- 'freq': test_class.mon_freq,
- 'duration': test_class.mon_duration,
- 'offset': test_class.mon_offset,
- 'data_path': test_class.mon_data_path
- }
- return mon_info
-
-
-def setup_phone_wireless(test_class,
- bt_on,
- wifi_on,
- screen_status,
- network=None,
- regular_mode=False):
- """Sets the phone in rock-bottom and in the desired wireless mode
-
- Args:
- test_class: the specific test class where test is running
- bt_on: Enable/Disable BT
- wifi_on: Enable/Disable WiFi
- screen_status: screen ON or OFF
- network: a dict of information for the WiFi network to connect
- regular_mode: enable cellular data (i.e., disable airplane mode)
- """
- # Initialize the dut to rock-bottom state
- dut_rockbottom(test_class.dut)
- brconfigs = None
- time.sleep(1)
-
- if regular_mode:
- test_class.dut.droid.connectivityToggleAirplaneMode(False)
- utils.set_mobile_data_always_on(test_class.dut, True)
- time.sleep(2)
-
- # Turn ON/OFF BT
- if bt_on == 'ON':
- enable_bluetooth(test_class.dut.droid, test_class.dut.ed)
- test_class.dut.log.info('BT is ON')
- else:
- disable_bluetooth(test_class.dut.droid)
- test_class.dut.droid.bluetoothDisableBLE()
- test_class.dut.log.info('BT is OFF')
- time.sleep(2)
-
- # Turn ON/OFF Wifi
- if wifi_on == 'ON':
- wutils.wifi_toggle_state(test_class.dut, True)
- test_class.dut.log.info('WiFi is ON')
- if network:
- # Set attenuation and connect to AP
- for attn in range(test_class.num_atten):
- test_class.attenuators[attn].set_atten(
- test_class.atten_level['zero_atten'][attn])
- test_class.log.info('Set attenuation level to all zero')
- brconfigs = ap_setup(test_class.access_point, network)
- wutils.wifi_connect(test_class.dut, network)
- else:
- wutils.wifi_toggle_state(test_class.dut, False)
- test_class.dut.log.info('WiFi is OFF')
- time.sleep(1)
-
- # Set the desired screen status
- if screen_status == 'OFF':
- test_class.dut.droid.goToSleepNow()
- test_class.dut.log.info('Screen is OFF')
- time.sleep(1)
-
- if brconfigs:
- return brconfigs
- else:
- return None
diff --git a/acts/framework/acts/test_utils/wifi/wifi_retail_ap.py b/acts/framework/acts/test_utils/wifi/wifi_retail_ap.py
index e96c119..c72cefe 100644
--- a/acts/framework/acts/test_utils/wifi/wifi_retail_ap.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_retail_ap.py
@@ -13,8 +13,11 @@
# 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.
+import fcntl
+import logging
+import selenium
import splinter
-from time import sleep
+import time
BROWSER_WAIT_SHORT = 1
BROWSER_WAIT_MED = 3
@@ -23,7 +26,7 @@
def create(configs):
- """ Factory method for retail AP class.
+ """Factory method for retail AP class.
Args:
configs: list of dicts containing ap settings. ap settings must contain
@@ -32,6 +35,7 @@
SUPPORTED_APS = {
("Netgear", "R7000"): "NetgearR7000AP",
("Netgear", "R7500"): "NetgearR7500AP",
+ ("Netgear", "R7800"): "NetgearR7800AP",
("Netgear", "R8000"): "NetgearR8000AP"
}
objs = []
@@ -49,8 +53,102 @@
return
+class BlockingBrowser(splinter.driver.webdriver.chrome.WebDriver):
+ """Class that implements a blocking browser session on top of selenium.
+
+ The class inherits from and builds upon splinter/selenium's webdriver class
+ and makes sure that only one such webdriver is active on a machine at any
+ single time. The class ensures single session operation using a lock file.
+ The class is to be used within context managers (e.g. with statements) to
+ ensure locks are always properly released.
+ """
+
+ def __init__(self, headless, timeout):
+ """Constructor for BlockingBrowser class.
+
+ Args:
+ headless: boolean to control visible/headless browser operation
+ timeout: maximum time allowed to launch browser
+ """
+ self.chrome_options = splinter.driver.webdriver.chrome.Options()
+ self.chrome_options.add_argument("--no-proxy-server")
+ self.chrome_options.add_argument("--no-sandbox")
+ self.chrome_options.add_argument("--allow-running-insecure-content")
+ self.chrome_options.add_argument("--ignore-certificate-errors")
+ self.chrome_capabilities = selenium.webdriver.common.desired_capabilities.DesiredCapabilities.CHROME.copy(
+ )
+ self.chrome_capabilities["acceptSslCerts"] = True
+ self.chrome_capabilities["acceptInsecureCerts"] = True
+ if headless:
+ self.chrome_options.add_argument("--headless")
+ self.chrome_options.add_argument("--disable-gpu")
+ self.lock_file_path = "/usr/local/bin/chromedriver"
+ self.timeout = timeout
+
+ def __enter__(self):
+ self.lock_file = open(self.lock_file_path, "r")
+ start_time = time.time()
+ while time.time() < start_time + self.timeout:
+ try:
+ fcntl.flock(self.lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ self.driver = selenium.webdriver.Chrome(
+ options=self.chrome_options,
+ desired_capabilities=self.chrome_capabilities)
+ self.element_class = splinter.driver.webdriver.WebDriverElement
+ self._cookie_manager = splinter.driver.webdriver.cookie_manager.CookieManager(
+ self.driver)
+ super(splinter.driver.webdriver.chrome.WebDriver,
+ self).__init__(2)
+ return super(BlockingBrowser, self).__enter__()
+ except BlockingIOError:
+ time.sleep(BROWSER_WAIT_SHORT)
+ raise TimeoutError("Could not start chrome browser in time.")
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ try:
+ super(BlockingBrowser, self).__exit__(exc_type, exc_value,
+ traceback)
+ except:
+ raise RuntimeError("Failed to quit browser. Releasing lock file.")
+ finally:
+ fcntl.flock(self.lock_file, fcntl.LOCK_UN)
+ self.lock_file.close()
+
+ def restart(self):
+ """Method to restart browser session without releasing lock file."""
+ self.quit()
+ self.__enter__()
+
+ def visit_persistent(self, url, page_load_timeout, num_tries):
+ """Method to visit webpages and retry upon failure.
+
+ The function visits a web page and checks the the resulting URL matches
+ the intended URL, i.e. no redirects have happened
+
+ Args:
+ url: the intended url
+ page_load_timeout: timeout for page visits
+ num_tries: number of tries before url is declared unreachable
+ """
+ self.driver.set_page_load_timeout(page_load_timeout)
+ for idx in range(num_tries):
+ try:
+ self.visit(url)
+ except:
+ try:
+ self.visit("about:blank")
+ except:
+ self.restart()
+ if self.url.split("/")[-1] == url.split("/")[-1]:
+ break
+ if idx == num_tries - 1:
+ logging.error("URL unreachable. Current URL: {}".format(
+ self.url))
+ raise RuntimeError("URL unreachable.")
+
+
class WifiRetailAP(object):
- """ Base class implementation for retail ap.
+ """Base class implementation for retail ap.
Base class provides functions whose implementation is shared by all aps.
If some functions such as set_power not supported by ap, checks will raise
@@ -61,23 +159,56 @@
raise NotImplementedError
def read_ap_settings(self):
- """ Function that reads current ap settings.
+ """Function that reads current ap settings.
Function implementation is AP dependent and thus base class raises exception
if function not implemented in child class.
"""
raise NotImplementedError
+ def validate_ap_settings(self):
+ """Function to validate ap settings.
+
+ This function compares the actual ap settings read from the web GUI
+ with the assumed settings saved in the AP object. When called after AP
+ configuration, this method helps ensure that our configuration was
+ successful.
+ Note: Calling this function updates the stored ap_settings
+
+ Raises:
+ ValueError: If read AP settings do not match stored settings.
+ """
+ assumed_ap_settings = self.ap_settings.copy()
+ actual_ap_settings = self.read_ap_settings()
+ if assumed_ap_settings != actual_ap_settings:
+ logging.warning(
+ "Discrepancy in AP settings. Some settings may have been overwritten."
+ )
+
def configure_ap(self):
- """ Function that configures ap based on values of ap_settings.
+ """Function that configures ap based on values of ap_settings.
Function implementation is AP dependent and thus base class raises exception
if function not implemented in child class.
"""
raise NotImplementedError
+ def set_region(self, region):
+ """Function that sets AP region.
+
+ This function sets the region for the AP. Note that this may overwrite
+ channel and bandwidth settings in cases where the new region does not
+ support the current wireless configuration.
+
+ Args:
+ region: string indicating AP region
+ """
+ logging.warning("Updating region may overwrite wireless settings.")
+ setting_to_update = {"region": region}
+ self.update_ap_settings(setting_to_update)
+
def set_radio_on_off(self, network, status):
- """ Function that turns the radio on or off
+ """Function that turns the radio on or off.
Args:
network: string containing network identifier (2G, 5G_1, 5G_2)
@@ -87,7 +218,7 @@
self.update_ap_settings(setting_to_update)
def set_ssid(self, network, ssid):
- """ Function that sets network SSID
+ """Function that sets network SSID.
Args:
network: string containing network identifier (2G, 5G_1, 5G_2)
@@ -97,7 +228,7 @@
self.update_ap_settings(setting_to_update)
def set_channel(self, network, channel):
- """ Function that sets network channel
+ """Function that sets network channel.
Args:
network: string containing network identifier (2G, 5G_1, 5G_2)
@@ -107,7 +238,7 @@
self.update_ap_settings(setting_to_update)
def set_bandwidth(self, network, bandwidth):
- """ Function that sets network bandwidth/mode
+ """Function that sets network bandwidth/mode.
Args:
network: string containing network identifier (2G, 5G_1, 5G_2)
@@ -117,7 +248,7 @@
self.update_ap_settings(setting_to_update)
def set_power(self, network, power):
- """ Function that sets network transmit power
+ """Function that sets network transmit power.
Args:
network: string containing network identifier (2G, 5G_1, 5G_2)
@@ -127,7 +258,7 @@
self.update_ap_settings(setting_to_update)
def set_security(self, network, security_type, *password):
- """ Function that sets network security setting and password (optional)
+ """Function that sets network security setting and password.
Args:
network: string containing network identifier (2G, 5G_1, 5G_2)
@@ -146,7 +277,7 @@
self.update_ap_settings(setting_to_update)
def update_ap_settings(self, *dict_settings, **named_settings):
- """ Function to update settings of existing AP.
+ """Function to update settings of existing AP.
Function copies arguments into ap_settings and calls configure_retail_ap
to apply them.
@@ -183,47 +314,53 @@
self.configure_ap()
def band_lookup_by_channel(self, channel):
- """ Function that gives band name by channel number.
+ """Function that gives band name by channel number.
Args:
channel: channel number to lookup
Returns:
band: name of band which this channel belongs to on this ap
"""
- for key, value in self.CHANNEL_BAND_MAP.items():
+ for key, value in self.channel_band_map.items():
if channel in value:
return key
raise ValueError("Invalid channel passed in argument.")
class NetgearR7000AP(WifiRetailAP):
- """ Class that implements Netgear R7500 AP.
- """
+ """Class that implements Netgear R7500 AP."""
def __init__(self, ap_settings):
self.ap_settings = ap_settings.copy()
- self.CONFIG_PAGE = "http://{}:{}@{}/WLG_wireless_dual_band_r10.htm".format(
- self.ap_settings["admin_username"],
- self.ap_settings["admin_password"], self.ap_settings["ip_address"])
- self.CONFIG_PAGE_NOLOGIN = "http://{}/WLG_wireless_dual_band_r10.htm".format(
- self.ap_settings["ip_address"])
- self.CONFIG_PAGE_ADVANCED = "http://{}/WLG_adv_dual_band2.htm".format(
- self.ap_settings["ip_address"])
- self.CHROME_OPTIONS = splinter.driver.webdriver.chrome.Options()
- self.CHROME_OPTIONS.add_argument("--no-proxy-server")
- self.CHROME_OPTIONS.add_argument("--no-sandbox")
- if self.ap_settings["headless_browser"]:
- self.CHROME_OPTIONS.add_argument("--headless")
- self.CHROME_OPTIONS.add_argument("--disable-gpu")
- self.NETWORKS = ["2G", "5G_1"]
- self.CHANNEL_BAND_MAP = {
+ self.init_gui_data()
+ # Read and update AP settings
+ self.read_ap_settings()
+ if ap_settings.items() <= self.ap_settings.items():
+ return
+ else:
+ self.update_ap_settings(ap_settings)
+
+ def init_gui_data(self):
+ """Function to initialize data used while interacting with web GUI"""
+ self.config_page = "{}://{}:{}@{}:{}/WLG_wireless_dual_band_r10.htm".format(
+ self.ap_settings["protocol"], self.ap_settings["admin_username"],
+ self.ap_settings["admin_password"], self.ap_settings["ip_address"],
+ self.ap_settings["port"])
+ self.config_page_nologin = "{}://{}:{}/WLG_wireless_dual_band_r10.htm".format(
+ self.ap_settings["protocol"], self.ap_settings["ip_address"],
+ self.ap_settings["port"])
+ self.config_page_advanced = "{}://{}:{}/WLG_adv_dual_band2.htm".format(
+ self.ap_settings["protocol"], self.ap_settings["ip_address"],
+ self.ap_settings["port"])
+ self.networks = ["2G", "5G_1"]
+ self.channel_band_map = {
"2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
"5G_1": [
36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120,
124, 128, 132, 136, 140, 149, 153, 157, 161, 165
]
}
- self.REGION_MAP = {
+ self.region_map = {
"1": "Africa",
"2": "Asia",
"3": "Australia",
@@ -247,7 +384,7 @@
"23": "Singapore",
"24": "Taiwan"
}
- self.CONFIG_PAGE_FIELDS = {
+ self.config_page_fields = {
"region": "WRegion",
("2G", "status"): "enable_ap",
("5G_1", "status"): "enable_ap_an",
@@ -264,45 +401,54 @@
("2G", "password"): "passphrase",
("5G_1", "password"): "passphrase_an"
}
- self.read_ap_settings()
- if ap_settings.items() <= self.ap_settings.items():
- return
- else:
- self.update_ap_settings(ap_settings)
-
- def read_ap_settings(self):
- BW_MODE_VALUES = {
+ self.bw_mode_values = {
"g and b": "11g",
"145Mbps": "VHT20",
"300Mbps": "VHT40",
"HT80": "VHT80"
}
- POWER_MODE_VALUES = {"1": "100%", "2": "75%", "3": "50%", "4": "25%"}
+ self.power_mode_values = {
+ "1": "100%",
+ "2": "75%",
+ "3": "50%",
+ "4": "25%"
+ }
+ self.bw_mode_text = {
+ "11g": "Up to 54 Mbps",
+ "VHT20": "Up to 289 Mbps",
+ "VHT40": "Up to 600 Mbps",
+ "VHT80": "Up to 1300 Mbps"
+ }
- with splinter.Browser("chrome", self.CHROME_OPTIONS) as browser:
+ def read_ap_settings(self):
+ """Function to read ap settings."""
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 600) as browser:
# Visit URL
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_SHORT)
- browser.visit(self.CONFIG_PAGE_NOLOGIN)
- sleep(BROWSER_WAIT_SHORT)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page_nologin,
+ BROWSER_WAIT_MED, 10)
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "status" in key:
- browser.visit(self.CONFIG_PAGE_ADVANCED)
+ browser.visit_persistent(self.config_page_advanced,
+ BROWSER_WAIT_MED, 10)
config_item = browser.find_by_name(value)
self.ap_settings["{}_{}".format(key[1], key[0])] = int(
config_item.first.checked)
- browser.visit(self.CONFIG_PAGE_NOLOGIN)
+ browser.visit_persistent(self.config_page_nologin,
+ BROWSER_WAIT_MED, 10)
else:
config_item = browser.find_by_name(value)
if "bandwidth" in key:
self.ap_settings["{}_{}".format(key[1], key[
- 0])] = BW_MODE_VALUES[config_item.first.value]
+ 0])] = self.bw_mode_values[config_item.first.value]
elif "power" in key:
- self.ap_settings["{}_{}".format(key[1], key[
- 0])] = POWER_MODE_VALUES[config_item.first.value]
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = self.power_mode_values[
+ config_item.first.value]
elif "region" in key:
- self.ap_settings["region"] = self.REGION_MAP[
+ self.ap_settings["region"] = self.region_map[
config_item.first.value]
elif "security_type" in key:
for item in config_item:
@@ -311,52 +457,51 @@
key[1], key[0])] = item.value
else:
config_item = browser.find_by_name(value)
- self.ap_settings["{}_{}".format(key[1], key[
- 0])] = config_item.first.value
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = config_item.first.value
return self.ap_settings.copy()
def configure_ap(self):
- BW_MODE_TEXT = {
- "11g": "Up to 54 Mbps",
- "VHT20": "Up to 289 Mbps",
- "VHT40": "Up to 600 Mbps",
- "VHT80": "Up to 1300 Mbps"
- }
+ """Function to configure ap wireless settings."""
# Turn radios on or off
self.configure_radio_on_off()
# Configure radios
- with splinter.Browser("chrome", self.CHROME_OPTIONS) as browser:
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 600) as browser:
# Visit URL
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_SHORT)
- browser.visit(self.CONFIG_PAGE_NOLOGIN)
- sleep(BROWSER_WAIT_SHORT)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page_nologin,
+ BROWSER_WAIT_MED, 10)
# Update region, and power/bandwidth for each network
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "power" in key:
config_item = browser.find_by_name(value).first
- config_item.select_by_text(
- self.ap_settings["{}_{}".format(key[1], key[0])])
+ config_item.select_by_text(self.ap_settings["{}_{}".format(
+ key[1], key[0])])
elif "region" in key:
config_item = browser.find_by_name(value).first
config_item.select_by_text(self.ap_settings["region"])
elif "bandwidth" in key:
config_item = browser.find_by_name(value).first
- config_item.select_by_text(BW_MODE_TEXT[self.ap_settings[
- "{}_{}".format(key[1], key[0])]])
+ try:
+ config_item.select_by_text(
+ self.bw_mode_text[self.ap_settings["{}_{}".format(
+ key[1], key[0])]])
+ except AttributeError:
+ logging.warning(
+ "Cannot select bandwidth. Keeping AP default.")
# Update security settings (passwords updated only if applicable)
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "security_type" in key:
- browser.choose(
- value,
- self.ap_settings["{}_{}".format(key[1], key[0])])
- if self.ap_settings["{}_{}".format(key[1], key[
- 0])] == "WPA2-PSK":
+ browser.choose(value, self.ap_settings["{}_{}".format(
+ key[1], key[0])])
+ if self.ap_settings["{}_{}".format(key[1],
+ key[0])] == "WPA2-PSK":
config_item = browser.find_by_name(
- self.CONFIG_PAGE_FIELDS[(key[0], "password"
- )]).first
+ self.config_page_fields[(key[0],
+ "password")]).first
config_item.fill(self.ap_settings["{}_{}".format(
"password", key[0])])
@@ -365,44 +510,50 @@
# wherein channel and SSID get overwritten when some other
# variables are changed. However, region does have to be set before
# channel in all cases.
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "ssid" in key:
config_item = browser.find_by_name(value).first
- config_item.fill(
- self.ap_settings["{}_{}".format(key[1], key[0])])
+ config_item.fill(self.ap_settings["{}_{}".format(
+ key[1], key[0])])
elif "channel" in key:
config_item = browser.find_by_name(value).first
- config_item.select(
- self.ap_settings["{}_{}".format(key[1], key[0])])
- sleep(BROWSER_WAIT_SHORT)
+ try:
+ config_item.select(self.ap_settings["{}_{}".format(
+ key[1], key[0])])
+ time.sleep(BROWSER_WAIT_SHORT)
+ except AttributeError:
+ logging.warning(
+ "Cannot select channel. Keeping AP default.")
try:
alert = browser.get_alert()
alert.accept()
except:
pass
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_SHORT)
browser.find_by_name("Apply").first.click()
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_SHORT)
try:
alert = browser.get_alert()
alert.accept()
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_SHORT)
except:
- sleep(BROWSER_WAIT_SHORT)
- browser.visit(self.CONFIG_PAGE)
+ time.sleep(BROWSER_WAIT_SHORT)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
+ 10)
def configure_radio_on_off(self):
- with splinter.Browser("chrome", self.CHROME_OPTIONS) as browser:
+ """Helper configuration function to turn radios on/off."""
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 600) as browser:
# Visit URL
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_SHORT)
- browser.visit(self.CONFIG_PAGE_ADVANCED)
- sleep(BROWSER_WAIT_SHORT)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page_advanced,
+ BROWSER_WAIT_MED, 10)
# Turn radios on or off
status_toggled = False
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "status" in key:
config_item = browser.find_by_name(value).first
current_status = int(config_item.checked)
@@ -415,43 +566,47 @@
config_item.uncheck()
if status_toggled:
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_SHORT)
browser.find_by_name("Apply").first.click()
- sleep(BROWSER_WAIT_EXTRA_LONG)
- browser.visit(self.CONFIG_PAGE)
+ time.sleep(BROWSER_WAIT_EXTRA_LONG)
+ browser.visit_persistent(self.config_page,
+ BROWSER_WAIT_EXTRA_LONG, 10)
class NetgearR7500AP(WifiRetailAP):
- """ Class that implements Netgear R7000 AP.
-
- NOTE: Many of the functions here are reused in NetgearR8000AP
- """
+ """Class that implements Netgear R7500 AP."""
def __init__(self, ap_settings):
self.ap_settings = ap_settings.copy()
- self.CONFIG_PAGE = "http://{}:{}@{}/index.htm".format(
- self.ap_settings["admin_username"],
- self.ap_settings["admin_password"], self.ap_settings["ip_address"])
- self.CONFIG_PAGE_NOLOGIN = "http://{}/index.htm".format(
- self.ap_settings["ip_address"])
- self.CONFIG_PAGE_ADVANCED = "http://{}:{}@{}/adv_index.htm".format(
- self.ap_settings["admin_username"],
- self.ap_settings["admin_password"], self.ap_settings["ip_address"])
- self.CHROME_OPTIONS = splinter.driver.webdriver.chrome.Options()
- self.CHROME_OPTIONS.add_argument("--no-proxy-server")
- self.CHROME_OPTIONS.add_argument("--no-sandbox")
- if self.ap_settings["headless_browser"]:
- self.CHROME_OPTIONS.add_argument("--headless")
- self.CHROME_OPTIONS.add_argument("--disable-gpu")
- self.NETWORKS = ["2G", "5G_1"]
- self.CHANNEL_BAND_MAP = {
+ self.init_gui_data()
+ # Read and update AP settings
+ self.read_ap_settings()
+ if ap_settings.items() <= self.ap_settings.items():
+ return
+ else:
+ self.update_ap_settings(ap_settings)
+
+ def init_gui_data(self):
+ """Function to initialize data used while interacting with web GUI"""
+ self.config_page = "{}://{}:{}@{}:{}/index.htm".format(
+ self.ap_settings["protocol"], self.ap_settings["admin_username"],
+ self.ap_settings["admin_password"], self.ap_settings["ip_address"],
+ self.ap_settings["port"])
+ self.config_page_nologin = "{}://{}:{}/index.htm".format(
+ self.ap_settings["protocol"], self.ap_settings["ip_address"],
+ self.ap_settings["port"])
+ self.config_page_advanced = "{}://{}:{}/adv_index.htm".format(
+ self.ap_settings["protocol"], self.ap_settings["ip_address"],
+ self.ap_settings["port"])
+ self.networks = ["2G", "5G_1"]
+ self.channel_band_map = {
"2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
"5G_1": [
36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120,
124, 128, 132, 136, 140, 149, 153, 157, 161, 165
]
}
- self.CONFIG_PAGE_FIELDS = {
+ self.config_page_fields = {
"region": "WRegion",
("2G", "status"): "enable_ap",
("5G_1", "status"): "enable_ap_an",
@@ -466,7 +621,7 @@
("2G", "password"): "passphrase",
("5G_1", "password"): "passphrase_an"
}
- self.REGION_MAP = {
+ self.region_map = {
"0": "Africa",
"1": "Asia",
"2": "Australia",
@@ -490,14 +645,17 @@
"20": "Singapore",
"21": "Taiwan"
}
- self.read_ap_settings()
- if ap_settings.items() <= self.ap_settings.items():
- return
- else:
- self.update_ap_settings(ap_settings)
-
- def read_ap_settings(self):
- BW_MODE_VALUES = {
+ self.bw_mode_text_2g = {
+ "11g": "Up to 54 Mbps",
+ "VHT20": "Up to 289 Mbps",
+ "VHT40": "Up to 600 Mbps"
+ }
+ self.bw_mode_text_5g = {
+ "VHT20": "Up to 347 Mbps",
+ "VHT40": "Up to 800 Mbps",
+ "VHT80": "Up to 1733 Mbps"
+ }
+ self.bw_mode_values = {
"1": "11g",
"2": "VHT20",
"3": "VHT40",
@@ -505,85 +663,79 @@
"8": "VHT40",
"9": "VHT80"
}
+
+ def read_ap_settings(self):
+ """Function to read ap wireless settings."""
# Get radio status (on/off)
self.read_radio_on_off()
# Get radio configuration. Note that if both radios are off, the below
# code will result in an error
- with splinter.Browser("chrome", self.CHROME_OPTIONS) as browser:
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_SHORT)
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_SHORT)
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 600) as browser:
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ time.sleep(BROWSER_WAIT_SHORT)
wireless_button = browser.find_by_id("wireless").first
wireless_button.click()
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_MED)
with browser.get_iframe("formframe") as iframe:
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "bandwidth" in key:
config_item = iframe.find_by_name(value).first
- self.ap_settings["{}_{}".format(key[1], key[
- 0])] = BW_MODE_VALUES[config_item.value]
+ self.ap_settings["{}_{}".format(
+ key[1],
+ key[0])] = self.bw_mode_values[config_item.value]
elif "region" in key:
config_item = iframe.find_by_name(value).first
- self.ap_settings["region"] = self.REGION_MAP[
+ self.ap_settings["region"] = self.region_map[
config_item.value]
elif "password" in key:
try:
config_item = iframe.find_by_name(value).first
- self.ap_settings["{}_{}".format(key[1], key[
- 0])] = config_item.value
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = config_item.value
self.ap_settings["{}_{}".format(
"security_type", key[0])] = "WPA2-PSK"
except:
- self.ap_settings["{}_{}".format(key[1], key[
- 0])] = "defaultpassword"
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = "defaultpassword"
self.ap_settings["{}_{}".format(
"security_type", key[0])] = "Disable"
elif ("channel" in key) or ("ssid" in key):
config_item = iframe.find_by_name(value).first
- self.ap_settings["{}_{}".format(key[1], key[
- 0])] = config_item.value
+ self.ap_settings["{}_{}".format(
+ key[1], key[0])] = config_item.value
else:
pass
return self.ap_settings.copy()
def configure_ap(self):
- BW_MODE_TEXT_2G = {
- "11g": "Up to 54 Mbps",
- "VHT20": "Up to 289 Mbps",
- "VHT40": "Up to 600 Mbps"
- }
- BW_MODE_TEXT_5G = {
- "VHT20": "Up to 347 Mbps",
- "VHT40": "Up to 800 Mbps",
- "VHT80": "Up to 1733 Mbps"
- }
-
+ """Function to configure ap wireless settings."""
# Turn radios on or off
self.configure_radio_on_off()
# Configure radios
- with splinter.Browser("chrome", self.CHROME_OPTIONS) as browser:
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_SHORT)
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_SHORT)
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 600) as browser:
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ time.sleep(BROWSER_WAIT_SHORT)
wireless_button = browser.find_by_id("wireless").first
wireless_button.click()
- sleep(BROWSER_WAIT_MED)
+ time.sleep(BROWSER_WAIT_MED)
with browser.get_iframe("formframe") as iframe:
# Update AP region. Must be done before channel setting
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "region" in key:
config_item = iframe.find_by_name(value).first
config_item.select_by_text(self.ap_settings["region"])
# Update wireless settings for each network
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "ssid" in key:
config_item = iframe.find_by_name(value).first
- config_item.fill(
- self.ap_settings["{}_{}".format(key[1], key[0])])
+ config_item.fill(self.ap_settings["{}_{}".format(
+ key[1], key[0])])
elif "channel" in key:
channel_string = "0" * (int(
self.ap_settings["{}_{}".format(key[1], key[0])]
@@ -592,110 +744,145 @@
48 < int(self.ap_settings["{}_{}".format(
key[1], key[0])]) < 149)
config_item = iframe.find_by_name(value).first
- config_item.select_by_text(channel_string)
+ try:
+ config_item.select_by_text(channel_string)
+ except AttributeError:
+ logging.warning(
+ "Cannot select channel. Keeping AP default.")
elif key == ("2G", "bandwidth"):
config_item = iframe.find_by_name(value).first
- config_item.select_by_text(
- str(BW_MODE_TEXT_2G[self.ap_settings[
- "{}_{}".format(key[1], key[0])]]))
+ try:
+ config_item.select_by_text(
+ str(self.bw_mode_text_2g[self.ap_settings[
+ "{}_{}".format(key[1], key[0])]]))
+ except AttributeError:
+ logging.warning(
+ "Cannot select bandwidth. Keeping AP default.")
elif key == ("5G_1", "bandwidth"):
config_item = iframe.find_by_name(value).first
- config_item.select_by_text(
- str(BW_MODE_TEXT_5G[self.ap_settings[
- "{}_{}".format(key[1], key[0])]]))
-
+ try:
+ config_item.select_by_text(
+ str(self.bw_mode_text_5g[self.ap_settings[
+ "{}_{}".format(key[1], key[0])]]))
+ except AttributeError:
+ logging.warning(
+ "Cannot select bandwidth. Keeping AP default.")
# Update passwords for WPA2-PSK protected networks
# (Must be done after security type is selected)
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "security_type" in key:
- iframe.choose(
- value,
- self.ap_settings["{}_{}".format(key[1], key[0])])
- if self.ap_settings["{}_{}".format(key[1], key[
- 0])] == "WPA2-PSK":
+ iframe.choose(value, self.ap_settings["{}_{}".format(
+ key[1], key[0])])
+ if self.ap_settings["{}_{}".format(
+ key[1], key[0])] == "WPA2-PSK":
config_item = iframe.find_by_name(
- self.CONFIG_PAGE_FIELDS[(key[0], "password"
- )]).first
+ self.config_page_fields[(key[0],
+ "password")]).first
config_item.fill(self.ap_settings["{}_{}".format(
"password", key[0])])
apply_button = iframe.find_by_name("Apply")
apply_button[0].click()
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_SHORT)
try:
alert = browser.get_alert()
alert.accept()
except:
pass
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_SHORT)
try:
alert = browser.get_alert()
alert.accept()
except:
pass
- sleep(BROWSER_WAIT_SHORT)
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_LONG)
+ time.sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_EXTRA_LONG)
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
+ 10)
def configure_radio_on_off(self):
- with splinter.Browser("chrome", self.CHROME_OPTIONS) as browser:
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_SHORT)
- browser.visit(self.CONFIG_PAGE_ADVANCED)
- sleep(BROWSER_WAIT_SHORT)
+ """Helper configuration function to turn radios on/off."""
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 600) as browser:
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page_advanced,
+ BROWSER_WAIT_MED, 10)
+ time.sleep(BROWSER_WAIT_SHORT)
wireless_button = browser.find_by_id("advanced_bt").first
wireless_button.click()
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_SHORT)
wireless_button = browser.find_by_id("wladv").first
wireless_button.click()
- sleep(BROWSER_WAIT_MED)
+ time.sleep(BROWSER_WAIT_MED)
with browser.get_iframe("formframe") as iframe:
# Turn radios on or off
status_toggled = False
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "status" in key:
config_item = iframe.find_by_name(value).first
current_status = int(config_item.checked)
if current_status != self.ap_settings["{}_{}".format(
key[1], key[0])]:
status_toggled = True
- if self.ap_settings["{}_{}".format(key[1], key[
- 0])]:
+ if self.ap_settings["{}_{}".format(key[1],
+ key[0])]:
config_item.check()
else:
config_item.uncheck()
if status_toggled:
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_SHORT)
browser.find_by_name("Apply").first.click()
- sleep(BROWSER_WAIT_EXTRA_LONG)
- browser.visit(self.CONFIG_PAGE)
+ time.sleep(BROWSER_WAIT_EXTRA_LONG)
+ browser.visit_persistent(self.config_page,
+ BROWSER_WAIT_EXTRA_LONG, 10)
def read_radio_on_off(self):
- with splinter.Browser("chrome", self.CHROME_OPTIONS) as browser:
- browser.visit(self.CONFIG_PAGE)
- sleep(BROWSER_WAIT_SHORT)
- browser.visit(self.CONFIG_PAGE_ADVANCED)
- sleep(BROWSER_WAIT_SHORT)
+ """Helper configuration function to read radio status."""
+ with BlockingBrowser(self.ap_settings["headless_browser"],
+ 600) as browser:
+ browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+ browser.visit_persistent(self.config_page_advanced,
+ BROWSER_WAIT_MED, 10)
wireless_button = browser.find_by_id("advanced_bt").first
wireless_button.click()
- sleep(BROWSER_WAIT_SHORT)
+ time.sleep(BROWSER_WAIT_SHORT)
wireless_button = browser.find_by_id("wladv").first
wireless_button.click()
- sleep(BROWSER_WAIT_MED)
+ time.sleep(BROWSER_WAIT_MED)
with browser.get_iframe("formframe") as iframe:
# Turn radios on or off
- for key, value in self.CONFIG_PAGE_FIELDS.items():
+ for key, value in self.config_page_fields.items():
if "status" in key:
config_item = iframe.find_by_name(value).first
self.ap_settings["{}_{}".format(key[1], key[0])] = int(
config_item.checked)
+class NetgearR7800AP(NetgearR7500AP):
+ """Class that implements Netgear R7800 AP.
+
+ Since most of the class' implementation is shared with the R7500, this
+ class inherits from NetgearR7500AP and simply redifines config parameters
+ """
+
+ def __init__(self, ap_settings):
+ self.ap_settings = ap_settings.copy()
+ self.init_gui_data()
+ # Overwrite minor differences from R7500 AP
+ self.bw_mode_text_2g["VHT20"] = "Up to 347 Mbps"
+ # Read and update AP settings
+ self.read_ap_settings()
+ if ap_settings.items() <= self.ap_settings.items():
+ return
+ else:
+ self.update_ap_settings(ap_settings)
+
+
class NetgearR8000AP(NetgearR7000AP):
- """ Class that implements Netgear R8000 AP.
+ """Class that implements Netgear R8000 AP.
Since most of the class' implementation is shared with the R7000, this
class inherits from NetgearR7000AP and simply redifines config parameters
@@ -703,50 +890,25 @@
def __init__(self, ap_settings):
self.ap_settings = ap_settings.copy()
- self.CONFIG_PAGE = "http://{}:{}@{}/WLG_wireless_dual_band_r8000.htm".format(
- self.ap_settings["admin_username"],
- self.ap_settings["admin_password"], self.ap_settings["ip_address"])
- self.CONFIG_PAGE_NOLOGIN = "http://{}/WLG_wireless_dual_band_r8000.htm".format(
- self.ap_settings["ip_address"])
- self.CONFIG_PAGE_ADVANCED = "http://{}/WLG_adv_dual_band2_r8000.htm".format(
- self.ap_settings["ip_address"])
- self.CHROME_OPTIONS = splinter.driver.webdriver.chrome.Options()
- self.CHROME_OPTIONS.add_argument("--no-proxy-server")
- self.CHROME_OPTIONS.add_argument("--no-sandbox")
- if self.ap_settings["headless_browser"]:
- self.CHROME_OPTIONS.add_argument("--headless")
- self.CHROME_OPTIONS.add_argument("--disable-gpu")
- self.NETWORKS = ["2G", "5G_1", "5G_2"]
- self.CHANNEL_BAND_MAP = {
+ self.init_gui_data()
+ # Overwrite minor differences from R7000 AP
+ self.config_page = "{}://{}:{}@{}:{}/WLG_wireless_dual_band_r8000.htm".format(
+ self.ap_settings["protocol"], self.ap_settings["admin_username"],
+ self.ap_settings["admin_password"], self.ap_settings["ip_address"],
+ self.ap_settings["port"])
+ self.config_page_nologin = "{}://{}:{}/WLG_wireless_dual_band_r8000.htm".format(
+ self.ap_settings["protocol"], self.ap_settings["ip_address"],
+ self.ap_settings["port"])
+ self.config_page_advanced = "{}://{}:{}/WLG_adv_dual_band2_r8000.htm".format(
+ self.ap_settings["protocol"], self.ap_settings["ip_address"],
+ self.ap_settings["port"])
+ self.networks = ["2G", "5G_1", "5G_2"]
+ self.channel_band_map = {
"2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
"5G_1": [36, 40, 44, 48],
"5G_2": [149, 153, 157, 161, 165]
}
- self.REGION_MAP = {
- "1": "Africa",
- "2": "Asia",
- "3": "Australia",
- "4": "Canada",
- "5": "Europe",
- "6": "Israel",
- "7": "Japan",
- "8": "Korea",
- "9": "Mexico",
- "10": "South America",
- "11": "United States",
- "12": "Middle East(Algeria/Syria/Yemen)",
- "14": "Russia",
- "16": "China",
- "17": "India",
- "18": "Malaysia",
- "19": "Middle East(Iran/Labanon/Qatar)",
- "20": "Middle East(Turkey/Egypt/Tunisia/Kuwait)",
- "21": "Middle East(Saudi Arabia)",
- "22": "Middle East(United Arab Emirates)",
- "23": "Singapore",
- "24": "Taiwan"
- }
- self.CONFIG_PAGE_FIELDS = {
+ self.config_page_fields = {
"region": "WRegion",
("2G", "status"): "enable_ap",
("5G_1", "status"): "enable_ap_an",
@@ -767,6 +929,7 @@
("5G_1", "password"): "passphrase_an",
("5G_2", "password"): "passphrase_an_2"
}
+ # Read and update AP settings
self.read_ap_settings()
if ap_settings.items() <= self.ap_settings.items():
return
diff --git a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
index fca5085..0c96fed 100755
--- a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
@@ -35,12 +35,33 @@
# Like onSuccess for start background scan and confirmation on wifi state
# change.
SHORT_TIMEOUT = 30
+ROAMING_TIMEOUT = 30
# Speed of light in m/s.
SPEED_OF_LIGHT = 299792458
DEFAULT_PING_ADDR = "https://www.google.com/robots.txt"
+roaming_attn = {
+ "AP1_on_AP2_off": [
+ 0,
+ 0,
+ 95,
+ 95
+ ],
+ "AP1_off_AP2_on": [
+ 95,
+ 95,
+ 0,
+ 0
+ ],
+ "default": [
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
class WifiEnums():
@@ -50,9 +71,11 @@
PWD_KEY = "password"
frequency_key = "frequency"
APBAND_KEY = "apBand"
+ HIDDEN_KEY = "hiddenSSID"
WIFI_CONFIG_APBAND_2G = 0
WIFI_CONFIG_APBAND_5G = 1
+ WIFI_CONFIG_APBAND_AUTO = -1
WIFI_WPS_INFO_PBC = 0
WIFI_WPS_INFO_DISPLAY = 1
@@ -211,6 +234,10 @@
REPORT_EVENT_AFTER_EACH_SCAN = 1
REPORT_EVENT_FULL_SCAN_RESULT = 2
+ SCAN_TYPE_LOW_LATENCY = 0
+ SCAN_TYPE_LOW_POWER = 1
+ SCAN_TYPE_HIGH_ACCURACY = 2
+
# US Wifi frequencies
ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
2457, 2462]
@@ -513,6 +540,47 @@
results.append(n)
return results
+def wait_for_wifi_state(ad, state, assert_on_fail=True):
+ """Waits for the device to transition to the specified wifi state
+
+ Args:
+ ad: An AndroidDevice object.
+ state: Wifi state to wait for.
+ assert_on_fail: If True, error checks in this function will raise test
+ failure signals.
+
+ Returns:
+ If assert_on_fail is False, function returns True if the device transitions
+ to the specified state, False otherwise. If assert_on_fail is True, no return value.
+ """
+ return _assert_on_fail_handler(
+ _wait_for_wifi_state, assert_on_fail, ad, state=state)
+
+
+def _wait_for_wifi_state(ad, state):
+ """Toggles the state of wifi.
+
+ TestFailure signals are raised when something goes wrong.
+
+ Args:
+ ad: An AndroidDevice object.
+ state: Wifi state to wait for.
+ """
+ if state == ad.droid.wifiCheckState():
+ # Check if the state is already achieved, so we don't wait for the
+ # state change event by mistake.
+ return
+ ad.droid.wifiStartTrackingStateChange()
+ fail_msg = "Device did not transition to Wi-Fi state to %s on %s." % (state,
+ ad.serial)
+ try:
+ ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
+ lambda x: x["data"]["enabled"] == state,
+ SHORT_TIMEOUT)
+ except Empty:
+ asserts.assert_equal(state, ad.droid.wifiCheckState(), fail_msg)
+ finally:
+ ad.droid.wifiStopTrackingStateChange()
def wifi_toggle_state(ad, new_state=None, assert_on_fail=True):
"""Toggles the state of wifi.
@@ -549,19 +617,16 @@
return
ad.droid.wifiStartTrackingStateChange()
ad.log.info("Setting Wi-Fi state to %s.", new_state)
+ ad.ed.clear_all_events()
# Setting wifi state.
ad.droid.wifiToggleState(new_state)
fail_msg = "Failed to set Wi-Fi state to %s on %s." % (new_state,
ad.serial)
try:
- event = ad.ed.pop_event(wifi_constants.SUPPLICANT_CON_CHANGED,
- SHORT_TIMEOUT)
- asserts.assert_equal(event['data']['Connected'], new_state, fail_msg)
+ ad.ed.wait_for_event(wifi_constants.WIFI_STATE_CHANGED,
+ lambda x: x["data"]["enabled"] == new_state,
+ SHORT_TIMEOUT)
except Empty:
- # Supplicant connection event is not always reliable. We double check
- # here and call it a success as long as the new state equals the
- # expected state.
- time.sleep(5)
asserts.assert_equal(new_state, ad.droid.wifiCheckState(), fail_msg)
finally:
ad.droid.wifiStopTrackingStateChange()
@@ -688,6 +753,7 @@
Args:
ad: An AndroidDevice object.
"""
+ ad.ed.clear_all_events()
ad.droid.wifiStartScan()
try:
ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
@@ -695,6 +761,99 @@
asserts.fail("Wi-Fi results did not become available within 60s.")
+def start_wifi_connection_scan_and_return_status(ad):
+ """
+ Starts a wifi connection scan and wait for results to become available
+ or a scan failure to be reported.
+
+ Args:
+ ad: An AndroidDevice object.
+ Returns:
+ True: if scan succeeded & results are available
+ False: if scan failed
+ """
+ ad.ed.clear_all_events()
+ ad.droid.wifiStartScan()
+ try:
+ events = ad.ed.pop_events(
+ "WifiManagerScan(ResultsAvailable|Failure)", 60)
+ except Empty:
+ asserts.fail(
+ "Wi-Fi scan results/failure did not become available within 60s.")
+ # If there are multiple matches, we check for atleast one success.
+ for event in events:
+ if event["name"] == "WifiManagerScanResultsAvailable":
+ return True
+ elif event["name"] == "WifiManagerScanFailure":
+ ad.log.debug("Scan failure received")
+ return False
+
+
+def start_wifi_connection_scan_and_check_for_network(ad, network_ssid,
+ max_tries=3):
+ """
+ Start connectivity scans & checks if the |network_ssid| is seen in
+ scan results. The method performs a max of |max_tries| connectivity scans
+ to find the network.
+
+ Args:
+ ad: An AndroidDevice object.
+ network_ssid: SSID of the network we are looking for.
+ max_tries: Number of scans to try.
+ Returns:
+ True: if network_ssid is found in scan results.
+ False: if network_ssid is not found in scan results.
+ """
+ for num_tries in range(max_tries):
+ if start_wifi_connection_scan_and_return_status(ad):
+ scan_results = ad.droid.wifiGetScanResults()
+ match_results = match_networks(
+ {WifiEnums.SSID_KEY: network_ssid}, scan_results)
+ if len(match_results) > 0:
+ return True
+ return False
+
+
+def start_wifi_connection_scan_and_ensure_network_found(ad, network_ssid,
+ max_tries=3):
+ """
+ Start connectivity scans & ensure the |network_ssid| is seen in
+ scan results. The method performs a max of |max_tries| connectivity scans
+ to find the network.
+ This method asserts on failure!
+
+ Args:
+ ad: An AndroidDevice object.
+ network_ssid: SSID of the network we are looking for.
+ max_tries: Number of scans to try.
+ """
+ ad.log.info("Starting scans to ensure %s is present", network_ssid)
+ assert_msg = "Failed to find " + network_ssid + " in scan results" \
+ " after " + str(max_tries) + " tries"
+ asserts.assert_true(start_wifi_connection_scan_and_check_for_network(
+ ad, network_ssid, max_tries), assert_msg)
+
+
+def start_wifi_connection_scan_and_ensure_network_not_found(ad, network_ssid,
+ max_tries=3):
+ """
+ Start connectivity scans & ensure the |network_ssid| is not seen in
+ scan results. The method performs a max of |max_tries| connectivity scans
+ to find the network.
+ This method asserts on failure!
+
+ Args:
+ ad: An AndroidDevice object.
+ network_ssid: SSID of the network we are looking for.
+ max_tries: Number of scans to try.
+ """
+ ad.log.info("Starting scans to ensure %s is not present", network_ssid)
+ assert_msg = "Found " + network_ssid + " in scan results" \
+ " after " + str(max_tries) + " tries"
+ asserts.assert_false(start_wifi_connection_scan_and_check_for_network(
+ ad, network_ssid, max_tries), assert_msg)
+
+
def start_wifi_background_scan(ad, scan_setting):
"""Starts wifi background scan.
@@ -711,7 +870,7 @@
return event['data']
-def start_wifi_tethering(ad, ssid, password, band=None):
+def start_wifi_tethering(ad, ssid, password, band=None, hidden=None):
"""Starts wifi tethering on an android_device.
Args:
@@ -720,6 +879,7 @@
password: The password the soft AP should use.
band: The band the soft AP should be set on. It should be either
WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
+ hidden: boolean to indicate if the AP needs to be hidden or not.
Returns:
No return value. Error checks in this function will raise test failure signals
@@ -729,6 +889,8 @@
config[WifiEnums.PWD_KEY] = password
if band:
config[WifiEnums.APBAND_KEY] = band
+ if hidden:
+ config[WifiEnums.HIDDEN_KEY] = hidden
asserts.assert_true(
ad.droid.wifiSetWifiApConfiguration(config),
"Failed to update WifiAp Configuration")
@@ -780,7 +942,6 @@
"""
ad.droid.wifiStartTrackingTetherStateChange()
ad.droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
- ad.droid.wifiSetApEnabled(False, None)
try:
ad.ed.pop_event("WifiManagerApDisabled", 30)
ad.ed.wait_for_event("TetherStateChanged",
@@ -952,24 +1113,27 @@
ad.droid.wifiStartTrackingStateChange()
event = ad.ed.pop_event("WifiNetworkDisconnected", 10)
ad.droid.wifiStopTrackingStateChange()
- except queue.Empty:
+ except Empty:
raise signals.TestFailure("Device did not disconnect from the network")
-def connect_to_wifi_network(ad, network):
+def connect_to_wifi_network(ad, network, assert_on_fail=True,
+ check_connectivity=True):
"""Connection logic for open and psk wifi networks.
Args:
- params: A tuple of network info and AndroidDevice object.
+ ad: AndroidDevice to use for connection
+ network: network info of the network to connect to
+ assert_on_fail: If true, errors from wifi_connect will raise
+ test failure signals.
"""
- droid = ad.droid
- ed = ad.ed
- SSID = network[WifiEnums.SSID_KEY]
- ed.clear_all_events()
- start_wifi_connection_scan(ad)
- scan_results = droid.wifiGetScanResults()
- assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
- wifi_connect(ad, network, num_of_tries=3)
+ start_wifi_connection_scan_and_ensure_network_found(
+ ad, network[WifiEnums.SSID_KEY])
+ wifi_connect(ad,
+ network,
+ num_of_tries=3,
+ assert_on_fail=assert_on_fail,
+ check_connectivity=check_connectivity)
def connect_to_wifi_network_with_id(ad, network_id, network_ssid):
@@ -983,12 +1147,7 @@
False otherwise.
"""
- ad.ed.clear_all_events()
- start_wifi_connection_scan(ad)
- scan_results = ad.droid.wifiGetScanResults()
- assert_network_in_list({
- WifiEnums.SSID_KEY: network_ssid
- }, scan_results)
+ start_wifi_connection_scan_and_ensure_network_found(ad, network_ssid)
wifi_connect_by_id(ad, network_id)
connect_data = ad.droid.wifiGetConnectionInfo()
connect_ssid = connect_data[WifiEnums.SSID_KEY]
@@ -999,7 +1158,8 @@
return True
-def wifi_connect(ad, network, num_of_tries=1, assert_on_fail=True):
+def wifi_connect(ad, network, num_of_tries=1, assert_on_fail=True,
+ check_connectivity=True):
"""Connect an Android device to a wifi network.
Initiate connection to a wifi network, wait for the "connected" event, then
@@ -1021,10 +1181,11 @@
Returns True if the connection was successful, False otherwise.
"""
return _assert_on_fail_handler(
- _wifi_connect, assert_on_fail, ad, network, num_of_tries=num_of_tries)
+ _wifi_connect, assert_on_fail, ad, network, num_of_tries=num_of_tries,
+ check_connectivity=check_connectivity)
-def _wifi_connect(ad, network, num_of_tries=1):
+def _wifi_connect(ad, network, num_of_tries=1, check_connectivity=True):
"""Connect an Android device to a wifi network.
Initiate connection to a wifi network, wait for the "connected" event, then
@@ -1062,10 +1223,11 @@
# Wait for data connection to stabilize.
time.sleep(5)
- internet = validate_connection(ad, DEFAULT_PING_ADDR)
- if not internet:
- raise signals.TestFailure("Failed to connect to internet on %s" %
- expected_ssid)
+ if check_connectivity:
+ internet = validate_connection(ad, DEFAULT_PING_ADDR)
+ if not internet:
+ raise signals.TestFailure("Failed to connect to internet on %s" %
+ expected_ssid)
except Empty:
asserts.fail("Failed to start connection process to %s on %s" %
(network, ad.serial))
@@ -1480,3 +1642,65 @@
asserts.fail(("Either two or four attenuators are required for this "
"test, but found %s") % num_of_attns)
return [attn0, attn1]
+
+def set_attns(attenuator, attn_val_name):
+ """Sets attenuation values on attenuators used in this test.
+
+ Args:
+ attenuator: The attenuator object.
+ attn_val_name: Name of the attenuation value pair to use.
+ """
+ logging.info("Set attenuation values to %s", roaming_attn[attn_val_name])
+ try:
+ attenuator[0].set_atten(roaming_attn[attn_val_name][0])
+ attenuator[1].set_atten(roaming_attn[attn_val_name][1])
+ attenuator[2].set_atten(roaming_attn[attn_val_name][2])
+ attenuator[3].set_atten(roaming_attn[attn_val_name][3])
+ except:
+ logging.exception("Failed to set attenuation values %s.",
+ attn_val_name)
+ raise
+
+
+def trigger_roaming_and_validate(dut, attenuator, attn_val_name, expected_con):
+ """Sets attenuators to trigger roaming and validate the DUT connected
+ to the BSSID expected.
+
+ Args:
+ attenuator: The attenuator object.
+ attn_val_name: Name of the attenuation value pair to use.
+ expected_con: The network information of the expected network.
+ """
+ expected_con = {
+ WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY],
+ WifiEnums.BSSID_KEY: expected_con["bssid"],
+ }
+ set_attns(attenuator, attn_val_name)
+ logging.info("Wait %ss for roaming to finish.", ROAMING_TIMEOUT)
+ time.sleep(ROAMING_TIMEOUT)
+ try:
+ # Wakeup device and verify connection.
+ dut.droid.wakeLockAcquireBright()
+ dut.droid.wakeUpNow()
+ cur_con = dut.droid.wifiGetConnectionInfo()
+ verify_wifi_connection_info(dut, expected_con)
+ expected_bssid = expected_con[WifiEnums.BSSID_KEY]
+ logging.info("Roamed to %s successfully", expected_bssid)
+ if not validate_connection(dut):
+ raise signals.TestFailure("Fail to connect to internet on %s" %
+ expected_ssid)
+ finally:
+ dut.droid.wifiLockRelease()
+ dut.droid.goToSleepNow()
+
+
+def create_softap_config():
+ """Create a softap config with random ssid and password."""
+ ap_ssid = "softap_" + utils.rand_ascii_str(8)
+ ap_password = utils.rand_ascii_str(8)
+ logging.info("softap setup: %s %s", ap_ssid, ap_password)
+ config = {
+ WifiEnums.SSID_KEY: ap_ssid,
+ WifiEnums.PWD_KEY: ap_password,
+ }
+ return config
diff --git a/acts/framework/acts/utils.py b/acts/framework/acts/utils.py
index 62c5d58..e6b943a 100755
--- a/acts/framework/acts/utils.py
+++ b/acts/framework/acts/utils.py
@@ -707,6 +707,12 @@
If new_state is False, turn off location service.
If new_state if True, set location service to "High accuracy".
"""
+ ad.adb.shell("content insert --uri "
+ " content://com.google.settings/partner --bind "
+ "name:s:network_location_opt_in --bind value:s:1")
+ ad.adb.shell("content insert --uri "
+ " content://com.google.settings/partner --bind "
+ "name:s:use_location_for_services --bind value:s:1")
if new_state:
ad.adb.shell("settings put secure location_providers_allowed +gps")
ad.adb.shell("settings put secure location_providers_allowed +network")
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
index 48c71bb..66e7052 100755
--- a/acts/framework/setup.py
+++ b/acts/framework/setup.py
@@ -17,7 +17,6 @@
from distutils import cmd
from distutils import log
import os
-import pip
import shutil
import setuptools
from setuptools.command import test
@@ -29,11 +28,14 @@
# mock-1.0.1 is the last version compatible with setuptools <17.1,
# which is what comes with Ubuntu 14.04 LTS.
'mock<=1.0.1',
+ 'numpy',
'pyserial',
'shellescape',
'protobuf',
'roman',
'scapy-python3',
+ 'pylibftdi',
+ 'xlsxwriter'
]
if sys.version_info < (3, ):
@@ -85,7 +87,7 @@
for package in required_packages:
self.announce('Installing %s...' % package, log.INFO)
- pip.main(['install', package])
+ pip.main(['install', '-v', '--no-cache-dir', package])
self.announce('Dependencies installed.')
@@ -122,7 +124,7 @@
def run(self):
"""Entry point for the uninstaller."""
# Remove the working directory from the python path. This ensures that
- # Source code is not accidently tarageted.
+ # Source code is not accidentally targeted.
our_dir = os.path.abspath(os.path.dirname(__file__))
if our_dir in sys.path:
sys.path.remove(our_dir)
@@ -167,6 +169,13 @@
},
url="http://www.android.com/")
+ if {'-u', '--uninstall', 'uninstall'}.intersection(sys.argv):
+ act_path = '/usr/local/bin/act.py'
+ if os.path.islink(act_path):
+ os.unlink(act_path)
+ elif os.path.exists(act_path):
+ os.remove(act_path)
+
if __name__ == '__main__':
main()
diff --git a/acts/framework/tests/AttenuatorSanityTest.py b/acts/framework/tests/AttenuatorSanityTest.py
index b2f5f47..7f86ef9 100644
--- a/acts/framework/tests/AttenuatorSanityTest.py
+++ b/acts/framework/tests/AttenuatorSanityTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright (C) 2016 The Android Open Source Project
#
diff --git a/acts/framework/tests/IntegrationTest.py b/acts/framework/tests/IntegrationTest.py
index c8bdf2f..3cb34b7 100755
--- a/acts/framework/tests/IntegrationTest.py
+++ b/acts/framework/tests/IntegrationTest.py
@@ -31,7 +31,3 @@
self.log.info("This is a bare minimal test to make sure the basic ACTS"
"test flow works.")
asserts.explicit_pass("Hello World")
-
-
-if __name__ == "__main__":
- test_runner.main()
diff --git a/acts/framework/tests/Sl4aSanityTest.py b/acts/framework/tests/Sl4aSanityTest.py
index c3671a3..a88272f 100644
--- a/acts/framework/tests/Sl4aSanityTest.py
+++ b/acts/framework/tests/Sl4aSanityTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright (C) 2016 The Android Open Source Project
#
diff --git a/acts/framework/tests/SnifferSanityTest.py b/acts/framework/tests/SnifferSanityTest.py
index fa9ed8b..0787873 100644
--- a/acts/framework/tests/SnifferSanityTest.py
+++ b/acts/framework/tests/SnifferSanityTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright (C) 2016 The Android Open Source Project
#
diff --git a/acts/framework/tests/acts_adb_test.py b/acts/framework/tests/acts_adb_test.py
index b56ef8b..5e0544a 100755
--- a/acts/framework/tests/acts_adb_test.py
+++ b/acts/framework/tests/acts_adb_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2017 - The Android Open Source Project
#
diff --git a/acts/framework/tests/acts_android_device_test.py b/acts/framework/tests/acts_android_device_test.py
index 02aa485..b2cb0b5 100755
--- a/acts/framework/tests/acts_android_device_test.py
+++ b/acts/framework/tests/acts_android_device_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
@@ -26,13 +26,16 @@
# Mock log path for a test run.
MOCK_LOG_PATH = "/tmp/logs/MockTest/xx-xx-xx_xx-xx-xx/"
+
# Mock start and end time of the adb cat.
MOCK_ADB_LOGCAT_BEGIN_TIME = "1970-01-02 21:03:20.123"
MOCK_ADB_LOGCAT_END_TIME = "1970-01-02 21:22:02.000"
MOCK_ADB_EPOCH_BEGIN_TIME = 191000123
MOCK_SERIAL = 1
-MOCK_BUILD_ID = "ABC1.123456.007"
+MOCK_RELEASE_BUILD_ID = "ABC1.123456.007"
+MOCK_DEV_BUILD_ID = "ABC-MR1"
+MOCK_NYC_BUILD_ID = "N4F27P"
def get_mock_ads(num):
@@ -60,13 +63,20 @@
return [ad.serial for ad in get_mock_ads(5)]
-class MockAdbProxy():
+class MockAdbProxy(object):
"""Mock class that swaps out calls to adb with mock calls."""
- def __init__(self, serial, fail_br=False, fail_br_before_N=False):
+ def __init__(self,
+ serial,
+ fail_br=False,
+ fail_br_before_N=False,
+ build_id=MOCK_RELEASE_BUILD_ID):
self.serial = serial
self.fail_br = fail_br
self.fail_br_before_N = fail_br_before_N
+ self.return_value = None
+ self.return_multiple = False
+ self.build_id = build_id
def shell(self, params, ignore_status=False, timeout=60):
if params == "id -u":
@@ -79,10 +89,15 @@
if self.fail_br_before_N:
return "/system/bin/sh: bugreportz: not found"
return "1.1"
+ else:
+ if self.return_multiple:
+ return self.return_value.pop(0)
+ else:
+ return self.return_value
def getprop(self, params):
if params == "ro.build.id":
- return MOCK_BUILD_ID
+ return self.build_id
elif params == "ro.build.version.incremental":
return "123456789"
elif params == "ro.build.type":
@@ -98,7 +113,9 @@
def bugreport(self, params, timeout=android_device.BUG_REPORT_TIMEOUT):
expected = os.path.join(
logging.log_path, "AndroidDevice%s" % self.serial,
- "test_something", "AndroidDevice%s_sometime" % self.serial)
+ "test_something", "AndroidDevice%s_%s" %
+ (self.serial,
+ logger.normalize_log_line_timestamp(MOCK_ADB_LOGCAT_BEGIN_TIME)))
assert expected in params, "Expected '%s', got '%s'." % (expected,
params)
@@ -228,25 +245,28 @@
# These tests mock out any interaction with the OS and real android device
# in AndroidDeivce.
- @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+ @mock.patch(
+ 'acts.controllers.adb.AdbProxy',
+ return_value=MockAdbProxy(MOCK_SERIAL))
@mock.patch(
'acts.controllers.fastboot.FastbootProxy',
- return_value=MockFastbootProxy(1))
+ return_value=MockFastbootProxy(MOCK_SERIAL))
def test_AndroidDevice_instantiation(self, MockFastboot, MockAdbProxy):
"""Verifies the AndroidDevice object's basic attributes are correctly
set after instantiation.
"""
- mock_serial = 1
- ad = android_device.AndroidDevice(serial=mock_serial)
+ ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
self.assertEqual(ad.serial, 1)
self.assertEqual(ad.model, "fakemodel")
self.assertIsNone(ad.adb_logcat_process)
self.assertIsNone(ad.adb_logcat_file_path)
expected_lp = os.path.join(logging.log_path,
- "AndroidDevice%s" % mock_serial)
+ "AndroidDevice%s" % MOCK_SERIAL)
self.assertEqual(ad.log_path, expected_lp)
- @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+ @mock.patch(
+ 'acts.controllers.adb.AdbProxy',
+ return_value=MockAdbProxy(MOCK_SERIAL))
@mock.patch(
'acts.controllers.fastboot.FastbootProxy',
return_value=MockFastbootProxy(MOCK_SERIAL))
@@ -260,11 +280,14 @@
self.assertEqual(build_info["build_id"], "ABC1.123456.007")
self.assertEqual(build_info["build_type"], "userdebug")
- @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+ @mock.patch(
+ 'acts.controllers.adb.AdbProxy',
+ return_value=MockAdbProxy(MOCK_SERIAL, build_id=MOCK_DEV_BUILD_ID))
@mock.patch(
'acts.controllers.fastboot.FastbootProxy',
return_value=MockFastbootProxy(MOCK_SERIAL))
- def test_AndroidDevice_build_info_dev(self, MockFastboot, MockAdbProxy):
+ def test_AndroidDevice_build_info_release(self, MockFastboot,
+ MockAdbProxy):
"""Verifies the AndroidDevice object's basic attributes are correctly
set after instantiation.
"""
@@ -283,6 +306,36 @@
@mock.patch(
'acts.controllers.fastboot.FastbootProxy',
return_value=MockFastbootProxy(MOCK_SERIAL))
+ def test_AndroidDevice_build_info_dev(self, MockFastboot, MockAdbProxy):
+ """Verifies the AndroidDevice object's basic attributes are correctly
+ set after instantiation.
+ """
+ ad = android_device.AndroidDevice(serial=1)
+ build_info = ad.build_info
+ self.assertEqual(build_info["build_id"], "123456789")
+ self.assertEqual(build_info["build_type"], "userdebug")
+
+ @mock.patch(
+ 'acts.controllers.adb.AdbProxy',
+ return_value=MockAdbProxy(MOCK_SERIAL, build_id=MOCK_NYC_BUILD_ID))
+ @mock.patch(
+ 'acts.controllers.fastboot.FastbootProxy',
+ return_value=MockFastbootProxy(MOCK_SERIAL))
+ def test_AndroidDevice_build_info_nyc(self, MockFastboot, MockAdbProxy):
+ """Verifies the AndroidDevice object's build id is set correctly for
+ NYC releases.
+ """
+ ad = android_device.AndroidDevice(serial=1)
+ build_info = ad.build_info
+ self.assertEqual(build_info["build_id"], MOCK_NYC_BUILD_ID)
+
+ @mock.patch(
+ 'acts.controllers.adb.AdbProxy',
+ return_value=MockAdbProxy(MOCK_SERIAL))
+ @mock.patch(
+ 'acts.controllers.fastboot.FastbootProxy',
+ return_value=MockFastbootProxy(MOCK_SERIAL))
+
@mock.patch('acts.utils.create_dir')
@mock.patch('acts.utils.exe_cmd')
def test_AndroidDevice_take_bug_report(self, exe_mock, create_dir_mock,
@@ -290,19 +343,18 @@
"""Verifies AndroidDevice.take_bug_report calls the correct adb command
and writes the bugreport file to the correct path.
"""
- mock_serial = 1
- ad = android_device.AndroidDevice(serial=mock_serial)
- ad.take_bug_report("test_something", "sometime")
+ ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
+ ad.take_bug_report("test_something", 234325.32)
expected_path = os.path.join(
logging.log_path, "AndroidDevice%s" % ad.serial, "test_something")
create_dir_mock.assert_called_with(expected_path)
@mock.patch(
'acts.controllers.adb.AdbProxy',
- return_value=MockAdbProxy(1, fail_br=True))
+ return_value=MockAdbProxy(MOCK_SERIAL, fail_br=True))
@mock.patch(
'acts.controllers.fastboot.FastbootProxy',
- return_value=MockFastbootProxy(1))
+ return_value=MockFastbootProxy(MOCK_SERIAL))
@mock.patch('acts.utils.create_dir')
@mock.patch('acts.utils.exe_cmd')
def test_AndroidDevice_take_bug_report_fail(
@@ -310,19 +362,18 @@
"""Verifies AndroidDevice.take_bug_report writes out the correct message
when taking bugreport fails.
"""
- mock_serial = 1
- ad = android_device.AndroidDevice(serial=mock_serial)
+ ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
expected_msg = "Failed to take bugreport on 1: OMG I died!"
with self.assertRaisesRegex(android_device.AndroidDeviceError,
expected_msg):
- ad.take_bug_report("test_something", "sometime")
+ ad.take_bug_report("test_something", 4346343.23)
@mock.patch(
'acts.controllers.adb.AdbProxy',
- return_value=MockAdbProxy(1, fail_br_before_N=True))
+ return_value=MockAdbProxy(MOCK_SERIAL, fail_br_before_N=True))
@mock.patch(
'acts.controllers.fastboot.FastbootProxy',
- return_value=MockFastbootProxy(1))
+ return_value=MockFastbootProxy(MOCK_SERIAL))
@mock.patch('acts.utils.create_dir')
@mock.patch('acts.utils.exe_cmd')
def test_AndroidDevice_take_bug_report_fallback(
@@ -330,17 +381,18 @@
"""Verifies AndroidDevice.take_bug_report falls back to traditional
bugreport on builds that do not have bugreportz.
"""
- mock_serial = 1
- ad = android_device.AndroidDevice(serial=mock_serial)
- ad.take_bug_report("test_something", "sometime")
+ ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
+ ad.take_bug_report("test_something", MOCK_ADB_EPOCH_BEGIN_TIME)
expected_path = os.path.join(
logging.log_path, "AndroidDevice%s" % ad.serial, "test_something")
create_dir_mock.assert_called_with(expected_path)
- @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+ @mock.patch(
+ 'acts.controllers.adb.AdbProxy',
+ return_value=MockAdbProxy(MOCK_SERIAL))
@mock.patch(
'acts.controllers.fastboot.FastbootProxy',
- return_value=MockFastbootProxy(1))
+ return_value=MockFastbootProxy(MOCK_SERIAL))
@mock.patch('acts.utils.create_dir')
@mock.patch('acts.utils.start_standing_subprocess', return_value="process")
@mock.patch('acts.utils.stop_standing_subprocess')
@@ -352,8 +404,7 @@
object, including various function calls and the expected behaviors of
the calls.
"""
- mock_serial = 1
- ad = android_device.AndroidDevice(serial=mock_serial)
+ ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
expected_msg = ("Android device .* does not have an ongoing adb logcat"
" collection.")
# Expect error if stop is called before start.
@@ -383,10 +434,12 @@
self.assertIsNone(ad.adb_logcat_process)
self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
- @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+ @mock.patch(
+ 'acts.controllers.adb.AdbProxy',
+ return_value=MockAdbProxy(MOCK_SERIAL))
@mock.patch(
'acts.controllers.fastboot.FastbootProxy',
- return_value=MockFastbootProxy(1))
+ return_value=MockFastbootProxy(MOCK_SERIAL))
@mock.patch('acts.utils.create_dir')
@mock.patch('acts.utils.start_standing_subprocess', return_value="process")
@mock.patch('acts.utils.stop_standing_subprocess')
@@ -398,8 +451,7 @@
object, including various function calls and the expected behaviors of
the calls.
"""
- mock_serial = 1
- ad = android_device.AndroidDevice(serial=mock_serial)
+ ad = android_device.AndroidDevice(serial=MOCK_SERIAL)
ad.adb_logcat_param = "-b radio"
expected_msg = ("Android device .* does not have an ongoing adb logcat"
" collection.")
@@ -418,6 +470,7 @@
start_proc_mock.assert_called_with(adb_cmd % (ad.serial,
expected_log_path))
self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
+
@mock.patch(
'acts.controllers.adb.AdbProxy',
return_value=MockAdbProxy(MOCK_SERIAL))
diff --git a/acts/framework/tests/acts_asserts_test.py b/acts/framework/tests/acts_asserts_test.py
index dbf39d9..da8d4c7 100755
--- a/acts/framework/tests/acts_asserts_test.py
+++ b/acts/framework/tests/acts_asserts_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/tests/acts_base_class_test.py b/acts/framework/tests/acts_base_class_test.py
index 7ee3e85..d4ae599 100755
--- a/acts/framework/tests/acts_base_class_test.py
+++ b/acts/framework/tests/acts_base_class_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
@@ -54,9 +54,9 @@
class MockBaseTest(base_test.BaseTestClass):
def test_func(self):
asserts.assert_true(
- self.current_test_name == "test_func",
- ("Got "
- "unexpected test name %s.") % self.current_test_name)
+ self.current_test_name == "test_func",
+ ("Got "
+ "unexpected test name %s.") % self.current_test_name)
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run(test_names=["test_func"])
diff --git a/acts/framework/tests/acts_host_utils_test.py b/acts/framework/tests/acts_host_utils_test.py
index e75d27a..f13d328 100755
--- a/acts/framework/tests/acts_host_utils_test.py
+++ b/acts/framework/tests/acts_host_utils_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
@@ -14,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import mock
import socket
import unittest
@@ -26,19 +25,6 @@
under acts.controllers.adb.
"""
- def test_is_port_available_positive(self):
- # Unfortunately, we cannot do this test reliably for SOCK_STREAM
- # because the kernel allow this socket to linger about for some
- # small amount of time. We're not using SO_REUSEADDR because we
- # are working around behavior on darwin where binding to localhost
- # on some port and then binding again to the wildcard address
- # with SO_REUSEADDR seems to be allowed.
- test_s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- test_s.bind(('localhost', 0))
- port = test_s.getsockname()[1]
- test_s.close()
- self.assertTrue(host_utils.is_port_available(port))
-
def test_detects_udp_port_in_use(self):
test_s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
test_s.bind(('localhost', 0))
diff --git a/acts/framework/tests/acts_import_test_utils_test.py b/acts/framework/tests/acts_import_test_utils_test.py
index 2171b38..a0a66fd 100755
--- a/acts/framework/tests/acts_import_test_utils_test.py
+++ b/acts/framework/tests/acts_import_test_utils_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/tests/acts_import_unit_test.py b/acts/framework/tests/acts_import_unit_test.py
index 5f0b20c..eb78c9f 100755
--- a/acts/framework/tests/acts_import_unit_test.py
+++ b/acts/framework/tests/acts_import_unit_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
@@ -19,7 +19,7 @@
import sys
import uuid
-if sys.version_info < (3,):
+if sys.version_info < (3, ):
import warnings
with warnings.catch_warnings():
@@ -51,8 +51,9 @@
'acts/controllers/native.py',
'acts/controllers/native_android_device.py',
'acts/test_utils/wifi/wifi_power_test_utils.py',
- 'acts/framework/acts/controllers/packet_sender.py',
+ 'acts/controllers/packet_sender.py',
'acts/test_utils/wifi/wifi_retail_ap.py',
+ 'acts/test_utils/bt/bt_power_test_utils.py',
]
diff --git a/acts/framework/tests/acts_job_test.py b/acts/framework/tests/acts_job_test.py
index f93bf3d..a86ca91 100755
--- a/acts/framework/tests/acts_job_test.py
+++ b/acts/framework/tests/acts_job_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/tests/acts_logger_test.py b/acts/framework/tests/acts_logger_test.py
index dd18ae7..e804e45 100755
--- a/acts/framework/tests/acts_logger_test.py
+++ b/acts/framework/tests/acts_logger_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/tests/acts_records_test.py b/acts/framework/tests/acts_records_test.py
index cbf6561..ee59258 100755
--- a/acts/framework/tests/acts_records_test.py
+++ b/acts/framework/tests/acts_records_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
@@ -49,7 +49,7 @@
d[records.TestResultEnums.RECORD_BEGIN_TIME] = record.begin_time
d[records.TestResultEnums.RECORD_END_TIME] = record.end_time
d[records.TestResultEnums.
- RECORD_LOG_BEGIN_TIME] = record.log_begin_time
+ RECORD_LOG_BEGIN_TIME] = record.log_begin_time
d[records.TestResultEnums.RECORD_LOG_END_TIME] = record.log_end_time
d[records.TestResultEnums.RECORD_UID] = None
d[records.TestResultEnums.RECORD_CLASS] = None
diff --git a/acts/framework/tests/acts_relay_controller_test.py b/acts/framework/tests/acts_relay_controller_test.py
index 2676bff..8e21fe0 100755
--- a/acts/framework/tests/acts_relay_controller_test.py
+++ b/acts/framework/tests/acts_relay_controller_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
@@ -40,6 +40,7 @@
RelayDevice = relay_device.RelayDevice
RelayRig = relay_rig.RelayRig
SainSmartBoard = sain_smart_board.SainSmartBoard
+RelayDeviceConnectionError = errors.RelayDeviceConnectionError
class MockBoard(RelayBoard):
@@ -324,6 +325,18 @@
self.ss_board.set(self.r0.position, RelayState.NO)
self.assertNotEqual(os.stat(self.test_dir[7:] + '00').st_atime, 0)
+ def test_connection_error_no_tux(self):
+ default_status_msg = self.STATUS_MSG
+ self.STATUS_MSG = self.STATUS_MSG.replace('TUX', '')
+ try:
+ self._set_status_page('1111111111111111')
+ self.ss_board.get_relay_status(0)
+ except RelayDeviceConnectionError:
+ self.STATUS_MSG = default_status_msg
+ return
+
+ self.fail('Should have thrown an error without TUX appearing.')
+
class ActsRelayRigTest(unittest.TestCase):
def setUp(self):
@@ -700,7 +713,7 @@
})
self.mock_board = self.mock_rig.boards['MockBoard']
self.fugu_config = {
- 'type': 'GenericRelayDevice',
+ 'type': 'FuguRemote',
'name': 'UniqueDeviceName',
'mac_address': '00:00:00:00:00:00',
'relays': {
diff --git a/acts/framework/tests/acts_test_runner_test.py b/acts/framework/tests/acts_test_runner_test.py
index 451ee98..5b21ea9 100755
--- a/acts/framework/tests/acts_test_runner_test.py
+++ b/acts/framework/tests/acts_test_runner_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
@@ -209,7 +209,11 @@
@mock.patch(
'acts.controllers.android_device.AndroidDevice.ensure_screen_on',
return_value=True)
- def test_run_two_test_classes(self, mock_ensure_screen_on, mock_get_all,
+ @mock.patch(
+ 'acts.controllers.android_device.AndroidDevice.exit_setup_wizard',
+ return_value=True)
+ def test_run_two_test_classes(self, mock_exit_setup_wizard,
+ mock_ensure_screen_on, mock_get_all,
mock_list_adb, mock_fastboot, mock_adb):
"""Verifies that runing more than one test class in one test run works
proerly.
diff --git a/acts/framework/tests/acts_test_ssh.py b/acts/framework/tests/acts_test_ssh.py
index eb4efe7..c6f1250 100755
--- a/acts/framework/tests/acts_test_ssh.py
+++ b/acts/framework/tests/acts_test_ssh.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/tests/acts_unittest_suite.py b/acts/framework/tests/acts_unittest_suite.py
index 3b68d08..a4f1c94 100755
--- a/acts/framework/tests/acts_unittest_suite.py
+++ b/acts/framework/tests/acts_unittest_suite.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/tests/acts_utils_test.py b/acts/framework/tests/acts_utils_test.py
index fd7b083..5114e2a 100755
--- a/acts/framework/tests/acts_utils_test.py
+++ b/acts/framework/tests/acts_utils_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/tests/audio_analysis_unittest.py b/acts/framework/tests/audio_analysis_unittest.py
new file mode 100644
index 0000000..d90b822
--- /dev/null
+++ b/acts/framework/tests/audio_analysis_unittest.py
@@ -0,0 +1,358 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import logging
+import numpy
+import os
+import unittest
+
+import acts.test_utils.audio_analysis_lib.audio_analysis as audio_analysis
+import acts.test_utils.audio_analysis_lib.audio_data as audio_data
+
+
+class SpectralAnalysisTest(unittest.TestCase):
+ def setUp(self):
+ """Uses the same seed to generate noise for each test."""
+ numpy.random.seed(0)
+
+ def dummy_peak_detection(self, array, window_size):
+ """Detects peaks in an array in simple way.
+
+ A point (i, array[i]) is a peak if array[i] is the maximum among
+ array[i - half_window_size] to array[i + half_window_size].
+ If array[i - half_window_size] to array[i + half_window_size] are all
+ equal, then there is no peak in this window.
+
+ Args:
+ array: The input array to detect peaks in. Array is a list of
+ absolute values of the magnitude of transformed coefficient.
+ window_size: The window to detect peaks.
+
+ Returns:
+ A list of tuples:
+ [(peak_index_1, peak_value_1), (peak_index_2, peak_value_2),
+ ...]
+ where the tuples are sorted by peak values.
+
+ """
+ half_window_size = window_size / 2
+ length = len(array)
+
+ def mid_is_peak(array, mid, left, right):
+ """Checks if value at mid is the largest among left to right.
+
+ Args:
+ array: A list of numbers.
+ mid: The mid index.
+ left: The left index.
+ rigth: The right index.
+
+ Returns:
+ True if array[index] is the maximum among numbers in array
+ between index [left, right] inclusively.
+
+ """
+ value_mid = array[int(mid)]
+ for index in range(int(left), int(right) + 1):
+ if index == mid:
+ continue
+ if array[index] >= value_mid:
+ return False
+ return True
+
+ results = []
+ for mid in range(length):
+ left = max(0, mid - half_window_size)
+ right = min(length - 1, mid + half_window_size)
+ if mid_is_peak(array, mid, left, right):
+ results.append((mid, array[int(mid)]))
+
+ # Sort the peaks by values.
+ return sorted(results, key=lambda x: x[1], reverse=True)
+
+ def testPeakDetection(self):
+ array = [0, 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 5, 3, 2, 1, 1, 1, 1, 1]
+ result = audio_analysis.peak_detection(array, 4)
+ golden_answer = [(12, 5), (4, 4)]
+ self.assertEqual(result, golden_answer)
+
+ def testPeakDetectionLarge(self):
+ array = numpy.random.uniform(0, 1, 1000000)
+ window_size = 100
+ logging.debug('Test large array using dummy peak detection')
+ dummy_answer = self.dummy_peak_detection(array, window_size)
+ logging.debug('Test large array using improved peak detection')
+ improved_answer = audio_analysis.peak_detection(array, window_size)
+ logging.debug('Compare the result')
+ self.assertEqual(dummy_answer, improved_answer)
+
+ def testSpectralAnalysis(self):
+ rate = 48000
+ length_in_secs = 0.5
+ freq_1 = 490.0
+ freq_2 = 60.0
+ coeff_1 = 1
+ coeff_2 = 0.3
+ samples = length_in_secs * rate
+ noise = numpy.random.standard_normal(int(samples)) * 0.005
+ x = numpy.linspace(0.0, (samples - 1) * 1.0 / rate, samples)
+ y = (coeff_1 * numpy.sin(freq_1 * 2.0 * numpy.pi * x) + coeff_2 *
+ numpy.sin(freq_2 * 2.0 * numpy.pi * x)) + noise
+ results = audio_analysis.spectral_analysis(y, rate)
+ # Results should contains
+ # [(490, 1*k), (60, 0.3*k), (0, 0.1*k)] where 490Hz is the dominant
+ # frequency with coefficient 1, 60Hz is the second dominant frequency
+ # with coefficient 0.3, 0Hz is from Gaussian noise with coefficient
+ # around 0.1. The k constant is resulted from window function.
+ logging.debug('Results: %s', results)
+ self.assertTrue(abs(results[0][0] - freq_1) < 1)
+ self.assertTrue(abs(results[1][0] - freq_2) < 1)
+ self.assertTrue(
+ abs(results[0][1] / results[1][1] - coeff_1 / coeff_2) < 0.01)
+
+ def testSpectralAnalysisRealData(self):
+ """This unittest checks the spectral analysis works on real data."""
+ file_path = os.path.join(
+ os.path.dirname(__file__), 'test_data', '1k_2k.raw')
+ binary = open(file_path, 'rb').read()
+ data = audio_data.AudioRawData(binary, 2, 'S32_LE')
+ saturate_value = audio_data.get_maximum_value_from_sample_format(
+ 'S32_LE')
+ golden_frequency = [1000, 2000]
+ for channel in [0, 1]:
+ normalized_signal = audio_analysis.normalize_signal(
+ data.channel_data[channel], saturate_value)
+ spectral = audio_analysis.spectral_analysis(normalized_signal,
+ 48000, 0.02)
+ logging.debug('channel %s: %s', channel, spectral)
+ self.assertTrue(
+ abs(spectral[0][0] - golden_frequency[channel]) < 5,
+ 'Dominant frequency is not correct')
+
+ def testNotMeaningfulData(self):
+ """Checks that sepectral analysis handles un-meaningful data."""
+ rate = 48000
+ length_in_secs = 0.5
+ samples = length_in_secs * rate
+ noise_amplitude = audio_analysis.MEANINGFUL_RMS_THRESHOLD * 0.5
+ noise = numpy.random.standard_normal(int(samples)) * noise_amplitude
+ results = audio_analysis.spectral_analysis(noise, rate)
+ self.assertEqual([(0, 0)], results)
+
+ def testEmptyData(self):
+ """Checks that sepectral analysis rejects empty data."""
+ with self.assertRaises(audio_analysis.EmptyDataError):
+ results = audio_analysis.spectral_analysis([], 100)
+
+
+class NormalizeTest(unittest.TestCase):
+ def testNormalize(self):
+ y = [1, 2, 3, 4, 5]
+ normalized_y = audio_analysis.normalize_signal(y, 10)
+ expected = numpy.array([0.1, 0.2, 0.3, 0.4, 0.5])
+ for i in range(len(y)):
+ self.assertEqual(expected[i], normalized_y[i])
+
+
+class AnomalyTest(unittest.TestCase):
+ def setUp(self):
+ """Creates a test signal of sine wave."""
+ # Use the same seed for each test case.
+ numpy.random.seed(0)
+
+ self.block_size = 120
+ self.rate = 48000
+ self.freq = 440
+ length_in_secs = 0.25
+ self.samples = length_in_secs * self.rate
+ x = numpy.linspace(0.0, (self.samples - 1) * 1.0 / self.rate,
+ self.samples)
+ self.y = numpy.sin(self.freq * 2.0 * numpy.pi * x)
+
+ def add_noise(self):
+ """Add noise to the test signal."""
+ noise_amplitude = 0.3
+ noise = numpy.random.standard_normal(len(self.y)) * noise_amplitude
+ self.y = self.y + noise
+
+ def insert_anomaly(self):
+ """Inserts an anomaly to the test signal.
+
+ The anomaly self.anomaly_samples should be created before calling this
+ method.
+
+ """
+ self.anomaly_start_secs = 0.1
+ self.y = numpy.insert(self.y,
+ int(self.anomaly_start_secs * self.rate),
+ self.anomaly_samples)
+
+ def generate_skip_anomaly(self):
+ """Skips a section of test signal."""
+ self.anomaly_start_secs = 0.1
+ self.anomaly_duration_secs = 0.005
+ anomaly_append_secs = self.anomaly_start_secs + self.anomaly_duration_secs
+ anomaly_start_index = self.anomaly_start_secs * self.rate
+ anomaly_append_index = anomaly_append_secs * self.rate
+ self.y = numpy.append(self.y[:int(anomaly_start_index)],
+ self.y[int(anomaly_append_index):])
+
+ def create_constant_anomaly(self, amplitude):
+ """Creates an anomaly of constant samples.
+
+ Args:
+ amplitude: The amplitude of the constant samples.
+
+ """
+ self.anomaly_duration_secs = 0.005
+ self.anomaly_samples = ([amplitude] *
+ int(self.anomaly_duration_secs * self.rate))
+
+ def run_analysis(self):
+ """Runs the anomaly detection."""
+ self.results = audio_analysis.anomaly_detection(
+ self.y, self.rate, self.freq, self.block_size)
+ logging.debug('Results: %s', self.results)
+
+ def check_no_anomaly(self):
+ """Verifies that there is no anomaly in detection result."""
+ self.run_analysis()
+ self.assertFalse(self.results)
+
+ def check_anomaly(self):
+ """Verifies that there is anomaly in detection result.
+
+ The detection result should contain anomaly time stamps that are
+ close to where anomaly was inserted. There can be multiple anomalies
+ since the detection depends on the block size.
+
+ """
+ self.run_analysis()
+ self.assertTrue(self.results)
+ # Anomaly can be detected as long as the detection window of block size
+ # overlaps with anomaly.
+ expected_detected_range_secs = (
+ self.anomaly_start_secs - float(self.block_size) / self.rate,
+ self.anomaly_start_secs + self.anomaly_duration_secs)
+ for detected_secs in self.results:
+ self.assertTrue(detected_secs <= expected_detected_range_secs[1])
+ self.assertTrue(detected_secs >= expected_detected_range_secs[0])
+
+ def testGoodSignal(self):
+ """Sine wave signal with no noise or anomaly."""
+ self.check_no_anomaly()
+
+ def testGoodSignalNoise(self):
+ """Sine wave signal with noise."""
+ self.add_noise()
+ self.check_no_anomaly()
+
+ def testZeroAnomaly(self):
+ """Sine wave signal with no noise but with anomaly.
+
+ This test case simulates underrun in digital data where there will be
+ one block of samples with 0 amplitude.
+
+ """
+ self.create_constant_anomaly(0)
+ self.insert_anomaly()
+ self.check_anomaly()
+
+ def testZeroAnomalyNoise(self):
+ """Sine wave signal with noise and anomaly.
+
+ This test case simulates underrun in analog data where there will be
+ one block of samples with amplitudes close to 0.
+
+ """
+ self.create_constant_anomaly(0)
+ self.insert_anomaly()
+ self.add_noise()
+ self.check_anomaly()
+
+ def testLowConstantAnomaly(self):
+ """Sine wave signal with low constant anomaly.
+
+ The anomaly is one block of constant values.
+
+ """
+ self.create_constant_anomaly(0.05)
+ self.insert_anomaly()
+ self.check_anomaly()
+
+ def testLowConstantAnomalyNoise(self):
+ """Sine wave signal with low constant anomaly and noise.
+
+ The anomaly is one block of constant values.
+
+ """
+ self.create_constant_anomaly(0.05)
+ self.insert_anomaly()
+ self.add_noise()
+ self.check_anomaly()
+
+ def testHighConstantAnomaly(self):
+ """Sine wave signal with high constant anomaly.
+
+ The anomaly is one block of constant values.
+
+ """
+ self.create_constant_anomaly(2)
+ self.insert_anomaly()
+ self.check_anomaly()
+
+ def testHighConstantAnomalyNoise(self):
+ """Sine wave signal with high constant anomaly and noise.
+
+ The anomaly is one block of constant values.
+
+ """
+ self.create_constant_anomaly(2)
+ self.insert_anomaly()
+ self.add_noise()
+ self.check_anomaly()
+
+ def testSkippedAnomaly(self):
+ """Sine wave signal with skipped anomaly.
+
+ The anomaly simulates the symptom where a block is skipped.
+
+ """
+ self.generate_skip_anomaly()
+ self.check_anomaly()
+
+ def testSkippedAnomalyNoise(self):
+ """Sine wave signal with skipped anomaly with noise.
+
+ The anomaly simulates the symptom where a block is skipped.
+
+ """
+ self.generate_skip_anomaly()
+ self.add_noise()
+ self.check_anomaly()
+
+ def testEmptyData(self):
+ """Checks that anomaly detection rejects empty data."""
+ self.y = []
+ with self.assertRaises(audio_analysis.EmptyDataError):
+ self.check_anomaly()
+
+
+if __name__ == '__main__':
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+ unittest.main()
diff --git a/acts/framework/tests/audio_quality_measurement_unittest.py b/acts/framework/tests/audio_quality_measurement_unittest.py
new file mode 100644
index 0000000..0166ce9
--- /dev/null
+++ b/acts/framework/tests/audio_quality_measurement_unittest.py
@@ -0,0 +1,268 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import logging
+import math
+import numpy
+import unittest
+
+import acts.test_utils.audio_analysis_lib.audio_data as audio_data
+import acts.test_utils.audio_analysis_lib.audio_analysis as audio_analysis
+import acts.test_utils.audio_analysis_lib.audio_quality_measurement as \
+ audio_quality_measurement
+
+
+class NoiseLevelTest(unittest.TestCase):
+ def setUp(self):
+ """Uses the same seed to generate noise for each test."""
+ numpy.random.seed(0)
+
+ def testNoiseLevel(self):
+ # Generates the standard sin wave with standard_noise portion of noise.
+ rate = 48000
+ length_in_secs = 2
+ frequency = 440
+ amplitude = 1
+ standard_noise = 0.05
+
+ wave = []
+ for index in range(0, rate * length_in_secs):
+ phase = 2.0 * math.pi * frequency * float(index) / float(rate)
+ sine_wave = math.sin(phase)
+ noise = standard_noise * numpy.random.standard_normal()
+ wave.append(float(amplitude) * (sine_wave + noise))
+
+ # Calculates the average value after applying teager operator.
+ teager_value_of_wave, length = 0, len(wave)
+ for i in range(1, length - 1):
+ ith_teager_value = abs(wave[i] * wave[i] - wave[i - 1] * wave[i +
+ 1])
+ ith_teager_value *= max(1, abs(wave[i]))
+ teager_value_of_wave += ith_teager_value
+ teager_value_of_wave /= float(length * (amplitude**2))
+
+ noise = audio_quality_measurement.noise_level(
+ amplitude, frequency, rate, teager_value_of_wave)
+
+ self.assertTrue(abs(noise - standard_noise) < 0.01)
+
+
+class ErrorTest(unittest.TestCase):
+ def testError(self):
+ value1 = [0.2, 0.4, 0.1, 0.01, 0.01, 0.01]
+ value2 = [0.3, 0.3, 0.08, 0.0095, 0.0098, 0.0099]
+ error = [0.5, 0.25, 0.2, 0.05, 0.02, 0.01]
+ for i in range(len(value1)):
+ ret = audio_quality_measurement.error(value1[i], value2[i])
+ self.assertTrue(abs(ret - error[i]) < 0.001)
+
+
+class QualityMeasurementTest(unittest.TestCase):
+ def setUp(self):
+ """Creates a test signal of sine wave."""
+ numpy.random.seed(0)
+
+ self.rate = 48000
+ self.freq = 440
+ self.amplitude = 1
+ length_in_secs = 2
+ self.samples = length_in_secs * self.rate
+ self.y = []
+ for index in range(self.samples):
+ phase = 2.0 * math.pi * self.freq * float(index) / float(self.rate)
+ sine_wave = math.sin(phase)
+ self.y.append(float(self.amplitude) * sine_wave)
+
+ def add_noise(self):
+ """Adds noise to the test signal."""
+ noise_amplitude = 0.01 * self.amplitude
+ for index in range(self.samples):
+ noise = noise_amplitude * numpy.random.standard_normal()
+ self.y[index] += noise
+
+ def generate_delay(self):
+ """Generates some delays during playing."""
+ self.delay_start_time = [0.200, 0.375, 0.513, 0.814, 1.000, 1.300]
+ self.delay_end_time = [0.201, 0.377, 0.516, 0.824, 1.100, 1.600]
+
+ for i in range(len(self.delay_start_time)):
+ start_index = int(self.delay_start_time[i] * self.rate)
+ end_index = int(self.delay_end_time[i] * self.rate)
+ for j in range(start_index, end_index):
+ self.y[j] = 0
+
+ def generate_artifacts_before_playback(self):
+ """Generates artifacts before playing."""
+ silence_before_playback_end_time = 0.2
+ end_index = int(silence_before_playback_end_time * self.rate)
+ for i in range(0, end_index):
+ self.y[i] = 0
+ noise_start_index = int(0.1 * self.rate)
+ noise_end_index = int(0.1005 * self.rate)
+ for i in range(noise_start_index, noise_end_index):
+ self.y[i] = 3 * self.amplitude
+
+ def generate_artifacts_after_playback(self):
+ """Generates artifacts after playing."""
+ silence_after_playback_start_time = int(1.9 * self.rate)
+ noise_start_index = int(1.95 * self.rate)
+ noise_end_index = int((1.95 + 0.02) * self.rate)
+
+ for i in range(silence_after_playback_start_time, self.samples):
+ self.y[i] = 0
+ for i in range(noise_start_index, noise_end_index):
+ self.y[i] = self.amplitude
+
+ def generate_burst_during_playback(self):
+ """Generates bursts during playing."""
+ self.burst_start_time = [0.300, 0.475, 0.613, 0.814, 1.300]
+ self.burst_end_time = [0.301, 0.476, 0.614, 0.815, 1.301]
+
+ for i in range(len(self.burst_start_time)):
+ start_index = int(self.burst_start_time[i] * self.rate)
+ end_index = int(self.burst_end_time[i] * self.rate)
+ for j in range(start_index, end_index):
+ self.y[j] = self.amplitude * (3 + numpy.random.uniform(-1, 1))
+
+ def generate_volume_changing(self):
+ "Generates volume changing during playing."
+ start_time = [0.300, 1.400]
+ end_time = [0.600, 1.700]
+ for i in range(len(start_time)):
+ start_index = int(start_time[i] * self.rate)
+ end_index = int(end_time[i] * self.rate)
+ for j in range(start_index, end_index):
+ self.y[j] *= 1.4
+ self.volume_changing = [+1, -1, +1, -1]
+ self.volume_changing_time = [0.3, 0.6, 1.4, 1.7]
+
+ def testGoodSignal(self):
+ """Sine wave signal with no noise or artifacts."""
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(len(result['volume_changes']) == 0)
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+
+ def testGoodSignalNoise(self):
+ """Sine wave signal with noise."""
+ self.add_noise()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(len(result['volume_changes']) == 0)
+ self.assertTrue(0.009 < result['equivalent_noise_level'] and
+ result['equivalent_noise_level'] < 0.011)
+
+ def testDelay(self):
+ """Sine wave with delay during playing."""
+ self.generate_delay()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(
+ len(result['volume_changes']) == 2 * len(self.delay_start_time))
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+
+ self.assertTrue(
+ len(result['artifacts']['delay_during_playback']) ==
+ len(self.delay_start_time))
+ for i in range(len(result['artifacts']['delay_during_playback'])):
+ delta = abs(result['artifacts']['delay_during_playback'][i][0] -
+ self.delay_start_time[i])
+ self.assertTrue(delta < 0.001)
+ duration = self.delay_end_time[i] - self.delay_start_time[i]
+ delta = abs(result['artifacts']['delay_during_playback'][i][1] -
+ duration)
+ self.assertTrue(delta < 0.001)
+
+ def testArtifactsBeforePlayback(self):
+ """Sine wave with artifacts before playback."""
+ self.generate_artifacts_before_playback()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 1)
+ delta = abs(result['artifacts']['noise_before_playback'][0][0] - 0.1)
+ self.assertTrue(delta < 0.01)
+ delta = abs(result['artifacts']['noise_before_playback'][0][1] - 0.005)
+ self.assertTrue(delta < 0.004)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(len(result['volume_changes']) == 0)
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+
+ def testArtifactsAfterPlayback(self):
+ """Sine wave with artifacts after playback."""
+ self.generate_artifacts_after_playback()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 1)
+ delta = abs(result['artifacts']['noise_after_playback'][0][0] - 1.95)
+ self.assertTrue(delta < 0.01)
+ delta = abs(result['artifacts']['noise_after_playback'][0][1] - 0.02)
+ self.assertTrue(delta < 0.001)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(len(result['volume_changes']) == 0)
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+
+ def testBurstDuringPlayback(self):
+ """Sine wave with burst during playback."""
+ self.generate_burst_during_playback()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 5)
+ self.assertTrue(len(result['volume_changes']) == 10)
+ self.assertTrue(result['equivalent_noise_level'] > 0.02)
+ for i in range(len(result['artifacts']['burst_during_playback'])):
+ delta = abs(self.burst_start_time[i] - result['artifacts'][
+ 'burst_during_playback'][i])
+ self.assertTrue(delta < 0.002)
+
+ def testVolumeChanging(self):
+ """Sine wave with volume changing during playback."""
+ self.generate_volume_changing()
+ result = audio_quality_measurement.quality_measurement(self.y,
+ self.rate)
+ self.assertTrue(len(result['artifacts']['noise_before_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['noise_after_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['delay_during_playback']) == 0)
+ self.assertTrue(len(result['artifacts']['burst_during_playback']) == 0)
+ self.assertTrue(result['equivalent_noise_level'] < 0.005)
+ self.assertTrue(
+ len(result['volume_changes']) == len(self.volume_changing))
+ for i in range(len(self.volume_changing)):
+ self.assertTrue(
+ abs(self.volume_changing_time[i] - result['volume_changes'][i][
+ 0]) < 0.01)
+ self.assertTrue(
+ self.volume_changing[i] == result['volume_changes'][i][1])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts/framework/tests/controllers/__init__.py b/acts/framework/tests/controllers/__init__.py
index 9727988..9006087 100644
--- a/acts/framework/tests/controllers/__init__.py
+++ b/acts/framework/tests/controllers/__init__.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2018 - The Android Open Source Project
#
diff --git a/acts/framework/tests/controllers/sl4a_lib/__init__.py b/acts/framework/tests/controllers/sl4a_lib/__init__.py
index 9727988..9006087 100644
--- a/acts/framework/tests/controllers/sl4a_lib/__init__.py
+++ b/acts/framework/tests/controllers/sl4a_lib/__init__.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2018 - The Android Open Source Project
#
diff --git a/acts/framework/tests/controllers/sl4a_lib/rpc_client_test.py b/acts/framework/tests/controllers/sl4a_lib/rpc_client_test.py
old mode 100644
new mode 100755
index dbaa2ab..a663ab3
--- a/acts/framework/tests/controllers/sl4a_lib/rpc_client_test.py
+++ b/acts/framework/tests/controllers/sl4a_lib/rpc_client_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2018 - The Android Open Source Project
#
diff --git a/acts/framework/tests/controllers/sl4a_lib/rpc_connection_test.py b/acts/framework/tests/controllers/sl4a_lib/rpc_connection_test.py
index ce1d1a6..0190702 100755
--- a/acts/framework/tests/controllers/sl4a_lib/rpc_connection_test.py
+++ b/acts/framework/tests/controllers/sl4a_lib/rpc_connection_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/tests/controllers/sl4a_lib/sl4a_manager_test.py b/acts/framework/tests/controllers/sl4a_lib/sl4a_manager_test.py
old mode 100644
new mode 100755
index ded5f65..f6e751e
--- a/acts/framework/tests/controllers/sl4a_lib/sl4a_manager_test.py
+++ b/acts/framework/tests/controllers/sl4a_lib/sl4a_manager_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2018 - The Android Open Source Project
#
@@ -72,7 +72,8 @@
class Sl4aManagerTest(unittest.TestCase):
"""Tests the sl4a_manager.Sl4aManager class."""
- FIND_PORT_RETRIES = 0
+ ATTEMPT_INTERVAL = .25
+ MAX_WAIT_ON_SERVER_SECONDS = 1
_SL4A_LAUNCH_SERVER_CMD = ''
_SL4A_CLOSE_SERVER_CMD = ''
_SL4A_ROOT_FIND_PORT_CMD = ''
@@ -82,8 +83,10 @@
@classmethod
def setUpClass(cls):
# Copy all module constants before testing begins.
- Sl4aManagerTest.FIND_PORT_RETRIES = \
- sl4a_manager.FIND_PORT_RETRIES
+ Sl4aManagerTest.ATTEMPT_INTERVAL = \
+ sl4a_manager.ATTEMPT_INTERVAL
+ Sl4aManagerTest.MAX_WAIT_ON_SERVER_SECONDS = \
+ sl4a_manager.MAX_WAIT_ON_SERVER_SECONDS
Sl4aManagerTest._SL4A_LAUNCH_SERVER_CMD = \
sl4a_manager._SL4A_LAUNCH_SERVER_CMD
Sl4aManagerTest._SL4A_CLOSE_SERVER_CMD = \
@@ -97,8 +100,10 @@
def setUp(self):
# Restore all module constants at the beginning of each test case.
- sl4a_manager.FIND_PORT_RETRIES = \
- Sl4aManagerTest.FIND_PORT_RETRIES
+ sl4a_manager.ATTEMPT_INTERVAL = \
+ Sl4aManagerTest.ATTEMPT_INTERVAL
+ sl4a_manager.MAX_WAIT_ON_SERVER_SECONDS = \
+ Sl4aManagerTest.MAX_WAIT_ON_SERVER_SECONDS
sl4a_manager._SL4A_LAUNCH_SERVER_CMD = \
Sl4aManagerTest._SL4A_LAUNCH_SERVER_CMD
sl4a_manager._SL4A_CLOSE_SERVER_CMD = \
@@ -143,7 +148,8 @@
# One call for each session
self.assertSetEqual(set(returned_ports), {12345, 15973, 67890, 75638})
- def test_start_sl4a_server_uses_all_retries(self):
+ @mock.patch('time.sleep', return_value=None)
+ def test_start_sl4a_server_uses_all_retries(self, _):
"""Tests sl4a_manager.Sl4aManager.start_sl4a_server().
Tests to ensure that _start_sl4a_server retries and successfully returns
@@ -154,19 +160,21 @@
side_effects = []
expected_port = 12345
- for _ in range(sl4a_manager.FIND_PORT_RETRIES - 1):
+ for _ in range(int(sl4a_manager.MAX_WAIT_ON_SERVER_SECONDS /
+ sl4a_manager.ATTEMPT_INTERVAL) - 1):
side_effects.append(None)
side_effects.append(expected_port)
manager = sl4a_manager.create_sl4a_manager(adb)
manager._get_open_listening_port = mock.Mock(side_effect=side_effects)
try:
- found_port = manager.start_sl4a_server(0, try_interval=0)
+ found_port = manager.start_sl4a_server(0)
self.assertTrue(found_port)
except rpc_client.Sl4aConnectionError:
self.fail('start_sl4a_server failed to respect FIND_PORT_RETRIES.')
- def test_start_sl4a_server_fails_all_retries(self):
+ @mock.patch('time.sleep', return_value=None)
+ def test_start_sl4a_server_fails_all_retries(self, _):
"""Tests sl4a_manager.Sl4aManager.start_sl4a_server().
Tests to ensure that start_sl4a_server throws an error if all retries
@@ -176,13 +184,14 @@
adb.shell = lambda _, **kwargs: ''
side_effects = []
- for _ in range(sl4a_manager.FIND_PORT_RETRIES):
+ for _ in range(int(sl4a_manager.MAX_WAIT_ON_SERVER_SECONDS /
+ sl4a_manager.ATTEMPT_INTERVAL)):
side_effects.append(None)
manager = sl4a_manager.create_sl4a_manager(adb)
manager._get_open_listening_port = mock.Mock(side_effect=side_effects)
try:
- manager.start_sl4a_server(0, try_interval=0)
+ manager.start_sl4a_server(0)
self.fail('Sl4aConnectionError was not thrown.')
except rpc_client.Sl4aConnectionError:
pass
@@ -309,7 +318,7 @@
Tests that SL4A is started if it was not already running.
"""
adb = mock.Mock()
- adb.shell = mock.Mock(side_effect=['', ''])
+ adb.shell = mock.Mock(side_effect=['', '', ''])
manager = sl4a_manager.create_sl4a_manager(adb)
manager.is_sl4a_installed = lambda: True
@@ -319,28 +328,6 @@
self.fail('An error should not have been thrown.')
adb.shell.assert_called_with(sl4a_manager._SL4A_START_SERVICE_CMD)
- def test_start_sl4a_does_not_start_sl4a_if_sl4a_is_running(self):
- """Tests sl4a_manager.Sl4aManager.start_sl4a_service().
-
- Tests that SL4A is not started if it is already running.
- """
-
- def fail_on_start_service(command):
- if command == sl4a_manager._SL4A_START_SERVICE_CMD:
- self.fail(
- 'Called start command when SL4A was already started.')
- return 'SL4A has already started.'
-
- adb = mock.Mock()
- adb.shell = fail_on_start_service
-
- manager = sl4a_manager.create_sl4a_manager(adb)
- manager.is_sl4a_installed = lambda: True
- try:
- manager.start_sl4a_service()
- except rpc_client.MissingSl4AError:
- self.fail('An error should not have been thrown.')
-
def test_create_session_uses_oldest_server_port(self):
"""Tests sl4a_manager.Sl4aManager.create_session().
@@ -412,6 +399,7 @@
session_5.terminate = lambda *args, **kwargs: called_on(session_5)
manager.sessions[5] = session_5
+ manager._get_all_ports = lambda: []
manager.terminate_all_sessions()
# No duplicates calls to terminate.
self.assertEqual(
@@ -428,7 +416,9 @@
closed_ports = list()
def close(command):
- closed_ports.append(command)
+ if str.isdigit(command):
+ closed_ports.append(command)
+ return ''
adb = mock.Mock()
adb.shell = close
@@ -437,6 +427,7 @@
manager = sl4a_manager.Sl4aManager(adb)
manager._sl4a_ports = set(ports_to_close)
+ manager._get_all_ports = lambda: []
manager.terminate_all_sessions()
# No duplicate calls to close port
diff --git a/acts/framework/tests/controllers/sl4a_lib/sl4a_session_test.py b/acts/framework/tests/controllers/sl4a_lib/sl4a_session_test.py
old mode 100644
new mode 100755
index d16fb99..f056d2a
--- a/acts/framework/tests/controllers/sl4a_lib/sl4a_session_test.py
+++ b/acts/framework/tests/controllers/sl4a_lib/sl4a_session_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2018 - The Android Open Source Project
#
diff --git a/acts/framework/tests/controllers/sl4a_lib/test_suite.py b/acts/framework/tests/controllers/sl4a_lib/test_suite.py
index add8d6c..3f87225 100755
--- a/acts/framework/tests/controllers/sl4a_lib/test_suite.py
+++ b/acts/framework/tests/controllers/sl4a_lib/test_suite.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2018 - The Android Open Source Project
#
@@ -13,21 +13,6 @@
# 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.
-#!/usr/bin/env python3.4
-#
-# Copyright 2016 - 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.
import sys
import unittest
diff --git a/acts/framework/tests/libs/__init__.py b/acts/framework/tests/libs/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/tests/libs/__init__.py
diff --git a/acts/framework/tests/libs/ota/__init__.py b/acts/framework/tests/libs/ota/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/tests/libs/ota/__init__.py
diff --git a/acts/framework/tests/libs/ota/ota_runners/__init__.py b/acts/framework/tests/libs/ota/ota_runners/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_runners/__init__.py
diff --git a/acts/framework/tests/libs/ota/ota_runners/ota_runner_factory_test.py b/acts/framework/tests/libs/ota/ota_runners/ota_runner_factory_test.py
new file mode 100644
index 0000000..042f226
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_runners/ota_runner_factory_test.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import unittest
+
+import logging
+import mock
+
+from acts.controllers import android_device
+from acts.libs.ota.ota_runners import ota_runner
+from acts.libs.ota.ota_runners import ota_runner_factory
+from acts import config_parser
+
+
+class OtaRunnerFactoryTests(unittest.TestCase):
+ """Tests all of the functions in the ota_runner_factory module."""
+
+ def setUp(self):
+ self.device = mock.MagicMock()
+ self.device.serial = 'fake_serial'
+
+ def test_get_ota_value_from_config_no_map_key_missing(self):
+ acts_config = {}
+ with self.assertRaises(config_parser.ActsConfigError):
+ ota_runner_factory.get_ota_value_from_config(
+ acts_config, 'ota_tool', self.device)
+
+ def test_get_ota_value_from_config_with_map_key_missing(self):
+ acts_config = {'ota_map': {'fake_serial': 'MockOtaTool'}}
+ with self.assertRaises(config_parser.ActsConfigError):
+ ota_runner_factory.get_ota_value_from_config(
+ acts_config, 'ota_tool', self.device)
+
+ def test_get_ota_value_from_config_with_map_key_found(self):
+ expected_value = '/path/to/tool'
+ acts_config = {
+ 'ota_map': {
+ 'fake_serial': 'MockOtaTool'
+ },
+ 'ota_tool_MockOtaTool': expected_value
+ }
+ ret = ota_runner_factory.get_ota_value_from_config(
+ acts_config, 'ota_tool', self.device)
+ self.assertEqual(expected_value, ret)
+
+ def test_create_from_configs_raise_when_non_default_tool_path_missing(
+ self):
+ acts_config = {
+ 'ota_tool': 'FakeTool',
+ }
+ try:
+ ota_runner_factory.create_from_configs(acts_config, self.device)
+ except config_parser.ActsConfigError:
+ return
+ self.fail('create_from_configs did not throw an error when a tool was'
+ 'specified without a tool path.')
+
+ def test_create_from_configs_without_map_makes_proper_calls(self):
+ acts_config = {
+ 'ota_package': 'jkl;',
+ 'ota_sl4a': 'qaz',
+ 'ota_tool': 'FakeTool',
+ 'FakeTool': 'qwerty'
+ }
+ function_path = 'acts.libs.ota.ota_runners.ota_runner_factory.create'
+ with mock.patch(function_path) as mocked_function:
+ ota_runner_factory.create_from_configs(acts_config, self.device)
+ mocked_function.assert_called_with('jkl;', 'qaz', self.device,
+ 'FakeTool', 'qwerty')
+
+ def test_create_from_configs_with_map_makes_proper_calls(self):
+ acts_config = {
+ 'ota_map': {
+ 'fake_serial': "hardwareA"
+ },
+ 'ota_package_hardwareA': 'jkl;',
+ 'ota_sl4a_hardwareA': 'qaz',
+ 'ota_tool_hardwareA': 'FakeTool',
+ 'FakeTool': 'qwerty'
+ }
+ function_path = 'acts.libs.ota.ota_runners.ota_runner_factory.create'
+ with mock.patch(function_path) as mocked_function:
+ ota_runner_factory.create_from_configs(acts_config, self.device)
+ mocked_function.assert_called_with('jkl;', 'qaz', self.device,
+ 'FakeTool', 'qwerty')
+
+ def test_create_raise_on_ota_pkg_and_sl4a_fields_have_different_types(
+ self):
+ with mock.patch('acts.libs.ota.ota_tools.ota_tool_factory.create'):
+ with self.assertRaises(TypeError):
+ ota_runner_factory.create('ota_package', ['ota_sl4a'],
+ self.device)
+
+ def test_create_raise_on_ota_package_not_a_list_or_string(self):
+ with mock.patch('acts.libs.ota.ota_tools.ota_tool_factory.create'):
+ with self.assertRaises(TypeError):
+ ota_runner_factory.create({
+ 'ota': 'pkg'
+ }, {'ota': 'sl4a'}, self.device)
+
+ def test_create_returns_single_ota_runner_on_ota_package_being_a_str(self):
+ with mock.patch('acts.libs.ota.ota_tools.ota_tool_factory.create'):
+ ret = ota_runner_factory.create('', '', self.device)
+ self.assertEqual(type(ret), ota_runner.SingleUseOtaRunner)
+
+ def test_create_returns_multi_ota_runner_on_ota_package_being_a_list(self):
+ with mock.patch('acts.libs.ota.ota_tools.ota_tool_factory.create'):
+ ret = ota_runner_factory.create([], [], self.device)
+ self.assertEqual(type(ret), ota_runner.MultiUseOtaRunner)
+
+ def test_create_returns_bound_ota_runner_on_second_request(self):
+ with mock.patch('acts.libs.ota.ota_tools.ota_tool_factory.create'):
+ first_return = ota_runner_factory.create([], [], self.device)
+ logging.disable(logging.WARNING)
+ second_return = ota_runner_factory.create([], [], self.device)
+ logging.disable(logging.NOTSET)
+ self.assertEqual(first_return, second_return)
+
+ def test_create_returns_different_ota_runner_on_second_request(self):
+ with mock.patch('acts.libs.ota.ota_tools.ota_tool_factory.create'):
+ first_return = ota_runner_factory.create(
+ [], [], self.device, use_cached_runners=False)
+ second_return = ota_runner_factory.create(
+ [], [], self.device, use_cached_runners=False)
+ self.assertNotEqual(first_return, second_return)
diff --git a/acts/framework/tests/libs/ota/ota_runners/ota_runner_test.py b/acts/framework/tests/libs/ota/ota_runners/ota_runner_test.py
new file mode 100644
index 0000000..7acd6d6
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_runners/ota_runner_test.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import unittest
+import mock
+
+from acts.libs.ota.ota_tools import ota_tool
+from acts.libs.ota.ota_runners import ota_runner
+from acts.controllers import android_device
+
+
+class MockOtaTool(ota_tool.OtaTool):
+ def __init__(self, command):
+ super(MockOtaTool, self).__init__(command)
+ self.update_call_count = 0
+ self.cleanup_call_count = 0
+
+ def update(self, unused):
+ self.update_call_count += 1
+
+ def cleanup(self, unused):
+ self.cleanup_call_count += 1
+
+ def reset_count(self):
+ self.update_call_count = 0
+ self.cleanup_call_count = 0
+
+ def assert_calls_equal(self, test, number_of_expected_calls):
+ test.assertEqual(number_of_expected_calls, self.update_call_count)
+ test.assertEqual(number_of_expected_calls, self.cleanup_call_count)
+
+
+class OtaRunnerImpl(ota_runner.OtaRunner):
+ """Sets properties to return an empty string to allow OtaRunner tests."""
+
+ def get_sl4a_apk(self):
+ return ''
+
+ def get_ota_package(self):
+ return ''
+
+
+class OtaRunnerTest(unittest.TestCase):
+ """Tests the OtaRunner class."""
+
+ def setUp(self):
+ self.prev_sl4a_service_setup_time = ota_runner.SL4A_SERVICE_SETUP_TIME
+ ota_runner.SL4A_SERVICE_SETUP_TIME = 0
+
+ def tearDown(self):
+ ota_runner.SL4A_SERVICE_SETUP_TIME = self.prev_sl4a_service_setup_time
+
+ def test_update(self):
+ device = mock.MagicMock()
+ tool = MockOtaTool('mock_command')
+ runner = OtaRunnerImpl(tool, device)
+ runner.android_device.adb.getprop = mock.Mock(side_effect=['a', 'b'])
+ runner._update()
+ device.stop_services.assert_called()
+ device.wait_for_boot_completion.assert_called()
+ device.start_services.assert_called()
+ device.adb.install.assert_called()
+ tool.assert_calls_equal(self, 1)
+
+ def test_update_fail_on_no_change_to_build(self):
+ device = mock.MagicMock()
+ tool = MockOtaTool('mock_command')
+ runner = OtaRunnerImpl(tool, device)
+ runner.android_device.adb.getprop = mock.Mock(side_effect=['a', 'a'])
+ try:
+ runner._update()
+ self.fail('Matching build fingerprints did not throw an error!')
+ except ota_runner.OtaError:
+ pass
+
+ def test_init(self):
+ device = mock.MagicMock()
+ tool = MockOtaTool('mock_command')
+ runner = ota_runner.OtaRunner(tool, device)
+
+ self.assertEqual(runner.ota_tool, tool)
+ self.assertEqual(runner.android_device, device)
+ self.assertEqual(runner.serial, device.serial)
+
+
+class SingleUseOtaRunnerTest(unittest.TestCase):
+ """Tests the SingleUseOtaRunner class."""
+
+ def setUp(self):
+ self.device = mock.MagicMock()
+ self.tool = MockOtaTool('mock_command')
+
+ def test_update_first_update_runs(self):
+ runner = ota_runner.SingleUseOtaRunner(self.tool, self.device, '', '')
+ try:
+ with mock.patch.object(ota_runner.OtaRunner, '_update'):
+ runner.update()
+ except ota_runner.OtaError:
+ self.fail('SingleUseOtaRunner threw an exception on the first '
+ 'update call.')
+
+ def test_update_second_update_raises_error(self):
+ runner = ota_runner.SingleUseOtaRunner(self.tool, self.device, '', '')
+ with mock.patch.object(ota_runner.OtaRunner, '_update'):
+ runner.update()
+ try:
+ runner.update()
+ except ota_runner.OtaError:
+ return
+ self.fail('SingleUseOtaRunner did not throw an exception on the second'
+ 'update call.')
+
+ def test_can_update_no_updates_called(self):
+ runner = ota_runner.SingleUseOtaRunner(self.tool, self.device, '', '')
+ self.assertEqual(True, runner.can_update())
+
+ def test_can_update_has_updated_already(self):
+ runner = ota_runner.SingleUseOtaRunner(self.tool, self.device, '', '')
+ with mock.patch.object(ota_runner.OtaRunner, '_update'):
+ runner.update()
+ self.assertEqual(False, runner.can_update())
+
+ def test_get_ota_package(self):
+ runner = ota_runner.SingleUseOtaRunner(self.tool, self.device, 'a',
+ 'b')
+ self.assertEqual(runner.get_ota_package(), 'a')
+
+ def test_get_sl4a_apk(self):
+ runner = ota_runner.SingleUseOtaRunner(self.tool, self.device, 'a',
+ 'b')
+ self.assertEqual(runner.get_sl4a_apk(), 'b')
+
+
+class MultiUseOtaRunnerTest(unittest.TestCase):
+ """Tests the MultiUseOtaRunner class."""
+
+ def setUp(self):
+ self.device = mock.MagicMock()
+ self.tool = MockOtaTool('mock_command')
+
+ def test_update_first_update_runs(self):
+ runner = ota_runner.MultiUseOtaRunner(self.tool, self.device, [''],
+ [''])
+ try:
+ with mock.patch.object(ota_runner.OtaRunner, '_update'):
+ runner.update()
+ except ota_runner.OtaError:
+ self.fail('MultiUseOtaRunner threw an exception on the first '
+ 'update call.')
+
+ def test_update_multiple_updates_run(self):
+ runner = ota_runner.MultiUseOtaRunner(self.tool, self.device,
+ ['first_pkg', 'second_pkg'],
+ ['first_apk', 'second_apk'])
+ with mock.patch.object(ota_runner.OtaRunner, '_update'):
+ runner.update()
+ try:
+ runner.update()
+ except ota_runner.OtaError:
+ self.fail('MultiUseOtaRunner threw an exception before '
+ 'running out of update packages.')
+
+ def test_update_too_many_update_calls_raises_error(self):
+ runner = ota_runner.MultiUseOtaRunner(self.tool, self.device,
+ ['first_pkg', 'second_pkg'],
+ ['first_apk', 'second_apk'])
+ with mock.patch.object(ota_runner.OtaRunner, '_update'):
+ runner.update()
+ runner.update()
+ try:
+ runner.update()
+ except ota_runner.OtaError:
+ return
+ self.fail('MultiUseOtaRunner did not throw an exception after running '
+ 'out of update packages.')
+
+ def test_can_update_no_updates_called(self):
+ runner = ota_runner.MultiUseOtaRunner(self.tool, self.device,
+ ['first_pkg', 'second_pkg'],
+ ['first_apk', 'second_apk'])
+ self.assertEqual(True, runner.can_update())
+
+ def test_can_update_has_more_updates_left(self):
+ runner = ota_runner.MultiUseOtaRunner(self.tool, self.device,
+ ['first_pkg', 'second_pkg'],
+ ['first_apk', 'second_apk'])
+ with mock.patch.object(ota_runner.OtaRunner, '_update'):
+ runner.update()
+ self.assertEqual(True, runner.can_update())
+
+ def test_can_update_ran_out_of_updates(self):
+ runner = ota_runner.MultiUseOtaRunner(self.tool, self.device,
+ ['first_pkg', 'second_pkg'],
+ ['first_apk', 'second_apk'])
+ with mock.patch.object(ota_runner.OtaRunner, '_update'):
+ runner.update()
+ runner.update()
+ self.assertEqual(False, runner.can_update())
+
+ def test_get_ota_package(self):
+ runner = ota_runner.MultiUseOtaRunner(self.tool, self.device,
+ ['first_pkg', 'second_pkg'],
+ ['first_apk', 'second_apk'])
+ self.assertEqual(runner.get_ota_package(), 'first_pkg')
+
+ def test_get_sl4a_apk(self):
+ runner = ota_runner.MultiUseOtaRunner(self.tool, self.device,
+ ['first_pkg', 'second_pkg'],
+ ['first_apk', 'second_apk'])
+ self.assertEqual(runner.get_sl4a_apk(), 'first_apk')
diff --git a/acts/framework/tests/libs/ota/ota_tools/__init__.py b/acts/framework/tests/libs/ota/ota_tools/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_tools/__init__.py
diff --git a/acts/framework/tests/libs/ota/ota_tools/adb_sideload_ota_tool_test.py b/acts/framework/tests/libs/ota/ota_tools/adb_sideload_ota_tool_test.py
new file mode 100644
index 0000000..05c8aaf
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_tools/adb_sideload_ota_tool_test.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import mock
+import unittest
+from acts.controllers import android_device
+from acts.libs.ota.ota_runners import ota_runner
+from acts.libs.ota.ota_tools import ota_tool
+from acts.libs.ota.ota_tools import adb_sideload_ota_tool
+
+
+def get_mock_android_device(serial='', ssh_connection=None):
+ """Returns a mocked AndroidDevice with a mocked adb/fastboot."""
+ with mock.patch('acts.controllers.adb.AdbProxy') as adb_proxy, (
+ mock.patch('acts.controllers.fastboot.FastbootProxy')) as fb_proxy:
+ fb_proxy.return_value.devices.return_value = ""
+ ret = mock.Mock(
+ android_device.AndroidDevice(
+ serial=serial, ssh_connection=ssh_connection))
+ fb_proxy.reset_mock()
+ return ret
+
+
+class AdbSideloadOtaToolTest(unittest.TestCase):
+ """Tests the OtaTool class."""
+
+ def test_init(self):
+ expected_value = 'commmand string'
+ self.assertEqual(
+ ota_tool.OtaTool(expected_value).command, expected_value)
+
+ def setUp(self):
+ self.sl4a_service_setup_time = ota_runner.SL4A_SERVICE_SETUP_TIME
+ ota_runner.SL4A_SERVICE_SETUP_TIME = 0
+
+ def tearDown(self):
+ ota_runner.SL4A_SERVICE_SETUP_TIME = self.sl4a_service_setup_time
+
+ @staticmethod
+ def test_start():
+ # This test could have a bunch of verify statements,
+ # but its probably not worth it.
+ device = get_mock_android_device()
+ tool = adb_sideload_ota_tool.AdbSideloadOtaTool('')
+ runner = ota_runner.SingleUseOtaRunner(tool, device, '', '')
+ runner.android_device.adb.getprop = mock.Mock(side_effect=['a', 'b'])
+ runner.update()
diff --git a/acts/framework/tests/libs/ota/ota_tools/ota_tool_factory_test.py b/acts/framework/tests/libs/ota/ota_tools/ota_tool_factory_test.py
new file mode 100644
index 0000000..9e15177
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_tools/ota_tool_factory_test.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import unittest
+from acts.libs.ota.ota_tools import ota_tool_factory
+
+
+class MockOtaTool(object):
+ def __init__(self, command):
+ self.command = command
+
+
+class OtaToolFactoryTests(unittest.TestCase):
+ def setUp(self):
+ ota_tool_factory._constructed_tools = {}
+
+ def test_create_constructor_exists(self):
+ ota_tool_factory._CONSTRUCTORS = {
+ MockOtaTool.__name__: lambda command: MockOtaTool(command),
+ }
+ ret = ota_tool_factory.create(MockOtaTool.__name__, 'command')
+ self.assertEqual(type(ret), MockOtaTool)
+ self.assertTrue(ret in ota_tool_factory._constructed_tools.values())
+
+ def test_create_not_in_constructors(self):
+ ota_tool_factory._CONSTRUCTORS = {}
+ with self.assertRaises(KeyError):
+ ota_tool_factory.create(MockOtaTool.__name__, 'command')
+
+ def test_create_returns_cached_tool(self):
+ ota_tool_factory._CONSTRUCTORS = {
+ MockOtaTool.__name__: lambda command: MockOtaTool(command),
+ }
+ ret_a = ota_tool_factory.create(MockOtaTool.__name__, 'command')
+ ret_b = ota_tool_factory.create(MockOtaTool.__name__, 'command')
+ self.assertEqual(ret_a, ret_b)
diff --git a/acts/framework/tests/libs/ota/ota_tools/ota_tool_test.py b/acts/framework/tests/libs/ota/ota_tools/ota_tool_test.py
new file mode 100644
index 0000000..73ee599
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_tools/ota_tool_test.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import unittest
+from acts.libs.ota.ota_tools import ota_tool
+
+
+class OtaToolTests(unittest.TestCase):
+ """Tests the OtaTool class."""
+
+ def test_init(self):
+ expected_value = 'commmand string'
+ self.assertEqual(
+ ota_tool.OtaTool(expected_value).command, expected_value)
+
+ def test_start_throws_error_on_unimplemented(self):
+ obj = 'some object'
+ with self.assertRaises(NotImplementedError):
+ ota_tool.OtaTool('').update(obj)
+
+ def test_end_is_not_abstract(self):
+ obj = 'some object'
+ try:
+ ota_tool.OtaTool('').cleanup(obj)
+ except:
+ self.fail('End is not required and should be a virtual function.')
diff --git a/acts/framework/tests/libs/ota/ota_tools/update_device_ota_tool_test.py b/acts/framework/tests/libs/ota/ota_tools/update_device_ota_tool_test.py
new file mode 100644
index 0000000..b541f43
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_tools/update_device_ota_tool_test.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import mock
+import unittest
+from acts.controllers import android_device
+from acts.libs.ota.ota_runners import ota_runner
+from acts.libs.ota.ota_tools import update_device_ota_tool
+
+
+def get_mock_android_device(serial='', ssh_connection=None):
+ """Returns a mocked AndroidDevice with a mocked adb/fastboot."""
+ with mock.patch('acts.controllers.adb.AdbProxy') as adb_proxy, (
+ mock.patch('acts.controllers.fastboot.FastbootProxy')) as fb_proxy:
+ fb_proxy.return_value.devices.return_value = ""
+ ret = mock.Mock(
+ android_device.AndroidDevice(
+ serial=serial, ssh_connection=ssh_connection))
+ fb_proxy.reset_mock()
+ return ret
+
+
+class UpdateDeviceOtaToolTest(unittest.TestCase):
+ """Tests for UpdateDeviceOtaTool."""
+
+ def setUp(self):
+ self.sl4a_service_setup_time = ota_runner.SL4A_SERVICE_SETUP_TIME
+ ota_runner.SL4A_SERVICE_SETUP_TIME = 0
+
+ def tearDown(self):
+ ota_runner.SL4A_SERVICE_SETUP_TIME = self.sl4a_service_setup_time
+
+ def test_update(self):
+ with mock.patch('tempfile.mkdtemp') as mkdtemp, (
+ mock.patch('shutil.rmtree')) as rmtree, (
+ mock.patch('acts.utils.unzip_maintain_permissions')):
+ mkdtemp.return_value = ''
+ rmtree.return_value = ''
+ device = get_mock_android_device()
+ tool = update_device_ota_tool.UpdateDeviceOtaTool('')
+ runner = mock.Mock(
+ ota_runner.SingleUseOtaRunner(tool, device, '', ''))
+ runner.return_value.android_device = device
+ with mock.patch('acts.libs.proc.job.run'):
+ tool.update(runner)
+ del tool
+
+ def test_del(self):
+ with mock.patch('tempfile.mkdtemp') as mkdtemp, (
+ mock.patch('shutil.rmtree')) as rmtree, (
+ mock.patch('acts.utils.unzip_maintain_permissions')):
+ mkdtemp.return_value = ''
+ rmtree.return_value = ''
+ tool = update_device_ota_tool.UpdateDeviceOtaTool('')
+ del tool
+ self.assertTrue(mkdtemp.called)
+ self.assertTrue(rmtree.called)
diff --git a/acts/framework/tests/libs/ota/ota_updater_test.py b/acts/framework/tests/libs/ota/ota_updater_test.py
new file mode 100644
index 0000000..466ea69
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_updater_test.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import mock
+import unittest
+from acts.libs.ota import ota_updater
+from acts.libs.ota.ota_runners import ota_runner
+
+
+class MockAndroidDevice(object):
+ def __init__(self, serial):
+ self.serial = serial
+ self.log = mock.Mock()
+ self.take_bug_report = mock.MagicMock()
+
+
+class MockOtaRunner(object):
+ def __init__(self):
+ self.call_count = 0
+ self.should_fail = False
+ self.can_update_value = 'CAN_UPDATE_CALLED'
+
+ def set_failure(self, should_fail=True):
+ self.should_fail = should_fail
+
+ def update(self):
+ self.call_count += 1
+ if self.should_fail:
+ raise ota_runner.OtaError
+
+ def can_update(self):
+ return self.can_update_value
+
+
+class OtaUpdaterTests(unittest.TestCase):
+ """Tests the methods in the ota_updater module."""
+
+ def test_initialize(self):
+ user_params = {'a': 1, 'b': 2, 'c': 3}
+ android_devices = ['x', 'y', 'z']
+ with mock.patch('acts.libs.ota.ota_runners.ota_runner_factory.'
+ 'create_from_configs') as fn:
+ ota_updater.initialize(user_params, android_devices)
+ for i in range(len(android_devices)):
+ fn.assert_has_call(mock.call(user_params, android_devices[i]))
+ self.assertSetEqual(
+ set(android_devices), set(ota_updater.ota_runners.keys()))
+
+ def test_check_initialization_is_initialized(self):
+ device = MockAndroidDevice('serial')
+ ota_updater.ota_runners = {
+ device: ota_runner.OtaRunner('tool', device)
+ }
+ try:
+ ota_updater._check_initialization(device)
+ except ota_runner.OtaError:
+ self.fail('_check_initialization raised for initialized runner!')
+
+ def test_check_initialization_is_not_initialized(self):
+ device = MockAndroidDevice('serial')
+ ota_updater.ota_runners = {}
+ with self.assertRaises(KeyError):
+ ota_updater._check_initialization(device)
+
+ def test_update_do_not_ignore_failures_and_failures_occur(self):
+ device = MockAndroidDevice('serial')
+ runner = MockOtaRunner()
+ runner.set_failure(True)
+ ota_updater.ota_runners = {device: runner}
+ with self.assertRaises(ota_runner.OtaError):
+ ota_updater.update(device)
+
+ def test_update_ignore_failures_and_failures_occur(self):
+ device = MockAndroidDevice('serial')
+ runner = MockOtaRunner()
+ runner.set_failure(True)
+ ota_updater.ota_runners = {device: runner}
+ try:
+ ota_updater.update(device, ignore_update_errors=True)
+ except ota_runner.OtaError:
+ self.fail('OtaError was raised when errors are to be ignored!')
+
+ def test_can_update(self):
+ device = MockAndroidDevice('serial')
+ runner = MockOtaRunner()
+ ota_updater.ota_runners = {device: runner}
+ self.assertEqual(ota_updater.can_update(device), 'CAN_UPDATE_CALLED')
diff --git a/acts/framework/tests/libs/ota/unittest_bundle.py b/acts/framework/tests/libs/ota/unittest_bundle.py
new file mode 100755
index 0000000..e0019f1
--- /dev/null
+++ b/acts/framework/tests/libs/ota/unittest_bundle.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+import sys
+import unittest
+
+
+def main():
+ suite = unittest.TestLoader().discover(
+ start_dir='./acts/framework/tests/libs/ota', pattern='*_test.py')
+ return suite
+
+
+if __name__ == "__main__":
+ test_suite = main()
+ runner = unittest.TextTestRunner()
+ test_run = runner.run(test_suite)
+ sys.exit(not test_run.wasSuccessful())
diff --git a/acts/framework/tests/mock_controller.py b/acts/framework/tests/mock_controller.py
index a3893fd..65b2673 100644
--- a/acts/framework/tests/mock_controller.py
+++ b/acts/framework/tests/mock_controller.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
diff --git a/acts/framework/tests/test_data/1k_2k.raw b/acts/framework/tests/test_data/1k_2k.raw
new file mode 100644
index 0000000..42e7ab9
--- /dev/null
+++ b/acts/framework/tests/test_data/1k_2k.raw
Binary files differ
diff --git a/acts/framework/tests/test_runner_test.py b/acts/framework/tests/test_runner_test.py
new file mode 100755
index 0000000..65f50c9
--- /dev/null
+++ b/acts/framework/tests/test_runner_test.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 - 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.
+
+from mock import Mock
+import unittest
+import tempfile
+
+from acts import keys
+from acts import test_runner
+
+import mock_controller
+
+
+class TestRunnerTest(unittest.TestCase):
+ def setUp(self):
+ self.tmp_dir = tempfile.mkdtemp()
+ self.base_mock_test_config = {
+ "testbed": {
+ "name": "SampleTestBed",
+ },
+ "logpath": self.tmp_dir,
+ "cli_args": None,
+ "testpaths": ["./"],
+ "icecream": 42,
+ "extra_param": "haha"
+ }
+
+ def create_mock_context(self):
+ context = Mock()
+ context.__exit__ = Mock()
+ context.__enter__ = Mock()
+ return context
+
+ def create_test_classes(self, class_names):
+ return {
+ class_name: Mock(return_value=self.create_mock_context())
+ for class_name in class_names
+ }
+
+ def test_class_name_pattern_single(self):
+ class_names = ['test_class_1', 'test_class_2']
+ pattern = 'test*1'
+ tr = test_runner.TestRunner(self.base_mock_test_config, [(pattern,
+ None)])
+
+ test_classes = self.create_test_classes(class_names)
+ tr.import_test_modules = Mock(return_value=test_classes)
+ tr.run()
+ self.assertTrue(test_classes[class_names[0]].called)
+ self.assertFalse(test_classes[class_names[1]].called)
+
+ def test_class_name_pattern_multi(self):
+ class_names = ['test_class_1', 'test_class_2', 'other_name']
+ pattern = 'test_class*'
+ tr = test_runner.TestRunner(self.base_mock_test_config, [(pattern,
+ None)])
+
+ test_classes = self.create_test_classes(class_names)
+ tr.import_test_modules = Mock(return_value=test_classes)
+ tr.run()
+ self.assertTrue(test_classes[class_names[0]].called)
+ self.assertTrue(test_classes[class_names[1]].called)
+ self.assertFalse(test_classes[class_names[2]].called)
+
+ def test_class_name_pattern_question_mark(self):
+ class_names = ['test_class_1', 'test_class_12']
+ pattern = 'test_class_?'
+ tr = test_runner.TestRunner(self.base_mock_test_config, [(pattern,
+ None)])
+
+ test_classes = self.create_test_classes(class_names)
+ tr.import_test_modules = Mock(return_value=test_classes)
+ tr.run()
+ self.assertTrue(test_classes[class_names[0]].called)
+ self.assertFalse(test_classes[class_names[1]].called)
+
+ def test_class_name_pattern_char_seq(self):
+ class_names = ['test_class_1', 'test_class_2', 'test_class_3']
+ pattern = 'test_class_[1357]'
+ tr = test_runner.TestRunner(self.base_mock_test_config, [(pattern,
+ None)])
+
+ test_classes = self.create_test_classes(class_names)
+ tr.import_test_modules = Mock(return_value=test_classes)
+ tr.run()
+ self.assertTrue(test_classes[class_names[0]].called)
+ self.assertFalse(test_classes[class_names[1]].called)
+ self.assertTrue(test_classes[class_names[2]].called)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/acts/tests/google/ble/bt5/AdvertisingSetTest.py b/acts/tests/google/ble/bt5/AdvertisingSetTest.py
index 4427bb5..0f0c352 100644
--- a/acts/tests/google/ble/bt5/AdvertisingSetTest.py
+++ b/acts/tests/google/ble/bt5/AdvertisingSetTest.py
@@ -43,7 +43,6 @@
class AdvertisingSetTest(BluetoothBaseTest):
default_timeout = 10
- max_scan_instances = 28
report_delay = 2000
scan_callbacks = []
adv_callbacks = []
diff --git a/acts/tests/google/ble/bt5/Bt5ScanTest.py b/acts/tests/google/ble/bt5/Bt5ScanTest.py
index 7f7b7dc..633489f 100644
--- a/acts/tests/google/ble/bt5/Bt5ScanTest.py
+++ b/acts/tests/google/ble/bt5/Bt5ScanTest.py
@@ -41,7 +41,6 @@
class Bt5ScanTest(BluetoothBaseTest):
default_timeout = 10
- max_scan_instances = 28
report_delay = 2000
scan_callbacks = []
adv_callbacks = []
diff --git a/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py b/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
index 24e078a..83b5ddc 100644
--- a/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
+++ b/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
@@ -37,7 +37,7 @@
class ConcurrentBleScanningTest(BluetoothBaseTest):
default_timeout = 20
- max_concurrent_scans = 28
+ max_concurrent_scans = 27
def __init__(self, controllers):
BluetoothBaseTest.__init__(self, controllers)
@@ -264,13 +264,13 @@
try:
self.scn_ad.ed.pop_event(
scan_failed.format(scan_callback), self.default_timeout)
- self.log.info(
- "Found scan event successfully. Iteration {} successful."
+ self.log.error(
+ "Unexpected scan event found. Iteration {} successful."
.format(i))
- except Exception:
- self.log.info("Failed to find a onScanFailed event for callback {}"
- .format(scan_callback))
test_result = False
+ except Exception:
+ self.log.info("No onScanFailed event for callback {} as expected."
+ .format(scan_callback))
for callback in scan_callback_list:
self.scn_ad.droid.bleStopBleScan(callback)
return test_result
diff --git a/acts/tests/google/ble/concurrency/ConcurrentGattConnectTest.py b/acts/tests/google/ble/concurrency/ConcurrentGattConnectTest.py
new file mode 100644
index 0000000..2e50d2b
--- /dev/null
+++ b/acts/tests/google/ble/concurrency/ConcurrentGattConnectTest.py
@@ -0,0 +1,293 @@
+#/usr/bin/env python3.4
+#
+# 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.
+"""
+Test script for concurrent Gatt connections.
+Testbed assumes 6 Android devices. One will be the central and the rest
+peripherals.
+"""
+
+from queue import Empty
+import concurrent.futures
+import threading
+import time
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts.test_utils.bt.bt_constants import bt_profile_constants
+from acts.test_utils.bt.bt_constants import gatt_characteristic
+from acts.test_utils.bt.bt_constants import gatt_characteristic_value_format
+from acts.test_utils.bt.bt_constants import gatt_char_desc_uuids
+from acts.test_utils.bt.bt_constants import gatt_descriptor
+from acts.test_utils.bt.bt_constants import gatt_service_types
+from acts.test_utils.bt.bt_constants import scan_result
+from acts.test_utils.bt.bt_gatt_utils import run_continuous_write_descriptor
+from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+from acts.test_utils.bt.gatts_lib import GattServerLib
+from acts.test_decorators import test_tracker_info
+
+service_uuid = '0000a00a-0000-1000-8000-00805f9b34fb'
+characteristic_uuid = 'aa7edd5a-4d1d-4f0e-883a-d145616a1630'
+descriptor_uuid = "00000003-0000-1000-8000-00805f9b34fb"
+
+gatt_server_read_descriptor_sample = {
+ 'services': [{
+ 'uuid':
+ service_uuid,
+ 'type':
+ gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid':
+ characteristic_uuid,
+ 'properties':
+ gatt_characteristic['property_write'],
+ 'permissions':
+ gatt_characteristic['permission_write'],
+ 'instance_id':
+ 0x002a,
+ 'value_type':
+ gatt_characteristic_value_format['string'],
+ 'value':
+ 'Test Database',
+ 'descriptors': [{
+ 'uuid': descriptor_uuid,
+ 'permissions': gatt_descriptor['permission_write'],
+ }]
+ }]
+ }]
+}
+
+
+class ConcurrentGattConnectTest(BluetoothBaseTest):
+ bt_default_timeout = 10
+ max_connections = 5
+ # List of tuples (android_device, advertise_callback)
+ advertise_callbacks = []
+ # List of tuples (android_device, advertisement_name)
+ advertisement_names = []
+ list_of_arguments_list = []
+
+ def __init__(self, controllers):
+ BluetoothBaseTest.__init__(self, controllers)
+ self.pri_dut = self.android_devices[0]
+
+ def setup_class(self):
+ super(BluetoothBaseTest, self).setup_class()
+
+ # Create 5 advertisements from different android devices
+ for i in range(1, self.max_connections + 1):
+ # Set device name
+ ad = self.android_devices[i]
+ name = "test_adv_{}".format(i)
+ self.advertisement_names.append((ad, name))
+ ad.droid.bluetoothSetLocalName(name)
+
+ # Setup and start advertisements
+ ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
+ ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
+ ble_advertise_settings_modes['low_latency'])
+ advertise_data = ad.droid.bleBuildAdvertiseData()
+ advertise_settings = ad.droid.bleBuildAdvertiseSettings()
+ advertise_callback = ad.droid.bleGenBleAdvertiseCallback()
+ ad.droid.bleStartBleAdvertising(advertise_callback, advertise_data,
+ advertise_settings)
+ self.advertise_callbacks.append((ad, advertise_callback))
+
+ def obtain_address_list_from_scan(self):
+ """Returns the address list of all devices that match the scan filter.
+
+ Returns:
+ A list if all devices are found; None is any devices are not found.
+ """
+ # From central device, scan for all appropriate addresses by name.
+ filter_list = self.pri_dut.droid.bleGenFilterList()
+ self.pri_dut.droid.bleSetScanSettingsScanMode(
+ ble_scan_settings_modes['low_latency'])
+ scan_settings = self.pri_dut.droid.bleBuildScanSetting()
+ scan_callback = self.pri_dut.droid.bleGenScanCallback()
+ for android_device, name in self.advertisement_names:
+ self.pri_dut.droid.bleSetScanFilterDeviceName(name)
+ self.pri_dut.droid.bleBuildScanFilter(filter_list)
+ self.pri_dut.droid.bleStartBleScan(filter_list, scan_settings,
+ scan_callback)
+ address_list = []
+ devices_found = []
+ # Set the scan time out to 20 sec to provide enough time to discover the
+ # devices in busy environment
+ scan_timeout = 20
+ end_time = time.time() + scan_timeout
+ while time.time() < end_time and len(address_list) < len(
+ self.advertisement_names):
+ try:
+ event = self.pri_dut.ed.pop_event(
+ "BleScan{}onScanResults".format(scan_callback),
+ self.bt_default_timeout)
+
+ adv_name = event['data']['Result']['deviceInfo']['name']
+ mac_address = event['data']['Result']['deviceInfo']['address']
+ # Look up the android device handle based on event name
+ device = [
+ item for item in self.advertisement_names
+ if adv_name in item
+ ]
+ devices_found.append(device[0][0].serial)
+ if len(device) is not 0:
+ address_list_tuple = (device[0][0], mac_address)
+ else:
+ continue
+ result = [item for item in address_list if mac_address in item]
+ # if length of result is 0, it indicates that we have discovered
+ # new mac address.
+ if len(result) is 0:
+ self.log.info("Found new mac address: {}".format(
+ address_list_tuple[1]))
+ address_list.append(address_list_tuple)
+ except Empty as err:
+ self.log.error("Failed to find any scan results.")
+ return None
+ if len(address_list) < self.max_connections:
+ self.log.info("Only found these devices: {}".format(devices_found))
+ self.log.error("Could not find all necessary advertisements.")
+ return None
+ return address_list
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='6638282c-69b5-4237-9f0d-18e131424a9f')
+ def test_concurrent_gatt_connections(self):
+ """Test max concurrent GATT connections
+
+ Connect to all peripherals.
+
+ Steps:
+ 1. Scan
+ 2. Save addresses
+ 3. Connect all addresses of the peripherals
+
+ Expected Result:
+ All connections successful.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: Bluetooth, GATT
+ Priority: 2
+ """
+
+ address_list = self.obtain_address_list_from_scan()
+ if address_list is None:
+ return False
+
+ # Connect to all addresses
+ for address_tuple in address_list:
+ address = address_tuple[1]
+ try:
+ autoconnect = False
+ bluetooth_gatt, gatt_callback = setup_gatt_connection(
+ self.pri_dut, address, autoconnect)
+ self.log.info("Successfully connected to {}".format(address))
+ except Exception as err:
+ self.log.error(
+ "Failed to establish connection to {}".format(address))
+ return False
+ if (len(
+ self.pri_dut.droid.bluetoothGetConnectedLeDevices(
+ bt_profile_constants['gatt_server'])) !=
+ self.max_connections):
+ self.log.error("Did not reach max connection count.")
+ return False
+
+ return True
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='660bf05e-a8e5-45f3-b42b-b66b4ac0d85f')
+ def test_data_transfer_to_concurrent_gatt_connections(self):
+ """Test writing GATT descriptors concurrently to many peripherals.
+
+ Connect to all peripherals and write gatt descriptors concurrently.
+
+
+ Steps:
+ 1. Scan the addresses by names
+ 2. Save mac addresses of the peripherals
+ 3. Connect all addresses of the peripherals and write gatt descriptors
+
+
+ Expected Result:
+ All connections and data transfers are successful.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: Bluetooth, GATT
+ Priority: 2
+ """
+
+ address_list = self.obtain_address_list_from_scan()
+ if address_list is None:
+ return False
+
+ # Connect to all addresses
+ executor = concurrent.futures.ThreadPoolExecutor(max_workers=10)
+
+ for address_tuple in address_list:
+ ad, address = address_tuple
+
+ gatts = GattServerLib(log=self.log, dut=ad)
+ gatt_server, gatt_server_callback = gatts.setup_gatts_db(
+ database=gatt_server_read_descriptor_sample)
+
+ try:
+ bluetooth_gatt, gatt_callback = setup_gatt_connection(
+ self.pri_dut, address, autoconnect=False)
+ self.log.info("Successfully connected to {}".format(address))
+
+ except Exception as err:
+ self.log.error(
+ "Failed to establish connection to {}".format(address))
+ return False
+
+ if self.pri_dut.droid.gattClientDiscoverServices(bluetooth_gatt):
+ event = self.pri_dut.ed.pop_event(
+ "GattConnect{}onServicesDiscovered".format(bluetooth_gatt),
+ self.bt_default_timeout)
+ discovered_services_index = event['data']['ServicesIndex']
+ else:
+ self.log.info("Failed to discover services.")
+ return False
+ services_count = self.pri_dut.droid.gattClientGetDiscoveredServicesCount(
+ discovered_services_index)
+
+ arguments_list = [
+ self.pri_dut.droid, self.pri_dut.ed, ad.droid, ad.ed,
+ gatt_server, gatt_server_callback, bluetooth_gatt,
+ services_count, discovered_services_index, 100
+ ]
+ self.list_of_arguments_list.append(arguments_list)
+
+ for arguments_list in self.list_of_arguments_list:
+ executor.submit(run_continuous_write_descriptor, *arguments_list)
+
+ executor.shutdown(wait=True)
+
+ if (len(
+ self.pri_dut.droid.bluetoothGetConnectedLeDevices(
+ bt_profile_constants['gatt_server'])) !=
+ self.max_connections):
+ self.log.error("Failed to write concurrently.")
+ return False
+
+ return True
diff --git a/acts/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py b/acts/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
new file mode 100644
index 0000000..bff59e7
--- /dev/null
+++ b/acts/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
@@ -0,0 +1,507 @@
+#/usr/bin/env python3.4
+#
+# Copyright 2017 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.
+"""
+Test script to execute Bluetooth Connection-orient Channel (CoC) functionality for
+2 connections test cases. This test was designed to be run in a shield box.
+"""
+
+import threading
+import time
+
+from queue import Empty
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
+from acts.test_utils.bt.bt_coc_test_utils import do_multi_connection_throughput
+from acts.test_utils.bt.bt_constants import default_le_data_length
+from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
+from acts.test_utils.bt.bt_constants import l2cap_coc_header_size
+from acts.test_utils.bt.bt_constants import l2cap_max_inactivity_delay_after_disconnect
+from acts.test_utils.bt.bt_constants import le_connection_event_time_step_ms
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.bt.bt_test_utils import kill_bluetooth_process
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts.test_utils.bt.bt_test_utils import write_read_verify_data
+from acts.test_utils.bt.bt_test_utils import verify_server_and_client_connected
+
+
+class BleCoc2ConnTest(BluetoothBaseTest):
+ def __init__(self, controllers):
+ BluetoothBaseTest.__init__(self, controllers)
+ self.client_ad = self.android_devices[0]
+ self.server_ad = self.android_devices[1]
+ # Note that some tests required a third device.
+ if len(self.android_devices) > 2:
+ self.server2_ad = self.android_devices[2]
+
+ def setup_class(self):
+ return setup_multiple_devices_for_bt_test(self.android_devices)
+
+ def teardown_test(self):
+ self.client_ad.droid.bluetoothSocketConnStop()
+ self.server_ad.droid.bluetoothSocketConnStop()
+ # Give sufficient time for the physical LE link to be disconnected.
+ time.sleep(l2cap_max_inactivity_delay_after_disconnect)
+
+ # This utility function calculates the max and min connection event (ce) time.
+ # The formula is that the min/max ce time should be less than half the connection
+ # interval and must be multiples of the le_connection_event_time_step.
+ def _calc_min_max_ce_time(self, le_connection_interval):
+ conn_event_time_steps = int((le_connection_interval/2)/le_connection_event_time_step_ms)
+ conn_event_time_steps -= 1
+ return (le_connection_event_time_step_ms * conn_event_time_steps)
+
+ def _run_coc_connection_throughput_2_conn(
+ self,
+ is_secured,
+ buffer_size,
+ le_connection_interval=0,
+ le_tx_data_length=default_le_data_length,
+ min_ce_len=0,
+ max_ce_len=0):
+
+ # The num_iterations is that number of repetitions of each
+ # set of buffers r/w.
+ # number_buffers is the total number of data buffers to transmit per
+ # set of buffers r/w.
+ # buffer_size is the number of bytes per L2CAP data buffer.
+ num_iterations = 10
+ number_buffers = 100
+
+ # Make sure at least 3 phones are setup
+ if len(self.android_devices) <= 2:
+ self.log.info("test_coc_connection_throughput_2_conn: "
+ "Error: 3rd phone not configured in file")
+ return False
+
+ self.log.info(
+ "_run_coc_connection_throughput_2_conn: is_secured={}, Interval={}, buffer_size={}, "
+ "le_tx_data_length={}, min_ce_len={}".format(is_secured, le_connection_interval,
+ buffer_size, le_tx_data_length, min_ce_len))
+ status, client_conn_id1, server_conn_id1 = orchestrate_coc_connection(
+ self.client_ad, self.server_ad, True, is_secured,
+ le_connection_interval, le_tx_data_length, default_bluetooth_socket_timeout_ms,
+ min_ce_len, max_ce_len)
+ if not status:
+ return False
+
+ status, client_conn_id2, server_conn_id2 = orchestrate_coc_connection(
+ self.client_ad, self.server2_ad, True, is_secured,
+ le_connection_interval, le_tx_data_length, default_bluetooth_socket_timeout_ms,
+ min_ce_len, max_ce_len)
+ if not status:
+ return False
+
+ list_server_ad = [self.server_ad, self.server2_ad]
+ list_client_conn_id = [client_conn_id1, client_conn_id2]
+ data_rate = do_multi_connection_throughput(
+ self.client_ad, list_server_ad, list_client_conn_id,
+ num_iterations, number_buffers, buffer_size)
+ if data_rate <= 0:
+ return False
+
+ self.log.info(
+ "test_coc_connection_throughput_2_conn: throughput=%d bytes per "
+ "sec", data_rate)
+
+ self.client_ad.droid.bluetoothSocketConnStop(client_conn_id1)
+ self.client_ad.droid.bluetoothSocketConnStop(client_conn_id2)
+ self.server_ad.droid.bluetoothSocketConnStop(server_conn_id1)
+ self.server2_ad.droid.bluetoothSocketConnStop(server_conn_id2)
+ return True
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='27226006-b725-4312-920e-6193cf0539d4')
+ def test_coc_insecured_connection_throughput_2_conn(self):
+ """Test LE CoC data throughput on two insecured connections
+
+ Test Data Throughput of 2 L2CAP CoC insecured connections.
+ 3 phones are required.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server#1 AD.
+ The connection is insecured.
+ 3. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 4. Establish a L2CAP CoC connection from the client to the server#2 AD.
+ The connection is insecured.
+ 5. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 6. Write data from the client to both server#1 and server#2.
+ 7. Verify data matches from client and server
+
+ Expected Result:
+ L2CAP CoC connections are established and data written correctly to both servers.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ # Note: A 117 octets buffer size would fix nicely to a 123 bytes Data Length
+ status = self._run_coc_connection_throughput_2_conn(False, 117)
+ return status
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='1a5fb032-8a27-42f1-933f-3e39311c09a6')
+ def test_coc_secured_connection_throughput_2_conn(self):
+ """Test LE CoC data throughput on two secured connections
+
+ Test Data Throughput of 2 L2CAP CoC secured connections.
+ 3 phones are required.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server#1 AD.
+ The connection is secured.
+ 3. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 4. Establish a L2CAP CoC connection from the client to the server#2 AD.
+ The connection is secured.
+ 5. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 6. Write data from the client to both server#1 and server#2.
+ 7. Verify data matches from client and server
+
+ Expected Result:
+ L2CAP CoC connections are established and data written correctly to both servers.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ # Note: A 117 octets buffer size would fix nicely to a 123 bytes Data Length
+ status = self._run_coc_connection_throughput_2_conn(True, 117)
+ return status
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='b198f8cc-26af-44bd-bb4d-7dc8f8645617')
+ def test_coc_connection_throughput_2_conn_NOSEC_10CI_60SIZE(self):
+ """Test LE CoC data throughput with 10msec CI and 60bytes buffer size.
+
+ Test data throughput of 2 L2CAP CoC insecured connections with 20msec connection interval
+ and 60 bytes buffer size. 3 phones are required.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server#1 AD.
+ The connection is insecured.
+ 3. Set the connection interval to 20 msec and buffer size to 60 bytes.
+ 4. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 5. Establish a L2CAP CoC connection from the client to the server#2 AD.
+ The connection is insecured.
+ 6. Set the connection interval to 20 msec and buffer size to 60 bytes.
+ 7. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 8. Write data from the client to both server#1 and server#2.
+ 9. Verify data matches from client and server
+
+ Expected Result:
+ L2CAP CoC connections are established and data written correctly to both servers.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+
+ is_secured = False
+ le_connection_interval = 10
+ buffer_size = 60
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+
+ status = self._run_coc_connection_throughput_2_conn(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+ self._calc_min_max_ce_time(le_connection_interval),
+ self._calc_min_max_ce_time(le_connection_interval))
+ return status
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='12dc2a6c-8283-4617-a911-42335dd693a8')
+ def test_coc_connection_throughput_2_conn_NOSEC_10CI_80SIZE(self):
+ """Test LE CoC data throughput with 10msec CI and 80bytes buffer size.
+
+ Test data throughput of 2 L2CAP CoC insecured connections with 20msec connection interval
+ and 80 bytes buffer size. 3 phones are required.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server#1 AD.
+ The connection is insecured.
+ 3. Set the connection interval to 20 msec and buffer size to 80 bytes.
+ 4. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 5. Establish a L2CAP CoC connection from the client to the server#2 AD.
+ The connection is insecured.
+ 6. Set the connection interval to 20 msec and buffer size to 80 bytes.
+ 7. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 8. Write data from the client to both server#1 and server#2.
+ 9. Verify data matches from client and server
+
+ Expected Result:
+ L2CAP CoC connections are established and data written correctly to both servers.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+
+ is_secured = False
+ le_connection_interval = 10
+ buffer_size = 80
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ status = self._run_coc_connection_throughput_2_conn(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+ self._calc_min_max_ce_time(le_connection_interval),
+ self._calc_min_max_ce_time(le_connection_interval))
+ return status
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='4730df05-3909-4adf-a365-7f0c3258c402')
+ def test_coc_connection_throughput_2_conn_NOSEC_10CI_120SIZE(self):
+ """Test LE CoC data throughput with 10msec CI and 120bytes buffer size.
+
+ Test data throughput of 2 L2CAP CoC insecured connections with 20msec connection interval
+ and 120 bytes buffer size. 3 phones are required.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server#1 AD.
+ The connection is insecured.
+ 3. Set the connection interval to 20 msec and buffer size to 120 bytes.
+ 4. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 5. Establish a L2CAP CoC connection from the client to the server#2 AD.
+ The connection is insecured.
+ 6. Set the connection interval to 20 msec and buffer size to 120 bytes.
+ 7. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 8. Write data from the client to both server#1 and server#2.
+ 9. Verify data matches from client and server
+
+ Expected Result:
+ L2CAP CoC connections are established and data written correctly to both servers.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+
+ is_secured = False
+ le_connection_interval = 10
+ buffer_size = 120
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ status = self._run_coc_connection_throughput_2_conn(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+ self._calc_min_max_ce_time(le_connection_interval),
+ self._calc_min_max_ce_time(le_connection_interval))
+ return status
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='471a8748-b0a5-4be5-9322-7c75e2b5d048')
+ def test_coc_connection_throughput_2_conn_NOSEC_15CI_120SIZE(self):
+ """Test LE CoC data throughput with 15msec CI and 120bytes buffer size.
+
+ Test data throughput of 2 L2CAP CoC insecured connections with 15msec connection interval
+ and 120 bytes buffer size. 3 phones are required.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server#1 AD.
+ The connection is insecured.
+ 3. Set the connection interval to 15 msec and buffer size to 120 bytes.
+ 4. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 5. Establish a L2CAP CoC connection from the client to the server#2 AD.
+ The connection is insecured.
+ 6. Set the connection interval to 15 msec and buffer size to 120 bytes.
+ 7. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 8. Write data from the client to both server#1 and server#2.
+ 9. Verify data matches from client and server
+
+ Expected Result:
+ L2CAP CoC connections are established and data written correctly to both servers.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+
+ is_secured = False
+ le_connection_interval = 15
+ buffer_size = 120
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ status = self._run_coc_connection_throughput_2_conn(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+ self._calc_min_max_ce_time(le_connection_interval),
+ self._calc_min_max_ce_time(le_connection_interval))
+ return status
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='053e59c2-f312-4bec-beaf-9e4efdce063a')
+ def test_coc_connection_throughput_2_conn_NOSEC_15CI_180SIZE(self):
+ """Test LE CoC data throughput with 15msec CI and 180bytes buffer size.
+
+ Test data throughput of 2 L2CAP CoC insecured connections with 15msec connection interval
+ and 120 bytes buffer size. 3 phones are required.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server#1 AD.
+ The connection is insecured.
+ 3. Set the connection interval to 15 msec and buffer size to 180 bytes.
+ 4. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 5. Establish a L2CAP CoC connection from the client to the server#2 AD.
+ The connection is insecured.
+ 6. Set the connection interval to 15 msec and buffer size to 180 bytes.
+ 7. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 8. Write data from the client to both server#1 and server#2.
+ 9. Verify data matches from client and server
+
+ Expected Result:
+ L2CAP CoC connections are established and data written correctly to both servers.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+
+ is_secured = False
+ le_connection_interval = 15
+ buffer_size = 180
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ status = self._run_coc_connection_throughput_2_conn(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+ self._calc_min_max_ce_time(le_connection_interval),
+ self._calc_min_max_ce_time(le_connection_interval))
+ return status
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='2b43caa6-76b3-48c5-b342-32ebb31ac52c')
+ def test_coc_connection_throughput_2_conn_NOSEC_20CI_240SIZE(self):
+ """Test LE CoC data throughput with 20msec CI and 240bytes buffer size.
+
+ Test data throughput of 2 L2CAP CoC insecured connections with 20msec connection interval
+ and 240 bytes buffer size. 3 phones are required.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server#1 AD.
+ The connection is insecured.
+ 3. Set the connection interval to 20 msec and buffer size to 240 bytes.
+ 4. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 5. Establish a L2CAP CoC connection from the client to the server#2 AD.
+ The connection is insecured.
+ 6. Set the connection interval to 20 msec and buffer size to 240 bytes.
+ 7. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 8. Write data from the client to both server#1 and server#2.
+ 9. Verify data matches from client and server
+
+ Expected Result:
+ L2CAP CoC connections are established and data written correctly to both servers.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+
+ is_secured = False
+ le_connection_interval = 20
+ buffer_size = 240
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ status = self._run_coc_connection_throughput_2_conn(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+ self._calc_min_max_ce_time(le_connection_interval),
+ self._calc_min_max_ce_time(le_connection_interval))
+ return status
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='f630df02-3fd6-4aa0-bc15-06837b705e97')
+ def test_coc_connection_throughput_2_conn_NOSEC_30CI_240SIZE(self):
+ """Test LE CoC data throughput with 30msec CI and 240bytes buffer size.
+
+ Test data throughput of 2 L2CAP CoC insecured connections with 20msec connection interval
+ and 240 bytes buffer size. 3 phones are required.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server#1 AD.
+ The connection is insecured.
+ 3. Set the connection interval to 30 msec and buffer size to 240 bytes.
+ 4. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 5. Establish a L2CAP CoC connection from the client to the server#2 AD.
+ The connection is insecured.
+ 6. Set the connection interval to 30 msec and buffer size to 240 bytes.
+ 7. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 8. Write data from the client to both server#1 and server#2.
+ 9. Verify data matches from client and server
+
+ Expected Result:
+ L2CAP CoC connections are established and data written correctly to both servers.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ is_secured = False
+ le_connection_interval = 30
+ buffer_size = 240
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ status = self._run_coc_connection_throughput_2_conn(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+ self._calc_min_max_ce_time(le_connection_interval),
+ self._calc_min_max_ce_time(le_connection_interval))
+ return status
diff --git a/acts/tests/google/ble/conn_oriented_chan/BleCocTest.py b/acts/tests/google/ble/conn_oriented_chan/BleCocTest.py
new file mode 100644
index 0000000..e5d093f
--- /dev/null
+++ b/acts/tests/google/ble/conn_oriented_chan/BleCocTest.py
@@ -0,0 +1,584 @@
+#/usr/bin/env python3.4
+#
+# Copyright 2017 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.
+"""
+Test script to execute Bluetooth Connection-orient Channel (CoC) functionality
+test cases. This test was designed to be run in a shield box.
+"""
+
+import threading
+import time
+
+from queue import Empty
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
+from acts.test_utils.bt.bt_coc_test_utils import do_multi_connection_throughput
+from acts.test_utils.bt.bt_constants import default_le_data_length
+from acts.test_utils.bt.bt_constants import l2cap_coc_header_size
+from acts.test_utils.bt.bt_constants import l2cap_max_inactivity_delay_after_disconnect
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.bt.bt_test_utils import kill_bluetooth_process
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts.test_utils.bt.bt_test_utils import write_read_verify_data
+from acts.test_utils.bt.bt_test_utils import verify_server_and_client_connected
+
+
+class BleCocTest(BluetoothBaseTest):
+ message = (
+ "Space: the final frontier. These are the voyages of "
+ "the starship Enterprise. Its continuing mission: to explore "
+ "strange new worlds, to seek out new life and new civilizations,"
+ " to boldly go where no man has gone before.")
+
+ def __init__(self, controllers):
+ BluetoothBaseTest.__init__(self, controllers)
+ self.client_ad = self.android_devices[0]
+ self.server_ad = self.android_devices[1]
+ # Note that some tests required a third device.
+ if len(self.android_devices) > 2:
+ self.server2_ad = self.android_devices[2]
+
+ def setup_class(self):
+ return setup_multiple_devices_for_bt_test(self.android_devices)
+
+ def teardown_test(self):
+ self.client_ad.droid.bluetoothSocketConnStop()
+ self.server_ad.droid.bluetoothSocketConnStop()
+ # Give sufficient time for the physical LE link to be disconnected.
+ time.sleep(l2cap_max_inactivity_delay_after_disconnect)
+
+ def _run_coc_connection_throughput(
+ self,
+ is_secured,
+ buffer_size,
+ le_connection_interval=0,
+ le_tx_data_length=default_le_data_length):
+
+ # The num_iterations is that number of repetitions of each
+ # set of buffers r/w.
+ # number_buffers is the total number of data buffers to transmit per
+ # set of buffers r/w.
+ # buffer_size is the number of bytes per L2CAP data buffer.
+ number_buffers = 100
+ num_iterations = 10
+
+ self.log.info(
+ "_run_coc_connection_throughput: calling "
+ "orchestrate_coc_connection. is_secured={}, Connection Interval={}msec, "
+ "buffer_size={}bytes".format(is_secured, le_connection_interval,
+ buffer_size))
+ status, client_conn_id, server_conn_id = orchestrate_coc_connection(
+ self.client_ad, self.server_ad, True, is_secured,
+ le_connection_interval, le_tx_data_length)
+ if not status:
+ return False
+
+ list_server_ad = [self.server_ad]
+ list_client_conn_id = [client_conn_id]
+ data_rate = do_multi_connection_throughput(
+ self.client_ad, list_server_ad, list_client_conn_id,
+ num_iterations, number_buffers, buffer_size)
+ if data_rate <= 0:
+ return False
+ self.log.info(
+ "_run_coc_connection_throughput: throughput=%d bytes per sec",
+ data_rate)
+
+ return True
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='b6989966-c504-4934-bcd7-57fb4f7fde9c')
+ def test_coc_secured_connection(self):
+ """Test Bluetooth LE CoC secured connection
+
+ Test LE CoC though establishing a basic connection with security.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish an LE CoC Secured connection from the client to the server AD.
+ 3. Verify that the LE CoC connection is active from both the client and
+ server.
+ Expected Result:
+ LE CoC connection is established then disconnected succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+ is_secured = True
+ self.log.info(
+ "_test_coc_secured_connection: calling orchestrate_coc_connection but "
+ "isBle=1 and securedConn={}".format(is_secured))
+ status, client_conn_id, server_conn_id = orchestrate_coc_connection(
+ self.client_ad, self.server_ad, True, is_secured)
+ if not status:
+ return False
+
+ return True
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='6587792c-78fb-469f-9084-772c249f97de')
+ def test_coc_insecured_connection(self):
+ """Test Bluetooth LE CoC insecured connection
+
+ Test LE CoC though establishing a basic connection with no security.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish an LE CoC Secured connection from the client to the server AD.
+ 3. Verify that the LE CoC connection is active from both the client and
+ server.
+ Expected Result:
+ LE CoC connection is established then disconnected succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+ is_secured = False
+ self.log.info(
+ "test_coc_insecured_connection: calling orchestrate_coc_connection but "
+ "isBle=1 and securedConn={}".format(is_secured))
+ status, client_conn_id, server_conn_id = orchestrate_coc_connection(
+ self.client_ad, self.server_ad, True, is_secured)
+ if not status:
+ return False
+
+ return True
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='32a7b02e-f2b5-4193-b414-36c8815ac407')
+ def test_coc_secured_connection_write_ascii(self):
+ """Test LE CoC secured connection writing and reading ascii data
+
+ Test LE CoC though establishing a secured connection and reading and writing ascii data.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish an LE CoC connection from the client to the server AD. The security of
+ connection is TRUE.
+ 3. Verify that the LE CoC connection is active from both the client and
+ server.
+ 4. Write data from the client and read received data from the server.
+ 5. Verify data matches from client and server
+ 6. Disconnect the LE CoC connection.
+
+ Expected Result:
+ LE CoC connection is established then disconnected succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+ is_secured = True
+ self.log.info(
+ "test_coc_secured_connection_write_ascii: calling "
+ "orchestrate_coc_connection. is_secured={}".format(is_secured))
+ status, client_conn_id, server_conn_id = orchestrate_coc_connection(
+ self.client_ad, self.server_ad, True, is_secured)
+ if not status:
+ return False
+ if not write_read_verify_data(self.client_ad, self.server_ad,
+ self.message, False):
+ return False
+ if not verify_server_and_client_connected(self.client_ad,
+ self.server_ad):
+ return False
+
+ return True
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='12537d27-79c9-40a0-8bdb-d023b0e36b58')
+ def test_coc_insecured_connection_write_ascii(self):
+ """Test LE CoC insecured connection writing and reading ascii data
+
+ Test LE CoC though establishing a connection.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish an LE CoC connection from the client to the server AD. The security of
+ connection is FALSE.
+ 3. Verify that the LE CoC connection is active from both the client and
+ server.
+ 4. Write data from the client and read received data from the server.
+ 5. Verify data matches from client and server
+ 6. Disconnect the LE CoC connection.
+
+ Expected Result:
+ LE CoC connection is established then disconnected succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+ is_secured = False
+ self.log.info(
+ "test_coc_secured_connection_write_ascii: calling "
+ "orchestrate_coc_connection. is_secured={}".format(is_secured))
+ status, client_conn_id, server_conn_id = orchestrate_coc_connection(
+ self.client_ad, self.server_ad, True, is_secured)
+ if not status:
+ return False
+ if not write_read_verify_data(self.client_ad, self.server_ad,
+ self.message, False):
+ return False
+ if not verify_server_and_client_connected(self.client_ad,
+ self.server_ad):
+ return False
+
+ return True
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='214037f4-f0d1-47db-86a7-5230c71bdcac')
+ def test_coc_secured_connection_throughput(self):
+ """Test LE CoC writing and measured data throughput with security
+
+ Test CoC thoughput by establishing a secured connection and sending data.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server AD.
+ 3. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 4. Write data from the client to server.
+ 5. Verify data matches from client and server
+ 6. Disconnect the L2CAP CoC connections.
+
+ Expected Result:
+ CoC connection is established then disconnected succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+
+ is_secured = True
+ # Note: A 117 octets buffer size would fix nicely to a 123 bytes Data Length
+ return self._run_coc_connection_throughput(is_secured, 117)
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='6dc019bb-c3bf-4c98-978e-e2c5755058d7')
+ def test_coc_insecured_connection_throughput(self):
+ """Test LE CoC writing and measured data throughput without security.
+
+ Test CoC thoughput by establishing an insecured connection and sending data.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Establish a L2CAP CoC connection from the client to the server AD.
+ 3. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 4. Write data from the client to server.
+ 5. Verify data matches from client and server
+ 6. Disconnect the L2CAP CoC connections.
+
+ Expected Result:
+ CoC connection is established then disconnected succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 1
+ """
+
+ is_secured = False
+ # Note: A 117 octets buffer size would fix nicely to a 123 bytes Data Length
+ return self._run_coc_connection_throughput(is_secured, 117)
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='0af94805-1550-426c-bfdd-191b8b3a4c12')
+ def test_coc_connection_throughput_NOSEC_10CI_60SIZE(self):
+ """Test LE CoC data throughput with 10msec CI and 60bytes buffer size.
+
+ Test CoC thoughput by establishing a connection and sending data with 10msec
+ Connection Interval and 60 bytes data buffer size.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Change the Connection Interval to 10msec.
+ 3. Change Payload Buffer Size to 60 bytes.
+ 4. Establish a L2CAP CoC connection from the client to the server AD.
+ 5. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 6. Write data from the client to server.
+ 7. Verify data matches from client and server
+ 8. Disconnect the L2CAP CoC connections.
+
+ Expected Result:
+ CoC connection is established, check transmitted data contents, then disconnected
+ succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ is_secured = False
+ le_connection_interval = 10
+ buffer_size = 60
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ return self._run_coc_connection_throughput(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='c32dac07-623a-4fdd-96c6-387a76afb2af')
+ def test_coc_connection_throughput_NOSEC_10CI_80SIZE(self):
+ """Test LE CoC data throughput with 10msec CI and 80bytes buffer size.
+
+ Test CoC thoughput by establishing a connection and sending data with 10msec
+ Connection Interval and 80 bytes data buffer size.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Change the Connection Interval to 10msec.
+ 3. Change Payload Buffer Size to 80 bytes.
+ 4. Establish a L2CAP CoC connection from the client to the server AD.
+ 5. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 6. Write data from the client to server.
+ 7. Verify data matches from client and server
+ 8. Disconnect the L2CAP CoC connections.
+
+ Expected Result:
+ CoC connection is established, check transmitted data contents, then disconnected
+ succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ is_secured = False
+ le_connection_interval = 10
+ buffer_size = 80
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ return self._run_coc_connection_throughput(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='45d1b0c1-73b6-483f-ac6b-c3cec805da32')
+ def test_coc_connection_throughput_NOSEC_10CI_120SIZE(self):
+ """Test LE CoC data throughput with 10msec CI and 120bytes buffer size.
+
+ Test CoC thoughput by establishing a connection and sending data with 10msec
+ Connection Interval and 120 bytes data buffer size.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Change the Connection Interval to 10msec.
+ 3. Change Payload Buffer Size to 120 bytes.
+ 4. Establish a L2CAP CoC connection from the client to the server AD.
+ 5. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 6. Write data from the client to server.
+ 7. Verify data matches from client and server
+ 8. Disconnect the L2CAP CoC connections.
+
+ Expected Result:
+ CoC connection is established, check transmitted data contents, then disconnected
+ succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ is_secured = False
+ le_connection_interval = 10
+ buffer_size = 120
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ return self._run_coc_connection_throughput(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='85f07f07-1017-42db-b38d-df0bf2fce804')
+ def test_coc_connection_throughput_NOSEC_15CI_120SIZE(self):
+ """Test LE CoC data throughput with 15msec CI and 120bytes buffer size.
+
+ Test CoC thoughput by establishing a connection and sending data with 15msec
+ Connection Interval and 120 bytes data buffer size.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Change the Connection Interval to 15msec.
+ 3. Change Payload Buffer Size to 120 bytes.
+ 4. Establish a L2CAP CoC connection from the client to the server AD.
+ 5. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 6. Write data from the client to server.
+ 7. Verify data matches from client and server
+ 8. Disconnect the L2CAP CoC connections.
+
+ Expected Result:
+ CoC connection is established, check transmitted data contents, then disconnected
+ succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ is_secured = False
+ le_connection_interval = 15
+ buffer_size = 120
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ return self._run_coc_connection_throughput(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='4d3d4a06-7bbb-4a8c-9016-f326560cebb0')
+ def test_coc_connection_throughput_NOSEC_15CI_180SIZE(self):
+ """Test LE CoC data throughput with 15msec CI and 180bytes buffer size.
+
+ Test CoC thoughput by establishing a connection and sending data with 15msec
+ Connection Interval and 180 bytes data buffer size.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Change the Connection Interval to 15msec.
+ 3. Change Payload Buffer Size to 180 bytes.
+ 4. Establish a L2CAP CoC connection from the client to the server AD.
+ 5. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 6. Write data from the client to server.
+ 7. Verify data matches from client and server
+ 8. Disconnect the L2CAP CoC connections.
+
+ Expected Result:
+ CoC connection is established, check transmitted data contents, then disconnected
+ succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ is_secured = False
+ le_connection_interval = 15
+ buffer_size = 180
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ return self._run_coc_connection_throughput(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='124d85ba-41e6-4ab7-a017-99a88db7524a')
+ def test_coc_connection_throughput_NOSEC_20CI_240SIZE(self):
+ """Test LE CoC data throughput with 20msec CI and 240bytes buffer size.
+
+ Test CoC thoughput by establishing a connection and sending data with 20msec
+ Connection Interval and 240 bytes data buffer size.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Change the Connection Interval to 20msec.
+ 3. Change Payload Buffer Size to 240 bytes.
+ 4. Establish a L2CAP CoC connection from the client to the server AD.
+ 5. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 6. Write data from the client to server.
+ 7. Verify data matches from client and server
+ 8. Disconnect the L2CAP CoC connections.
+
+ Expected Result:
+ CoC connection is established, check transmitted data contents, then disconnected
+ succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ is_secured = False
+ le_connection_interval = 20
+ buffer_size = 240
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ return self._run_coc_connection_throughput(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='218932bc-ebb0-4c2b-96ad-220c600b50b1')
+ def test_coc_connection_throughput_NOSEC_30CI_240SIZE(self):
+ """Test LE CoC data throughput with 30msec CI and 240bytes buffer size.
+
+ Test CoC thoughput by establishing a connection and sending data with 30msec
+ Connection Interval and 240 bytes data buffer size.
+
+ Steps:
+ 1. Get the mac address of the server device.
+ 2. Change the Connection Interval to 30msec.
+ 3. Change Payload Buffer Size to 240 bytes.
+ 4. Establish a L2CAP CoC connection from the client to the server AD.
+ 5. Verify that the L2CAP CoC connection is active from both the client
+ and server.
+ 6. Write data from the client to server.
+ 7. Verify data matches from client and server
+ 8. Disconnect the L2CAP CoC connections.
+
+ Expected Result:
+ CoC connection is established, check transmitted data contents, then disconnected
+ succcessfully.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: BLE, CoC
+ Priority: 2
+ """
+
+ is_secured = False
+ le_connection_interval = 30
+ buffer_size = 240
+ le_tx_data_length = buffer_size + l2cap_coc_header_size
+ return self._run_coc_connection_throughput(
+ is_secured, buffer_size, le_connection_interval, le_tx_data_length)
diff --git a/acts/tests/google/ble/examples/BleExamplesTest.py b/acts/tests/google/ble/examples/BleExamplesTest.py
index 6056086..2c2174b 100644
--- a/acts/tests/google/ble/examples/BleExamplesTest.py
+++ b/acts/tests/google/ble/examples/BleExamplesTest.py
@@ -19,7 +19,7 @@
import pprint
-from acts.controllers import android_devices
+from acts.controllers import android_device
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt.bt_constants import adv_succ
from acts.test_utils.bt.bt_constants import scan_result
diff --git a/acts/tests/google/ble/examples/GattServerExampleTest.py b/acts/tests/google/ble/examples/GattServerExampleTest.py
new file mode 100644
index 0000000..cdd18df
--- /dev/null
+++ b/acts/tests/google/ble/examples/GattServerExampleTest.py
@@ -0,0 +1,67 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_constants import gatt_characteristic
+from acts.test_utils.bt.bt_constants import gatt_descriptor
+from acts.test_utils.bt.bt_constants import gatt_service_types
+from acts.test_utils.bt.bt_constants import gatt_characteristic_value_format
+from acts.test_utils.bt.bt_constants import gatt_char_desc_uuids
+from acts.test_utils.bt.gatts_lib import GattServerLib
+
+service_uuid = '0000a00a-0000-1000-8000-00805f9b34fb'
+characteristic_uuid = 'aa7edd5a-4d1d-4f0e-883a-d145616a1630'
+descriptor_uuid = gatt_char_desc_uuids['client_char_cfg']
+
+gatt_server_read_descriptor_sample = {
+ 'services': [{
+ 'uuid':
+ service_uuid,
+ 'type':
+ gatt_service_types['primary'],
+ 'characteristics': [{
+ 'uuid':
+ characteristic_uuid,
+ 'properties':
+ gatt_characteristic['property_read'],
+ 'permissions':
+ gatt_characteristic['permission_read'],
+ 'instance_id':
+ 0x002a,
+ 'value_type':
+ gatt_characteristic_value_format['string'],
+ 'value':
+ 'Test Database',
+ 'descriptors': [{
+ 'uuid': descriptor_uuid,
+ 'permissions': gatt_descriptor['permission_read'],
+ }]
+ }]
+ }]
+}
+
+
+class GattServerExampleTest(BluetoothBaseTest):
+ def __init__(self, controllers):
+ BluetoothBaseTest.__init__(self, controllers)
+ self.dut = self.android_devices[0]
+
+ @BluetoothBaseTest.bt_test_wrap
+ def test_create_gatt_server_db_example(self):
+ gatts = GattServerLib(log=self.log, dut=self.dut)
+ gatts.setup_gatts_db(database=gatt_server_read_descriptor_sample)
+ self.log.info(gatts.list_all_uuids())
+ return True
diff --git a/acts/tests/google/ble/gatt/GattConnectTest.py b/acts/tests/google/ble/gatt/GattConnectTest.py
index 667611d..dd6857c 100644
--- a/acts/tests/google/ble/gatt/GattConnectTest.py
+++ b/acts/tests/google/ble/gatt/GattConnectTest.py
@@ -23,15 +23,20 @@
from acts.test_decorators import test_tracker_info
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
+from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
+from acts.test_utils.bt.bt_constants import ble_scan_settings_match_nums
from acts.test_utils.bt.bt_constants import bt_profile_constants
from acts.test_utils.bt.bt_constants import gatt_characteristic
from acts.test_utils.bt.bt_constants import gatt_descriptor
from acts.test_utils.bt.bt_constants import gatt_service_types
from acts.test_utils.bt.bt_constants import gatt_cb_err
from acts.test_utils.bt.bt_constants import gatt_cb_strings
+from acts.test_utils.bt.bt_constants import gatt_connection_state
from acts.test_utils.bt.bt_constants import gatt_mtu_size
from acts.test_utils.bt.bt_constants import gatt_phy_mask
from acts.test_utils.bt.bt_constants import gatt_transport
+from acts.test_utils.bt.bt_constants import scan_result
from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
from acts.test_utils.bt.bt_gatt_utils import wait_for_gatt_disconnect_event
@@ -43,6 +48,8 @@
from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+PHYSICAL_DISCONNECT_TIMEOUT = 5
+
class GattConnectTest(BluetoothBaseTest):
adv_instances = []
@@ -61,6 +68,12 @@
bluetooth_gatt_list = []
self.gatt_server_list = []
self.adv_instances = []
+ # Ensure there is ample time for a physical disconnect in between
+ # testcases.
+ self.log.info(
+ "Waiting for {} seconds for physical GATT disconnections".format(
+ PHYSICAL_DISCONNECT_TIMEOUT))
+ time.sleep(PHYSICAL_DISCONNECT_TIMEOUT)
def teardown_test(self):
for bluetooth_gatt in self.bluetooth_gatt_list:
@@ -90,8 +103,8 @@
event = self.per_ad.ed.pop_event(expected_event,
self.default_timeout)
except Empty:
- self.log.error(gatt_cb_strings['serv_added_err'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_strings['serv_added_err'].format(expected_event))
return False
if event['data']['serviceUuid'].lower() != uuid.lower():
self.log.error("Uuid mismatch. Found: {}, Expected {}.".format(
@@ -111,8 +124,8 @@
mtu_size_found, expected_mtu))
return False
except Empty:
- self.log.error(gatt_cb_err['mtu_changed_err'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['mtu_changed_err'].format(expected_event))
return False
expected_event = gatt_cb_strings['mtu_serv_changed'].format(
@@ -126,8 +139,8 @@
mtu_size_found, expected_mtu))
return False
except Empty:
- self.log.error(gatt_cb_err['mtu_serv_changed_err'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['mtu_serv_changed_err'].format(expected_event))
return False
return True
@@ -215,9 +228,9 @@
return False
self.per_ad.droid.bleStopBleAdvertising(adv_callback)
try:
- event = self.cen_ad.ed.pop_event(gatt_cb_strings[
- 'gatt_conn_change'].format(gatt_callback,
- self.default_timeout))
+ event = self.cen_ad.ed.pop_event(
+ gatt_cb_strings['gatt_conn_change'].format(
+ gatt_callback, self.default_timeout))
self.log.error(
"Connection event found when not expected: {}".format(event))
return False
@@ -264,11 +277,12 @@
gatt_server_cb)
self.gatt_server_list.append(gatt_server)
autoconnect = False
- mac_address, adv_callback = (
+ mac_address, adv_callback, scan_callback = (
get_mac_address_of_generic_advertisement(self.cen_ad, self.per_ad))
try:
bluetooth_gatt, gatt_callback = setup_gatt_connection(
self.cen_ad, mac_address, autoconnect)
+ self.cen_ad.droid.bleStopBleScan(scan_callback)
self.bluetooth_gatt_list.append(bluetooth_gatt)
except GattTestUtilsError as err:
self.log.error(err)
@@ -293,8 +307,8 @@
event = self.cen_ad.ed.pop_event(expected_event,
self.default_timeout)
except Empty:
- self.log.error(gatt_cb_err['gatt_conn_change_err'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['gatt_conn_changed_err'].format(expected_event))
test_result = False
return self._orchestrate_gatt_disconnection(bluetooth_gatt,
gatt_callback)
@@ -332,7 +346,7 @@
gatt_server = self.per_ad.droid.gattServerOpenGattServer(
gatt_server_cb)
self.gatt_server_list.append(gatt_server)
- mac_address, adv_callback = (
+ mac_address, adv_callback, scan_callback = (
get_mac_address_of_generic_advertisement(self.cen_ad, self.per_ad))
# Make GATT connection 1
try:
@@ -342,6 +356,7 @@
False,
transport=gatt_transport['auto'],
opportunistic=False)
+ self.cen_ad.droid.bleStopBleScan(scan_callback)
self.bluetooth_gatt_list.append(bluetooth_gatt_1)
except GattTestUtilsError as err:
self.log.error(err)
@@ -572,8 +587,8 @@
try:
self.cen_ad.ed.pop_event(expected_event, self.default_timeout)
except Empty:
- self.log.error(gatt_cb_err['rd_remote_rssi_err'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['rd_remote_rssi_err'].format(expected_event))
return self._orchestrate_gatt_disconnection(bluetooth_gatt,
gatt_callback)
@@ -624,8 +639,8 @@
event = self.cen_ad.ed.pop_event(expected_event,
self.default_timeout)
except Empty:
- self.log.error(gatt_cb_err['gatt_serv_disc'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['gatt_serv_disc'].format(expected_event))
return False
return self._orchestrate_gatt_disconnection(bluetooth_gatt,
gatt_callback)
@@ -684,8 +699,8 @@
self.default_timeout)
discovered_services_index = event['data']['ServicesIndex']
except Empty:
- self.log.error(gatt_cb_err['gatt_serv_disc'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['gatt_serv_disc'].format(expected_event))
return False
log_gatt_server_uuids(self.cen_ad, discovered_services_index)
return self._orchestrate_gatt_disconnection(bluetooth_gatt,
@@ -741,8 +756,8 @@
event = self.cen_ad.ed.pop_event(expected_event,
self.default_timeout)
except Empty:
- self.log.error(gatt_cb_err['gatt_serv_disc'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['gatt_serv_disc'].format(expected_event))
return False
discovered_services_index = event['data']['ServicesIndex']
log_gatt_server_uuids(self.cen_ad, discovered_services_index)
@@ -782,7 +797,7 @@
gatt_server = self.per_ad.droid.gattServerOpenGattServer(
gatt_server_cb)
self.gatt_server_list.append(gatt_server)
- mac_address, adv_callback = get_mac_address_of_generic_advertisement(
+ mac_address, adv_callback, scan_callback = get_mac_address_of_generic_advertisement(
self.cen_ad, self.per_ad)
autoconnect = False
for i in range(1000):
@@ -790,11 +805,12 @@
try:
bluetooth_gatt, gatt_callback = setup_gatt_connection(
self.cen_ad, mac_address, autoconnect)
+ self.cen_ad.droid.bleStopBleScan(scan_callback)
except GattTestUtilsError as err:
self.log.error(err)
return False
- test_result = self._orchestrate_gatt_disconnection(bluetooth_gatt,
- gatt_callback)
+ test_result = self._orchestrate_gatt_disconnection(
+ bluetooth_gatt, gatt_callback)
if not test_result:
self.log.info("Failed to disconnect from peripheral device.")
return False
@@ -854,8 +870,8 @@
gatt_characteristic['permission_write_encrypted_mitm'])
gatt_service = self.per_ad.droid.gattServerCreateService(
service_uuid, gatt_service_types['primary'])
- self.per_ad.droid.gattServerAddCharacteristicToService(gatt_service,
- characteristic)
+ self.per_ad.droid.gattServerAddCharacteristicToService(
+ gatt_service, characteristic)
self.per_ad.droid.gattServerAddService(gatt_server, gatt_service)
result = self._find_service_added_event(gatt_server_cb, service_uuid)
if not result:
@@ -871,8 +887,8 @@
event = self.cen_ad.ed.pop_event(expected_event,
self.default_timeout)
except Empty:
- self.log.error(gatt_cb_err['gatt_serv_disc'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['gatt_serv_disc'].format(expected_event))
return False
discovered_services_index = event['data']['ServicesIndex']
else:
@@ -901,8 +917,8 @@
bonded_devices = \
self.cen_ad.droid.bluetoothGetBondedDevices()
for device in bonded_devices:
- if ('name' in device.keys() and
- device['name'] == target_name):
+ if ('name' in device.keys()
+ and device['name'] == target_name):
bonded = True
break
bonded = False
@@ -911,8 +927,8 @@
bonded_devices = \
self.per_ad.droid.bluetoothGetBondedDevices()
for device in bonded_devices:
- if ('name' in device.keys() and
- device['name'] == target_name):
+ if ('name' in device.keys()
+ and device['name'] == target_name):
bonded = True
break
for ad in [self.cen_ad, self.per_ad]:
@@ -922,8 +938,8 @@
time.sleep(2)
bonded_devices = ad.droid.bluetoothGetBondedDevices()
if len(bonded_devices) > 0:
- self.log.error("Failed to unbond devices: {}".format(
- bonded_devices))
+ self.log.error(
+ "Failed to unbond devices: {}".format(bonded_devices))
return False
return self._orchestrate_gatt_disconnection(bluetooth_gatt,
gatt_callback)
@@ -989,3 +1005,151 @@
self.adv_instances.append(adv_callback)
return self._orchestrate_gatt_disconnection(bluetooth_gatt,
gatt_callback)
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='a0a37ca6-9fa8-4d35-9fdb-0e25b4b8a363')
+ def test_gatt_connect_second_adv_after_canceling_first_adv(self):
+ """Test GATT connection to peripherals second advertising address.
+
+ The the ability of cancelling GATT connections and trying to reconnect
+ to the same device via a different address.
+
+ Steps:
+ 1. A starts advertising
+ 2. B starts scanning and finds A's mac address
+ 3. Stop advertisement from step 1. Start a new advertisement on A and
+ find the new new mac address, B knows of both old and new address.
+ 4. B1 sends connect request to old address of A
+ 5. B1 cancel connect attempt after 10 seconds
+ 6. B1 sends connect request to new address of A
+ 7. Verify B1 establish connection to A in less than 10 seconds
+
+ Expected Result:
+ Verify that a connection was established only on the second
+ advertisement's mac address.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: LE, Advertising, Scanning, GATT
+ Priority: 3
+ """
+ autoconnect = False
+ transport = gatt_transport['auto']
+ opportunistic = False
+ # Setup a basic Gatt server on the peripheral
+ gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
+ gatt_server = self.per_ad.droid.gattServerOpenGattServer(
+ gatt_server_cb)
+
+ # Set advertisement settings to include local name in advertisement
+ # and set the advertising mode to low_latency.
+ self.per_ad.droid.bleSetAdvertiseSettingsIsConnectable(True)
+ self.per_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
+ self.per_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
+ ble_advertise_settings_modes['low_latency'])
+
+ # Setup necessary advertisement objects.
+ advertise_data = self.per_ad.droid.bleBuildAdvertiseData()
+ advertise_settings = self.per_ad.droid.bleBuildAdvertiseSettings()
+ advertise_callback = self.per_ad.droid.bleGenBleAdvertiseCallback()
+
+ # Step 1: Start advertisement
+ self.per_ad.droid.bleStartBleAdvertising(
+ advertise_callback, advertise_data, advertise_settings)
+
+ # Setup scan settings for low_latency scanning and to include the local name
+ # of the advertisement started in step 1.
+ filter_list = self.cen_ad.droid.bleGenFilterList()
+ self.cen_ad.droid.bleSetScanSettingsNumOfMatches(
+ ble_scan_settings_match_nums['one'])
+ self.cen_ad.droid.bleSetScanFilterDeviceName(
+ self.per_ad.droid.bluetoothGetLocalName())
+ self.cen_ad.droid.bleBuildScanFilter(filter_list)
+ self.cen_ad.droid.bleSetScanSettingsScanMode(
+ ble_scan_settings_modes['low_latency'])
+
+ # Setup necessary scan objects.
+ scan_settings = self.cen_ad.droid.bleBuildScanSetting()
+ scan_callback = self.cen_ad.droid.bleGenScanCallback()
+
+ # Step 2: Start scanning on central Android device and find peripheral
+ # address.
+ self.cen_ad.droid.bleStartBleScan(filter_list, scan_settings,
+ scan_callback)
+ expected_event_name = scan_result.format(scan_callback)
+ try:
+ mac_address_pre_restart = self.cen_ad.ed.pop_event(
+ expected_event_name, self.default_timeout)['data']['Result'][
+ 'deviceInfo']['address']
+ self.log.info(
+ "Peripheral advertisement found with mac address: {}".format(
+ mac_address_pre_restart))
+ except Empty:
+ self.log.info("Peripheral advertisement not found")
+ test_result = False
+
+ # Step 3: Restart peripheral advertising such that a new mac address is
+ # created.
+ self.per_ad.droid.bleStopBleAdvertising(advertise_callback)
+ self.per_ad.droid.bleStartBleAdvertising(
+ advertise_callback, advertise_data, advertise_settings)
+
+ try:
+ mac_address_post_restart = self.cen_ad.ed.pop_event(
+ expected_event_name, self.default_timeout)['data']['Result'][
+ 'deviceInfo']['address']
+ self.log.info(
+ "Peripheral advertisement found with mac address: {}".format(
+ mac_address_post_restart))
+ except Empty:
+ self.log.info("Peripheral advertisement not found")
+ test_result = False
+
+ # Steps 4: Try to connect to the first mac address
+ gatt_callback = self.cen_ad.droid.gattCreateGattCallback()
+ self.log.info(
+ "Gatt Connect to mac address {}.".format(mac_address_pre_restart))
+ bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt(
+ gatt_callback, mac_address_pre_restart, autoconnect, transport,
+ opportunistic, gatt_phy_mask['1m_mask'])
+ self.bluetooth_gatt_list.append(bluetooth_gatt)
+ expected_event = gatt_cb_strings['gatt_conn_change'].format(
+ gatt_callback)
+ try:
+ event = self.cen_ad.ed.pop_event(expected_event,
+ self.default_timeout)
+ self.log.error(
+ "Connection callback updated unexpectedly: {}".format(event))
+ return False
+ except Empty:
+ self.log.info("No connection update as expected.")
+
+ # Step 5: Cancel connection request.
+ self.cen_ad.droid.gattClientDisconnect(bluetooth_gatt)
+
+ # Step 6: Connect to second mac address.
+ self.log.info(
+ "Gatt Connect to mac address {}.".format(mac_address_post_restart))
+ bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt(
+ gatt_callback, mac_address_post_restart, autoconnect, transport,
+ opportunistic, gatt_phy_mask['1m_mask'])
+ self.bluetooth_gatt_list.append(bluetooth_gatt)
+ expected_event = gatt_cb_strings['gatt_conn_change'].format(
+ gatt_callback)
+
+ # Step 7: Verify connection was setup successfully.
+ try:
+ event = self.cen_ad.ed.pop_event(expected_event,
+ self.default_timeout)
+ self.log.info(
+ "Connection callback updated successfully: {}".format(event))
+ if event['data']['State'] != gatt_connection_state['connected']:
+ self.log.error(
+ "Could not establish a connection to the peripheral.")
+ return False
+ except Empty:
+ self.log.error("No connection update was found.")
+ return False
+ return self.cen_ad.droid.gattClientDisconnect(bluetooth_gatt)
diff --git a/acts/tests/google/ble/gatt/GattToolTest.py b/acts/tests/google/ble/gatt/GattToolTest.py
index d9921b4..2b0b619 100644
--- a/acts/tests/google/ble/gatt/GattToolTest.py
+++ b/acts/tests/google/ble/gatt/GattToolTest.py
@@ -27,19 +27,19 @@
from queue import Empty
import time
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
from acts.test_utils.bt.bt_constants import gatt_cb_err
from acts.test_utils.bt.bt_constants import gatt_cb_strings
from acts.test_utils.bt.bt_constants import gatt_descriptor
from acts.test_utils.bt.bt_constants import gatt_transport
+from acts.test_utils.bt.bt_constants import scan_result
from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
from acts.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_constants import scan_result
class GattToolTest(BluetoothBaseTest):
@@ -84,8 +84,8 @@
def _is_peripheral_advertising(self):
self.cen_ad.droid.bleSetScanFilterDeviceAddress(self.ble_mac_address)
- self.cen_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
- 'low_latency'])
+ self.cen_ad.droid.bleSetScanSettingsScanMode(
+ ble_scan_settings_modes['low_latency'])
filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
self.cen_ad.droid)
self.cen_ad.droid.bleBuildScanFilter(filter_list)
@@ -96,8 +96,8 @@
test_result = True
try:
self.cen_ad.ed.pop_event(expected_event_name, self.DEFAULT_TIMEOUT)
- self.log.info("Peripheral found with event: {}".format(
- expected_event_name))
+ self.log.info(
+ "Peripheral found with event: {}".format(expected_event_name))
except Empty:
self.log.info("Peripheral not advertising or not found: {}".format(
self.ble_mac_address))
@@ -110,8 +110,8 @@
time.sleep(2) #Grace timeout for unbonding to finish
bonded_devices = self.cen_ad.droid.bluetoothGetBondedDevices()
if bonded_devices:
- self.log.error("Failed to unbond device... found: {}".format(
- bonded_devices))
+ self.log.error(
+ "Failed to unbond device... found: {}".format(bonded_devices))
return False
return True
@@ -145,9 +145,9 @@
self.AUTOCONNECT = False
start_time = self._get_time_in_milliseconds()
try:
- bluetooth_gatt, gatt_callback = (
- setup_gatt_connection(self.cen_ad, self.ble_mac_address,
- self.AUTOCONNECT, gatt_transport['le']))
+ bluetooth_gatt, gatt_callback = (setup_gatt_connection(
+ self.cen_ad, self.ble_mac_address, self.AUTOCONNECT,
+ gatt_transport['le']))
except GattTestUtilsError as err:
self.log.error(err)
return False
@@ -247,9 +247,9 @@
Priority: 2
"""
try:
- bluetooth_gatt, gatt_callback = (
- setup_gatt_connection(self.cen_ad, self.ble_mac_address,
- self.AUTOCONNECT, gatt_transport['le']))
+ bluetooth_gatt, gatt_callback = (setup_gatt_connection(
+ self.cen_ad, self.ble_mac_address, self.AUTOCONNECT,
+ gatt_transport['le']))
except GattTestUtilsError as err:
self.log.error(err)
return False
@@ -261,8 +261,8 @@
self.DEFAULT_TIMEOUT)
discovered_services_index = event['data']['ServicesIndex']
except Empty:
- self.log.error(gatt_cb_err['gatt_serv_disc'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['gatt_serv_disc'].format(expected_event))
return False
log_gatt_server_uuids(self.cen_ad, discovered_services_index)
try:
@@ -382,9 +382,9 @@
if not self._pair_with_peripheral():
return False
try:
- bluetooth_gatt, gatt_callback = (
- setup_gatt_connection(self.cen_ad, self.ble_mac_address,
- self.AUTOCONNECT, gatt_transport['le']))
+ bluetooth_gatt, gatt_callback = (setup_gatt_connection(
+ self.cen_ad, self.ble_mac_address, self.AUTOCONNECT,
+ gatt_transport['le']))
except GattTestUtilsError as err:
self.log.error(err)
return False
@@ -396,8 +396,8 @@
self.DEFAULT_TIMEOUT)
discovered_services_index = event['data']['ServicesIndex']
except Empty:
- self.log.error(gatt_cb_err['gatt_serv_disc'].format(
- expected_event))
+ self.log.error(
+ gatt_cb_err['gatt_serv_disc'].format(expected_event))
return False
# TODO: in setup save service_cound and discovered_services_index
# programatically
@@ -431,7 +431,7 @@
# set 15 minute notification test time
notification_test_time = 900
end_time = time.time() + notification_test_time
- expected_event = GattCbStrings.CHAR_CHANGE.value.format(gatt_callback)
+ expected_event = gatt_cb_strings['char_change'].format(gatt_callback)
while time.time() < end_time:
try:
event = self.cen_ad.ed.pop_event(expected_event,
@@ -440,6 +440,6 @@
except Empty as err:
print(err)
self.log.error(
- GattCbStrings.CHAR_CHANGE_ERR.value.format(expected_event))
+ gatt_cb_err['char_change_err'].format(expected_event))
return False
return True
diff --git a/acts/tests/google/ble/scan/BleBackgroundScanTest.py b/acts/tests/google/ble/scan/BleBackgroundScanTest.py
index 3cb2842..55fb86b 100644
--- a/acts/tests/google/ble/scan/BleBackgroundScanTest.py
+++ b/acts/tests/google/ble/scan/BleBackgroundScanTest.py
@@ -19,6 +19,7 @@
from queue import Empty
+from acts import utils
from acts.test_decorators import test_tracker_info
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt.bt_test_utils import bluetooth_off
@@ -38,7 +39,6 @@
class BleBackgroundScanTest(BluetoothBaseTest):
default_timeout = 10
- max_scan_instances = 28
report_delay = 2000
scan_callbacks = []
adv_callbacks = []
@@ -50,6 +50,12 @@
self.scn_ad = self.android_devices[0]
self.adv_ad = self.android_devices[1]
+ def setup_class(self):
+ super(BluetoothBaseTest, self).setup_class()
+ utils.set_location_service(self.scn_ad, True)
+ utils.set_location_service(self.adv_ad, True)
+ return True
+
def setup_test(self):
# Always start tests with Bluetooth enabled and BLE disabled.
enable_bluetooth(self.scn_ad.droid, self.scn_ad.ed)
@@ -100,8 +106,8 @@
"""
self.scn_ad.droid.bluetoothEnableBLE()
self._setup_generic_advertisement()
- self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
- 'low_latency'])
+ self.scn_ad.droid.bleSetScanSettingsScanMode(
+ ble_scan_settings_modes['low_latency'])
filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
self.scn_ad.droid)
self.scn_ad.droid.bleSetScanFilterDeviceName(
@@ -152,8 +158,8 @@
"""
self._setup_generic_advertisement()
self.scn_ad.droid.bluetoothEnableBLE()
- self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
- 'low_latency'])
+ self.scn_ad.droid.bleSetScanSettingsScanMode(
+ ble_scan_settings_modes['low_latency'])
filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
self.scn_ad.droid)
self.scn_ad.droid.bleSetScanFilterDeviceName(
@@ -174,8 +180,9 @@
try:
self.scn_ad.ed.pop_event(expected_event, self.default_timeout)
except Empty:
- self.log.error("Scan Result event not found. Expected {}".
- format(expected_event))
+ self.log.error(
+ "Scan Result event not found. Expected {}".format(
+ expected_event))
return False
except Exception:
self.log.info(
@@ -213,6 +220,7 @@
"""
ble_state_error_msg = "Bluetooth LE State not OK {}. Expected {} got {}"
# Enable BLE always available (effectively enabling BT in location)
+ self.scn_ad.shell.enable_ble_scanning()
self.scn_ad.droid.bluetoothEnableBLE()
self.scn_ad.droid.bluetoothToggleState(False)
try:
@@ -237,8 +245,9 @@
try:
self.scn_ad.ed.pop_event(bluetooth_le_off, self.default_timeout)
except Empty:
- self.log.error("Bluetooth LE Off event not found. Expected {}".
- format(bluetooth_le_off))
+ self.log.error(
+ "Bluetooth LE Off event not found. Expected {}".format(
+ bluetooth_le_off))
return False
state = self.scn_ad.droid.bluetoothGetLeState()
if state != bt_adapter_states['off']:
diff --git a/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py b/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py
index 37f7e07..cdca99c 100644
--- a/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py
+++ b/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py
@@ -18,6 +18,7 @@
"""
from queue import Empty
+from acts import utils
from acts.test_decorators import test_tracker_info
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
@@ -35,7 +36,6 @@
class BleOnLostOnFoundTest(BluetoothBaseTest):
default_timeout = 10
- max_scan_instances = 28
active_scan_callback_list = []
active_adv_callback_list = []
@@ -44,6 +44,12 @@
self.scn_ad = self.android_devices[0]
self.adv_ad = self.android_devices[1]
+ def setup_class(self):
+ super(BluetoothBaseTest, self).setup_class()
+ utils.set_location_service(self.scn_ad, True)
+ utils.set_location_service(self.adv_ad, True)
+ return True
+
def teardown_test(self):
cleanup_scanners_and_advertisers(
self.scn_ad, self.active_adv_callback_list, self.adv_ad,
diff --git a/acts/tests/google/ble/scan/BleOpportunisticScanTest.py b/acts/tests/google/ble/scan/BleOpportunisticScanTest.py
index 514116a..e907cbb 100644
--- a/acts/tests/google/ble/scan/BleOpportunisticScanTest.py
+++ b/acts/tests/google/ble/scan/BleOpportunisticScanTest.py
@@ -23,6 +23,7 @@
from queue import Empty
+from acts import utils
from acts.test_decorators import test_tracker_info
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
@@ -37,7 +38,7 @@
class BleOpportunisticScanTest(BluetoothBaseTest):
default_timeout = 10
- max_scan_instances = 28
+ max_scan_instances = 27
report_delay = 2000
scan_callbacks = []
adv_callbacks = []
@@ -49,6 +50,12 @@
self.scn_ad = self.android_devices[0]
self.adv_ad = self.android_devices[1]
+ def setup_class(self):
+ super(BluetoothBaseTest, self).setup_class()
+ utils.set_location_service(self.scn_ad, True)
+ utils.set_location_service(self.adv_ad, True)
+ return True
+
def teardown_test(self):
cleanup_scanners_and_advertisers(
self.scn_ad, self.active_scan_callback_list, self.adv_ad,
diff --git a/acts/tests/google/ble/scan/BleScanScreenStateTest.py b/acts/tests/google/ble/scan/BleScanScreenStateTest.py
index b6c17f6..2593461 100644
--- a/acts/tests/google/ble/scan/BleScanScreenStateTest.py
+++ b/acts/tests/google/ble/scan/BleScanScreenStateTest.py
@@ -23,6 +23,7 @@
import time
from queue import Empty
+from acts import utils
from acts.test_decorators import test_tracker_info
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt.bt_constants import adv_succ
@@ -38,7 +39,7 @@
class BleScanScreenStateTest(BluetoothBaseTest):
advertise_callback = -1
- max_concurrent_scans = 28
+ max_concurrent_scans = 27
scan_callback = -1
shorter_scan_timeout = 2
@@ -47,6 +48,12 @@
self.scn_ad = self.android_devices[0]
self.adv_ad = self.android_devices[1]
+ def setup_class(self):
+ super(BluetoothBaseTest, self).setup_class()
+ utils.set_location_service(self.scn_ad, True)
+ utils.set_location_service(self.adv_ad, True)
+ return True
+
def _setup_generic_advertisement(self):
self.adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
ble_advertise_settings_modes['low_latency'])
diff --git a/acts/tests/google/ble/system_tests/BleStressTest.py b/acts/tests/google/ble/system_tests/BleStressTest.py
index a1387ab..5be082a 100644
--- a/acts/tests/google/ble/system_tests/BleStressTest.py
+++ b/acts/tests/google/ble/system_tests/BleStressTest.py
@@ -168,7 +168,8 @@
self.adv_ad.droid.bleStartBleAdvertising(
advertise_callback, advertise_data, advertise_settings)
expected_advertise_event_name = "".join(
- ["BleAdvertise", str(advertise_callback), "onSuccess"])
+ ["BleAdvertise",
+ str(advertise_callback), "onSuccess"])
worker = self.adv_ad.ed.handle_event(
self.bleadvertise_verify_onsuccess_handler,
expected_advertise_event_name, ([]), self.default_timeout)
@@ -176,7 +177,8 @@
self.log.debug(worker.result(self.default_timeout))
except Empty as error:
self.log.debug(" ".join(
- ["Test failed with Empty error:", str(error)]))
+ ["Test failed with Empty error:",
+ str(error)]))
test_result = False
except concurrent.futures._base.TimeoutError as error:
self.log.debug(" ".join([
@@ -219,7 +221,8 @@
self.adv_ad.droid.bleStartBleAdvertising(
advertise_callback, advertise_data, advertise_settings)
expected_advertise_event_name = "".join(
- ["BleAdvertise", str(advertise_callback), "onSuccess"])
+ ["BleAdvertise",
+ str(advertise_callback), "onSuccess"])
worker = self.adv_ad.ed.handle_event(
self.bleadvertise_verify_onsuccess_handler,
expected_advertise_event_name, ([]), self.default_timeout)
@@ -227,7 +230,8 @@
self.log.debug(worker.result(self.default_timeout))
except Empty as error:
self.log.debug(" ".join(
- ["Test failed with Empty error:", str(error)]))
+ ["Test failed with Empty error:",
+ str(error)]))
test_result = False
except concurrent.futures._base.TimeoutError as error:
self.log.debug(" ".join([
@@ -247,7 +251,8 @@
self.log.debug(worker.result(self.default_timeout))
except Empty as error:
self.log.debug(" ".join(
- ["Test failed with Empty error:", str(error)]))
+ ["Test failed with Empty error:",
+ str(error)]))
test_result = False
except concurrent.futures._base.TimeoutError as error:
self.log.debug(" ".join([
@@ -318,7 +323,7 @@
iterations = 100
for i in range(iterations):
try:
- target_address, adv_callback = get_mac_address_of_generic_advertisement(
+ target_address, adv_callback, scan_callback = get_mac_address_of_generic_advertisement(
self.scn_ad, self.adv_ad)
except BtTestUtilsError as err:
self.log.error(err)
@@ -339,6 +344,7 @@
self.log.error("Failed to unbond device from advertiser.")
return False
self.adv_ad.droid.bleStopBleAdvertising(adv_callback)
+ self.scn_ad.droid.bleStopBleScan(scan_callback)
# Magic sleep to let unbonding finish
time.sleep(2)
return True
diff --git a/acts/tests/google/bt/RfcommTest.py b/acts/tests/google/bt/RfcommTest.py
index 0a9ab5c..3160469 100644
--- a/acts/tests/google/bt/RfcommTest.py
+++ b/acts/tests/google/bt/RfcommTest.py
@@ -55,23 +55,11 @@
def setup_class(self):
return setup_multiple_devices_for_bt_test(self.android_devices)
- def setup_test(self):
- for a in self.android_devices:
- if not clear_bonded_devices(a):
- return False
- for a in self.android_devices:
- a.ed.clear_all_events()
- return True
-
def teardown_test(self):
self.client_ad.droid.bluetoothRfcommCloseClientSocket()
self.server_ad.droid.bluetoothRfcommCloseServerSocket()
return True
- def on_fail(self, test_name, begin_time):
- take_btsnoop_logs(self.android_devices, self, test_name)
- reset_bluetooth(self.android_devices)
-
def teardown_test(self):
if verify_server_and_client_connected(
self.client_ad, self.server_ad, log=False):
@@ -263,8 +251,8 @@
TAGS: Classic, RFCOMM
Priority: 3
"""
- return self._test_rfcomm_connection_with_uuid(bt_rfcomm_uuids[
- 'base_uuid'])
+ return self._test_rfcomm_connection_with_uuid(
+ bt_rfcomm_uuids['base_uuid'])
@BluetoothBaseTest.bt_test_wrap
@test_tracker_info(uuid='42c8d861-48b3-423b-ae8c-df140ebaad9d')
@@ -339,8 +327,8 @@
TAGS: Classic, RFCOMM
Priority: 3
"""
- return self._test_rfcomm_connection_with_uuid(bt_rfcomm_uuids[
- 'rfcomm'])
+ return self._test_rfcomm_connection_with_uuid(
+ bt_rfcomm_uuids['rfcomm'])
@BluetoothBaseTest.bt_test_wrap
@test_tracker_info(uuid='e3c05357-99ec-4819-86e4-1363e3359317')
@@ -390,8 +378,8 @@
TAGS: Classic, RFCOMM
Priority: 3
"""
- return self._test_rfcomm_connection_with_uuid(bt_rfcomm_uuids[
- 'tcs_bin'])
+ return self._test_rfcomm_connection_with_uuid(
+ bt_rfcomm_uuids['tcs_bin'])
@BluetoothBaseTest.bt_test_wrap
@test_tracker_info(uuid='ea1cfc32-d3f0-4420-a8e5-793c6ddf5820')
@@ -416,8 +404,8 @@
TAGS: Classic, RFCOMM
Priority: 3
"""
- return self._test_rfcomm_connection_with_uuid(bt_rfcomm_uuids[
- 'tcs_at'])
+ return self._test_rfcomm_connection_with_uuid(
+ bt_rfcomm_uuids['tcs_at'])
@BluetoothBaseTest.bt_test_wrap
@test_tracker_info(uuid='5b0d5608-38a5-48f7-b3e5-dc52a4a681dd')
@@ -667,8 +655,8 @@
TAGS: Classic, RFCOMM
Priority: 3
"""
- return self._test_rfcomm_connection_with_uuid(bt_rfcomm_uuids[
- 'hardcopy_control_channel'])
+ return self._test_rfcomm_connection_with_uuid(
+ bt_rfcomm_uuids['hardcopy_control_channel'])
@BluetoothBaseTest.bt_test_wrap
@test_tracker_info(uuid='1ae6ca34-87ab-48ad-8da8-98c997538af4')
@@ -693,8 +681,8 @@
TAGS: Classic, RFCOMM
Priority: 3
"""
- return self._test_rfcomm_connection_with_uuid(bt_rfcomm_uuids[
- 'hardcopy_data_channel'])
+ return self._test_rfcomm_connection_with_uuid(
+ bt_rfcomm_uuids['hardcopy_data_channel'])
@BluetoothBaseTest.bt_test_wrap
@test_tracker_info(uuid='d18ed311-a533-4306-944a-6f0f95eac141')
@@ -719,8 +707,8 @@
TAGS: Classic, RFCOMM
Priority: 3
"""
- return self._test_rfcomm_connection_with_uuid(bt_rfcomm_uuids[
- 'hardcopy_notification'])
+ return self._test_rfcomm_connection_with_uuid(
+ bt_rfcomm_uuids['hardcopy_notification'])
@BluetoothBaseTest.bt_test_wrap
@test_tracker_info(uuid='ab0af819-7d26-451d-8275-1119ee3c8df8')
@@ -820,8 +808,8 @@
TAGS: Classic, RFCOMM
Priority: 3
"""
- return self._test_rfcomm_connection_with_uuid(bt_rfcomm_uuids[
- 'mcap_control_channel'])
+ return self._test_rfcomm_connection_with_uuid(
+ bt_rfcomm_uuids['mcap_control_channel'])
@BluetoothBaseTest.bt_test_wrap
@test_tracker_info(uuid='ba3ab84c-bc61-442c-944c-af4fbca157f1')
diff --git a/acts/tests/google/bt/audio_lab/BtChameleonTest.py b/acts/tests/google/bt/audio_lab/BtChameleonTest.py
new file mode 100644
index 0000000..f600acd
--- /dev/null
+++ b/acts/tests/google/bt/audio_lab/BtChameleonTest.py
@@ -0,0 +1,223 @@
+# /usr/bin/env python3.4
+#
+# Copyright (C) 2017 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.
+"""
+Test script to automate the Bluetooth audio testing and analysis.
+
+Quick way to generate necessary audio files:
+sudo apt-get install sox
+sox -b 16 -r 48000 -c 2 -n audio_file_2k1k_10_sec.wav synth 10 sine 2000 sine 3000
+sox -b 16 -r 48000 -c 2 -n audio_file_2k1k_300_sec.wav synth 300 sine 2000 sine 3000
+
+"""
+import os
+import subprocess
+import time
+
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.audio_analysis_lib.check_quality import quality_analysis
+from acts.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest
+from acts.test_utils.bt.bt_constants import audio_bits_per_sample_32
+from acts.test_utils.bt.bt_constants import audio_channel_mode_8
+from acts.test_utils.bt.bt_constants import audio_sample_rate_48000
+from acts.test_utils.bt.bt_constants import delay_after_binding_seconds
+from acts.test_utils.bt.bt_constants import delay_before_record_seconds
+from acts.test_utils.bt.bt_constants import fpga_linein_bus_endpoint
+from acts.test_utils.bt.bt_constants import headphone_bus_endpoint
+from acts.test_utils.bt.bt_constants import silence_wait_seconds
+
+from acts import utils
+
+
+class BtChameleonTest(BtFunhausBaseTest):
+
+ audio_file_2k1k_10_sec = "audio_file_2k1k_10_sec.wav"
+ audio_file_2k1k_300_sec = "audio_file_2k1k_300_sec.wav"
+ android_sdcard_music_path = "/sdcard/Music"
+
+ def __init__(self, controllers):
+ BtFunhausBaseTest.__init__(self, controllers)
+ self.chameleon = self.chameleon_devices[0]
+ self.dut = self.android_devices[0]
+ self.raw_audio_dest = "{}/{}".format(self.android_devices[0].log_path,
+ "Chameleon_audio")
+ utils.create_dir(self.raw_audio_dest)
+ self.chameleon.audio_board_connect(1, headphone_bus_endpoint)
+ self.chameleon.audio_board_connect(1, fpga_linein_bus_endpoint)
+ time.sleep(delay_after_binding_seconds)
+
+ def _orchestrate_audio_quality_test(self, output_file_prefix_name,
+ bits_per_sample, rate, record_seconds,
+ channel, audio_to_play):
+ audio_analysis_filename = "{}_audio_analysis.txt".format(
+ output_file_prefix_name)
+ bluetooth_bind_time_seconds = 5
+ port_id = 6
+ has_file = True
+ # Additional sleep to allow full connection of Bluetooth device
+ # from test setup.
+ time.sleep(bluetooth_bind_time_seconds)
+ self.chameleon.start_capturing_audio(port_id, has_file)
+ time.sleep(delay_before_record_seconds)
+ self.dut.droid.mediaPlayOpen("file://{}".format(audio_to_play))
+ time.sleep(record_seconds + silence_wait_seconds)
+ raw_audio_info = self.chameleon_devices[0].stop_capturing_audio(
+ port_id)
+ self.ad.droid.mediaPlayStopAll()
+ raw_audio_path = raw_audio_info[0]
+ dest_file_path = "{}/{}_recording.raw".format(self.raw_audio_dest,
+ output_file_prefix_name)
+ self.chameleon.scp(raw_audio_path, dest_file_path)
+ self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
+ self.collect_bluetooth_manager_metrics_logs(self.android_devices)
+ analysis_path = "{}/{}".format(self.raw_audio_dest,
+ audio_analysis_filename)
+ try:
+ quality_analysis(
+ filename=dest_file_path,
+ output_file=analysis_path,
+ bit_width=bits_per_sample,
+ rate=rate,
+ channel=channel,
+ spectral_only=False)
+ except Exception as err:
+ self.log.exception("Failed to analyze raw audio: {}".format(err))
+ return False
+ # TODO: Log results to proto
+ return True
+
+ @test_tracker_info(uuid='b808fed6-5cb0-4e40-9522-c0f410cd77e8')
+ def test_run_bt_audio_quality_2k1k_10_sec_sine_wave(self):
+ """Measure audio quality over Bluetooth by playing a 1k2k sine wave.
+
+ Play a sine wave and measure the analysis of 1kHz and 2kHz on two
+ different channels for 10 seconds:
+ 1. Delays during playback.
+ 2. Noise before playback.
+ 3. Noise after playback.
+ 4. Bursts during playback.
+ 5. Volume changes.
+
+ Steps:
+ 1. Connect Chameleon headphone audio bus endpoint.
+ 2. Connect FPGA line-in bus endpoint.
+ 3. Clear audio routes on the Chameleon device.
+ 4. Start capturing audio on the Chameleon device.
+ 5. Start playing the sine wave on the Android device.
+ 6. Record for record_seconds + silence_wait_seconds.
+ 7. Stop recording audio on the Chameleon device.
+ 8. Stop playing audio on the Android Device.
+ 9. Pull raw recorded audio from the Chameleon device.
+ 10. Analyze raw audio and log results.
+
+
+ Expected Result:
+ Audio is recorded and processed successfully.
+
+ Returns:
+ True if Pass
+ False if Fail
+
+ TAGS: Classic, A2DP, Chameleon
+ Priority: 2
+ """
+ sox_call = "{}{}".format("sox -b 16 -r 48000 -c 2 -n {}".format(
+ self.audio_file_2k1k_10_sec), " synth 10 sine 2000 sine 3000")
+ subprocess.call(sox_call, shell=True)
+ sox_audio_path = "{}/{}".format(
+ os.path.dirname(os.path.realpath(self.audio_file_2k1k_10_sec)),
+ self.audio_file_2k1k_10_sec)
+ sox_audio_path = os.path.join(
+ os.path.dirname(os.path.realpath(self.audio_file_2k1k_10_sec)),
+ self.audio_file_2k1k_10_sec)
+ self.dut.adb.push("{} {}".format(sox_audio_path,
+ self.android_sdcard_music_path))
+ output_file_prefix_name = "{}_{}".format("test_2k1k_10_sec",
+ time.time())
+ bits_per_sample = audio_bits_per_sample_32
+ rate = audio_sample_rate_48000
+ record_seconds = 10 # The length in seconds for how long to record
+ channel = audio_channel_mode_8
+ audio_to_play = "{}/{}".format(self.android_sdcard_music_path,
+ self.audio_file_2k1k_10_sec)
+ audio_to_play = os.path.join(self.android_sdcard_music_path,
+ self.audio_file_2k1k_10_sec)
+ return self._orchestrate_audio_quality_test(
+ output_file_prefix_name=output_file_prefix_name,
+ bits_per_sample=bits_per_sample,
+ rate=rate,
+ record_seconds=record_seconds,
+ channel=channel,
+ audio_to_play=audio_to_play)
+
+ @test_tracker_info(uuid='7e971cef-6637-4198-929a-7ecc712121d7')
+ def test_run_bt_audio_quality_2k1k_300_sec_sine_wave(self):
+ """Measure audio quality over Bluetooth by playing a 1k2k sine wave.
+
+ Play a sine wave and measure the analysis of 1kHz and 2kHz on two
+ different channels for 300 seconds:
+ 1. Delays during playback.
+ 2. Noise before playback.
+ 3. Noise after playback.
+ 4. Bursts during playback.
+ 5. Volume changes.
+
+ Steps:
+ 1. Connect Chameleon headphone audio bus endpoint.
+ 2. Connect FPGA line-in bus endpoint.
+ 3. Clear audio routes on the Chameleon device.
+ 4. Start capturing audio on the Chameleon device.
+ 5. Start playing the sine wave on the Android device.
+ 6. Record for record_seconds + silence_wait_seconds.
+ 7. Stop recording audio on the Chameleon device.
+ 8. Stop playing audio on the Android Device.
+ 9. Pull raw recorded audio from the Chameleon device.
+ 10. Analyze raw audio and log results.
+
+
+ Expected Result:
+ Audio is recorded and processed successfully.
+
+ Returns:
+ True if Pass
+ False if Fail
+
+ TAGS: Classic, A2DP, Chameleon
+ Priority: 2
+ """
+ sox_call = "{}{}".format("sox -b 16 -r 48000 -c 2 -n {}".format(
+ self.audio_file_2k1k_300_sec), " synth 300 sine 2000 sine 3000")
+ subprocess.call(sox_call, shell=True)
+ sox_audio_path = os.path.join(
+ os.path.dirname(os.path.realpath(self.audio_file_2k1k_300_sec)),
+ self.audio_file_2k1k_300_sec)
+ self.dut.adb.push("{} {}".format(sox_audio_path,
+ self.android_sdcard_music_path))
+ output_file_prefix_name = "{}_{}".format("test_2k1k_300_sec.wav",
+ time.time())
+ bits_per_sample = audio_bits_per_sample_32
+ rate = audio_sample_rate_48000
+ record_seconds = 300 # The length in seconds for how long to record
+ channel = audio_channel_mode_8
+ audio_to_play = os.path.join(self.android_sdcard_music_path,
+ self.audio_file_2k1k_300_sec)
+
+ return self._orchestrate_audio_quality_test(
+ output_file_prefix_name=output_file_prefix_name,
+ bits_per_sample=bits_per_sample,
+ rate=rate,
+ record_seconds=record_seconds,
+ channel=channel,
+ audio_to_play=audio_to_play)
diff --git a/acts/tests/google/bt/audio_lab/BtFunhausTest.py b/acts/tests/google/bt/audio_lab/BtFunhausTest.py
index 2e697af..705900b 100644
--- a/acts/tests/google/bt/audio_lab/BtFunhausTest.py
+++ b/acts/tests/google/bt/audio_lab/BtFunhausTest.py
@@ -34,14 +34,14 @@
"""Test audio quality over 12 hours.
This test is designed to run Bluetooth audio for 12 hours
- and collect relavant logs. If all devices disconnect during
+ and collect relevant logs. If all devices disconnect during
the test or Bluetooth is off on all devices, then fail the
test.
Steps:
1. For each Android device connected run steps 2-5.
2. Open and play media file of music pushed to device
- 3. Set media to loop indefintely.
+ 3. Set media to loop indefinitely.
4. After 12 hours collect bluetooth_manager dumpsys information
5. Stop media player
diff --git a/acts/tests/google/bt/car_bt/BtCarHfpConferenceTest.py b/acts/tests/google/bt/car_bt/BtCarHfpConferenceTest.py
index 60784b8..52b660c 100644
--- a/acts/tests/google/bt/car_bt/BtCarHfpConferenceTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarHfpConferenceTest.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+# /usr/bin/env python3.4
#
# Copyright (C) 2016 The Android Open Source Project
#
@@ -130,7 +130,7 @@
# Give time for state to update due to carrier limitations
time.sleep(SHORT_TIMEOUT)
# Extract the call.
- #input("Continue?")
+ # input("Continue?")
call_2 = car_telecom_utils.get_calls_in_states(
self.log, self.hf, [tel_defines.CALL_STATE_RINGING])
if len(call_2) != 1:
@@ -139,8 +139,7 @@
# Accept the call on HF
if not car_telecom_utils.accept_call(self.log, self.hf, call_2[0]):
- self.hf.log.info("Accepting call failed {}".format(
- calls_in_ringing))
+ self.hf.log.info("Accepting call failed {}".format(self.hf.serial))
return False
# Merge the calls now.
diff --git a/acts/tests/google/bt/car_bt/BtCarHfpTest.py b/acts/tests/google/bt/car_bt/BtCarHfpTest.py
index 3bdfc56..b883753 100644
--- a/acts/tests/google/bt/car_bt/BtCarHfpTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarHfpTest.py
@@ -30,6 +30,8 @@
BLUETOOTH_PKG_NAME = "com.android.bluetooth"
CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING"
CALL_TYPE_INCOMING = "CALL_TYPE_INCOMING"
+AUDIO_STATE_DISCONNECTED = 0
+AUDIO_STATE_ROUTED = 2
SHORT_TIMEOUT = 5
@@ -212,6 +214,59 @@
"""
return self.dial_a_hangup_b(self.re, self.re, self.ag_phone_number)
+ def test_bluetooth_voice_recognition_assistant(self):
+ """
+ Tests if we can initate a remote Voice Recognition session.
+
+ Precondition:
+ 1. Devices are connected.
+
+ Steps:
+ 1. Verify that audio isn't routed between the HF and AG.
+ 2. From the HF send a BVRA command.
+ 3. Verify that audio is routed from the HF to AG.
+ 4. From the HF send a BVRA command to stop the session.
+ 5. Verify that audio is no longer routed from the HF to AG.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ Priority: 0
+ """
+ audio_state = self.hf.droid.bluetoothHfpClientGetAudioState(
+ self.ag.droid.bluetoothGetLocalAddress())
+ if (audio_state != AUDIO_STATE_DISCONNECTED):
+ self.log.info(
+ "Audio connected before test started, current state {}.".
+ format(str(audio_state)))
+ return False
+ bvra_started = self.hf.droid.bluetoothHfpClientStartVoiceRecognition(
+ self.ag.droid.bluetoothGetLocalAddress())
+ if (bvra_started != True):
+ self.log.info("BVRA Failed to start.")
+ return False
+ time.sleep(SHORT_TIMEOUT)
+ audio_state = self.hf.droid.bluetoothHfpClientGetAudioState(
+ self.ag.droid.bluetoothGetLocalAddress())
+ if (audio_state != AUDIO_STATE_ROUTED):
+ self.log.info("Audio didn't route, current state {}.".format(
+ str(audio_state)))
+ return False
+ bvra_stopped = self.hf.droid.bluetoothHfpClientStopVoiceRecognition(
+ self.ag.droid.bluetoothGetLocalAddress())
+ if (bvra_stopped != True):
+ self.log.info("BVRA Failed to stop.")
+ return False
+ time.sleep(SHORT_TIMEOUT)
+ audio_state = self.hf.droid.bluetoothHfpClientGetAudioState(
+ self.ag.droid.bluetoothGetLocalAddress())
+ if (audio_state != AUDIO_STATE_DISCONNECTED):
+ self.log.info("Audio didn't cleanup, current state {}.".format(
+ str(audio_state)))
+ return False
+ return True
+
def dial_a_hangup_b(self, caller, callee, ph=""):
"""
a, b and c can be either of AG, HF or Remote.
diff --git a/acts/tests/google/bt/car_bt/BtCarMapMceTest.py b/acts/tests/google/bt/car_bt/BtCarMapMceTest.py
index ba8760a..1a32b25 100644
--- a/acts/tests/google/bt/car_bt/BtCarMapMceTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarMapMceTest.py
@@ -54,6 +54,13 @@
time.sleep(4)
return True
+ def setup_test(self):
+ if not bt_test_utils.connect_pri_to_sec(
+ self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value])):
+ return False
+ # Grace time for connection to complete.
+ time.sleep(3)
+
def message_delivered(self, device):
try:
self.MCE.ed.pop_event(EventSmsDeliverSuccess, 15)
@@ -97,15 +104,11 @@
@test_tracker_info(uuid='0858347a-e649-4f18-85b6-6990cc311dee')
@BluetoothBaseTest.bt_test_wrap
def test_send_message(self):
- bt_test_utils.connect_pri_to_sec(
- self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
return self.send_message([self.REMOTE])
@test_tracker_info(uuid='b25caa53-3c7f-4cfa-a0ec-df9a8f925fe5')
@BluetoothBaseTest.bt_test_wrap
def test_receive_message(self):
- bt_test_utils.connect_pri_to_sec(
- self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
self.MSE.log.info("Start Tracking SMS.")
self.MSE.droid.smsStartTrackingIncomingSmsMessage()
self.REMOTE.log.info("Ready to send")
@@ -130,6 +133,9 @@
@test_tracker_info(uuid='19444142-1d07-47dc-860b-f435cba46fca')
@BluetoothBaseTest.bt_test_wrap
def test_send_message_failure_no_map_connection(self):
+ if not bt_test_utils.disconnect_pri_from_sec(
+ self.MCE, self.MSE, [BtEnum.BluetoothProfile.MAP_MCE.value]):
+ return False
return not self.send_message([self.REMOTE])
@test_tracker_info(uuid='c7e569c0-9f6c-49a4-8132-14bc544ccb53')
@@ -148,8 +154,6 @@
@test_tracker_info(uuid='8cdb4a54-3f18-482f-be3d-acda9c4cbeed')
@BluetoothBaseTest.bt_test_wrap
def test_disconnect_failure_send_message(self):
- connected = bt_test_utils.connect_pri_to_sec(
- self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
addr = self.MSE.droid.bluetoothGetLocalAddress()
if bt_test_utils.is_map_mce_device_connected(self.MCE, addr):
connected = True
@@ -167,8 +171,6 @@
@test_tracker_info(uuid='2d79a896-b1c1-4fb7-9924-db8b5c698be5')
@BluetoothBaseTest.bt_test_wrap
def manual_test_send_message_to_contact(self):
- bt_test_utils.connect_pri_to_sec(
- self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
contacts = self.MCE.droid.contactsGetContactIds()
self.log.info(contacts)
selected_contact = self.MCE.droid.contactsDisplayContactPickList()
@@ -181,6 +183,4 @@
@test_tracker_info(uuid='8ce9a7dd-3b5e-4aee-a897-30740e2439c3')
@BluetoothBaseTest.bt_test_wrap
def test_send_message_to_multiple_phones(self):
- bt_test_utils.connect_pri_to_sec(
- self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
return self.send_message([self.REMOTE, self.REMOTE])
diff --git a/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py b/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
index 7865bc1..4611c0d 100644
--- a/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+# /usr/bin/env python3.4
#
# Copyright (C) 2016 The Android Open Source Project
#
@@ -23,8 +23,8 @@
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt import bt_test_utils
from acts.test_utils.car import car_bt_utils
-from acts.test_utils.car import car_media_utils
from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import is_a2dp_connected
class BtCarMediaConnectionTest(BluetoothBaseTest):
@@ -58,26 +58,6 @@
self.SNK, self.SRC, [BtEnum.BluetoothProfile.A2DP_SINK],
BtEnum.BluetoothPriorityLevel.PRIORITY_ON)
- def is_a2dp_connected(self, device1, device2):
- """
- Convenience Function to see if the 2 devices are connected on
- A2dp.
- ToDo: Move to bt_test_utils if used in more places.
- Args:
- device1: Device 1
- device2: Device 2
- Returns:
- True if Connected
- False if Not connected
- """
- devices = device1.droid.bluetoothA2dpSinkGetConnectedDevices()
- for device in devices:
- self.device1.log.info("A2dp Connected device {}".format(device[
- "name"]))
- if (device["address"] == device2.droid.bluetoothGetLocalAddress()):
- return True
- return False
-
@test_tracker_info(uuid='1934c0d5-3fa3-43e5-a91f-2c8a4424f5cd')
@BluetoothBaseTest.bt_test_wrap
def test_a2dp_connect_disconnect_from_src(self):
@@ -99,18 +79,19 @@
Priority: 0
"""
- if (car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC)):
+ if (is_a2dp_connected(self.SNK, self.SRC)):
self.log.info("Already Connected")
else:
if (not bt_test_utils.connect_pri_to_sec(
self.SRC, self.SNK,
set([BtEnum.BluetoothProfile.A2DP.value]))):
return False
-
+ # Delay to establish A2DP connection before disconnecting
+ time.sleep(5)
result = bt_test_utils.disconnect_pri_from_sec(
self.SRC, self.SNK, [BtEnum.BluetoothProfile.A2DP.value])
# Grace timeout to allow a2dp time to disconnect
- time.sleep(3)
+ time.sleep(2)
if not result:
# Additional profile connection check for b/
if bt_test_utils.is_a2dp_src_device_connected(
@@ -119,7 +100,7 @@
return False
# Logging if we connected right back, since that happens sometimes
# Not failing the test if it did though
- if (car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC)):
+ if (is_a2dp_connected(self.SNK, self.SRC)):
self.SNK.log.error("Still connected after a disconnect")
return True
@@ -146,18 +127,20 @@
Priority: 0
"""
# Connect
- if car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC):
+ if is_a2dp_connected(self.SNK, self.SRC):
self.log.info("Already Connected")
else:
if (not bt_test_utils.connect_pri_to_sec(
self.SNK, self.SRC,
set([BtEnum.BluetoothProfile.A2DP_SINK.value]))):
return False
+ # Delay to establish A2DP connection before disconnecting
+ time.sleep(5)
# Disconnect
result = bt_test_utils.disconnect_pri_from_sec(
self.SNK, self.SRC, [BtEnum.BluetoothProfile.A2DP_SINK.value])
# Grace timeout to allow a2dp time to disconnect
- time.sleep(3)
+ time.sleep(2)
if not result:
# Additional profile connection check for b/
if bt_test_utils.is_a2dp_snk_device_connected(
@@ -166,6 +149,6 @@
return False
# Logging if we connected right back, since that happens sometimes
# Not failing the test if it did though
- if car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC):
+ if is_a2dp_connected(self.SNK, self.SRC):
self.SNK.log.error("Still connected after a disconnect")
return True
diff --git a/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py b/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
index 05d1737..3e5b28f 100644
--- a/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
@@ -25,8 +25,9 @@
from acts.test_utils.bt import bt_test_utils
from acts.test_utils.bt import BtEnum
from acts.test_utils.car import car_media_utils
-from acts.utils import exe_cmd
-from acts.controllers import adb
+from acts.test_utils.bt.bt_test_utils import is_a2dp_connected
+from acts.keys import Config
+
DEFAULT_WAIT_TIME = 1.0
DEFAULT_EVENT_TIMEOUT = 1.0
@@ -126,8 +127,7 @@
if not super(BtCarMediaPassthroughTest, self).teardown_test():
return False
# If A2dp connection was disconnected as part of the test, connect it back
- if not (car_media_utils.is_a2dp_connected(self.log, self.SNK,
- self.SRC)):
+ if not (is_a2dp_connected(self.SNK,self.SRC)):
result = bt_test_utils.connect_pri_to_sec(
self.SRC, self.SNK, set([BtEnum.BluetoothProfile.A2DP.value]))
if not result:
@@ -235,8 +235,7 @@
Priority: 0
"""
- if not (car_media_utils.is_a2dp_connected(self.log, self.SNK,
- self.SRC)):
+ if not (is_a2dp_connected(self.SNK,self.SRC)):
self.SNK.log.error('No A2dp Connection')
return False
diff --git a/acts/tests/google/bt/car_bt/BtCarPairingTest.py b/acts/tests/google/bt/car_bt/BtCarPairingTest.py
index 09810d1..f28ca83 100644
--- a/acts/tests/google/bt/car_bt/BtCarPairingTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarPairingTest.py
@@ -38,6 +38,12 @@
self.car = self.android_devices[0]
self.ph = self.android_devices[1]
+ def teardown_test(self):
+ for ad in self.android_devices:
+ bt_test_utils.clear_bonded_devices(ad)
+ # Give the stack time to unbond.
+ time.sleep(UNBOND_TIMEOUT)
+
@test_tracker_info(uuid='f56e915-eef7-45cd-b5a6-771f6ef72602')
@BluetoothBaseTest.bt_test_wrap
def test_simple_pairing(self):
diff --git a/acts/tests/google/bt/car_bt/BtCarPbapTest.py b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
index bc1b930..9d195bc 100644
--- a/acts/tests/google/bt/car_bt/BtCarPbapTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
@@ -55,12 +55,10 @@
"android.permission.WRITE_CONTACTS",
"android.permission.READ_EXTERNAL_STORAGE"
]
- for permission in permissions_list:
- self.pse.adb.shell(
- "pm grant com.google.android.contacts {}".format(permission))
- for permission in permissions_list:
- self.pce.adb.shell("pm grant com.android.contacts {}".format(
- permission))
+ for device in self.android_devices:
+ for permission in permissions_list:
+ device.adb.shell(
+ "pm grant com.google.android.contacts {}".format(permission))
# Pair the devices.
# This call may block until some specified timeout in bt_test_utils.py.
@@ -105,20 +103,24 @@
if not super(BtCarPbapTest, self).setup_test():
return False
self.pse.droid.callLogsEraseAll()
- if not (bt_contacts_utils.erase_contacts(self.pse) and
- bt_contacts_utils.erase_contacts(self.pce)):
- return False
- # Allow all content providers to synchronize.
- time.sleep(1)
- return True
+ return self.erase_all_contacts()
def teardown_test(self):
if not super(BtCarPbapTest, self).teardown_test():
return False
+ for device in self.android_devices:
+ bt_contacts_utils.delete_vcf_files(device)
+
self.pce.droid.bluetoothPbapClientDisconnect(
self.pse.droid.bluetoothGetLocalAddress())
- bt_contacts_utils.erase_contacts(self.pse)
- return True
+ return self.erase_all_contacts()
+
+ def erase_all_contacts(self):
+ try:
+ return all(bt_contacts_utils.erase_contacts(device) for device in self.android_devices)
+ finally:
+ # Allow all content providers to synchronize.
+ time.sleep(1)
def verify_contacts_match(self):
bt_contacts_utils.export_device_contacts_to_vcf(
@@ -330,7 +332,6 @@
phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE)
-
return self.connect_and_verify(phone_numbers_added)
@test_tracker_info(uuid='2aa2bd00-86cc-4f39-a06a-90b17ea5b320')
@@ -352,7 +353,6 @@
Pass if True
Fail if False
"""
-
bt_contacts_utils.add_call_log(
self.pse, bt_contacts_utils.INCOMMING_CALL_TYPE,
bt_contacts_utils.generate_random_phone_number().phone_number,
@@ -426,18 +426,19 @@
Pass if True
Fail if False
"""
-
PSE1_CONTACTS_FILE = "{}{}".format(PSE_CONTACTS_FILE, "1")
PSE2_CONTACTS_FILE = "{}{}".format(PSE_CONTACTS_FILE, "2")
bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
PSE1_CONTACTS_FILE, 100)
- phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
- self.pse, self.contacts_destination_path, PSE1_CONTACTS_FILE)
+ bt_contacts_utils.import_device_contacts_from_vcf(self.pse,
+ self.contacts_destination_path,
+ PSE1_CONTACTS_FILE)
bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
PSE2_CONTACTS_FILE, 100)
- phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
- self.pse2, self.contacts_destination_path, PSE2_CONTACTS_FILE)
+ bt_contacts_utils.import_device_contacts_from_vcf(self.pse2,
+ self.contacts_destination_path,
+ PSE2_CONTACTS_FILE)
self.pce.droid.bluetoothPbapClientDisconnect(
self.pse.droid.bluetoothGetLocalAddress())
@@ -457,9 +458,7 @@
bt_test_utils.connect_pri_to_sec(
self.pce, self.pse2,
set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
-
bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 200)
-
bt_contacts_utils.export_device_contacts_to_vcf(
self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE)
@@ -487,4 +486,4 @@
bt_contacts_utils.erase_contacts(self.pse)
bt_contacts_utils.erase_contacts(self.pse2)
- return pse1_matches and pse2_matches and pse1andpse2_matches
+ return pse1_matches and pse2_matches and pse1andpse2_matches
\ No newline at end of file
diff --git a/acts/tests/google/bt/hid/HidDeviceTest.py b/acts/tests/google/bt/hid/HidDeviceTest.py
new file mode 100644
index 0000000..cdd1094
--- /dev/null
+++ b/acts/tests/google/bt/hid/HidDeviceTest.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python3.4
+#
+# Copyright (C) 2017 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 HID Device Test.
+"""
+
+from acts.base_test import BaseTestClass
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts.test_utils.bt.bt_test_utils import hid_keyboard_report
+from acts.test_utils.bt.bt_test_utils import hid_device_send_key_data_report
+from acts.test_utils.bt.bt_constants import hid_connection_timeout
+from acts.test_utils.bt import bt_constants
+import time
+
+
+class HidDeviceTest(BluetoothBaseTest):
+ tests = None
+ default_timeout = 10
+
+ def __init__(self, controllers):
+ BaseTestClass.__init__(self, controllers)
+ self.host_ad = self.android_devices[0]
+ self.device_ad = self.android_devices[1]
+
+ def setup_test(self):
+ for a in self.android_devices:
+ if not clear_bonded_devices(a):
+ return False
+ for a in self.android_devices:
+ a.ed.clear_all_events()
+
+ i = 0
+ while not self.device_ad.droid.bluetoothHidDeviceIsReady():
+ time.sleep(1)
+ i += 1
+ self.log.info("BluetoothHidDevice NOT Ready")
+ if i == 10:
+ return False
+
+ if not self.device_ad.droid.bluetoothHidDeviceRegisterApp():
+ self.log.error("Device: registration failed")
+ return False
+
+ self.log.info("Device: registration done")
+ return True
+
+ def teardown_test(self):
+ self.log.info("Device: unregister")
+ self.device_ad.droid.bluetoothHidDeviceUnregisterApp()
+ time.sleep(2)
+ return True
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='047afb31-96c5-4a56-acb5-2b216037f35d')
+ def test_hid(self):
+ """Test HID Host and Device basic functionality
+
+ Test the HID Device framework app registration; test HID Host sending
+ report through HID control channel and interrupt channel.
+
+ Steps:
+ 1. Bluetooth HID device registers the Bluetooth input device service.
+ 2. Get the MAC address of the HID host and HID device.
+ 3. Establish HID profile connection from the HID host to the HID device.
+ 4. HID host sends set_report, get_report, set_protocol, send_data to
+ the HID device, and check if the HID device receives them.
+ 5. HID device sends data report, report_error, reply_report commands to
+ the HID host, and check if the HID host receives them.
+
+ Expected Result:
+ HID profile connection is successfully established; all commands and
+ data reports are correctly handled.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: Classic, HID
+ Priority: 1
+ """
+
+ test_result = True
+
+ pair_pri_to_sec(self.host_ad, self.device_ad, attempts=3)
+
+ self.log.info("Device bonded: {}".format(
+ self.device_ad.droid.bluetoothGetBondedDevices()))
+ self.log.info("Host bonded: {}".format(
+ self.host_ad.droid.bluetoothGetBondedDevices()))
+
+ host_id = self.host_ad.droid.bluetoothGetLocalAddress()
+ device_id = self.device_ad.droid.bluetoothGetLocalAddress()
+
+ self.host_ad.droid.bluetoothConnectBonded(device_id)
+
+ time.sleep(hid_connection_timeout)
+ self.log.info("Device: connected: {}".format(
+ self.device_ad.droid.bluetoothHidDeviceGetConnectedDevices()))
+
+ self.log.info("Host: set report")
+ self.host_ad.droid.bluetoothHidSetReport(
+ device_id, 1, bt_constants.hid_default_set_report_payload)
+
+ try:
+ hid_device_callback = self.device_ad.ed.pop_event(
+ bt_constants.hid_on_set_report_event,
+ bt_constants.hid_default_event_timeout)
+ except Empty as err:
+ self.log.error("Callback not received: {}".format(err))
+ test_result = False
+
+ self.log.info("Host: get report")
+ self.host_ad.droid.bluetoothHidGetReport(device_id, 1, 1, 1024)
+
+ try:
+ hid_device_callback = self.device_ad.ed.pop_event(
+ bt_constants.hid_on_get_report_event,
+ bt_constants.hid_default_event_timeout)
+ except Empty as err:
+ self.log.error("Callback not received: {}".format(err))
+ test_result = False
+
+ self.log.info("Host: set_protocol")
+ self.host_ad.droid.bluetoothHidSetProtocolMode(device_id, 1)
+
+ try:
+ hid_device_callback = self.device_ad.ed.pop_event(
+ bt_constants.hid_on_set_protocol_event,
+ bt_constants.hid_default_event_timeout)
+ except Empty as err:
+ self.log.error("Callback not received: {}".format(err))
+ test_result = False
+
+ self.log.info("Host: send data")
+ self.host_ad.droid.bluetoothHidSendData(device_id, "It's a report")
+
+ try:
+ hid_device_callback = self.device_ad.ed.pop_event(
+ bt_constants.hid_on_intr_data_event,
+ bt_constants.hid_default_event_timeout)
+ except Empty as err:
+ self.log.error("Callback not received: {}".format(err))
+ test_result = False
+
+ self.log.info("Device: send data report through interrupt channel")
+ hid_device_send_key_data_report(host_id, self.device_ad, "04")
+ hid_device_send_key_data_report(host_id, self.device_ad, "05")
+
+ self.log.info("Device: report error")
+ self.device_ad.droid.bluetoothHidDeviceReportError(host_id, 1)
+
+ self.log.info("Device: reply report")
+ self.device_ad.droid.bluetoothHidDeviceReplyReport(
+ host_id, 1, 1, hid_keyboard_report("04"))
+
+ self.log.info("Device bonded: {}".format(
+ self.device_ad.droid.bluetoothGetBondedDevices()))
+ self.log.info("Host bonded: {}".format(
+ self.host_ad.droid.bluetoothGetBondedDevices()))
+
+ return test_result
+
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='5ddc3eb1-2b8d-43b5-bdc4-ba577d90481d')
+ def test_hid_host_unplug(self):
+ """Test HID Host Virtual_cable_unplug
+
+ Test the HID host and HID device handle Virtual_cable_unplug correctly
+
+ Steps:
+ 1. Bluetooth HID device registers the Bluetooth input device service.
+ 2. Get the MAC address of the HID host and HID device.
+ 3. Establish HID profile connection from the HID host to the HID device.
+ 4. HID host sends virtual_cable_unplug command to the HID device.
+
+ Expected Result:
+ HID profile connection is successfully established; After the HID host
+ sends virtual_cable_unplug command to the HID device, both disconnect
+ each other, but not unpair.
+
+ Returns:
+ Pass if True
+ Fail if False
+
+ TAGS: Classic, HID
+ Priority: 2
+ """
+
+ test_result = True
+ pair_pri_to_sec(self.host_ad, self.device_ad, attempts=3)
+
+ self.log.info("Device bonded: {}".format(
+ self.device_ad.droid.bluetoothGetBondedDevices()))
+ self.log.info("Host bonded: {}".format(
+ self.host_ad.droid.bluetoothGetBondedDevices()))
+
+ host_id = self.host_ad.droid.bluetoothGetLocalAddress()
+ device_id = self.device_ad.droid.bluetoothGetLocalAddress()
+
+ self.host_ad.droid.bluetoothConnectBonded(device_id)
+
+ time.sleep(hid_connection_timeout)
+ self.log.info("Device connected: {}".format(
+ self.device_ad.droid.bluetoothHidDeviceGetConnectedDevices()))
+
+ self.log.info("Device: send data report through interrupt channel")
+ hid_device_send_key_data_report(host_id, self.device_ad, "04")
+ hid_device_send_key_data_report(host_id, self.device_ad, "05")
+
+ self.log.info("Host: virtual unplug")
+ self.host_ad.droid.bluetoothHidVirtualUnplug(device_id)
+
+ try:
+ hid_device_callback = self.device_ad.ed.pop_event(
+ bt_constants.hid_on_virtual_cable_unplug_event,
+ bt_constants.hid_default_event_timeout)
+ except Empty as err:
+ self.log.error("Callback not received: {}".format(err))
+ test_result = False
+
+ self.log.info("Device bonded: {}".format(
+ self.device_ad.droid.bluetoothGetBondedDevices()))
+ self.log.info("Host bonded: {}".format(
+ self.host_ad.droid.bluetoothGetBondedDevices()))
+
+ if self.device_ad.droid.bluetoothGetBondedDevices():
+ self.log.error("HID device didn't unbond on virtual_cable_unplug")
+ test_result = False
+
+ if self.host_ad.droid.bluetoothGetBondedDevices():
+ self.log.error("HID host didn't unbond on virtual_cable_unplug")
+ test_result = False
+
+ return test_result
+
diff --git a/acts/tests/google/bt/pan/BtPanTest.py b/acts/tests/google/bt/pan/BtPanTest.py
index e018019..f7ce968 100644
--- a/acts/tests/google/bt/pan/BtPanTest.py
+++ b/acts/tests/google/bt/pan/BtPanTest.py
@@ -98,7 +98,8 @@
self.log.error("Could not establish a PAN connection.")
return False
self.pan_dut.droid.bluetoothPanSetBluetoothTethering(False)
- if verify_http_connection(self.log, self.panu_dut):
+ if not verify_http_connection(self.log, self.panu_dut,
+ expected_state=False):
self.log.error("PANU device still has internet access.")
return False
self.log.info("PANU device has no internet access as expected.")
diff --git a/acts/tests/google/bt/power/SetupBTPairingTest.py b/acts/tests/google/bt/power/SetupBTPairingTest.py
index 4478fd4..896d009 100644
--- a/acts/tests/google/bt/power/SetupBTPairingTest.py
+++ b/acts/tests/google/bt/power/SetupBTPairingTest.py
@@ -33,8 +33,14 @@
def __init__(self, controllers):
base_test.BaseTestClass.__init__(self, controllers)
+ def select_device_by_mac_address(self, mac_address):
+ for device in self.relay_devices:
+ if device.mac_address == mac_address:
+ return device
+ return self.relay_devices[0]
+
def setup_test(self):
- self.bt_device = self.relay_devices[0]
+ self.bt_device = self.select_device_by_mac_address(self.user_params["mac_address"])
def wait_for_test_completion(self):
port = int(self.user_params["socket_port"])
@@ -67,7 +73,7 @@
self.bt_device.setup()
self.bt_device.power_on()
# Wait for a moment between pushing buttons
- time.sleep(0.25)
+ time.sleep(2)
self.bt_device.enter_pairing_mode()
def test_bt_pairing(self):
diff --git a/acts/tests/google/bt/pts/BtCmdLineTest.py b/acts/tests/google/bt/pts/BtCmdLineTest.py
index 5091db3..b276274 100644
--- a/acts/tests/google/bt/pts/BtCmdLineTest.py
+++ b/acts/tests/google/bt/pts/BtCmdLineTest.py
@@ -37,10 +37,11 @@
def __init__(self, controllers):
BluetoothBaseTest.__init__(self, controllers)
if not "target_mac_address" in self.user_params.keys():
- self.log.error(
- "Missing mandatory user config \"target_mac_address\"!")
- return False
- self.target_mac_address = self.user_params["target_mac_address"].upper()
+ self.log.warning("Missing user config \"target_mac_address\"!")
+ self.target_mac_address = ""
+ else:
+ self.target_mac_address = self.user_params[
+ "target_mac_address"].upper()
self.android_devices[0].droid.bluetoothSetLocalName("CMD LINE Test")
if len(self.android_devices) > 1:
@@ -77,8 +78,8 @@
for filename in filenames:
file = os.path.join(dirname, filename)
#TODO: Handle file paths with spaces
- self.android_devices[0].adb.push(
- "{} {}".format(file, android_music_path))
+ self.android_devices[0].adb.push("{} {}".format(
+ file, android_music_path))
def setup_class(self):
return True
diff --git a/acts/tests/google/bt/pts/cmd_input.py b/acts/tests/google/bt/pts/cmd_input.py
index e97b1a4..7cf7f20 100644
--- a/acts/tests/google/bt/pts/cmd_input.py
+++ b/acts/tests/google/bt/pts/cmd_input.py
@@ -16,26 +16,22 @@
"""
Python script for wrappers to various libraries.
"""
-from acts.test_utils.bt.BtEnum import BluetoothScanModeType
-from acts.test_utils.bt.GattEnum import GattServerResponses
-from ble_lib import BleLib
-from bta_lib import BtaLib
-from config_lib import ConfigLib
-from gattc_lib import GattClientLib
-from gatts_lib import GattServerLib
-from rfcomm_lib import RfcommLib
+from acts.test_utils.bt.bt_constants import bt_scan_mode_types
+from acts.test_utils.bt.bt_constants import gatt_server_responses
+import acts.test_utils.bt.gatt_test_database as gatt_test_database
+from acts.test_utils.bt.bt_carkit_lib import E2eBtCarkitLib
+import threading
import time
import cmd
-import gatt_test_database
"""Various Global Strings"""
CMD_LOG = "CMD {} result: {}"
FAILURE = "CMD {} threw exception: {}"
class CmdInput(cmd.Cmd):
+ android_devices = []
"""Simple command processor for Bluetooth PTS Testing"""
- gattc_lib = None
def connect_hsp_helper(self, ad):
"""A helper function for making HSP connections"""
@@ -65,17 +61,16 @@
self.pri_dut = android_devices[0]
if len(android_devices) > 1:
self.sec_dut = android_devices[1]
+ if len(android_devices) > 2:
self.ter_dut = android_devices[2]
+ if len(android_devices) > 3:
+ self.qua_dut = android_devices[3]
self.mac_addr = mac_addr
self.log = log
-
- # Initialize libraries
- self.config_lib = ConfigLib(log, self.pri_dut)
- self.bta_lib = BtaLib(log, mac_addr, self.pri_dut)
- self.ble_lib = BleLib(log, mac_addr, self.pri_dut)
- self.gattc_lib = GattClientLib(log, mac_addr, self.pri_dut)
- self.gatts_lib = GattServerLib(log, mac_addr, self.pri_dut)
- self.rfcomm_lib = RfcommLib(log, mac_addr, self.pri_dut)
+ self.pri_dut.bta.set_target_mac_addr(mac_addr)
+ self.pri_dut.gattc.set_target_mac_addr(mac_addr)
+ self.pri_dut.rfcomm.set_target_mac_addr(mac_addr)
+ self.bt_carkit_lib = E2eBtCarkitLib(self.log, mac_addr)
def emptyline(self):
pass
@@ -84,8 +79,38 @@
"End Script"
return True
+ def do_reset_mac_address(self, line):
+ """Reset target mac address for libraries based on the current connected device"""
+ try:
+ device = self.pri_dut.droid.bluetoothGetConnectedDevices()[0]
+ #self.setup_vars(self.android_devices, device['address'], self.log)
+ self.log.info("New device is {}".format(device))
+ except Exception as err:
+ self.log.info("Failed to setup new vars with {}".format(err))
+
"""Begin GATT Client wrappers"""
+ def do_gattc_socket_conn_begin_connect_thread_psm(self, line):
+ cmd = ""
+ try:
+ self.pri_dut.gattc.socket_conn_begin_connect_thread_psm(line)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_gattc_socket_conn_begin_accept_thread_psm(self, line):
+ cmd = ""
+ try:
+ self.pri_dut.gattc.socket_conn_begin_accept_thread_psm(line)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_gattc_request_le_connection_parameters(self, line):
+ cmd = ""
+ try:
+ self.pri_dut.gattc.request_le_connection_parameters()
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
def do_gattc_connect_over_le(self, line):
"""Perform GATT connection over LE"""
cmd = "Gatt connect over LE"
@@ -93,7 +118,7 @@
autoconnect = False
if line:
autoconnect = bool(line)
- self.gattc_lib.connect_over_le(autoconnect)
+ self.pri_dut.gattc.connect_over_le(autoconnect)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -101,7 +126,7 @@
"""Perform GATT connection over BREDR"""
cmd = "Gatt connect over BR/EDR"
try:
- self.gattc_lib.connect_over_bredr()
+ self.pri_dut.gattc.connect_over_bredr()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -109,7 +134,7 @@
"""Perform GATT disconnect"""
cmd = "Gatt Disconnect"
try:
- self.gattc_lib.disconnect()
+ self.pri_dut.gattc.disconnect()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -117,7 +142,7 @@
"""GATT client read Characteristic by UUID."""
cmd = "GATT client read Characteristic by UUID."
try:
- self.gattc_lib.read_char_by_uuid(line)
+ self.pri_dut.gattc.read_char_by_uuid(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -125,7 +150,7 @@
"""Request MTU Change of input value"""
cmd = "Request MTU Value"
try:
- self.gattc_lib.request_mtu(line)
+ self.pri_dut.gattc.request_mtu(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -135,7 +160,7 @@
"""
cmd = "Discovery Services and list all UUIDS"
try:
- self.gattc_lib.list_all_uuids()
+ self.pri_dut.gattc.list_all_uuids()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -143,7 +168,7 @@
"""GATT Client discover services of GATT Server"""
cmd = "Discovery Services of GATT Server"
try:
- self.gattc_lib.discover_services()
+ self.pri_dut.gattc.discover_services()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -151,7 +176,7 @@
"""Perform Gatt Client Refresh"""
cmd = "GATT Client Refresh"
try:
- self.gattc_lib.refresh()
+ self.pri_dut.gattc.refresh()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -161,7 +186,7 @@
"""
cmd = "GATT Client Read By Instance ID"
try:
- self.gattc_lib.read_char_by_instance_id(line)
+ self.pri_dut.gattc.read_char_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -169,7 +194,15 @@
"""GATT Client Write to Characteristic by instance ID"""
cmd = "GATT Client write to Characteristic by instance ID"
try:
- self.gattc_lib.write_char_by_instance_id(line)
+ self.pri_dut.gattc.write_char_by_instance_id(line)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_gattc_write_char_by_instance_id_value(self, line):
+ """GATT Client Write to Characteristic by instance ID"""
+ cmd = "GATT Client write to Characteristic by instance ID"
+ try:
+ self.pri_dut.gattc.write_char_by_instance_id_value(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -177,14 +210,14 @@
"""GATT Client Write to Char that doesn't have write permission"""
cmd = "GATT Client Write to Char that doesn't have write permission"
try:
- self.gattc_lib.mod_write_char_by_instance_id(line)
+ self.pri_dut.gattc.mod_write_char_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_gattc_write_invalid_char_by_instance_id(self, line):
"""GATT Client Write to Char that doesn't exists"""
try:
- self.gattc_lib.write_invalid_char_by_instance_id(line)
+ self.pri_dut.gattc.write_invalid_char_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -192,7 +225,7 @@
"""GATT Client Read Char that doesn't have write permission"""
cmd = "GATT Client Read Char that doesn't have write permission"
try:
- self.gattc_lib.mod_read_char_by_instance_id(line)
+ self.pri_dut.gattc.mod_read_char_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -200,7 +233,7 @@
"""GATT Client Read Char that doesn't exists"""
cmd = "GATT Client Read Char that doesn't exists"
try:
- self.gattc_lib.read_invalid_char_by_instance_id(line)
+ self.pri_dut.gattc.read_invalid_char_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -208,7 +241,7 @@
"""GATT Client Write to Desc that doesn't have write permission"""
cmd = "GATT Client Write to Desc that doesn't have write permission"
try:
- self.gattc_lib.mod_write_desc_by_instance_id(line)
+ self.pri_dut.gattc.mod_write_desc_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -216,7 +249,7 @@
"""GATT Client Write to Desc that doesn't exists"""
cmd = "GATT Client Write to Desc that doesn't exists"
try:
- self.gattc_lib.write_invalid_desc_by_instance_id(line)
+ self.pri_dut.gattc.write_invalid_desc_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -224,7 +257,7 @@
"""GATT Client Read Desc that doesn't have write permission"""
cmd = "GATT Client Read Desc that doesn't have write permission"
try:
- self.gattc_lib.mod_read_desc_by_instance_id(line)
+ self.pri_dut.gattc.mod_read_desc_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -232,7 +265,7 @@
"""GATT Client Read Desc that doesn't exists"""
cmd = "GATT Client Read Desc that doesn't exists"
try:
- self.gattc_lib.read_invalid_desc_by_instance_id(line)
+ self.pri_dut.gattc.read_invalid_desc_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -240,7 +273,7 @@
"""GATT Client Read Char that doesn't have write permission"""
cmd = "GATT Client Read Char that doesn't have write permission"
try:
- self.gattc_lib.mod_read_char_by_uuid_and_instance_id(line)
+ self.pri_dut.gattc.mod_read_char_by_uuid_and_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -248,7 +281,7 @@
"""GATT Client Read Char that doesn't exist"""
cmd = "GATT Client Read Char that doesn't exist"
try:
- self.gattc_lib.read_invalid_char_by_uuid(line)
+ self.pri_dut.gattc.read_invalid_char_by_uuid(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -256,7 +289,7 @@
"""GATT Client Write to Descriptor by instance ID"""
cmd = "GATT Client Write to Descriptor by instance ID"
try:
- self.gattc_lib.write_desc_by_instance_id(line)
+ self.pri_dut.gattc.write_desc_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -264,7 +297,15 @@
"""GATT Client Enable Notification on Descriptor by instance ID"""
cmd = "GATT Client Enable Notification on Descriptor by instance ID"
try:
- self.gattc_lib.enable_notification_desc_by_instance_id(line)
+ self.pri_dut.gattc.enable_notification_desc_by_instance_id(line)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_gattc_enable_indication_desc_by_instance_id(self, line):
+ """GATT Client Enable Indication on Descriptor by instance ID"""
+ cmd = "GATT Client Enable Indication on Descriptor by instance ID"
+ try:
+ self.pri_dut.gattc.enable_indication_desc_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -272,7 +313,7 @@
"""GATT Client enable all notifications"""
cmd = "GATT Client enable all notifications"
try:
- self.gattc_lib.char_enable_all_notifications()
+ self.pri_dut.gattc.char_enable_all_notifications()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -280,7 +321,7 @@
"""GATT Client read char by non-existant instance id"""
cmd = "GATT Client read char by non-existant instance id"
try:
- self.gattc_lib.read_char_by_invalid_instance_id(line)
+ self.pri_dut.gattc.read_char_by_invalid_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -288,7 +329,7 @@
"""Begin a reliable write on the Bluetooth Gatt Client"""
cmd = "GATT Client Begin Reliable Write"
try:
- self.gattc_lib.begin_reliable_write()
+ self.pri_dut.gattc.begin_reliable_write()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -296,7 +337,7 @@
"""Abort a reliable write on the Bluetooth Gatt Client"""
cmd = "GATT Client Abort Reliable Write"
try:
- self.gattc_lib.abort_reliable_write()
+ self.pri_dut.gattc.abort_reliable_write()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -304,7 +345,7 @@
"""Execute a reliable write on the Bluetooth Gatt Client"""
cmd = "GATT Client Execute Reliable Write"
try:
- self.gattc_lib.execute_reliable_write()
+ self.pri_dut.gattc.execute_reliable_write()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -312,7 +353,7 @@
"""GATT Client read all Characteristic values"""
cmd = "GATT Client read all Characteristic values"
try:
- self.gattc_lib.read_all_char()
+ self.pri_dut.gattc.read_all_char()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -320,7 +361,7 @@
"""Write to every Characteristic on the GATT server"""
cmd = "GATT Client Write All Characteristics"
try:
- self.gattc_lib.write_all_char(line)
+ self.pri_dut.gattc.write_all_char(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -328,7 +369,7 @@
""" Write to every Descriptor on the GATT server """
cmd = "GATT Client Write All Descriptors"
try:
- self.gattc_lib.write_all_desc(line)
+ self.pri_dut.gattc.write_all_desc(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -336,7 +377,7 @@
"""Write 0x00 or 0x02 to notification descriptor [instance_id]"""
cmd = "Write 0x00 0x02 to notification descriptor"
try:
- self.gattc_lib.write_desc_notification_by_instance_id(line)
+ self.pri_dut.gattc.write_desc_notification_by_instance_id(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -344,7 +385,7 @@
"""Discover service by uuid"""
cmd = "Discover service by uuid"
try:
- self.gattc_lib.discover_service_by_uuid(line)
+ self.pri_dut.gattc.discover_service_by_uuid(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -352,7 +393,7 @@
"""Read all Descriptor values"""
cmd = "Read all Descriptor values"
try:
- self.gattc_lib.read_all_desc()
+ self.pri_dut.gattc.read_all_desc()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -363,14 +404,14 @@
"""Close Bluetooth Gatt Servers"""
cmd = "Close Bluetooth Gatt Servers"
try:
- self.gatts_lib.close_bluetooth_gatt_servers()
+ self.pri_dut.gatts.close_bluetooth_gatt_servers()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def complete_gatts_setup_database(self, text, line, begidx, endidx):
if not text:
- completions = list(gatt_test_database.GATT_SERVER_DB_MAPPING.keys(
- ))[:]
+ completions = list(
+ gatt_test_database.GATT_SERVER_DB_MAPPING.keys())[:]
else:
completions = [
s for s in gatt_test_database.GATT_SERVER_DB_MAPPING.keys()
@@ -381,10 +422,10 @@
def complete_gatts_send_response(self, text, line, begidx, endidx):
"""GATT Server database name completion"""
if not text:
- completions = list(GattServerResponses.keys())[:]
+ completions = list(gatt_server_responses.keys())[:]
else:
completions = [
- s for s in GattServerResponses.keys() if s.startswith(text)
+ s for s in gatt_server_responses.keys() if s.startswith(text)
]
return completions
@@ -392,10 +433,10 @@
endidx):
"""GATT Server database name completion"""
if not text:
- completions = list(GattServerResponses.keys())[:]
+ completions = list(gatt_server_responses.keys())[:]
else:
completions = [
- s for s in GattServerResponses.keys() if s.startswith(text)
+ s for s in gatt_server_responses.keys() if s.startswith(text)
]
return completions
@@ -403,10 +444,10 @@
endidx):
"""GATT Server database name completion"""
if not text:
- completions = list(GattServerResponses.keys())[:]
+ completions = list(gatt_server_responses.keys())[:]
else:
completions = [
- s for s in GattServerResponses.keys() if s.startswith(text)
+ s for s in gatt_server_responses.keys() if s.startswith(text)
]
return completions
@@ -416,7 +457,7 @@
"""
cmd = "Discovery Services and list all UUIDS"
try:
- self.gatts_lib.list_all_uuids()
+ self.pri_dut.gatts.list_all_uuids()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -424,7 +465,7 @@
"""Send a response to the GATT Client"""
cmd = "GATT server send response"
try:
- self.gatts_lib.send_response(line)
+ self.pri_dut.gatts.send_response(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -438,14 +479,15 @@
confirm = False
if confirm_str.lower() == 'true':
confirm = True
- self.gatts_lib.notify_characteristic_changed(instance_id, confirm)
+ self.pri_dut.gatts.notify_characteristic_changed(
+ instance_id, confirm)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_gatts_setup_database(self, line):
cmd = "Setup GATT Server database: {}".format(line)
try:
- self.gatts_lib.setup_gatts_db(
+ self.pri_dut.gatts.setup_gatts_db(
gatt_test_database.GATT_SERVER_DB_MAPPING.get(line))
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -460,7 +502,7 @@
value = []
for i in range(size):
value.append(i % 256)
- self.gatts_lib.gatt_server_characteristic_set_value_by_instance_id(
+ self.pri_dut.gatts.characteristic_set_value_by_instance_id(
instance_id, value)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -472,7 +514,8 @@
info = line.split()
instance_id = info[0]
confirm = bool(info[1])
- self.gatts_lib.notify_characteristic_changed(instance_id, confirm)
+ self.pri_dut.gatts.notify_characteristic_changed(
+ instance_id, confirm)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -480,7 +523,7 @@
"""Open a GATT Server instance"""
cmd = "Open an empty GATT Server"
try:
- self.gatts_lib.open()
+ self.pri_dut.gatts.open()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -488,7 +531,7 @@
"""Clear BluetoothGattServices from BluetoothGattServer"""
cmd = "Clear BluetoothGattServices from BluetoothGattServer"
try:
- self.gatts_lib.gatt_server_clear_services()
+ self.pri_dut.gatts.gatt_server_clear_services()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -496,7 +539,7 @@
"""Send continous response with random data"""
cmd = "Send continous response with random data"
try:
- self.gatts_lib.send_continuous_response(line)
+ self.pri_dut.gatts.send_continuous_response(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -504,7 +547,7 @@
"""Send continous response including requested data"""
cmd = "Send continous response including requested data"
try:
- self.gatts_lib.send_continuous_response_data(line)
+ self.pri_dut.gatts.send_continuous_response_data(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -530,7 +573,7 @@
return completions
def complete_ble_stop_advertisement(self, text, line, begidx, endidx):
- str_adv_list = list(map(str, self.ble_lib.ADVERTISEMENT_LIST))
+ str_adv_list = list(map(str, self.pri_dut.ble.ADVERTISEMENT_LIST))
if not text:
completions = str_adv_list[:]
else:
@@ -541,21 +584,39 @@
"""Start a connectable LE advertisement"""
cmd = "Start a connectable LE advertisement"
try:
- self.ble_lib.start_generic_connectable_advertisement(line)
+ self.pri_dut.ble.start_generic_connectable_advertisement(line)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def _start_max_advertisements(self, ad):
+ self.pri_dut.ble.start_max_advertisements(ad)
+
+ def do_ble_start_generic_connectable_beacon_swarm(self, line):
+ """Start a connectable LE advertisement"""
+ cmd = "Start as many advertisements as possible on all devices"
+ try:
+ threads = []
+ for ad in self.android_devices:
+ thread = threading.Thread(
+ target=self._start_max_advertisements, args=([ad]))
+ threads.append(thread)
+ thread.start()
+ for t in threads:
+ t.join()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_ble_start_connectable_advertisement_set(self, line):
"""Start a connectable advertisement set"""
try:
- self.ble_lib.start_connectable_advertisement_set(line)
+ self.pri_dut.ble.start_connectable_advertisement_set(line)
except Exception as err:
self.log.error("Failed to start advertisement: {}".format(err))
def do_ble_stop_all_advertisement_set(self, line):
"""Stop all advertisement sets"""
try:
- self.ble_lib.stop_all_advertisement_set(line)
+ self.pri_dut.ble.stop_all_advertisement_set(line)
except Exception as err:
self.log.error("Failed to stop advertisement: {}".format(err))
@@ -564,7 +625,7 @@
[uuid1 uuid2 ... uuidN]"""
cmd = "Add a valid service UUID to the advertisement."
try:
- self.ble_lib.adv_add_service_uuid_list(line)
+ self.pri_dut.ble.adv_add_service_uuid_list(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -572,7 +633,7 @@
"""Include local name in the advertisement. inputs: [true|false]"""
cmd = "Include local name in the advertisement."
try:
- self.ble_lib.adv_data_include_local_name(line)
+ self.pri_dut.ble.adv_data_include_local_name(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -580,7 +641,7 @@
"""Include tx power level in the advertisement. inputs: [true|false]"""
cmd = "Include local name in the advertisement."
try:
- self.ble_lib.adv_data_include_tx_power_level(line)
+ self.pri_dut.ble.adv_data_include_tx_power_level(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -589,7 +650,7 @@
[id data1 data2 ... dataN]"""
cmd = "Include manufacturer id and data to the advertisment."
try:
- self.ble_lib.adv_data_add_manufacturer_data(line)
+ self.pri_dut.ble.adv_data_add_manufacturer_data(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -597,19 +658,19 @@
"""Start a nonconnectable LE advertisement"""
cmd = "Start a nonconnectable LE advertisement"
try:
- self.ble_lib.start_generic_nonconnectable_advertisement(line)
+ self.pri_dut.ble.start_generic_nonconnectable_advertisement(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_ble_list_active_advertisement_ids(self, line):
"""List all active BLE advertisements"""
- self.log.info("IDs: {}".format(self.ble_lib.advertisement_list))
+ self.log.info("IDs: {}".format(self.pri_dut.ble.advertisement_list))
def do_ble_stop_all_advertisements(self, line):
"""Stop all LE advertisements"""
cmd = "Stop all LE advertisements"
try:
- self.ble_lib.stop_all_advertisements(line)
+ self.pri_dut.ble.stop_all_advertisements(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -617,7 +678,7 @@
"""Stop an LE advertisement"""
cmd = "Stop a connectable LE advertisement"
try:
- self.ble_lib.ble_stop_advertisement(line)
+ self.pri_dut.ble.ble_stop_advertisement(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -633,7 +694,7 @@
return completions
def complete_bta_set_scan_mode(self, text, line, begidx, endidx):
- completions = [e.name for e in BluetoothScanModeType]
+ completions = list(bt_scan_mode_types.keys())[:]
if not text:
completions = completions[:]
else:
@@ -644,7 +705,7 @@
"""Set the Scan mode of the Bluetooth Adapter"""
cmd = "Set the Scan mode of the Bluetooth Adapter"
try:
- self.bta_lib.set_scan_mode(line)
+ self.pri_dut.bta.set_scan_mode(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -652,7 +713,7 @@
"""Set Bluetooth Adapter Name"""
cmd = "Set Bluetooth Adapter Name"
try:
- self.bta_lib.set_device_name(line)
+ self.pri_dut.bta.set_device_name(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -660,7 +721,7 @@
"""Enable Bluetooth Adapter"""
cmd = "Enable Bluetooth Adapter"
try:
- self.bta_lib.enable()
+ self.pri_dut.bta.enable()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -668,7 +729,7 @@
"""Disable Bluetooth Adapter"""
cmd = "Disable Bluetooth Adapter"
try:
- self.bta_lib.disable()
+ self.pri_dut.bta.disable()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -676,7 +737,7 @@
"""Initiate bond to PTS device"""
cmd = "Initiate Bond"
try:
- self.bta_lib.init_bond()
+ self.pri_dut.bta.init_bond()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -684,7 +745,7 @@
"""Start BR/EDR Discovery"""
cmd = "Start BR/EDR Discovery"
try:
- self.bta_lib.start_discovery()
+ self.pri_dut.bta.start_discovery()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -692,7 +753,7 @@
"""Stop BR/EDR Discovery"""
cmd = "Stop BR/EDR Discovery"
try:
- self.bta_lib.stop_discovery()
+ self.pri_dut.bta.stop_discovery()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -700,7 +761,7 @@
"""Get Discovered Br/EDR Devices"""
cmd = "Get Discovered Br/EDR Devices\n"
try:
- self.bta_lib.get_discovered_devices()
+ self.pri_dut.bta.get_discovered_devices()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -708,7 +769,7 @@
"""Bond to PTS device"""
cmd = "Bond to the PTS dongle directly"
try:
- self.bta_lib.bond()
+ self.pri_dut.bta.bond()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -716,7 +777,7 @@
"""BTA disconnect"""
cmd = "BTA disconnect"
try:
- self.bta_lib.disconnect()
+ self.pri_dut.bta.disconnect()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -724,7 +785,7 @@
"""Unbond from PTS device"""
cmd = "Unbond from the PTS dongle"
try:
- self.bta_lib.unbond()
+ self.pri_dut.bta.unbond()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -732,7 +793,7 @@
"""Start or stop Bluetooth Pairing Helper"""
cmd = "Start or stop BT Pairing helper"
try:
- self.bta_lib.start_pairing_helper(line)
+ self.pri_dut.bta.start_pairing_helper(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -740,7 +801,7 @@
"""Push pairing pin to the Android Device"""
cmd = "Push the pin to the Android Device"
try:
- self.bta_lib.push_pairing_pin(line)
+ self.pri_dut.bta.push_pairing_pin(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -748,7 +809,7 @@
"""Get pairing PIN"""
cmd = "Get Pin Info"
try:
- self.bta_lib.get_pairing_pin()
+ self.pri_dut.bta.get_pairing_pin()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -756,7 +817,7 @@
"""BTA fetch UUIDS with SDP"""
cmd = "Fetch UUIDS with SDP"
try:
- self.bta_lib.fetch_uuids_with_sdp()
+ self.pri_dut.bta.fetch_uuids_with_sdp()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -764,14 +825,14 @@
"""Connect available profiles"""
cmd = "Connect all profiles possible"
try:
- self.bta_lib.connect_profiles()
+ self.pri_dut.bta.connect_profiles()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_bta_tts_speak(self, line):
cmd = "Open audio channel by speaking characters"
try:
- self.bta_lib.tts_speak()
+ self.pri_dut.bta.tts_speak()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -781,7 +842,7 @@
def do_rfcomm_connect(self, line):
"""Perform an RFCOMM connect"""
try:
- self.rfcomm_lib.connect(line)
+ self.pri_dut.rfcomm.connect(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -789,7 +850,7 @@
"""Open rfcomm socket"""
cmd = "Open RFCOMM socket"
try:
- self.rfcomm_lib.open_rfcomm_socket()
+ self.pri_dut.rfcomm.open_rfcomm_socket()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -797,42 +858,42 @@
"""Open L2CAP socket"""
cmd = "Open L2CAP socket"
try:
- self.rfcomm_lib.open_l2cap_socket()
+ self.pri_dut.rfcomm.open_l2cap_socket()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_rfcomm_write(self, line):
cmd = "Write String data over an RFCOMM connection"
try:
- self.rfcomm_lib.write(line)
+ self.pri_dut.rfcomm.write(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_rfcomm_write_binary(self, line):
cmd = "Write String data over an RFCOMM connection"
try:
- self.rfcomm_lib.write_binary(line)
+ self.pri_dut.rfcomm.write_binary(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_rfcomm_end_connect(self, line):
cmd = "End RFCOMM connection"
try:
- self.rfcomm_lib.end_connect()
+ self.pri_dut.rfcomm.end_connect()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_rfcomm_accept(self, line):
cmd = "Accept RFCOMM connection"
try:
- self.rfcomm_lib.accept(line)
+ self.pri_dut.rfcomm.accept(line)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
def do_rfcomm_stop(self, line):
cmd = "STOP RFCOMM Connection"
try:
- self.rfcomm_lib.stop()
+ self.pri_dut.rfcomm.stop()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -840,7 +901,7 @@
"""Open L2CAP socket"""
cmd = "Open L2CAP socket"
try:
- self.rfcomm_lib.open_l2cap_socket()
+ self.pri_dut.rfcomm.open_l2cap_socket()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -851,7 +912,7 @@
"""Reset Bluetooth Config file"""
cmd = "Reset Bluetooth Config file"
try:
- self.config_lib.reset()
+ self.pri_dut.config.reset()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -859,7 +920,7 @@
"""Set NonBondable Mode"""
cmd = "Set NonBondable Mode"
try:
- self.config_lib.set_nonbond()
+ self.pri_dut.config.set_nonbond()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -867,13 +928,32 @@
"""Set Disable MITM"""
cmd = "Set Disable MITM"
try:
- self.config_lib.set_disable_mitm()
+ self.pri_dut.config.set_disable_mitm()
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
"""End Config wrappers"""
"""Begin HFP/HSP wrapper"""
+ def do_bta_hfp_start_voice_recognition(self, line):
+ self.pri_dut.droid.bluetoothHspStartVoiceRecognition(self.mac_addr)
+
+ def do_bta_hfp_stop_voice_recognition(self, line):
+ self.pri_dut.droid.bluetoothHspStopVoiceRecognition(self.mac_addr)
+
+ def do_test_clcc_response(self, line):
+ # Experimental
+ clcc_index = 1
+ clcc_direction = 1
+ clcc_status = 4
+ clcc_mode = 0
+ clcc_mpty = False
+ clcc_number = "18888888888"
+ clcc_type = 0
+ self.pri_dut.droid.bluetoothHspClccResponse(
+ clcc_index, clcc_direction, clcc_status, clcc_mode, clcc_mpty,
+ clcc_number, clcc_type)
+
def do_bta_hsp_force_sco_audio_on(self, line):
"""HFP/HSP Force SCO Audio ON"""
cmd = "HFP/HSP Force SCO Audio ON"
@@ -916,8 +996,9 @@
self.mac_addr):
self.log.info(
FAILURE.format(
- cmd, "bluetoothHspDisconnectAudio returned false for "
- + self.mac_addr))
+ cmd,
+ "bluetoothHspDisconnectAudio returned false for " +
+ self.mac_addr))
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -949,8 +1030,7 @@
"""Get HID Report"""
cmd = "Get HID Report"
try:
- self.pri_dut.droid.bluetoothHidGetReport(self.mac_addr, "1", "1",
- 1024)
+ self.pri_dut.droid.bluetoothHidGetReport(self.mac_addr, 1, 1, 1024)
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -958,8 +1038,7 @@
"""Get HID Report"""
cmd = "Get HID Report"
try:
- self.pri_dut.droid.bluetoothHidSetReport(self.mac_addr, "1",
- "Test")
+ self.pri_dut.droid.bluetoothHidSetReport(self.mac_addr, 1, "Test")
except Exception as err:
self.log.info(FAILURE.format(cmd, err))
@@ -980,3 +1059,334 @@
self.log.info(FAILURE.format(cmd, err))
"""End HID wrappers"""
+ """Begin carkit test wrappers"""
+
+ def do_test_suite_generic_bt_tests(self, line):
+ """Run generic Bluetooth connection test suite"""
+ generic_bt_tests = [
+ tuple((self.bt_carkit_lib.disconnect_reconnect_multiple_iterations,
+ [self.pri_dut])),
+ tuple((self.bt_carkit_lib.disconnect_a2dp_only_then_reconnect,
+ [self.pri_dut])),
+ tuple((self.bt_carkit_lib.disconnect_hsp_only_then_reconnect,
+ [self.pri_dut])),
+ tuple((
+ self.bt_carkit_lib.disconnect_both_hsp_and_a2dp_then_reconnect,
+ [self.pri_dut])),
+ ]
+ try:
+ for func, param in generic_bt_tests:
+ try:
+ func(param)
+ except Exception:
+ self.log.info("Test {} failed.".format(func))
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_connect_hsp_helper(self, line):
+ """Connect to HSP/HFP with additional tries and help"""
+ cmd = "Connect to hsp with some help"
+ try:
+ self.bt_carkit_lib.connect_hsp_helper(self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_hfp_setup_multi_incomming_calls(self, line):
+ """Setup two incomming calls"""
+ cmd = "Setup multiple incomming calls"
+ try:
+ self.bt_carkit_lib.setup_multi_call(self.sec_dut, self.ter_dut,
+ self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_disconnect_reconnect_multiple_iterations(self, line):
+ """Quick disconnect/reconnect stress test"""
+ try:
+ self.bt_carkit_lib.disconnect_reconnect_multiple_iterations(
+ self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_disconnect_a2dp_only_then_reconnect(self, line):
+ """Test disconnect-reconnect a2dp only scenario from phone."""
+ cmd = "Test disconnect-reconnect a2dp only scenario from phone."
+ try:
+ self.bt_carkit_lib.disconnect_a2dp_only_then_reconnect(
+ self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_disconnect_hsp_only_then_reconnect(self, line):
+ """Test disconnect-reconnect hsp only scenario from phone."""
+ cmd = "Test disconnect-reconnect hsp only scenario from phone."
+ try:
+ self.bt_carkit_lib.disconnect_hsp_only_then_reconnect(self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_disconnect_both_hsp_and_a2dp_then_reconnect(self, line):
+ """Test disconnect-reconnect hsp and a2dp scenario from phone."""
+ cmd = "Test disconnect-reconnect hsp and a2dp scenario from phone."
+ try:
+ self.bt_carkit_lib.disconnect_both_hsp_and_a2dp_then_reconnect(
+ self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_outgoing_call_private_number(self, line):
+ """Test outgoing call scenario from phone to private number"""
+ cmd = "Test outgoing call scenario from phone to private number"
+ try:
+ self.bt_carkit_lib.outgoing_call_private_number(
+ self.pri_dut, self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_outgoing_call_a2dp_play_before_and_after(self, line):
+ """Test outgoing call scenario while playing music. Music should resume
+ after call."""
+ cmd = "Test outgoing call scenario while playing music. Music should " \
+ "resume after call."
+ try:
+ self.bt_carkit_lib.outgoing_call_a2dp_play_before_and_after(
+ self.pri_dut, self.sec_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_outgoing_call_unknown_contact(self, line):
+ """Test outgoing call scenario from phone to unknow contact"""
+ cmd = "Test outgoing call scenario from phone to unknow contact"
+ try:
+ self.bt_carkit_lib.outgoing_call_unknown_contact(
+ self.pri_dut, self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_incomming_call_private_number(self, line):
+ """Test incomming call scenario to phone from unknown contact"""
+ cmd = "Test incomming call scenario to phone from unknown contact"
+ try:
+ self.bt_carkit_lib.incomming_call_private_number(
+ self.pri_dut, self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_outgoing_call_multiple_iterations(self, line):
+ """Test outgoing call quickly 3 times"""
+ cmd = "Test outgoing call quickly 3 times"
+ try:
+ self.bt_carkit_lib.outgoing_call_multiple_iterations(
+ self.pri_dut, self.sec_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_outgoing_call_hsp_disabled_then_enabled_during_call(self, line):
+ """Test outgoing call hsp disabled then enable during call."""
+ cmd = "Test outgoing call hsp disabled then enable during call."
+ try:
+ self.bt_carkit_lib.outgoing_call_hsp_disabled_then_enabled_during_call(
+ self.pri_dut, self.sec_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_call_audio_routes(self, line):
+ """Test various audio routes scenario from phone."""
+ cmd = "Test various audio routes scenario from phone."
+ try:
+ self.bt_carkit_lib.call_audio_routes(self.pri_dut, self.sec_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_sms_receive_different_sizes(self, line):
+ """Test recieve sms of different sizes."""
+ cmd = "Test recieve sms of different sizes."
+ try:
+ self.bt_carkit_lib.sms_receive_different_sizes(
+ self.pri_dut, self.sec_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_sms_receive_multiple(self, line):
+ """Test recieve sms of different sizes."""
+ cmd = "Test recieve sms of different sizes."
+ try:
+ self.bt_carkit_lib.sms_receive_multiple(self.pri_dut, self.sec_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_sms_send_outgoing_texts(self, line):
+ """Test send sms of different sizes."""
+ cmd = "Test send sms of different sizes."
+ try:
+ self.bt_carkit_lib.sms_send_outgoing_texts(self.pri_dut,
+ self.sec_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_sms_during_incomming_call(self, line):
+ """Test incomming call scenario to phone from unknown contact"""
+ cmd = "Test incomming call scenario to phone from unknown contact"
+ try:
+ self.bt_carkit_lib.sms_during_incomming_call(
+ self.pri_dut, self.sec_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_multi_incomming_call(self, line):
+ """Test 2 incomming calls scenario to phone."""
+ cmd = "Test 2 incomming calls scenario to phone."
+ try:
+ self.bt_carkit_lib.multi_incomming_call(self.pri_dut, self.sec_dut,
+ self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_multi_call_audio_routing(self, line):
+ """Test 2 incomming calls scenario to phone, then test audio routing."""
+ cmd = "Test 2 incomming calls scenario to phone, then test audio" \
+ "routing."
+ try:
+ self.bt_carkit_lib.multi_call_audio_routing(
+ self.pri_dut, self.sec_dut, self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_multi_call_swap_multiple_times(self, line):
+ """Test 2 incomming calls scenario to phone, then swap the calls
+ multiple times"""
+ cmd = "Test 2 incomming calls scenario to phone, then swap the calls" \
+ "multiple times"
+ try:
+ self.bt_carkit_lib.multi_call_swap_multiple_times(
+ self.pri_dut, self.sec_dut, self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_multi_call_join_conference_call(self, line):
+ """Test 2 incomming calls scenario to phone then join the calls."""
+ cmd = "Test 2 incomming calls scenario to phone then join the calls."
+ try:
+ self.bt_carkit_lib.multi_call_join_conference_call(
+ self.pri_dut, self.sec_dut, self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_multi_call_join_conference_call_hangup_conf_call(self, line):
+ """Test 2 incomming calls scenario to phone then join the calls,
+ then terminate the call from the primary dut."""
+ cmd = "Test 2 incomming calls scenario to phone then join the calls, " \
+ "then terminate the call from the primary dut."
+ try:
+ self.bt_carkit_lib.multi_call_join_conference_call_hangup_conf_call(
+ self.pri_dut, self.sec_dut, self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_outgoing_multi_call_join_conference_call(self, line):
+ """Test 2 outgoing calls scenario from phone then join the calls."""
+ cmd = "Test 2 outgoing calls scenario from phone then join the calls."
+ try:
+ self.bt_carkit_lib.outgoing_multi_call_join_conference_call(
+ self.pri_dut, self.sec_dut, self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_multi_call_join_conference_call_audio_routes(self, line):
+ """Test 2 incomming calls scenario to phone then join the calls,
+ then test different audio routes."""
+ cmd = "Test 2 incomming calls scenario to phone then join the calls, " \
+ "then test different audio routes."
+ try:
+ self.bt_carkit_lib.multi_call_join_conference_call_audio_routes(
+ self.pri_dut, self.sec_dut, self.ter_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_avrcp_play_pause(self, line):
+ """Test avrcp play/pause commands multiple times from phone"""
+ cmd = "Test avrcp play/pause commands multiple times from phone"
+ try:
+ self.bt_carkit_lib.avrcp_play_pause(self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_avrcp_next_previous_song(self, line):
+ """Test AVRCP go to the next song then the previous song."""
+ cmd = "Test AVRCP go to the next song then the previous song."
+ try:
+ self.bt_carkit_lib.avrcp_next_previous_song(self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_avrcp_next_previous(self, line):
+ """Test AVRCP go to the next song then the press previous after a few
+ seconds."""
+ cmd = "Test AVRCP go to the next song then the press previous after " \
+ "a few seconds."
+ try:
+ self.bt_carkit_lib.avrcp_next_previous(self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_avrcp_next_repetative(self, line):
+ """Test AVRCP go to the next 10 times"""
+ cmd = "Test AVRCP go to the next 10 times"
+ try:
+ self.bt_carkit_lib.avrcp_next_repetative(self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def _process_question(self, question, expected_response):
+ while True:
+ try:
+ result = input(question.format(level)).lower()
+ except Exception as err:
+ print(err)
+
+ def do_e2e_cycle_battery_level(self, line):
+ """Cycle battery level through different values and verify result on carkit"""
+ cmd = "Test that verifies battery level indicator changes with the " \
+ "phone. Phone current level."
+ try:
+ self.bt_carkit_lib.cycle_absolute_volume_control(self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_cycle_absolute_volume_control(self, line):
+ """Cycle media volume level through different values and verify result on carkit"""
+ cmd = "Test aboslute volume on carkit by changed volume levels from phone."
+ try:
+ self.bt_carkit_lib.cycle_absolute_volume_control(self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_test_voice_recognition_from_phone(self, line):
+ """Test Voice Recognition from phone."""
+ cmd = "Test voice recognition from phone."
+ try:
+ self.bt_carkit_lib.test_voice_recognition_from_phone(self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ def do_e2e_test_audio_and_voice_recognition_from_phone(self, line):
+ """Test Voice Recognition from phone and confirm music audio continues."""
+ cmd = "Test Voice Recognition from phone and confirm music audio continues."
+ try:
+ self.bt_carkit_lib.test_audio_and_voice_recognition_from_phone(
+ self.pri_dut)
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ """End carkit test wrappers"""
+ """Begin adb shell test wrappers"""
+
+ def do_set_battery_level(self, line):
+ """Set battery level based on input"""
+ cmd = "Set battery level based on input"
+ try:
+ self.pri_dut.shell.set_battery_level(int(line))
+ except Exception as err:
+ self.log.info(FAILURE.format(cmd, err))
+
+ """End adb shell test wrappers"""
diff --git a/acts/tests/google/bt/pts/instructions/AVDTP_PTS_INSTUCTIONS b/acts/tests/google/bt/pts/instructions/AVDTP_PTS_INSTUCTIONS
new file mode 100644
index 0000000..8ce453a
--- /dev/null
+++ b/acts/tests/google/bt/pts/instructions/AVDTP_PTS_INSTUCTIONS
@@ -0,0 +1,20 @@
+# 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.
+
+DID
+=================================================================
+
+TC_SRC_INT_SIG_SMG_BV_23_C
+ #Before test run this and set it to false after the test is done.
+ adb shell setprop bluetooth.pts.force_a2dp_abort true
diff --git a/acts/tests/google/bt/pts/instructions/BNEP_PTS_INSTRUCTIONS b/acts/tests/google/bt/pts/instructions/BNEP_PTS_INSTRUCTIONS
new file mode 100644
index 0000000..389b4ef
--- /dev/null
+++ b/acts/tests/google/bt/pts/instructions/BNEP_PTS_INSTRUCTIONS
@@ -0,0 +1,80 @@
+# 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.
+
+DID
+=================================================================
+
+TC_CTRL_BV_01_C
+ bta_start_pairing_helper
+
+TC_CTRL_BV_02_C
+ bta_start_pairing_helper
+ bta_connect_profiles
+ # Wait 20 seconds
+
+TC_CTRL_BV_03_C
+ bta_start_pairing_helper
+
+TC_CTRL_BV_04_C
+ bta_start_pairing_helper
+
+TC_CTRL_BV_05_C
+ bta_start_pairing_helper
+
+TC_CTRL_BV_06_C
+ bta_start_pairing_helper
+
+TC_CTRL_BV_08_C
+ bta_start_pairing_helper
+
+TC_CTRL_BV_10_C
+ bta_start_pairing_helper
+
+TC_CTRL_BV_19_C
+ bta_start_pairing_helper
+
+TC_RX-TYPE-0_BV_11_C
+ bta_start_pairing_helper
+ [PTS Interaction]
+
+TC_RX-TYPE-0_BV_15_C
+ bta_start_pairing_helper
+ [PTS Interaction]
+
+TC_RX-TYPE-0_BV_16_C
+ bta_start_pairing_helper
+ [PTS Interaction]
+
+TC_RX-TYPE-0_BV_17_C
+ bta_start_pairing_helper
+ [PTS Interaction]
+
+TC_RX-TYPE-0_BV_18_C
+ bta_start_pairing_helper
+ [PTS Interaction]
+
+TC_RX-C_BV_12_C
+ bta_start_pairing_helper
+ [PTS Interaction]
+
+TC_RX-C-S_BV_13_C
+ bta_start_pairing_helper
+ [PTS Interaction]
+
+TC_RX-C-D_BV_14_C
+ bta_start_pairing_helper
+ [PTS Interaction]
+
+TC_TX-TYPE-0_BV_20_C
+ TBD
\ No newline at end of file
diff --git a/acts/tests/google/bt/pts/instructions/DID_PTS_INSTRUCTIONS b/acts/tests/google/bt/pts/instructions/DID_PTS_INSTRUCTIONS
index 0038cbb..289e266 100644
--- a/acts/tests/google/bt/pts/instructions/DID_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/DID_PTS_INSTRUCTIONS
@@ -16,13 +16,13 @@
=================================================================
TC_SR_SDI_BV_01_I
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
TC_SR_SDI_BV_02_I
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
TC_SR_SDI_BV_03_I
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
TC_SR_SDI_BV_04_I
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
diff --git a/acts/tests/google/bt/pts/instructions/GAP_PTS_INSTRUCTIONS b/acts/tests/google/bt/pts/instructions/GAP_PTS_INSTRUCTIONS
index 47da157..797c7d4 100644
--- a/acts/tests/google/bt/pts/instructions/GAP_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/GAP_PTS_INSTRUCTIONS
@@ -19,20 +19,20 @@
This sets the random address to a static address.
TC_MOD_NDIS_BV_01_C
- bta_set_scan_mode SCAN_MODE_NONE
+ bta_set_scan_mode none
[PTS Interaction]
TC_MOD_GDIS_BV_01_C
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
TC_MOD_GDIS_BV_02_C
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
TC_MOD_NCON_BV_01_C
- bta_set_scan_mode SCAN_MODE_NONE
+ bta_set_scan_mode none
TC_MOD_CON_BV_01_C
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
TC_DISC_NONM_BV_01_C
Note: Requires https://android-review.googlesource.com/344824
@@ -187,7 +187,7 @@
gattc_disconnect
Note: Run the test a first time and it will fail. Change the address to
-be the peer address in the PTS logs that start with:
+be the peer address in the PTS logs that start with:
SEC_LE?SEC_LE_REMOTE_CSRK_REQUEST_IND=PDU
peerAddr: 'xxxxxxxxxxxx'O
TC_BOND_NBON_BV_01_C
@@ -426,13 +426,13 @@
TC_DM_CON_BV_01_C
Note: Use Bluetooth Public Address
- bta_set_scan_mode SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
[PTS Interaction]
TC_DM_BON_BV_01_C
Note: This one may take multiple tries
Note: Use Bluetooth Public Address
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
bta_start_pairing_helper
[PTS Interaction]
[PTS will ask to disconnect, during this time run next cmd ASAP]
@@ -462,7 +462,7 @@
TC_DM_LEP_BV_01_C
Note: Use Bluetooth Public Address
bta_set_device_name [TSPX_iut_device_name_in_adv_packet_for_random_address]
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
[PTS Interaction]
ble_adv_data_include_local_name true
ble_start_generic_connectable_advertisement
@@ -470,7 +470,7 @@
TC_DM_LEP_BV_02_C
[PTS Interation]
[PTS Interation]
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
bta_disable
bta_enable
@@ -490,7 +490,7 @@
Note: Use Bluetooth Public Address
Note: Run these commands before executing the testcase on PTS Side
bta_set_device_name [TSPX_iut_device_name_in_adv_packet_for_random_address]
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
ble_adv_data_include_local_name true
ble_start_generic_connectable_advertisement
@@ -498,7 +498,7 @@
Note: Use Bluetooth Public Address
Note: Run these commands before executing the testcase on PTS Side
bta_set_device_name [TSPX_iut_device_name_in_adv_packet_for_random_address]
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
ble_adv_data_include_local_name true
ble_start_generic_connectable_advertisement
@@ -509,7 +509,7 @@
Note: Use Bluetooth Public Address
Note: Run the first 4 commands before executing the testcase on PTS Side
bta_set_device_name [TSPX_iut_device_name_in_adv_packet_for_random_address]
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
ble_adv_data_include_local_name true
ble_start_generic_connectable_advertisement
bta_start_discovery
@@ -523,7 +523,7 @@
Note: Use Bluetooth Public Address
Note: Run the first 4 commands before executing the testcase on PTS Side
bta_set_device_name [TSPX_iut_device_name_in_adv_packet_for_random_address]
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
ble_adv_data_include_local_name true
ble_start_generic_connectable_advertisement
gattc_connect_over_le
diff --git a/acts/tests/google/bt/pts/instructions/GATT_PTS_INSTRUCTIONS b/acts/tests/google/bt/pts/instructions/GATT_PTS_INSTRUCTIONS
index fe20ea8..49540b4 100644
--- a/acts/tests/google/bt/pts/instructions/GATT_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/GATT_PTS_INSTRUCTIONS
@@ -14,6 +14,14 @@
GATT
=================================================================
+Note: Bug in PTS forces GATT operations to be over BR/EDR. To run tests over
+LE disable BR/EDR in ICS when running tests.
+
+Note: As of PTS version 7.2.1 GATT server tests now performs scans appropriately. Change IXIT value for TSPX_iut_device_name_in_adv_packet_for_random_address to "CMD LINE Test" and make sure you have a connectable advertisement with that local name:
+ ble_adv_data_include_local_name true
+ ble_start_generic_connectable_advertisement
+
+
TC_CL_GAC_BV_01_C
gattc_connect_over_le
gattc_request_mtu 23
@@ -21,6 +29,14 @@
gattc_write_char_by_instance_id [handle] [size]
gattc_disconnect
+#Potential new instructions for SIG testcase rewrite
+TC_CL_GAC_BV_01_C
+ gattc_connect_over_le
+ gattc_request_mtu 23
+ [Read PTS Handle and Size]
+ gattc_write_invalid_char_by_instance_id [handle] [size]
+ gattc_disconnect
+
TC_CL_GAD_BV_01_C
gattc_connect_over_le
gattc_refresh
@@ -37,6 +53,7 @@
gattc_disconnect
TC_CL_GAD_BV_02_C
+ Note: Values of UUIDS sometimes changes.
gattc_connect_over_le
gattc_discover_service_by_uuid 1800
[PTS Interaction] Verify Values
@@ -68,84 +85,101 @@
TC_CL_GAD_BV_03_C
gattc_connect_over_le
+ gattc_refresh
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refresh
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refresh
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refresh
[PTS Interation]
gattc_disconnect
TC_CL_GAD_BV_04_C
gattc_connect_over_le
- gattc_list_all_uuids
+ gattc_refresh
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
- gattc_list_all_uuids
+ gattc_refresh
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
- gattc_list_all_uuids
+ gattc_refresh
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
- gattc_list_all_uuids
+ gattc_refresh
[PTS Interation]
gattc_disconnect
TC_CL_GAD_BV_05_C
gattc_connect_over_le
+ gattc_refesh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gttc_refesh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refesh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refesh
+ gattc_list_all_uuids
+ gattc_refesh
+ [PTS Interation]
+ gattc_disconnect
+ gattc_connect_over_le
+ gattc_refesh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refesh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refesh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
- gattc_list_all_uuids
- [PTS Interation]
- gattc_disconnect
- gattc_connect_over_le
+ gattc_refesh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
TC_CL_GAD_BV_06_C
gattc_connect_over_le
+ gattc_refresh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refresh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refresh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
gattc_connect_over_le
+ gattc_refresh
gattc_list_all_uuids
[PTS Interation]
gattc_disconnect
@@ -209,6 +243,36 @@
[PTS Interaction]
bta_unbond
+TC_CL_GAR_BV_03_C
+ gattc_connect_over_le
+ gattc_read_char_by_uuid [Input UUID]
+ [PTS Interaction] Verify values
+ gattc_read_char_by_uuid 000055f2-0000-0000-0123-456789abcdef
+
+TC_CL_GAR_BI_06_C
+ gattc_connect_over_le
+ gattc_read_char_by_uuid [Input UUID]
+ gattc_disconnect
+ [PTS Interaction] Verify values
+
+TC_CL_GAR_BI_07_C
+ gattc_connect_over_le
+ gattc_read_char_by_uuid [Input UUID]
+ gattc_disconnect
+ [PTS Interaction] Verify values
+
+TC_CL_GAR_BI_10_C
+ gattc_connect_over_le
+ gattc_read_char_by_uuid [Input UUID]
+ gattc_disconnect
+ [PTS Interaction] Verify values
+
+TC_CL_GAR_BI_11_C
+ gattc_connect_over_le
+ gattc_read_char_by_uuid [Input UUID]
+ gattc_disconnect
+ [PTS Interaction] Verify values
+
TC_CL_GAR_BV_04_C
gattc_connect_over_le
gattc_mod_read_char_by_instance_id [handle]
@@ -426,7 +490,7 @@
TC_CL_GAW_BI_08_C
gattc_connect_over_le
- do_gattc_mod_write_char_by_instance_id [handle] 43
+ gattc_mod_write_char_by_instance_id [handle] 43
[PTS Interaction]
gattc_disconnect
@@ -611,6 +675,15 @@
gattc_execute_reliable_write
gattc_disconnect
+TC_CL_GAW_BI_32_C - Alternate method
+ gattc_connect_over_le
+ gattc_write_invalid_char_by_instance_id 0029 1
+ gattc_write_invalid_char_by_instance_id 0029 21
+ gattc_write_invalid_char_by_instance_id 0029 21
+ gattc_write_invalid_char_by_instance_id 0029 21
+ gattc_disconnect
+
+
TC_CL_GAW_BI_33_C
gattc_connect_over_le
gattc_write_char_by_instance_id [handle] [size]
@@ -641,11 +714,12 @@
[PTS Interaction]
gattc_disconnect
-TC_CL_GAN_BV_01_C
- gatts_setup_database TEST_DB_5
- ble_start_generic_connectable_advertisement
- gatts_list_all_uuids
- gatts_notify_characteristic_changed [instance id from previous command of only characteristic.] true
+TC_CL_GAI_BV_01_C
+ gattc_connect_over_le
+ gattc_enable_indication_desc_by_instance_id 0113
+ [PTS Interaction]
+ gattc_disconnect
+
TC_CL_GAS_BV_01_C
gattc_connect_over_le
@@ -693,13 +767,13 @@
TC_SR_GAD_BV_07_C
Note: Use Public Address
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
gatts_open
[PTS Interaction]
TC_SR_GAD_BV_08_C
Note: Use Public Address
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
gatts_open
[PTS Interaction]
@@ -718,7 +792,8 @@
Note: Static Address OK
gatts_setup_database TEST_DB_3
ble_start_generic_connectable_advertisement
- gatts_send_response GATT_INVALID_PDU
+ [PTS Interaction] Enter handle of uuid 2a30 after running the command gatts_list_all_uuids
+ gatts_send_response GATT_FAILURE
TC_SR_GAR_BI_03_C
gatts_setup_database TEST_DB_1
@@ -752,8 +827,11 @@
TC_SR_GAR_BI_08_C
Note: Static Address OK
+ bta_start_pairing_helper
gatts_setup_database TEST_DB_1
ble_start_generic_connectable_advertisement
+ [PTS Interaction] Enter pin from PTS to phone
+ gatts_send_response GATT_FAILURE
TC_SR_GAR_BI_09_C
gatts_setup_database TEST_DB_1
@@ -795,6 +873,14 @@
gatts_send_response GATT_SUCCESS 24
gatts_send_response GATT_INVALID_OFFSET
+TC_SR_GAR_BI_13_C (on PTS 7.2.1)
+ gatts_setup_database LARGE_1
+ # Have a logcat going as such: adb logcat | grep GattServer11onCharacteristicReadRequest
+ # Every time the offset is > 23:
+ gatts_send_response GATT_INVALID_OFFSET
+ # Every time the offset is < 23:
+ gatts_send_response GATT_SUCCESS 24
+
TC_SR_GAR_BI_14_C
Note: Static Address OK
gatts_setup_database TEST_DB_3
@@ -817,13 +903,23 @@
gatts_send_response GATT_0C_ERR
gatts_send_response GATT_0C_ERR
-TC_SR_GAR_BV_05_C
+TC_SR_GAR_BV_05_C - Deprecated
Note: Static Address OK
gatts_setup_database TEST_DB_3
ble_start_generic_connectable_advertisement
[PTS Interaction] Enter 002a
gatts_send_response GATT_READ_NOT_PERMITTED
+TC_SR_GAR_BV_05_C - PTS 7.2.1
+ gatts_setup_database TEST_DB_3
+ bta_start_pairing_helper
+ ble_adv_data_include_local_name true
+ ble_start_generic_connectable_advertisement
+ [PTS Interaction] Enter PIN fro PTS to phone
+ gatts_send_response GATT_SUCCESS
+ gatts_send_response GATT_SUCCESS
+ [PTS Interaction] Verify values
+
TC_SR_GAR_BI_18_C
Note: Static Address OK
gatts_setup_database TEST_DB_3
@@ -977,7 +1073,7 @@
TC_SR_GAR_BI_35_C
Note: Use Public Address
gatts_setup_database LARGE_DB_1
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
[PTS Interaction]
gatts_send_response GATT_CONNECTION_CONGESTED
@@ -995,6 +1091,8 @@
gatts_send_response GATT_SUCCESS
gatts_send_response GATT_SUCCESS
gatts_send_response GATT_SUCCESS
+ atts_send_response GATT_SUCCESS
+ atts_send_response GATT_SUCCESS
TC_SR_GAW_BI_02_C
Note: Static Address OK
@@ -1045,7 +1143,7 @@
[PTS Interaction] Enter 002a
gatts_send_response GATT_WRITE_NOT_PERMITTED
-TC_SR_GAW_BI_09_C
+TC_SR_GAW_BI_09_C - Deprecated
Note: Static Address OK
gatts_setup_database PTS_TEST
ble_start_generic_connectable_advertisement
@@ -1055,6 +1153,20 @@
gatts_send_response GATT_SUCCESS
gatts_send_response GATT_INVALID_OFFSET
+TC_SR_GAW_BI_09_C - NEW INSTRUCTIONS
+ Note: This test is a little tricky. Need to monitor logcat as such: adb logcat | grep offset.
+ gatts_setup_database LARGE_DB_3
+ ble_adv_data_include_local_name true
+ ble_start_generic_connectable_advertisement
+ # Whenever offset is <=23
+ gatts_send_response GATT_SUCCESS 24
+ # Whenever offset is > 23
+ # If preparedWrite value is True
+ gatts_send_response GATT_SUCCESS 24
+ gatts_send_response GATT_INVALID_OFFSET
+ # If preparedWrite value is False
+ gatts_send_response GATT_INVALID_OFFSET
+
TC_SR_GAW_BI_11_C
Note: Static Address OK
gatts_setup_database TEST_DB_3
@@ -1086,9 +1198,9 @@
TC_SR_GAW_BV_10_C
Note: Static Address OK
Note: Make sure MTU is set to 23 on PTS
- gatts_setup_database PTS_TEST
+ gatts_setup_database LARGE_DB_3
ble_start_generic_connectable_advertisement
- Whenever PTS prompts: "Discover All Characteristics of Service Request completed successfully" run this cmd:
+ Whenever PTS prompts: "Discover All Characteristics of Service Request completed successfully" in the Output Tool Window run this cmd:
gatts_send_response GATT_SUCCESS 24
Otherwise always respond with:
gatts_send_response GATT_SUCCESS
@@ -1184,7 +1296,7 @@
gatts_send_response GATT_0C_ERR
gatts_send_response GATT_0C_ERR
-TC_SR_GAW_BV_09_C
+TC_SR_GAW_BV_09_C - DEPRECATED
Note: Static Address OK
gatts_setup_database TEST_DB_3
ble_start_generic_connectable_advertisement
@@ -1198,6 +1310,13 @@
gatts_send_response GATT_SUCCESS
gatts_send_response GATT_SUCCESS
+TC_SR_GAW_BV_09_C - New Instructions
+ Note: Static Address OK
+ gatts_setup_database LARGE_DB_1
+ ble_start_generic_connectable_advertisement
+ # Repeat below cmd until success
+ gatts_send_response GATT_SUCCESS
+
TC_SR_GAW_BI_25_C
Note: Static Address OK
gatts_setup_database TEST_DB_3
@@ -1263,6 +1382,7 @@
TC_SR_GAW_BI_33_C
Note: Static Address OK
+ Note: This testcase is tricky as the order randomises a bit each time...
gatts_setup_database LARGE_DB_3
ble_start_generic_connectable_advertisement
gatts_send_response GATT_SUCCESS 24
@@ -1307,18 +1427,22 @@
gatts_notify_characteristic_changed [Handle from PTS] false 10
TC_SR_GAI_BV_01_C
- gattc_connect_over_le
- gattc_write_desc_notification_by_instance_id 00f3 2
+ gatts_setup_database DB_TEST
+ ble_adv_data_include_local_name true
+ ble_start_generic_connectable_advertisement
[PTS Interaction] Verify value and click yes
- gattc_disconnect
+ gatts_notify_characteristic_changed 002a false 10
+ gatts_notify_characteristic_changed 002a true 10
+
TC_SR_GAS_BV_01_C
gatts_setup_database DB_TEST
ble_start_generic_connectable_advertisement
+ [PTS Interaction] Click ok
+ gatts_notify_characteristic_changed 002a false 10
gatts_setup_database TEST_DB_3
- Wait 60 seconds for PTS to process
-TC_SR_GAT_BV_01_C
+TC_SR_GAT_BV_01_C - Deprecated
gatts_setup_database LARGE_DB_3
ble_start_generic_connectable_advertisement
gatts_send_response GATT_SUCCESS
@@ -1326,3 +1450,22 @@
gatts_send_response GATT_SUCCESS
gatts_setup_database TEST_DB_3
Wait 30 seconds for PTS to process
+
+TC_SR_GAT_BV_01_C - Deprecated
+ gatts_setup_database Test_DB_5
+ ble_adv_data_include_local_name true
+ ble_start_generic_connectable_advertisement
+ gatts_notify_characteristic_changed 0013 true
+ gatts_notify_characteristic_changed 002a true
+ gatts_send_response GATT_SUCCESS
+ gatts_send_response GATT_SUCCESS
+ gatts_send_response GATT_SUCCESS
+ gatts_notify_characteristic_changed 0003 true
+ gatts_setup_database TEST_DB_1
+ [PTS WAIT] 30 seconds to timeout to occur
+
+TC_SR_UNS_BI_02_C
+ bta_start_pairing_helper
+ ble_adv_data_include_local_name true
+ ble_start_generic_connectable_advertisement
+ [PTS Interaction] Enter pin from PTS to phone
\ No newline at end of file
diff --git a/acts/tests/google/bt/pts/instructions/GAVDP_PTS_INSTRUCTIONS b/acts/tests/google/bt/pts/instructions/GAVDP_PTS_INSTRUCTIONS
index 02da89a..132a738 100644
--- a/acts/tests/google/bt/pts/instructions/GAVDP_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/GAVDP_PTS_INSTRUCTIONS
@@ -17,24 +17,24 @@
TC_ACP_APP_CON_BV_01_C
bta_start_pairing_helper
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
bta_tts_speak
TC_ACP_APP_TRC_BV_02_C
bta_start_pairing_helper
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
bta_connect_profiles
bta_tts_speak
TC_INT_APP_CONN_BV_01_C
bta_start_pairing_helper
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
bta_connect_profiles
bta_tts_speak
TC_INT_APP_TRC_BV_02_C
bta_start_pairing_helper
- bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ bta_set_scan_mode connectable_discoverable
bta_connect_profiles
bta_tts_speak
bta_tts_speak
\ No newline at end of file
diff --git a/acts/tests/google/bt/pts/instructions/L2CAP_PTS_INSTRUCTIONS b/acts/tests/google/bt/pts/instructions/L2CAP_PTS_INSTRUCTIONS
index 755046d..d41872f 100644
--- a/acts/tests/google/bt/pts/instructions/L2CAP_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/L2CAP_PTS_INSTRUCTIONS
@@ -101,24 +101,71 @@
TC_L2CAP_COS_ECH_BV_01_C
bta_enable
+TC_L2CP_COS_CFC_BV_01_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ rfcomm_write 10
+ [PTS Interaction] Verify value
+ rfcomm_stop
+ gattc_disconnect
+ bta_disable
+ bta_enable
+
+TC_L2CP_COS_CFC_BV_02_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ rfcomm_write 10
+ [PTS Interaction] Verify value
+ rfcomm_stop
+ gattc_disconnect
+
+TC_L2CP_COS_CFC_BV_03_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ rfcomm_write 10
+ [PTS Interaction] Verify value
+ rfcomm_stop
+ gattc_disconnect
+
+TC_L2CP_COS_CFC_BV_04_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ [PTS Interaction] Verify value
+ rfcomm_stop
+ gattc_disconnect
+
+TC_L2CP_COS_CFC_BV_05_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ [PTS Interaction] Verify value
+ rfcomm_stop
+ gattc_disconnect
+ bta_disable
+ bta_enable
+
TC_L2CAP_EXF_BV_01_C
- bta_set_scan_mode SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_L2CAP_EXF_BV_02_C
- bta_set_scan_mode SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_L2CAP_EXF_BV_03_C
- bta_set_scan_mode SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_L2CAP_EXF_BV_05_C
- bta_set_scan_mode SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_L2CAP_CMC_BV_09_C
- bta_set_scan_mode SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_L2CAP_CMC_BV_10_C
rfcomm_connect
rfcomm_stop
+ rfcomm_connect
+ rfcomm_stop
+ rfcomm_connect
+ rfcomm_stop
[Wait up to 10-15 seconds]
TC_L2CAP_CMC_BV_11_C
@@ -138,7 +185,7 @@
[PTS Interaction] Yes
TC_L2CAP_CMC_BI_05_C
- rfcomm_accept
+ rfcomm_connect
[Wait up to 10-15 seconds]
TC_L2CAP_CMC_BI_06_C
@@ -161,9 +208,80 @@
gattc_connect_over_le
gattc_disconnect
+TC_L2CAP_LE_CFC_BI_01_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ [PTS Interaction] Verify values
+ gattc_disconnect
+
+TC_L2CAP_LE_CFC_BV_01_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ [PTS Interaction] Verify values
+ gattc_disconnect
+
+TC_L2CAP_LE_CFC_BV_02_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ [PTS Interaction] Verify values
+ gattc_disconnect
+
+TC_L2CAP_LE_CFC_BV_03_C
+ TBD
+
+TC_L2CAP_LE_CFC_BV_04_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 241
+ [PTS Interaction] Verify values
+ gattc_disconnect
+
TC_L2CAP_LE_CFC_BV_05_C
gattc_connect_over_le
gattc_disconnect
bta_disable
bta_enable
gattc_connect_over_le
+
+TC_L2CAP_LE_CFC_BV_06_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ rfcomm_write 10
+ rfcomm_write 10
+ rfcomm_write 10
+ gattc_disconnect
+ bta_disable
+ bta_enable
+
+TC_L2CAP_LE_CFC_BV_07_C
+ TBD
+
+TC_L2CAP_LE_CFC_BV_09_C
+ TBD
+
+TC_L2CAP_LE_CFC_BV_16_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ [PTS Interaction] Verify values
+ gattc_disconnect
+
+TC_L2CAP_LE_CFC_BV_18_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ [PTS Interaction] Verify values
+ gattc_disconnect
+
+TC_L2CAP_LE_CFC_BV_19_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ [PTS Interaction] Verify values
+ gattc_disconnect
+
+TC_L2CAP_LE_CFC_BV_20_C
+ TBD
+
+TC_L2CAP_LE_CFC_BV_21_C
+ gattc_connect_over_le
+ gattc_socket_conn_begin_connect_thread_psm 1 0 1
+ [PTS Interaction] Verify values
+ gattc_disconnect
+
diff --git a/acts/tests/google/bt/pts/instructions/RFCOMM_PTS_INSTRUCTIONS b/acts/tests/google/bt/pts/instructions/RFCOMM_PTS_INSTRUCTIONS
index a977a26..29f5d95 100644
--- a/acts/tests/google/bt/pts/instructions/RFCOMM_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/RFCOMM_PTS_INSTRUCTIONS
@@ -16,83 +16,83 @@
=================================================================
TC_RFCOMM_DEVA_RFC_BV_01_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
rfcomm_connect 00001101-0000-1000-8000-00805F9B34FB
TC_RFCOMM_DEVA_RFC_BV_05_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
rfcomm_connect 00001101-0000-1000-8000-00805F9B34FB
TC_RFCOMM_DEVB_RFC_BV_02_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
TC_RFCOMM_DEVB_RFC_BV_06_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
TC_RFCOMM_DEVA-DEVB_RFC_BV_03_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
TC_RFCOMM_DEVA-DEVB_RFC_BV_04_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
[Wait for PTS prompt]
bta_disable
bta_enable
TC_RFCOMM_DEVA-DEVB_RFC_BV_07_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
[Wait for PTS prompt]
bta_disable
bta_enable
TC_RFCOMM_DEVA-DEVB_RFC_BV_08_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
TC_RFCOMM_DEVA-DEVB_RFC_BV_11_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
TC_RFCOMM_DEVA-DEVB_RFC_BV_13_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
TC_RFCOMM_DEVA-DEVB_RFC_BV_14_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
Wait 30 seconds
TC_RFCOMM_DEVA-DEVB_RFC_BV_15_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
TC_RFCOMM_DEVA-DEVB_RFC_BV_17_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
TC_RFCOMM_DEVA-DEVB_RFC_BV_19_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
TC_RFCOMM_DEVA-DEVB_RFC_BV_21_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
[Wait for PTS prompt]
bta_disable
bta_enable
TC_RFCOMM_DEVA-DEVB_RFC_BV_22_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
[Wait for PTS prompt]
bta_disable
bta_enable
TC_RFCOMM_DEVA-DEVB_RFC_BV_25_C
bta_start_pairing_helper
- Note: If not bonded run: bta_set_scan_mode SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ Note: If not bonded run: bta_set_scan_mode connectable_discoverable
diff --git a/acts/tests/google/bt/pts/instructions/SDP_PTS_INSTRUCTIONS b/acts/tests/google/bt/pts/instructions/SDP_PTS_INSTRUCTIONS
index 18dc31a..752d67a 100644
--- a/acts/tests/google/bt/pts/instructions/SDP_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/SDP_PTS_INSTRUCTIONS
@@ -16,132 +16,132 @@
=================================================================
TC_SR_SA_BI_01_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BI_02_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BI_03_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_01_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_03_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_05_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_08_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_09_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_12_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_13_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_17_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_20_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SA_BV_21_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SS_BI_01_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SS_BI_02_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SS_BV_01_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SS_BV_02_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SS_BV_03_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SS_BV_04_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BI_01_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BI_02_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_01_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_02_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_03_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_04_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_06_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_11_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_12_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_13_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_16_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_17_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_20_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
TC_SR_SSA_BV_23_C
bta_start_pairing_helper
- SCAN_MODE_CONNECTABLE
+ bta_set_scan_mode connectable
diff --git a/acts/tests/google/bt/system_tests/BtStressTest.py b/acts/tests/google/bt/system_tests/BtStressTest.py
index e756c9c..8e87afc 100644
--- a/acts/tests/google/bt/system_tests/BtStressTest.py
+++ b/acts/tests/google/bt/system_tests/BtStressTest.py
@@ -21,6 +21,8 @@
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_constants import bluetooth_off
+from acts.test_utils.bt.bt_constants import bluetooth_on
from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
from acts.test_utils.bt.bt_test_utils import reset_bluetooth
@@ -61,10 +63,24 @@
TAGS: Classic, Stress
Priority: 1
"""
+ dut = self.android_devices[0]
for n in range(self.iterations):
self.log.info("Toggling bluetooth iteration {}.".format(n + 1))
- if not reset_bluetooth([self.android_devices[0]]):
- self.log.error("Failure to reset Bluetooth")
+ dut.ed.clear_all_events()
+ try:
+ dut.droid.bluetoothToggleState(False)
+ dut.ed.pop_event(bluetooth_off, self.default_timeout)
+ except Exception as err:
+ dut.log.error(
+ "Failed to toggle off Bluetooth with error: {}".format(
+ err))
+ return False
+ try:
+ dut.droid.bluetoothToggleState(True)
+ dut.ed.pop_event(bluetooth_on, self.default_timeout)
+ except Exception as err:
+ dut.log.error(
+ "Failed to toggle on Bluetooth with error: {}".format(err))
return False
return True
@@ -115,7 +131,7 @@
time.sleep(2)
bonded_devices = ad.droid.bluetoothGetBondedDevices()
if len(bonded_devices) > 0:
- self.log.error("Failed to unbond devices: {}".format(
- bonded_devices))
+ self.log.error(
+ "Failed to unbond devices: {}".format(bonded_devices))
return False
return True
diff --git a/acts/tests/google/coex/functionality_tests/CoexBasicFunctionalityTest.py b/acts/tests/google/coex/functionality_tests/CoexBasicFunctionalityTest.py
new file mode 100644
index 0000000..87aa828
--- /dev/null
+++ b/acts/tests/google/coex/functionality_tests/CoexBasicFunctionalityTest.py
@@ -0,0 +1,267 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
+from acts.test_utils.coex.coex_test_utils import toggle_bluetooth
+from acts.test_utils.coex.coex_test_utils import start_fping
+
+
+class CoexBasicFunctionalityTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["iterations"]
+ self.unpack_userparams(req_params)
+
+ def toogle_bluetooth_with_iperf(self):
+ """Wrapper function to start iperf traffic and toggling bluetooth."""
+ self.run_iperf_and_get_result()
+ if not toggle_bluetooth(self.pri_ad, self.iterations):
+ return False
+ return self.teardown_result()
+
+ def start_discovery_with_iperf(self):
+ """Wrapper function to starts iperf traffic and bluetooth discovery,
+ gets all the devices discovered, stops discovery.
+ """
+ self.run_iperf_and_get_result()
+ for i in range(self.iterations):
+ self.log.info("Bluetooth inquiry iteration : {}".format(i))
+ if not perform_classic_discovery(self.pri_ad):
+ return False
+ return self.teardown_result()
+
+ def test_toogle_bluetooth_with_tcp_ul(self):
+ """Starts TCP-uplink traffic, when toggling bluetooth.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device when toggling bluetooth.
+
+ Steps:
+ 1. Start TCP-uplink traffic at background.
+ 2. Enable bluetooth.
+ 3. Disable bluetooth.
+ 4. Repeat steps 3 and 4 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_001
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_toogle_bluetooth_with_tcp_dl(self):
+ """Starts TCP-downlink traffic, when toggling bluetooth.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device when toggling bluetooth.
+
+ Steps:
+ 1. Start TCP-downlink traffic at background.
+ 2. Enable bluetooth.
+ 3. Disable bluetooth.
+ 4. Repeat steps 3 and 4 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_002
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_toogle_bluetooth_with_udp_ul(self):
+ """Starts UDP-uplink traffic, when toggling bluetooth.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device when toggling bluetooth.
+
+ Steps:
+ 1. Start UDP-uplink traffic at background.
+ 2. Enable bluetooth.
+ 3. Disable bluetooth.
+ 4. Repeat steps 3 and 4 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_003
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_toogle_bluetooth_with_udp_dl(self):
+ """Starts UDP-downlink traffic, when toggling bluetooth.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device when toggling bluetooth.
+
+ Steps:
+ 1. Start UDP-downlink traffic at background.
+ 2. Enable bluetooth.
+ 3. Disable bluetooth.
+ 4. Repeat steps 3 and 4 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_004
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_bluetooth_discovery_with_tcp_ul(self):
+ """Starts TCP-uplink traffic, along with bluetooth discovery.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device test the functional behaviour of bluetooth discovery.
+
+ Steps:
+ 1. Run TCP-uplink traffic at background.
+ 2. Enable bluetooth
+ 3. Start bluetooth discovery.
+ 4. List all discovered devices.
+ 5. Repeat step 3 and 4 for n iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_005
+ """
+ if not self.start_discovery_with_iperf():
+ return False
+ return True
+
+ def test_bluetooth_discovery_with_tcp_dl(self):
+ """Starts TCP-downlink traffic, along with bluetooth discovery.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device test the functional behaviour of bluetooth discovery.
+
+ Steps:
+ 1. Run TCP-downlink traffic at background.
+ 2. Enable bluetooth
+ 3. Start bluetooth discovery.
+ 4. List all discovered devices.
+ 5. Repeat step 3 and 4 for n iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_006
+ """
+ if not self.start_discovery_with_iperf():
+ return False
+ return True
+
+ def test_bluetooth_discovery_with_udp_ul(self):
+ """Starts UDP-uplink traffic, along with bluetooth discovery.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device test the functional behaviour of bluetooth discovery.
+
+ Steps:
+ 1. Run UDP-uplink traffic at background.
+ 2. Enable bluetooth
+ 3. Start bluetooth discovery.
+ 4. List all discovered devices.
+ 5. Repeat step 3 and 4 for n iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_007
+ """
+ if not self.start_discovery_with_iperf():
+ return False
+ return True
+
+ def test_bluetooth_discovery_with_udp_dl(self):
+ """Starts UDP-downlink traffic, along with bluetooth discovery.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the functional behaviour of bluetooth discovery.
+
+ Steps:
+ 1. Run UDP-downlink traffic at background.
+ 2. Enable bluetooth.
+ 3. Start bluetooth discovery.
+ 4. List all discovered devices.
+ 5. Repeat step 3 and 4 for n iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_008
+ """
+ if not self.start_discovery_with_iperf():
+ return False
+ return True
+
+ def test_toogle_bluetooth_with_fping(self):
+ """Starts fping, while toggling bluetooth.
+
+ This test is to start fping between host machine and android device
+ while toggling bluetooth.
+
+ Steps:
+ 1. Start fping on background.
+ 2. Enable bluetooth.
+ 3. Disable bluetooth.
+ 4. Repeat steps 3 and 4 for n iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_070
+ """
+ args = [lambda: start_fping(self.pri_ad, self.iperf["duration"])]
+ self.run_thread(args)
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return self.teardown_thread()
+
+ def test_bluetooth_discovery_with_fping(self):
+ """Starts fping, along with bluetooth discovery.
+
+ This test is to start fping between host machine and android device
+ and test functional behaviour of bluetooth discovery.
+
+ Steps:
+ 1. Start fping on background.
+ 2. Enable bluetooth.
+ 3. Start bluetooth discovery.
+ 4. Repeat step 3 for n iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_071
+ """
+ args = [lambda: start_fping(self.pri_ad, self.iperf["duration"])]
+ self.run_thread(args)
+ if not self.start_discovery_with_iperf():
+ return False
+ return self.teardown_thread()
diff --git a/acts/tests/google/coex/functionality_tests/CoexBtMultiProfileFunctionalityTest.py b/acts/tests/google/coex/functionality_tests/CoexBtMultiProfileFunctionalityTest.py
new file mode 100644
index 0000000..c5861af
--- /dev/null
+++ b/acts/tests/google/coex/functionality_tests/CoexBtMultiProfileFunctionalityTest.py
@@ -0,0 +1,231 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import connect_ble
+from acts.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
+from acts.test_utils.coex.coex_test_utils import multithread_func
+from acts.test_utils.coex.coex_test_utils import music_play_and_check_via_app
+from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts.test_utils.coex.coex_test_utils import setup_tel_config
+
+
+class CoexBtMultiProfileFunctionalityTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["sim_conf_file", "music_play_time"]
+ self.unpack_userparams(req_params)
+ self.ag_phone_number, self.re_phone_number = setup_tel_config(
+ self.pri_ad, self.sec_ad, self.sim_conf_file)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.audio_receiver.pairing_mode()
+ if not pair_and_connect_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.HEADSET.value]) and
+ set([BtEnum.BluetoothProfile.A2DP.value])):
+ self.log.error("Failed to pair and connect to headset")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.audio_receiver.clean_up()
+
+ def start_media_streaming_initiate_hfp_call_with_iperf(self):
+ """Start media streaming and initiate call from hf to check
+ SCO connection along with iperf.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ self.run_iperf_and_get_result()
+ if not music_play_and_check_via_app(
+ self.pri_ad, self.audio_receiver.mac_address):
+ self.log.error("Failed to stream music file")
+ return False
+ if not initiate_disconnect_from_hf(
+ self.audio_receiver, self.pri_ad, self.sec_ad,
+ self.iperf["duration"]):
+ self.log.error("Failed to initiate/hung up call")
+ return False
+ return self.teardown_result()
+
+ def ble_with_multiprofile_connection(self):
+ """Wrapper function to check ble connection alongwith a2dp streaming
+ and hfp call connection with iperf.
+ """
+ if not connect_ble(self.pri_ad, self.sec_ad):
+ self.log.error("Failed to connect BLE device")
+ return False
+ if not music_play_and_check_via_app(
+ self.pri_ad, self.audio_receiver.mac_address):
+ self.log.error("Failed to stream music file")
+ return False
+ self.run_iperf_and_get_result()
+ tasks = [(initiate_disconnect_from_hf,
+ (self.audio_receiver, self.pri_ad, self.sec_ad,
+ self.iperf["duration"]))]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def test_a2dp_streaming_hfp_call_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with media streaming and HFP call.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of media streaming
+ via A2DP and initiating a call when media streaming is ongoing to
+ check HFP.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 1. Enable bluetooth.
+ 2. Start media streaming to A2DP headset.
+ 4. Initiate a call from headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_066
+ """
+ if not self.start_media_streaming_initiate_hfp_call_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_hfp_call_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with media streaming and HFP call.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of media streaming
+ via A2DP and initiating a call when media streaming is ongoing to
+ check HFP.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 1. Enable bluetooth.
+ 2. Start media streaming to A2DP headset.
+ 4. Initiate a call from headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_067
+ """
+ if not self.start_media_streaming_initiate_hfp_call_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_hfp_call_with_udp_ul(self):
+ """Starts UDP-uplink traffic with media streaming and HFP call.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the functional behaviour of media streaming
+ via A2DP and initiating a call when media streaming is ongoing to
+ check HFP.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 1. Enable bluetooth.
+ 2. Start media streaming to A2DP headset.
+ 4. Initiate a call from headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_068
+ """
+ if not self.start_media_streaming_initiate_hfp_call_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_hfp_call_with_udp_dl(self):
+ """Starts UDP-downlink traffic with media streaming and HFP call.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the functional behaviour of media streaming
+ via A2DP and initiating a call when media streaming is ongoing to
+ check HFP.
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+ 1. Enable bluetooth.
+ 2. Start media streaming to A2DP headset.
+ 4. Initiate a call from headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_069
+ """
+ if not self.start_media_streaming_initiate_hfp_call_with_iperf():
+ return False
+ return True
+
+ def test_ble_connection_a2dp_streaming_hfp_call_with_tcp_ul(self):
+ """Starts TCP-uplink traffic while connecting to BLE device,
+ A2DP streaming and HFP call.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of BLE connection,
+ media streaming via A2DP and HFP call connection.
+
+ Steps:
+ 1. Enable Bluetooth.
+ 2. Connect to BLE device.
+ 3. Start media streaming to A2DP headset.
+ 4. Start TCP-uplink traffic.
+ 5. Initiate HFP call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_082
+ """
+ if not self.ble_with_multiprofile_connection():
+ return True
+ return False
+
+ def test_ble_connection_a2dp_streaming_hfp_call_with_tcp_dl(self):
+ """Starts TCP-downlink traffic while connecting to BLE device,
+ A2DP streaming and HFP call.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of BLE connection,
+ media streaming via A2DP and HFP call connection.
+
+ Steps:
+ 1. Enable Bluetooth.
+ 2. Connect to BLE device.
+ 3. Start media streaming to A2DP headset.
+ 4. Start TCP-uplink traffic.
+ 5. Initiate HFP call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_083.
+ """
+ if not self.ble_with_multiprofile_connection():
+ return True
+ return False
diff --git a/acts/tests/google/coex/functionality_tests/WlanWithA2dpFunctionalityTest.py b/acts/tests/google/coex/functionality_tests/WlanWithA2dpFunctionalityTest.py
new file mode 100644
index 0000000..28361c5
--- /dev/null
+++ b/acts/tests/google/coex/functionality_tests/WlanWithA2dpFunctionalityTest.py
@@ -0,0 +1,819 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import connect_dev_to_headset
+from acts.test_utils.coex.coex_test_utils import connect_ble
+from acts.test_utils.coex.coex_test_utils import disconnect_headset_from_dev
+from acts.test_utils.coex.coex_test_utils import multithread_func
+from acts.test_utils.coex.coex_test_utils import music_play_and_check
+from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
+from acts.test_utils.coex.coex_test_utils import toggle_screen_state
+from acts.test_utils.coex.coex_test_utils import start_fping
+
+BLUETOOTH_WAIT_TIME = 2
+
+
+class WlanWithA2dpFunctionalityTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["iterations"]
+ self.unpack_userparams(req_params)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.audio_receiver.power_on()
+ self.audio_receiver.pairing_mode()
+ if not pair_and_connect_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.A2DP.value])):
+ self.log.error("Failed to pair and connect to headset")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.audio_receiver.clean_up()
+
+ def connect_disconnect_a2dp_headset(self):
+ """Connects and disconnect a2dp profile on headset for multiple
+ iterations.
+
+ Steps:
+ 1.Connect a2dp profile on headset.
+ 2.Disconnect a2dp profile on headset.
+ 3.Repeat step 1 and 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ for i in range(0, self.iterations):
+ self.log.info("A2DP connect/disconnect Iteration {}".format(i))
+ if not connect_dev_to_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.A2DP.value])):
+ self.log.error("Failed to connect headset.")
+ return False
+
+ if not disconnect_headset_from_dev(
+ self.pri_ad, self.audio_receiver.mac_address,
+ [BtEnum.BluetoothProfile.A2DP.value]):
+ self.log.error("Failed to disconnect headset.")
+ return False
+ return True
+
+ def connect_disconnect_headset(self):
+ """Initiates connection to paired headset and disconnects headset.
+
+ Returns:
+ True if successful False otherwise.
+ """
+ for i in range(0, self.iterations):
+ self.pri_ad.droid.bluetoothConnectBonded(
+ self.audio_receiver.mac_address)
+ time.sleep(BLUETOOTH_WAIT_TIME)
+ if not self.pri_ad.droid.bluetoothIsDeviceConnected(
+ self.audio_receiver.mac_address):
+ return False
+ self.pri_ad.droid.bluetoothDisconnectConnected(
+ self.audio_receiver.mac_address)
+ return True
+
+ def perform_classic_discovery_with_iperf(self):
+ """Wrapper function to start iperf traffic and classic discovery"""
+ self.run_iperf_and_get_result()
+ if not perform_classic_discovery(self.pri_ad):
+ return False
+ return self.teardown_result()
+
+ def connect_disconnect_a2dp_headset_with_iperf(self):
+ """Wrapper function to start iperf traffic and connect/disconnect
+ to headset for N iterations.
+ """
+ self.run_iperf_and_get_result()
+ if not self.connect_disconnect_a2dp_headset():
+ return False
+ return self.teardown_result()
+
+ def music_streaming_bluetooth_discovery_with_iperf(self):
+ """Wrapper function to start iperf traffic, music streaming and
+ classic discovery.
+ """
+ self.run_iperf_and_get_result()
+ tasks = [(music_play_and_check,
+ (self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"])),
+ (perform_classic_discovery, (self.pri_ad,))]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def music_streaming_with_iperf(self):
+ """Wrapper function to start iperf traffic and music streaming."""
+ self.run_iperf_and_get_result()
+ if not music_play_and_check(
+ self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"]):
+ return False
+ return self.teardown_result()
+
+ def music_streaming_avrcp_controls_with_iperf(self):
+ """Wrapper function to start iperf traffic, music streaming and avrcp
+ controls.
+ """
+ self.run_iperf_and_get_result()
+ tasks = [(music_play_and_check,
+ (self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"])),
+ (self.avrcp_actions, ())]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def music_streaming_discovery_avrcp_controls_with_iperf(self):
+ """Wrapper function to start iperf traffic, music streaming, bluetooth
+ discovery and avrcp controls.
+ """
+ self.run_iperf_and_get_result()
+ tasks = [(music_play_and_check,
+ (self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"])),
+ (perform_classic_discovery, (self.pri_ad,)),
+ (self.avrcp_actions, ())]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def music_streaming_ble_connection_with_iperf(self):
+ """Wrapper function to start iperf traffic, music streaming and ble
+ connection.
+ """
+ self.run_iperf_and_get_result()
+ tasks = [(music_play_and_check,
+ (self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"])),
+ (connect_ble, (self.pri_ad, self.sec_ad))]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def test_inquiry_after_headset_connection_with_tcp_ul(self):
+ """Starts TCP-uplink traffic, start inquiry after bluetooth connection.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test functional behaviour of bluetooth discovery
+ after connecting to headset.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Start bluetooth discovery when headset is connected.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_009
+ """
+ if not self.perform_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_inquiry_after_headset_connection_with_tcp_dl(self):
+ """Starts TCP-downlink traffic, start inquiry after bluetooth connection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test functional behaviour of bluetooth discovery
+ after connecting to headset.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Start bluetooth discovery when headset is connected.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_010
+ """
+ if not self.perform_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_inquiry_after_headset_connection_with_udp_ul(self):
+ """Starts UDP-uplink traffic, start inquiry after bluetooth connection.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test functional behaviour of bluetooth discovery
+ after connecting to headset.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Start bluetooth discovery when headset is connected.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_011
+ """
+ if not self.perform_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_inquiry_after_headset_connection_with_udp_dl(self):
+ """Starts UDP-downlink traffic, start inquiry after bluetooth connection.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test functional behaviour of bluetooth discovery
+ after connecting to headset.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Start bluetooth discovery when headset is connected.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_012
+ """
+ if not self.perform_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_a2dp_headset_with_tcp_ul(self):
+ """Starts TCP-uplink traffic and connect/disconnect a2dp headset.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to a2dp headset.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Connect and disconnect A2DP headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_013
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_a2dp_headset_with_tcp_dl(self):
+ """Starts TCP-downlink traffic and connect/disconnect a2dp headset.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to a2dp headset.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Connect and disconnect A2DP headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_014
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_a2dp_headset_with_udp_ul(self):
+ """Starts UDP-uplink traffic and connect/disconnect a2dp headset.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to a2dp headset.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Connect and disconnect A2DP headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_015
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_a2dp_headset_with_udp_dl(self):
+ """Starts UDP-downlink traffic and connect/disconnect a2dp headset.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to a2dp headset.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Connect and disconnect A2DP headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_016
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_bluetooth_discovery_with_tcp_ul(self):
+ """Starts TCP-uplink traffic, with music streaming to a2dp headset and
+ bluetooth discovery.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test functional behaviour of a2dp music streaming
+ and bluetooth discovery.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Start bluetooth discovery on android device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_017
+ """
+ if not self.music_streaming_bluetooth_discovery_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_bluetooth_discovery_with_tcp_dl(self):
+ """Starts TCP-downlink traffic, with music streaming to a2dp headset
+ and bluetooth discovery.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test functional behaviour of a2dp music streaming
+ and bluetooth discovery.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Start bluetooth discovery on android device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_018
+ """
+ if not self.music_streaming_bluetooth_discovery_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_bluetooth_discovery_with_udp_ul(self):
+ """Starts UDP-uplink traffic, with music streaming to a2dp headset and
+ bluetooth discovery.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test functional behaviour of a2dp music streaming
+ and bluetooth discovery.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Start bluetooth discovery on android device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_019
+ """
+ if not self.music_streaming_bluetooth_discovery_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_bluetooth_discovery_with_udp_dl(self):
+ """Starts UDP-downlink traffic, with music streaming to a2dp headset
+ and bluetooth discovery.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test functional behaviour of a2dp music streaming
+ and bluetooth discovery.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Start bluetooth discovery on android device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_020
+ """
+ if not self.music_streaming_bluetooth_discovery_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with music streaming to a2dp headset.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_021
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with music streaming to a2dp headset.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_022
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_with_udp_ul(self):
+ """Starts UDP-uplink traffic with music streaming to a2dp headset.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_023
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_with_udp_dl(self):
+ """Starts UDP-downlink traffic with music streaming to a2dp headset.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_024
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_avrcp_controls_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with music streaming and avrcp controls.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming and avrcp controls.
+
+ 1. Run TCP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_025
+ """
+ if not self.music_streaming_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_avrcp_controls_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with music streaming and avrcp controls.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming and avrcp controls.
+
+ 1. Run TCP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_026
+ """
+ if not self.music_streaming_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_avrcp_controls_with_udp_ul(self):
+ """Starts UDP-uplink traffic with music streaming and avrcp controls.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming and avrcp controls.
+
+ 1. Run UDP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_027
+ """
+ if not self.music_streaming_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_avrcp_controls_with_udp_dl(self):
+ """Starts UDP-downlink traffic with music streaming and avrcp controls.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming and avrcp controls.
+
+ 1. Run UDP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_028
+ """
+ if not self.music_streaming_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_avrcp_controls_bluetooth_discovery_tcp_ul(self):
+ """Starts TCP-uplink traffic with music streaming, avrcp controls and
+ bluetooth discovery.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming, avrcp controls and bluetooth discovery.
+
+ 1. Run TCP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+ 4. Start bluetooth discovery.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_029
+ """
+ if not self.music_streaming_discovery_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_avrcp_controls_bluetooth_discovery_tcp_dl(self):
+ """Starts TCP-downlink traffic with music streaming, avrcp controls and
+ bluetooth discovery.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming, avrcp controls and bluetooth discovery.
+
+ 1. Run TCP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+ 4. Start bluetooth discovery.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_030
+ """
+ if not self.music_streaming_discovery_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_avrcp_controls_bluetooth_discovery_udp_ul(self):
+ """Starts UDP-uplink traffic with music streaming, avrcp controls and
+ bluetooth discovery.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming, avrcp controls and bluetooth discovery.
+
+ 1. Run UDP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+ 4. Start bluetooth discovery.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_031
+ """
+ if not self.music_streaming_discovery_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_avrcp_controls_bluetooth_discovery_udp_dl(self):
+ """Starts UDP-downlink traffic with music streaming, avrcp controls and
+ bluetooth discovery.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming, avrcp controls and bluetooth discovery.
+
+ 1. Run UDP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+ 4. Start bluetooth discovery.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_032
+ """
+ if not self.music_streaming_discovery_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_headset_with_fping(self):
+ """Starts fping, along with connection and disconnection of headset.
+
+ This test is to start fping between host machine and android device
+ with connection and disconnection of paired headset.
+
+ Steps:
+ 1. Start fping.
+ 2. Enable bluetooth
+ 3. Connect bluetooth headset.
+ 4. Disconnect bluetooth headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_076
+ """
+ args = [lambda: start_fping(self.pri_ad, self.iperf["duration"])]
+ self.run_thread(args)
+ if not self.connect_disconnect_headset():
+ return False
+ return self.teardown_thread()
+
+ def test_a2dp_streaming_with_fping(self):
+ """Starts fping along with a2dp streaming.
+
+ This test is to start fping between host machine and android device
+ and test the functional behaviour of music streaming to a2dp headset.
+
+ Steps:
+ 1. Start fping.
+ 1. Start media play on android device and check for music streaming.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_077
+ """
+ args = [lambda: start_fping(self.pri_ad, self.iperf["duration"])]
+ self.run_thread(args)
+ if not music_play_and_check(
+ self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"]):
+ return False
+ return self.teardown_thread()
+
+ def test_connect_disconnect_headset_toggle_screen_state_with_fping(self):
+ """Starts fping along with connection and disconnection of the headset.
+
+ This test is to start fping between host machine and android device
+ and test the functional behaviour of connection and disconnection of
+ the paired headset when screen is off and on.
+
+ Steps:
+ 1. Start fping.
+ 2. Connect bluetooth headset.
+ 4. Disconnect bluetooth headset.
+ 5. Screen on/off.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_079
+ """
+ tasks = [(start_fping, (self.pri_ad, self.iperf["duration"])),
+ (self.connect_disconnect_headset, ()),
+ (toggle_screen_state, (self.pri_ad, self.iterations))]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_thread()
+
+ def test_a2dp_streaming_toggle_screen_state_with_fping(self):
+ """Starts fping along with a2dp streaming.
+
+ This test is to start fping with traffic between host machine and
+ android device and test the functional behaviour of a2dp streaming when
+ screen turned on or off.
+
+ Steps:
+ 1. Start fping.
+ 2. Start media play on android device and check for music streaming.
+ 3. Start screen on/off of android device multiple times.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_080
+ """
+ tasks = [(start_fping, (self.pri_ad, self.iperf["duration"])),
+ (music_play_and_check,
+ (self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"])),
+ (toggle_screen_state, (self.pri_ad, self.iterations))]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_thread()
+
+ def test_a2dp_streaming_ble_connection_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with a2dp streaming and ble connection.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of ble connection
+ and a2dp streaming.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 2. Start media play on android device and check for music streaming.
+ 3. Initiate ble connection to android device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_084
+ """
+ if not self.music_streaming_ble_connection_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_ble_connection_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with a2dp streaming and ble connection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of ble connection
+ and a2dp streaming.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 2. Start media play on android device and check for music streaming.
+ 3. Initiate ble connection to android device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_085
+ """
+ if not self.music_streaming_ble_connection_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/functionality_tests/WlanWithHfpFunctionalityTest.py b/acts/tests/google/coex/functionality_tests/WlanWithHfpFunctionalityTest.py
new file mode 100644
index 0000000..983d4c1
--- /dev/null
+++ b/acts/tests/google/coex/functionality_tests/WlanWithHfpFunctionalityTest.py
@@ -0,0 +1,695 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import connect_dev_to_headset
+from acts.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
+from acts.test_utils.coex.coex_test_utils import initiate_disconnect_call_dut
+from acts.test_utils.coex.coex_test_utils import multithread_func
+from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
+from acts.test_utils.coex.coex_test_utils import connect_wlan_profile
+from acts.test_utils.coex.coex_test_utils import toggle_screen_state
+from acts.test_utils.coex.coex_test_utils import setup_tel_config
+from acts.test_utils.coex.coex_test_utils import start_fping
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import initiate_call
+
+BLUETOOTH_WAIT_TIME = 2
+
+
+class WlanWithHfpFunctionalityTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["sim_conf_file"]
+ self.unpack_userparams(req_params)
+ self.ag_phone_number, self.re_phone_number = setup_tel_config(
+ self.pri_ad, self.sec_ad, self.sim_conf_file)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.audio_receiver.pairing_mode()
+ if not pair_and_connect_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.HEADSET.value])):
+ self.log.error("Failed to pair and connect to headset.")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.audio_receiver.clean_up()
+
+ def call_from_sec_ad_to_pri_ad(self):
+ """Initiates the call from secondary device and accepts the call
+ from HF.
+
+ Steps:
+ 1. Initiate call from secondary device to primary device.
+ 2. Accept the call from HF.
+ 3. Hangup the call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not initiate_call(self.log, self.sec_ad, self.ag_phone_number):
+ self.log.error("Failed to initiate call")
+ return False
+ if not self.audio_receiver.accept_call():
+ self.log.error("Failed to answer call from HF.")
+ return False
+ if not hangup_call(self.log, self.pri_ad):
+ self.log.error("Failed to hangup call.")
+ return False
+ return False
+
+ def connect_to_headset_when_turned_off_with_iperf(self):
+ """Wrapper function to start iperf and test connection to headset
+ when it is turned off.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ self.run_iperf_and_get_result()
+ self.audio_receiver.clean_up()
+ if not connect_dev_to_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.HEADSET.value])):
+ self.log.error("Failed to connect to headset.")
+ return True
+ return False
+
+ def check_headset_reconnection_with_iperf(self):
+ """Wrapper function to start iperf and check behaviour of hfp
+ reconnection."""
+ self.run_iperf_and_get_result()
+ self.audio_receiver.clean_up()
+ self.audio_receiver.power_on()
+ if not self.pri_ad.droid.bluetoothIsDeviceConnected(
+ self.audio_receiver.mac_address):
+ self.log.error("Device not found in connected list")
+ return False
+ return self.teardown_result()
+
+ def initiate_call_from_hf_with_iperf(self):
+ """Wrapper function to start iperf and initiate call"""
+ self.run_iperf_and_get_result()
+ if not initiate_disconnect_from_hf(
+ self.audio_receiver, self.pri_ad, self.sec_ad,
+ self.iperf["duration"]):
+ return False
+ return self.teardown_result()
+
+ def initiate_call_from_hf_bt_discovery_with_iperf(self):
+ """Wrapper function to start iperf, initiate call and perform classic
+ discovery.
+ """
+ self.run_iperf_and_get_result()
+ tasks = [(initiate_disconnect_from_hf, (
+ self.audio_receiver, self.pri_ad, self.sec_ad,
+ self.iperf["duration"])),
+ (perform_classic_discovery, (self.pri_ad,))]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def initiate_call_associate_ap_with_iperf(self):
+ """Wrapper function to initiate call from primary device and associate
+ with access point and start iperf traffic."""
+ args = [
+ lambda: initiate_disconnect_call_dut(
+ self.pri_ad, self.sec_ad, self.iperf["duration"],
+ self.re_phone_number)
+ ]
+ self.run_thread(args)
+ if not connect_wlan_profile(self.pri_ad, self.network):
+ return False
+ self.run_iperf_and_get_result()
+ return self.teardown_result()
+
+ def test_hfp_call_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with hfp connection.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ and call.
+
+ Steps:.
+ 1. Start TCP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_042
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with hfp connection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ and call.
+
+ Steps:.
+ 1. Start TCP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_043
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_with_udp_ul(self):
+ """Starts UDP-uplink traffic with hfp connection.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ and call.
+
+ Steps:.
+ 1. Start UDP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_044
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_with_udp_dl(self):
+ """Starts UDP-downlink traffic with hfp connection.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ and call.
+
+ Steps:.
+ 1. Start UDP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_045
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_bluetooth_discovery_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with hfp connection and bluetooth
+ discovery.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ and call and bluetooth discovery.
+
+ Steps:.
+ 1. Start TCP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+ 3. Start bluetooth discovery.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_046
+ """
+ if not self.initiate_call_from_hf_bt_discovery_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_bluetooth_discovery_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with hfp connection and bluetooth
+ discovery.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ and call and bluetooth discovery.
+
+ Steps:.
+ 1. Start TCP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+ 3. Start bluetooth discovery.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_047
+ """
+ if not self.initiate_call_from_hf_bt_discovery_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_bluetooth_discovery_with_udp_ul(self):
+ """Starts UDP-uplink traffic with hfp connection and bluetooth
+ discovery.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ and call and bluetooth discovery.
+
+ Steps:.
+ 1. Start UDP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+ 3. Start bluetooth discovery.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_048
+ """
+ if not self.initiate_call_from_hf_bt_discovery_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_bluetooth_discovery_with_udp_dl(self):
+ """Starts UDP-downlink traffic with hfp connection and bluetooth
+ discovery.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ and call and bluetooth discovery.
+
+ Steps:.
+ 1. Start UDP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+ 3. Start bluetooth discovery.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_049
+ """
+ if not self.initiate_call_from_hf_bt_discovery_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_and_associate_ap_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with hfp call.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test functional behaviour of hfp call connection
+ while associating with AP.
+
+ Steps:
+ 1. Initiate call from HF and disconnect call from primary device.
+ 2. Associate with AP.
+ 3. Start TCP-uplink traffic.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_050
+ """
+ if not self.initiate_call_associate_ap_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_and_associate_ap_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with hfp call.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test functional behaviour of hfp call connection
+ while associating with AP.
+
+ Steps:
+ 1. Initiate call from HF and disconnect call from primary device.
+ 2. Associate with AP.
+ 3. Start TCP-downlink traffic.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_051
+ """
+ if not self.initiate_call_associate_ap_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_and_associate_ap_with_udp_ul(self):
+ """Starts UDP-uplink traffic with hfp call.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test functional behaviour of hfp call connection
+ while associating with AP.
+
+ Steps:
+ 1. Initiate call from HF and disconnect call from primary device.
+ 2. Associate with AP.
+ 3. Start UDP-uplink traffic.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_052
+ """
+ if not self.initiate_call_associate_ap_with_iperf():
+ return False
+ return True
+
+ def test_hfp_call_and_associate_ap_with_udp_dl(self):
+ """Starts UDP-downlink traffic with hfp call.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test functional behaviour of hfp call connection
+ while associating with AP.
+
+ Steps:
+ 1. Initiate call from HF and disconnect call from primary device.
+ 2. Associate with AP.
+ 3. Start UDP-downlink traffic.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_053
+ """
+ if not self.initiate_call_associate_ap_with_iperf():
+ return False
+ return True
+
+ def test_hfp_redial_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with hfp connection.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device with hfp connection.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 2. Initiate call from HF(last dialed number) and disconnect call
+ from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_054
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_hfp_redial_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with hfp connection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device with hfp connection.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 2. Initiate call from HF(last dialed number) and disconnect call
+ from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_055
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_hfp_redial_with_udp_ul(self):
+ """Starts UDP-uplink traffic with hfp connection.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device with hfp connection.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 2. Initiate call from HF(last dialed number) and disconnect call
+ from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_056
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_hfp_redial_with_udp_dl(self):
+ """Starts UDP-downlink traffic with hfp connection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device with hfp connection.
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+ 2. Initiate call from HF(last dialed number) and disconnect call
+ from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_057
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_hfp_reconnection_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with hfp reconnection.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of hfp reconnection.
+
+ Steps:.
+ 1. Start TCP-uplink traffic.
+ 2. Connect HF to DUT.
+ 3. Disconnect HF from DUT.
+ 4. Switch off the headset and turn ON HF to reconnect.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_062
+ """
+ if not self.check_headset_reconnection_with_iperf():
+ return False
+ return True
+
+ def test_hfp_reconnection_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with hfp reconnection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of hfp reconnection.
+
+ Steps:.
+ 1. Start TCP-downlink traffic.
+ 2. Connect HF to DUT.
+ 3. Disconnect HF from DUT.
+ 4. Switch off the headset and turn ON HF to reconnect.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_063
+ """
+ if not self.check_headset_reconnection_with_iperf():
+ return False
+ return True
+
+ def test_hfp_reconnection_with_udp_ul(self):
+ """Starts UDP-uplink traffic with hfp reconnection.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the functional behaviour of hfp reconnection.
+
+ Steps:.
+ 1. Start UDP-uplink traffic.
+ 2. Connect HF to DUT.
+ 3. Disconnect HF from DUT.
+ 4. Switch off the headset and turn ON HF to reconnect.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_064
+ """
+ if not self.check_headset_reconnection_with_iperf():
+ return False
+ return True
+
+ def test_hfp_reconnection_with_udp_dl(self):
+ """Starts UDP-downlink traffic with hfp reconnection.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the functional behaviour of hfp reconnection.
+
+ Steps:.
+ 1. Start UDP-downlink traffic.
+ 2. Connect HF to DUT.
+ 3. Disconnect HF from DUT.
+ 4. Switch off the headset and turn ON HF to reconnect.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_065
+ """
+ if not self.check_headset_reconnection_with_iperf():
+ return False
+ return True
+
+ def test_hfp_connection_when_hf_turned_off_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with hfp connection.
+
+ This test is to start TCP-Uplink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ when device is off.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 2. Make sure headset is turned off.
+ 3. Initiate hfp connection to headset from DUT.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_072
+ """
+ if not self.connect_to_headset_when_turned_off_with_iperf():
+ return False
+ return self.teardown_result()
+
+ def test_hfp_connection_when_hf_turned_off_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with hfp connection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ when device is off.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 2. Make sure headset is turned off.
+ 3. Initiate hfp connection to headset from DUT.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_073
+ """
+ if not self.connect_to_headset_when_turned_off_with_iperf():
+ return False
+ return self.teardown_result()
+
+ def test_hfp_connection_when_hf_turned_off_with_udp_ul(self):
+ """Starts UDP-uplink traffic with hfp connection.
+
+ This test is to start UDP-Uplink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ when device is off.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 2. Make sure headset is turned off.
+ 3. Initiate hfp connection to headset from DUT.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_074
+ """
+ if not self.connect_to_headset_when_turned_off_with_iperf():
+ return False
+ return self.teardown_result()
+
+ def test_hfp_connection_when_hf_turned_off_with_udp_dl(self):
+ """Starts UDP-downlink traffic with hfp connection.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the functional behaviour of hfp connection
+ when device is off.
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+ 2. Make sure headset is turned off.
+ 3. Initiate hfp connection to headset from DUT.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_075
+ """
+ if not self.connect_to_headset_when_turned_off_with_iperf():
+ return False
+ return self.teardown_result()
+
+ def test_hfp_call_with_fping(self):
+ """Starts fping with hfp call connection.
+
+ This test is to start fping between host machine and android device
+ and test the functional behaviour of hfp call.
+
+ Steps:
+ 1. Start fping from AP backend to android device.
+ 1. Initiate call from headset to secondary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_078
+ """
+ args = [lambda: start_fping(self.pri_ad, self.iperf["duration"])]
+ self.run_thread(args)
+ if not initiate_disconnect_from_hf(
+ self.audio_receiver,self.pri_ad, self.sec_ad,
+ self.iperf["duration"]):
+ return False
+ return self.teardown_thread()
+
+ def test_hfp_call_toggle_screen_state_with_fping(self):
+ """Starts fping with hfp call connection.
+
+ This test is to start fping between host machine and android device
+ and test the functional behaviour of hfp call when toggling the
+ screen state.
+
+ Steps:
+ 1. Start fping from AP backend.
+ 1. Initiate call from primary device headset to secondary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_081
+ """
+ tasks = [(start_fping, (self.pri_ad, self.iperf["duration"])),
+ (initiate_disconnect_from_hf, (
+ self.audio_receiver, self.pri_ad, self.sec_ad,
+ self.iperf["duration"])),
+ (toggle_screen_state, (self.pri_ad, self.iterations))]
+ if not multithread_func(self.log, tasks):
+ return False
+ return True
diff --git a/acts/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py b/acts/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py
new file mode 100644
index 0000000..98603d2
--- /dev/null
+++ b/acts/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py
@@ -0,0 +1,164 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
+
+
+class CoexBasicPerformanceTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def run_iperf_and_perform_discovery(self):
+ """Starts iperf client on host machine and bluetooth discovery
+ simultaneously.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ self.run_iperf_and_get_result()
+ if not perform_classic_discovery(self.pri_ad):
+ return False
+ return self.teardown_result()
+
+ def test_performance_with_bt_on_tcp_ul(self):
+ """Check throughput when bluetooth on.
+
+ This test is to start TCP-Uplink traffic between host machine and
+ android device and check the throughput when bluetooth is on.
+
+ Steps:
+ 1. Start TCP-uplink traffic when bluetooth is on.
+
+ Test Id: Bt_CoEx_kpi_005
+ """
+ self.run_iperf_and_get_result()
+ self.teardown_result()
+
+ def test_performance_with_bt_on_tcp_dl(self):
+ """Check throughput when bluetooth on.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and check the throughput when bluetooth is on.
+
+ Steps:
+ 1. Start TCP-downlink traffic when bluetooth is on.
+
+ Test Id: Bt_CoEx_kpi_006
+ """
+ self.run_iperf_and_get_result()
+ self.teardown_result()
+
+ def test_performance_with_bt_on_udp_ul(self):
+ """Check throughput when bluetooth on.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and check the throughput when bluetooth is on.
+
+ Steps:
+ 1. Start UDP-uplink traffic when bluetooth is on.
+
+ Test Id: Bt_CoEx_kpi_007
+ """
+ self.run_iperf_and_get_result()
+ self.teardown_result()
+
+ def test_performance_with_bt_on_udp_dl(self):
+ """Check throughput when bluetooth on.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and check the throughput when bluetooth is on.
+
+ Steps:
+ 1. Start UDP-downlink traffic when bluetooth is on.
+
+ Test Id: Bt_CoEx_kpi_008
+ """
+ self.run_iperf_and_get_result()
+ self.teardown_result()
+
+ def test_performance_with_bluetooth_discovery_tcp_ul(self):
+ """Check throughput when bluetooth discovery is ongoing.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and bluetooth discovery and checks throughput.
+
+ Steps:
+ 1. Start TCP-uplink traffic and bluetooth discovery parallelly.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_kpi_009
+ """
+ if not self.run_iperf_and_perform_discovery():
+ return False
+ return True
+
+ def test_performance_with_bluetooth_discovery_tcp_dl(self):
+ """Check throughput when bluetooth discovery is ongoing.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and bluetooth discovery and checks throughput.
+
+ Steps:
+ 1. Start TCP-downlink traffic and bluetooth discovery parallelly.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_kpi_010
+ """
+ if not self.run_iperf_and_perform_discovery():
+ return False
+ return True
+
+ def test_performance_with_bluetooth_discovery_udp_ul(self):
+ """Check throughput when bluetooth discovery is ongoing.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and bluetooth discovery and checks throughput.
+
+ Steps:
+ 1. Start UDP-uplink traffic and bluetooth discovery parallelly.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_kpi_011
+ """
+ if not self.run_iperf_and_perform_discovery():
+ return False
+ return True
+
+ def test_performance_with_bluetooth_discovery_udp_dl(self):
+ """Check throughput when bluetooth discovery is ongoing.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and bluetooth discovery and checks throughput.
+
+ Steps:
+ 1. Start UDP-downlink traffic and bluetooth discovery parallelly.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_kpi_012
+ """
+ if not self.run_iperf_and_perform_discovery():
+ return False
+ return True
diff --git a/acts/tests/google/coex/performance_tests/CoexBtMultiProfilePerformanceTest.py b/acts/tests/google/coex/performance_tests/CoexBtMultiProfilePerformanceTest.py
new file mode 100644
index 0000000..7616b7a
--- /dev/null
+++ b/acts/tests/google/coex/performance_tests/CoexBtMultiProfilePerformanceTest.py
@@ -0,0 +1,197 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.car.tel_telecom_utils import wait_for_dialing
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import connect_dev_to_headset
+from acts.test_utils.coex.coex_test_utils import music_play_and_check_via_app
+from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts.test_utils.coex.coex_test_utils import setup_tel_config
+from acts.test_utils.coex.coex_test_utils import connect_wlan_profile
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+
+
+class CoexBtMultiProfilePerformanceTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["sim_conf_file", "music_play_time"]
+ self.unpack_userparams(req_params)
+ self.ag_phone_number, self.re_phone_number = setup_tel_config(
+ self.pri_ad, self.sec_ad, self.sim_conf_file)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.audio_receiver.pairing_mode()
+ if not pair_and_connect_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.HEADSET.value]) and
+ set([BtEnum.BluetoothProfile.A2DP.value])):
+ self.log.error("Failed to pair and connect to headset")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.audio_receiver.clean_up()
+
+ def initiate_call_when_a2dp_streaming_on(self):
+ """Initiates HFP call, then check for call is present or not.
+
+ Disconnect a2dp profile and then connect HFP profile and
+ answer the call from reference device.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not initiate_call(self.log, self.pri_ad, self.re_phone_number):
+ self.log.error("Failed to initiate call")
+ return False
+ if wait_for_dialing(self.log, self.pri_ad):
+ self.pri_ad.droid.bluetoothDisconnectConnectedProfile(
+ self.audio_receiver.mac_address,
+ [BtEnum.BluetoothProfile.A2DP.value])
+ if not connect_dev_to_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ [BtEnum.BluetoothProfile.HEADSET.value]):
+ return False
+ if not wait_and_answer_call(self.log, self.sec_ad):
+ self.log.error("Failed to answer call in second device")
+ return False
+ time.sleep(self.iperf["duration"])
+ if not hangup_call(self.log, self.pri_ad):
+ self.log.error("Failed to hangup call")
+ return False
+ return True
+
+ def play_music_and_connect_wifi(self):
+ """Perform a2dp music streaming and scan and connect to wifi
+ network
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not music_play_and_check_via_app(
+ self.pri_ad, self.audio_receiver.mac_address):
+ self.log.error("Failed to stream music file")
+ return False
+ if not connect_wlan_profile(self.pri_ad, self.network):
+ return False
+ return True
+
+ def initiate_call_when_a2dp_streaming_with_iperf(self):
+ """Wrapper function to initiate call when a2dp streaming and starts
+ iperf.
+ """
+ if not self.play_music_and_connect_wifi():
+ return False
+ self.run_iperf_and_get_result()
+ if not self.initiate_call_when_a2dp_streaming_on():
+ return False
+ return self.teardown_result()
+
+ def test_performance_a2dp_streaming_hfp_call_tcp_ul(self):
+ """Check performance when a2dp streaming and hfp call..
+
+ This test is to check wifi performance when a2dp streaming and
+ hfp call performed sequentially with TCP-uplink traffic.
+
+ Steps:
+ 1.Enable bluetooth.
+ 2.Start a2dp streaming.
+ 3.Run TCP-uplink traffic.
+ 4.Initiate hfp call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_041
+ """
+ if not self.initiate_call_when_a2dp_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_hfp_call_tcp_dl(self):
+ """Check performance when a2dp streaming and hfp call..
+
+ This test is to check wifi performance when a2dp streaming and
+ hfp call performed sequentially with TCP-downlink traffic.
+
+ Steps:
+ 1.Enable bluetooth.
+ 2.Start a2dp streaming.
+ 3.Run TCP-downlink traffic.
+ 4.Initiate hfp call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_042
+ """
+ if not self.initiate_call_when_a2dp_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_hfp_call_udp_ul(self):
+ """Check performance when a2dp streaming and hfp call..
+
+ This test is to check wifi performance when a2dp streaming and
+ hfp call performed sequentially with UDP-uplink traffic.
+
+ Steps:
+ 1.Enable bluetooth.
+ 2.Start a2dp streaming.
+ 3.Run UDP-uplink traffic.
+ 4.Initiate hfp call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_043
+ """
+ if not self.initiate_call_when_a2dp_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_hfp_call_udp_dl(self):
+ """Check performance when a2dp streaming and hfp call..
+
+ This test is to check wifi performance when a2dp streaming and
+ hfp call performed sequentially with UDP-uplink traffic.
+
+ Steps:
+ 1.Enable bluetooth.
+ 2.Start a2dp streaming.
+ 3.Run UDP-uplink traffic.
+ 4.Initiate hfp call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_044
+ """
+ if not self.initiate_call_when_a2dp_streaming_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/performance_tests/WlanStandalonePerformanceTest.py b/acts/tests/google/coex/performance_tests/WlanStandalonePerformanceTest.py
new file mode 100644
index 0000000..c962efd
--- /dev/null
+++ b/acts/tests/google/coex/performance_tests/WlanStandalonePerformanceTest.py
@@ -0,0 +1,86 @@
+#/usr/bin/env python3.4
+#
+# 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.
+
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.bt.bt_test_utils import disable_bluetooth
+
+
+class WlanStandalonePerformanceTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ if not disable_bluetooth(self.pri_ad.droid):
+ self.log.info("Failed to disable bluetooth")
+ return False
+
+ def test_performance_wlan_standalone_tcp_ul(self):
+ """Check throughout for wlan standalone.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device for wlan-standalone.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+
+ Test Id: Bt_CoEx_kpi_001
+ """
+ self.run_iperf_and_get_result()
+ return self.teardown_result()
+
+ def test_performance_wlan_standalone_tcp_dl(self):
+ """Check throughout for wlan standalone.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device for wlan-standalone.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+
+ Test Id: Bt_CoEx_kpi_002
+ """
+ self.run_iperf_and_get_result()
+ return self.teardown_result()
+
+ def test_performance_wlan_standalone_udp_ul(self):
+ """Check throughout for wlan standalone.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device for wlan-standalone.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+
+ Test Id: Bt_CoEx_kpi_003
+ """
+ self.run_iperf_and_get_result()
+ return self.teardown_result()
+
+ def test_performance_wlan_standalone_udp_dl(self):
+ """Check throughout for wlan standalone.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device for wlan-standalone.
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+
+ Test Id: Bt_CoEx_kpi_004
+ """
+ self.run_iperf_and_get_result()
+ return self.teardown_result()
diff --git a/acts/tests/google/coex/performance_tests/WlanWithA2dpPerformanceTest.py b/acts/tests/google/coex/performance_tests/WlanWithA2dpPerformanceTest.py
new file mode 100644
index 0000000..99e8361
--- /dev/null
+++ b/acts/tests/google/coex/performance_tests/WlanWithA2dpPerformanceTest.py
@@ -0,0 +1,315 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import music_play_and_check
+from acts.test_utils.coex.coex_test_utils import multithread_func
+from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
+
+
+class WlanWithA2dpPerformanceTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+ self.tests = ("test_performance_a2dp_streaming_tcp_ul",)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.audio_receiver.pairing_mode()
+ if not pair_and_connect_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.A2DP.value])):
+ self.log.error("Failed to pair and connect to headset")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.audio_receiver.clean_up()
+
+ def initiate_music_streaming_to_headset_with_iperf(self):
+ """Initiate music streaming to headset and start iperf traffic."""
+ self.run_iperf_and_get_result()
+ if not music_play_and_check(
+ self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"]):
+ return False
+ return self.teardown_result()
+
+ def perform_discovery_with_iperf(self):
+ """Starts iperf traffic based on test and perform bluetooth classic
+ discovery.
+ """
+ self.run_iperf_and_get_result()
+ if not perform_classic_discovery(self.pri_ad):
+ return False
+ return self.teardown_result()
+
+ def music_streaming_and_avrcp_controls_with_iperf(self):
+ """Starts iperf traffic based on test and initiate music streaming and
+ check for avrcp controls.
+ """
+ self.run_iperf_and_get_result()
+ tasks = [(music_play_and_check,
+ (self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"])),
+ (self.avrcp_actions, ())]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def test_performance_a2dp_streaming_tcp_ul(self):
+ """Performance test to check throughput when streaming music.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the performance when music streamed to a2dp
+ headset.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 2. Start music streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_013
+ """
+ if not self.initiate_music_streaming_to_headset_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_tcp_dl(self):
+ """Performance test to check throughput when streaming music.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the performance when music streamed to a2dp
+ headset.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 2. Start music streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_014
+ """
+ if not self.initiate_music_streaming_to_headset_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_udp_ul(self):
+ """Performance test to check throughput when streaming music.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the performance when music streamed to a2dp
+ headset.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 2. Start music streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_015
+ """
+ if not self.initiate_music_streaming_to_headset_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_udp_dl(self):
+ """Performance test to check throughput when streaming music.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the performance when music streamed to a2dp
+ headset.
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+ 2. Start music streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_016
+ """
+ if not self.initiate_music_streaming_to_headset_with_iperf():
+ return False
+ return True
+
+ def test_performance_inquiry_after_headset_connection_with_tcp_ul(self):
+ """Performance test to check throughput when bluetooth discovery.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the performance when bluetooth discovery is
+ performed after connecting to headset.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Start bluetooth discovery when headset is connected.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_029
+ """
+ if not self.perform_discovery_with_iperf():
+ return False
+ return True
+
+ def test_performance_inquiry_after_headset_connection_with_tcp_dl(self):
+ """Performance test to check throughput when bluetooth discovery.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the performance when bluetooth discovery is
+ performed after connecting to headset.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Start bluetooth discovery when headset is connected.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_030
+ """
+ if not self.perform_discovery_with_iperf():
+ return False
+ return True
+
+ def test_performance_inquiry_after_headset_connection_with_udp_ul(self):
+ """Performance test to check throughput when bluetooth discovery.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the performance when bluetooth discovery is
+ performed after connecting to headset.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Start bluetooth discovery when headset is connected.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_031
+ """
+ if not self.perform_discovery_with_iperf():
+ return False
+ return True
+
+ def test_performance_inquiry_after_headset_connection_with_udp_dl(self):
+ """Performance test to check throughput when bluetooth discovery.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the performance when bluetooth discovery is
+ performed after connecting to headset.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Start bluetooth discovery when headset is connected.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_032
+ """
+ if not self.perform_discovery_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_avrcp_controls_with_tcp_ul(self):
+ """Performance test to check throughput when music streaming.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the wlan throughput when perfroming a2dp music
+ streaming and avrcp controls.
+
+ 1. Start TCP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_033
+ """
+ if not self.music_streaming_and_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_avrcp_controls_with_tcp_dl(self):
+ """Performance test to check throughput when music streaming.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the wlan throughput when perfroming a2dp music
+ streaming and avrcp controls.
+
+ 1. Start TCP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_034
+ """
+ if not self.music_streaming_and_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_avrcp_controls_with_udp_ul(self):
+ """Performance test to check throughput when music streaming.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the wlan throughput when perfroming a2dp music
+ streaming and avrcp controls.
+
+ 1. Start UDP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_035
+ """
+ if not self.music_streaming_and_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_avrcp_controls_with_udp_dl(self):
+ """Performance test to check throughput when music streaming.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the wlan throughput when perfroming a2dp music
+ streaming and avrcp controls.
+
+ 1. Start UDP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_036
+ """
+ if not self.music_streaming_and_avrcp_controls_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/performance_tests/WlanWithBlePerformanceTest.py b/acts/tests/google/coex/performance_tests/WlanWithBlePerformanceTest.py
new file mode 100644
index 0000000..12dfc13
--- /dev/null
+++ b/acts/tests/google/coex/performance_tests/WlanWithBlePerformanceTest.py
@@ -0,0 +1,300 @@
+#/usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt.bt_gatt_utils import close_gatt_client
+from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
+from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
+from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+
+
+class WlanWithBlePerformanceTest(CoexBaseTest):
+ default_timeout = 10
+ adv_instances = []
+ bluetooth_gatt_list = []
+ gatt_server_list = []
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.pri_ad.droid.bluetoothDisableBLE()
+ self.gatt_server_list = []
+ self.adv_instances = []
+
+ def teardown_test(self):
+ CoexBaseTest.teardown_test(self)
+ for bluetooth_gatt in self.bluetooth_gatt_list:
+ self.pri_ad.droid.gattClientClose(bluetooth_gatt)
+ for gatt_server in self.gatt_server_list:
+ self.sec_ad.droid.gattServerClose(gatt_server)
+ for adv in self.adv_instances:
+ self.sec_ad.droid.bleStopBleAdvertising(adv)
+ return True
+
+ def _orchestrate_gatt_disconnection(self, bluetooth_gatt, gatt_callback):
+ """Disconnect gatt connection between two devices.
+
+ Args:
+ bluetooth_gatt: Index of the BluetoothGatt object
+ gatt_callback: Index of gatt callback object.
+
+ Steps:
+ 1. Disconnect gatt connection.
+ 2. Close bluetooth gatt object.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ self.log.info("Disconnecting from peripheral device.")
+ try:
+ disconnect_gatt_connection(self.pri_ad, bluetooth_gatt,
+ gatt_callback)
+ close_gatt_client(self.pri_ad, bluetooth_gatt)
+ if bluetooth_gatt in self.bluetooth_gatt_list:
+ self.bluetooth_gatt_list.remove(bluetooth_gatt)
+ except GattTestUtilsError as err:
+ self.log.error(err)
+ return False
+ return True
+
+ def ble_start_stop_scan(self):
+ """Convenience method to start BLE scan and stop BLE scan.
+
+ Steps:
+ 1. Enable ble.
+ 2. Create LE scan objects.
+ 3. Start scan.
+ 4. Stop scan.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ self.pri_ad.droid.bluetoothEnableBLE()
+ filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+ self.pri_ad.droid)
+ self.pri_ad.droid.bleStartBleScan(filter_list, scan_settings,
+ scan_callback)
+ time.sleep(self.iperf["duration"])
+ try:
+ self.pri_ad.droid.bleStopBleScan(scan_callback)
+ except Exception as err:
+ self.log.error(str(err))
+ return False
+ return True
+
+ def initiate_ble_gatt_connection(self):
+ """Creates gatt connection and disconnect gatt connection.
+
+ Steps:
+ 1. Initializes gatt objects.
+ 2. Start a generic advertisement.
+ 3. Start a generic scanner.
+ 4. Find the advertisement and extract the mac address.
+ 5. Stop the first scanner.
+ 6. Create a GATT connection between the scanner and advertiser.
+ 7. Disconnect the GATT connection.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ self.pri_ad.droid.bluetoothEnableBLE()
+ gatt_server_cb = self.sec_ad.droid.gattServerCreateGattServerCallback()
+ gatt_server = self.sec_ad.droid.gattServerOpenGattServer(gatt_server_cb)
+ self.gatt_server_list.append(gatt_server)
+ try:
+ bluetooth_gatt, gatt_callback, adv_callback = (
+ orchestrate_gatt_connection(self.pri_ad, self.sec_ad))
+ self.bluetooth_gatt_list.append(bluetooth_gatt)
+ time.sleep(self.iperf["duration"])
+ except GattTestUtilsError as err:
+ self.log.error(err)
+ return False
+ self.adv_instances.append(adv_callback)
+ return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+ gatt_callback)
+
+ def ble_start_stop_scan_with_iperf(self):
+ self.run_iperf_and_get_result()
+ if not self.ble_start_stop_scan():
+ return False
+ return self.teardown_result()
+
+ def ble_gatt_connection_with_iperf(self):
+ self.run_iperf_and_get_result()
+ if not self.initiate_ble_gatt_connection():
+ return False
+ return self.teardown_result()
+
+ def test_performance_ble_scan_tcp_ul(self):
+ """Test performance with ble scan.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the wlan throughput when performing ble scan.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 2. Start and stop BLE scan.
+
+ Returns:
+ True if pass, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_021
+ """
+ if not self.ble_start_stop_scan_with_iperf():
+ return False
+ return True
+
+ def test_performance_ble_scan_tcp_dl(self):
+ """Test performance with ble scan.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the wlan throughput when performing ble scan.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 2. Start and stop BLE scan.
+
+ Returns:
+ True if pass, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_022
+ """
+ if not self.ble_start_stop_scan_with_iperf():
+ return False
+ return True
+
+ def test_performance_ble_scan_udp_ul(self):
+ """Test performance with ble scan.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the wlan throughput when performing ble scan.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 2. Start and stop BLE scan.
+
+ Returns:
+ True if pass, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_023
+ """
+ if not self.ble_start_stop_scan_with_iperf():
+ return False
+ return True
+
+ def test_performance_ble_scan_udp_dl(self):
+ """Test performance with ble scan.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the wlan throughput when performing ble scan.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 2. Start and stop BLE scan.
+
+ Returns:
+ True if pass, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_024
+ """
+ if not self.ble_start_stop_scan_with_iperf():
+ return False
+ return True
+
+ def test_performance_ble_connect_tcp_ul(self):
+ """Test performance with ble gatt connection.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the wlan throughput when ble gatt connection
+ is established.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 2. Initiate gatt connection.
+
+ Returns:
+ True if pass, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_025
+ """
+ if not self.ble_gatt_connection_with_iperf():
+ return False
+ return True
+
+ def test_performance_ble_connect_tcp_dl(self):
+ """Test performance with ble gatt connection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the wlan throughput when ble gatt connection
+ is established.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 2. Initiate gatt connection.
+
+ Returns:
+ True if pass, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_026
+ """
+ if not self.ble_gatt_connection_with_iperf():
+ return False
+ return True
+
+ def test_performance_ble_connect_udp_ul(self):
+ """Test performance with ble gatt connection.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the wlan throughput when ble gatt connection
+ is established.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 2. Initiate gatt connection.
+
+ Returns:
+ True if pass, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_027
+ """
+ if not self.ble_gatt_connection_with_iperf():
+ return False
+ return True
+
+ def test_performance_ble_connect_udp_dl(self):
+ """Test performance with ble gatt connection.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the wlan throughput when ble gatt connection
+ is established.
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+ 2. Initiate gatt connection.
+
+ Returns:
+ True if pass, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_028
+ """
+ if not self.ble_gatt_connection_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/performance_tests/WlanWithHfpPerformanceTest.py b/acts/tests/google/coex/performance_tests/WlanWithHfpPerformanceTest.py
new file mode 100644
index 0000000..781f272
--- /dev/null
+++ b/acts/tests/google/coex/performance_tests/WlanWithHfpPerformanceTest.py
@@ -0,0 +1,259 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import multithread_func
+from acts.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
+from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts.test_utils.coex.coex_test_utils import setup_tel_config
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import initiate_call
+
+
+class WlanWithHfpPerformanceTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["sim_conf_file"]
+ self.unpack_userparams(req_params)
+ self.ag_phone_number, self.re_phone_number = setup_tel_config(
+ self.pri_ad, self.sec_ad, self.sim_conf_file)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.audio_receiver.pairing_mode()
+ if not pair_and_connect_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.HEADSET.value])):
+ self.log.error("Failed to pair and connect to headset")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.audio_receiver.clean_up()
+
+ def call_from_sec_ad_to_pri_ad(self):
+ """Initiates the call from secondary device and accepts the call
+ from HF connected to primary device.
+
+ Steps:
+ 1. Initiate call from secondary device to primary device.
+ 2. Accept the call from HF.
+ 3. Hangup the call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not initiate_call(self.log, self.sec_ad, self.ag_phone_number):
+ self.log.error("Failed to initiate call")
+ return False
+ time.sleep(5) # Wait until initiate call.
+ if not self.audio_receiver.accept_call():
+ self.log.error("Failed to answer call from HF.")
+ return False
+ time.sleep(self.iperf["duration"])
+ if not hangup_call(self.log, self.pri_ad):
+ self.log.error("Failed to hangup call.")
+ return False
+ return False
+
+ def initiate_call_from_hf_with_iperf(self):
+ """Wrapper function to start iperf and initiate call."""
+ self.run_iperf_and_get_result()
+ if not initiate_disconnect_from_hf(
+ self.audio_receiver, self.pri_ad, self.sec_ad,
+ self.iperf["duration"]):
+ return False
+ return self.teardown_result()
+
+ def initiate_call_and_change_volume_with_iperf(self):
+ """Wrapper function to start iperf and initiate call and check avrcp
+ controls.
+ """
+ self.run_iperf_and_get_result()
+ tasks = [(self.call_from_sec_ad_to_pri_ad, ()),
+ (self.change_volume, ())]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def test_performance_hfp_call_tcp_ul(self):
+ """Test performance with hfp connection.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and check throughput when hfp connection
+ and call is active.
+
+ Steps:.
+ 1. Start TCP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_017
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_tcp_dl(self):
+ """Test performance with hfp connection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and check throughput when hfp connection
+ and call is active.
+
+ Steps:.
+ 1. Start TCP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_018
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_udp_ul(self):
+ """Test performance with hfp connection.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and check throughput when hfp connection
+ and call is active.
+
+ Steps:.
+ 1. Start UDP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_019
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_udp_dl(self):
+ """Test performance with hfp connection.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and check throughput when hfp connection
+ and call is active.
+
+ Steps:.
+ 1. Start UDP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_020
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_volume_check_tcp_ul(self):
+ """Test performance with hfp connection and perform volume actions.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and check throughput when hfp connection and perform
+ volume change when call is active.
+
+ Steps:.
+ 1. Start TCP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_037
+ """
+ if not self.initiate_call_and_change_volume_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_volume_check_tcp_dl(self):
+ """Test performance with hfp connection and perform volume actions.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and check throughput when hfp connection and perform
+ volume change when call is active.
+
+ Steps:.
+ 1. Start TCP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_038
+ """
+ if not self.initiate_call_and_change_volume_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_volume_check_udp_ul(self):
+ """Test performance with hfp connection and perform volume actions.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and check throughput when hfp connection and perform
+ volume change when call is active.
+
+ Steps:.
+ 1. Start UDP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_039
+ """
+ if not self.initiate_call_and_change_volume_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_volume_check_udp_dl(self):
+ """Test performance with hfp connection and perform volume actions.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and check throughput when hfp connection and perform
+ volume change when call is active.
+
+ Steps:.
+ 1. Start UDP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_040
+ """
+ if not self.initiate_call_and_change_volume_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/slave_role/functionality_tests/WlanWithA2dpFunctionalitySlaveTest.py b/acts/tests/google/coex/slave_role/functionality_tests/WlanWithA2dpFunctionalitySlaveTest.py
new file mode 100644
index 0000000..7e4c1ca
--- /dev/null
+++ b/acts/tests/google/coex/slave_role/functionality_tests/WlanWithA2dpFunctionalitySlaveTest.py
@@ -0,0 +1,265 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.bluez_test_utils import BluezUtils
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_constants import bluetooth_profiles
+from acts.test_utils.coex.coex_constants import WAIT_TIME
+from acts.test_utils.coex.coex_test_utils import music_play_and_check
+from acts.test_utils.coex.coex_test_utils import connect_wlan_profile
+
+
+class WlanWithA2dpFunctionalitySlaveTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["iterations"]
+ self.unpack_userparams(req_params)
+ self.device_id = str(self.pri_ad.droid.bluetoothGetLocalAddress())
+ self.dbus = BluezUtils()
+ self.adapter_mac_address = self.dbus.get_bluetooth_adapter_address()
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.pri_ad.droid.bluetoothMakeDiscoverable()
+ if not self.dbus.find_device(self.device_id):
+ self.log.info("Device is not discoverable")
+ return False
+ self.pri_ad.droid.bluetoothStartPairingHelper(True)
+ if not self.dbus.pair_bluetooth_device():
+ self.log.info("Pairing failed")
+ return False
+ if not self.dbus.connect_bluetooth_device(
+ bluetooth_profiles["A2DP_SRC"]):
+ self.log.info("Connection Failed")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.dbus.remove_bluetooth_device(self.device_id)
+
+ def connect_disconnect_a2dp_headset(self):
+ """Connect and disconnect a2dp profile from headset."""
+ for i in range(self.iterations):
+ if not self.dbus.disconnect_bluetooth_profile(
+ bluetooth_profiles["A2DP_SRC"], self.pri_ad):
+ self.log.info("Disconnection Failed")
+ return False
+ time.sleep(WAIT_TIME)
+ if not self.dbus.connect_bluetooth_device(
+ bluetooth_profiles["A2DP_SRC"]):
+ self.log.info("Connection Failed")
+ return False
+ return True
+
+ def connect_disconnect_a2dp_headset_with_iperf(self):
+ """Wrapper function to start iperf traffic and connect/disconnect
+ to headset for N iterations.
+ """
+ self.run_iperf_and_get_result()
+ if not self.connect_disconnect_a2dp_headset():
+ return False
+ return self.teardown_result()
+
+ def music_streaming_with_iperf(self):
+ """Wrapper function to start iperf traffic, music streaming
+ to headset and associate with access point for N iterations.
+ """
+ args = [
+ lambda: music_play_and_check(
+ self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"])
+ ]
+ self.run_thread(args)
+ if not connect_wlan_profile(self.pri_ad, self.network):
+ return False
+ self.run_iperf_and_get_result()
+ return self.teardown_result()
+
+ def test_connect_disconnect_a2dp_headset_slave_role_with_tcp_ul(self):
+ """Starts TCP-uplink traffic and connect/disconnect a2dp headset.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to a2dp headset when android device as a slave.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Initiate connection from a2dp headset(bluez).
+ 2. Connect and disconnect A2DP headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_034
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_a2dp_headset_slave_role_with_tcp_dl(self):
+ """Starts TCP-downlink traffic and connect/disconnect a2dp headset.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to a2dp headset when android device as a slave.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Initiate connection from a2dp headset(bluez).
+ 2. Connect and disconnect a2dp headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_035
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_a2dp_headset_slave_role_with_udp_ul(self):
+ """Starts UDP-uplink traffic and connect/disconnect a2dp headset.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to a2dp headset when android device as a slave.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Initiate connection from a2dp headset(bluez).
+ 2. Connect and disconnect a2dp headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_036
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_a2dp_headset_slave_role_with_udp_dl(self):
+ """Starts UDP-downlink traffic and connect/disconnect a2dp headset.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to a2dp headset when android device as a slave.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Initiate connection from a2dp headset(bluez).
+ 2. Connect and disconnect a2dp headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_037
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_slave_role_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with music streaming to a2dp headset.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming when device acts as a slave.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_038
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_slave_role_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with music streaming to a2dp headset.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming when device acts as a slave.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_039
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_slave_role_with_udp_ul(self):
+ """Starts UDP-uplink traffic with music streaming to a2dp headset.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming when device acts as a slave.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_040
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_a2dp_streaming_slave_role_with_udp_dl(self):
+ """Starts UDP-downlink traffic with music streaming to a2dp headset.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the functional behaviour of a2dp music
+ streaming when device acts as a slave.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_041
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/slave_role/functionality_tests/WlanWithHfpFunctionalitySlaveTest.py b/acts/tests/google/coex/slave_role/functionality_tests/WlanWithHfpFunctionalitySlaveTest.py
new file mode 100644
index 0000000..5781eae
--- /dev/null
+++ b/acts/tests/google/coex/slave_role/functionality_tests/WlanWithHfpFunctionalitySlaveTest.py
@@ -0,0 +1,167 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.bluez_test_utils import BluezUtils
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_constants import WAIT_TIME
+from acts.test_utils.coex.coex_constants import bluetooth_profiles
+
+
+class WlanWithHfpFunctionalitySlaveTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["iterations"]
+ self.unpack_userparams(req_params)
+ self.device_id = str(self.pri_ad.droid.bluetoothGetLocalAddress())
+ self.dbus = BluezUtils()
+ self.adapter_mac_address = self.dbus.get_bluetooth_adapter_address()
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.pri_ad.droid.bluetoothMakeDiscoverable()
+ if not self.dbus.find_device(self.device_id):
+ self.log.info("Device is not discoverable")
+ return False
+ self.pri_ad.droid.bluetoothStartPairingHelper(True)
+ if not self.dbus.pair_bluetooth_device():
+ self.log.info("Pairing failed")
+ return False
+ if not self.dbus.connect_bluetooth_device(bluetooth_profiles["HFP_AG"]):
+ self.log.info("Connection Failed")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.dbus.remove_bluetooth_device(self.device_id)
+
+ def connect_disconnect_hfp_headset(self):
+ """Connect and disconnect headset for multiple iterations."""
+ for i in range(self.iterations):
+ if not self.dbus.disconnect_bluetooth_profile(
+ bluetooth_profiles["HFP_AG"], self.pri_ad):
+ self.log.info("Disconnection Failed")
+ return False
+ time.sleep(WAIT_TIME)
+ if not self.dbus.connect_bluetooth_device(
+ bluetooth_profiles["HFP_AG"]):
+ self.log.info("Connection Failed")
+ return False
+ return True
+
+ def connect_disconnect_hfp_headset_with_iperf(self):
+ """Wrapper function to start iperf traffic and connect/disconnect
+ to headset for N iterations.
+ """
+ self.run_iperf_and_get_result()
+ if not self.connect_disconnect_hfp_headset():
+ return False
+ return self.teardown_result()
+
+ def test_connect_disconnect_hfp_headset_slave_role_with_tcp_ul(self):
+ """Starts TCP-uplink traffic and connect/disconnect hfp headset.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to hfp headset when android device as a slave.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Initiate connection from hfp headset(bluez).
+ 2. Connect and disconnect hfp headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_058
+ """
+ if not self.connect_disconnect_hfp_headset_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_hfp_headset_slave_role_with_tcp_dl(self):
+ """Starts TCP-downlink traffic and connect/disconnect hfp headset.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to hfp headset when android device as a slave.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Initiate connection from hfp headset(bluez).
+ 2. Connect and disconnect hfp headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_059
+ """
+ if not self.connect_disconnect_hfp_headset_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_hfp_headset_slave_role_with_udp_ul(self):
+ """Starts UDP-uplink traffic and connect/disconnect hfp headset.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to hfp headset when android device as a slave.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Initiate connection from hfp headset(bluez).
+ 2. Connect and disconnect hfp headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_060
+ """
+ if not self.connect_disconnect_hfp_headset_with_iperf():
+ return False
+ return True
+
+ def test_connect_disconnect_hfp_headset_slave_role_with_udp_dl(self):
+ """Starts UDP-downlink traffic and connect/disconnect hfp headset.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test functional behaviour of connection and
+ disconnection to hfp headset when android device as a slave.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Initiate connection from hfp headset(bluez).
+ 2. Connect and disconnect hfp headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_061
+ """
+ if not self.connect_disconnect_hfp_headset_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/slave_role/performance_tests/CoexBtMultiProfilePerformanceSlaveTest.py b/acts/tests/google/coex/slave_role/performance_tests/CoexBtMultiProfilePerformanceSlaveTest.py
new file mode 100644
index 0000000..68b7ab4
--- /dev/null
+++ b/acts/tests/google/coex/slave_role/performance_tests/CoexBtMultiProfilePerformanceSlaveTest.py
@@ -0,0 +1,186 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.bluez_test_utils import BluezUtils
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_constants import bluetooth_profiles
+from acts.test_utils.coex.coex_test_utils import music_play_and_check_via_app
+from acts.test_utils.coex.coex_test_utils import initiate_disconnect_call_dut
+from acts.test_utils.coex.coex_test_utils import connect_wlan_profile
+from acts.test_utils.coex.coex_test_utils import setup_tel_config
+
+
+class CoexBtMultiProfilePerformanceSlaveTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["sim_conf_file"]
+ self.unpack_userparams(req_params)
+ self.ag_phone_number, self.re_phone_number = setup_tel_config(
+ self.pri_ad, self.sec_ad, self.sim_conf_file)
+ self.device_id = str(self.pri_ad.droid.bluetoothGetLocalAddress())
+ self.dbus = BluezUtils()
+ self.adapter_mac_address = self.dbus.get_bluetooth_adapter_address()
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.pri_ad.droid.bluetoothMakeDiscoverable()
+ if not self.dbus.find_device(self.device_id):
+ self.log.error("Device is not discoverable")
+ return False
+ self.pri_ad.droid.bluetoothStartPairingHelper(True)
+ if not self.dbus.pair_bluetooth_device():
+ self.log.error("Pairing failed")
+ return False
+ if not self.dbus.connect_bluetooth_device(
+ bluetooth_profiles["HFP_AG"], bluetooth_profiles["A2DP_SRC"]):
+ self.log.error("Connection Failed")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.dbus.remove_bluetooth_device(self.device_id)
+
+ def initiate_call_when_a2dp_streaming_on(self):
+ """Initiates HFP call, then check for call is present or not.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not initiate_disconnect_call_dut(
+ self.pri_ad, self.sec_ad, self.iperf["duration"],
+ self.re_phone_number):
+ return False
+ return True
+
+ def play_music_and_connect_wifi(self):
+ """Perform A2DP music streaming and scan and connect to wifi
+ network
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not music_play_and_check_via_app(
+ self.pri_ad, self.adapter_mac_address):
+ self.log.error("Failed to stream music file")
+ return False
+ if not connect_wlan_profile(self.pri_ad, self.network):
+ return False
+ return True
+
+ def initiate_call_when_a2dp_streaming_with_iperf(self):
+ """Wrapper function to initiate call when a2dp streaming and starts
+ iperf.
+ """
+ if not self.play_music_and_connect_wifi():
+ return False
+ self.run_iperf_and_get_result()
+ if not self.initiate_call_when_a2dp_streaming_on():
+ return False
+ return self.teardown_result()
+
+ def test_performance_a2dp_streaming_hfp_call_tcp_ul(self):
+ """Check performance when a2dp streaming and hfp call..
+
+ This test is to check wifi performance when a2dp streaming and
+ hfp call performed sequentially with TCP-uplink traffic. Android
+ device is in slave role.
+
+ Steps:
+ 1.Enable bluetooth.
+ 2.Start a2dp streaming.
+ 3.Run TCP-uplink traffic.
+ 4.Initiate hfp call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_061
+ """
+ if not self.initiate_call_when_a2dp_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_hfp_call_tcp_dl(self):
+ """Check performance when a2dp streaming and hfp call..
+
+ This test is to check wifi performance when a2dp streaming and
+ hfp call performed sequentially with TCP-downlink traffic. Android
+ device is in slave role.
+
+ Steps:
+ 1.Enable bluetooth.
+ 2.Start a2dp streaming.
+ 3.Run TCP-downlink traffic.
+ 4.Initiate hfp call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_062
+ """
+ if not self.initiate_call_when_a2dp_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_hfp_call_udp_ul(self):
+ """Check performance when a2dp streaming and hfp call..
+
+ This test is to check wifi performance when a2dp streaming and
+ hfp call performed sequentially with UDP-uplink traffic. Android
+ device is in slave role.
+
+ Steps:
+ 1.Enable bluetooth.
+ 2.Start a2dp streaming.
+ 3.Run UDP-uplink traffic.
+ 4.Initiate hfp call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_063
+ """
+ if not self.initiate_call_when_a2dp_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_hfp_call_udp_dl(self):
+ """Check performance when a2dp streaming and hfp call..
+
+ This test is to check wifi performance when a2dp streaming and
+ hfp call performed sequentially with UDP-downlink traffic. Android
+ device is in slave role.
+
+ Steps:
+ 1.Enable bluetooth.
+ 2.Start a2dp streaming.
+ 3.Run UDP-downlink traffic.
+ 4.Initiate hfp call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Kpi_064
+ """
+ if not self.initiate_call_when_a2dp_streaming_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/slave_role/performance_tests/WlanWithA2dpPerformanceSlaveTest.py b/acts/tests/google/coex/slave_role/performance_tests/WlanWithA2dpPerformanceSlaveTest.py
new file mode 100644
index 0000000..bf9bff1
--- /dev/null
+++ b/acts/tests/google/coex/slave_role/performance_tests/WlanWithA2dpPerformanceSlaveTest.py
@@ -0,0 +1,238 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.bluez_test_utils import BluezUtils
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_constants import bluetooth_profiles
+from acts.test_utils.coex.coex_test_utils import music_play_and_check
+from acts.test_utils.coex.coex_test_utils import multithread_func
+
+
+class WlanWithA2dpPerformanceSlaveTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ self.device_id = str(self.pri_ad.droid.bluetoothGetLocalAddress())
+ self.dbus = BluezUtils()
+ self.adapter_mac_address = self.dbus.get_bluetooth_adapter_address()
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.pri_ad.droid.bluetoothMakeDiscoverable()
+ if not self.dbus.find_device(self.device_id):
+ self.log.info("Device is not discoverable")
+ return False
+ self.pri_ad.droid.bluetoothStartPairingHelper(True)
+ if not self.dbus.pair_bluetooth_device():
+ self.log.info("Pairing failed")
+ return False
+ if not self.dbus.connect_bluetooth_device(
+ bluetooth_profiles["A2DP_SRC"]):
+ self.log.info("Connection Failed")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.dbus.remove_bluetooth_device(self.device_id)
+
+ def music_streaming_with_iperf(self):
+ """Wrapper function to start iperf traffic, music streaming
+ to headset and associate with access point for N iterations.
+ """
+ self.run_iperf_and_get_result()
+ if not music_play_and_check(
+ self.pri_ad, self.adapter_mac_address, self.music_file_to_play,
+ self.iperf["duration"]):
+ return False
+ return self.teardown_result()
+
+ def music_streaming_avrcp_controls_with_iperf(self):
+ """Wrapper function to start iperf traffic, music streaming and avrcp
+ controls.
+ """
+ self.run_iperf_and_get_result()
+ tasks = [(music_play_and_check,
+ (self.pri_ad, self.adapter_mac_address,
+ self.music_file_to_play, self.iperf["duration"])),
+ (self.dbus.avrcp_actions, (self.device_id,))]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def test_performance_a2dp_streaming_slave_role_with_tcp_ul(self):
+ """Starts TCP-uplink traffic with music streaming to a2dp headset.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the performance of a2dp music
+ streaming and wifi throughput when device acts as a slave.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_045
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_slave_role_with_tcp_dl(self):
+ """Starts TCP-downlink traffic with music streaming to a2dp headset.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the performance of a2dp music
+ streaming and wifi throughput when device acts as a slave.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_046
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_slave_role_with_udp_ul(self):
+ """Starts UDP-uplink traffic with music streaming to a2dp headset.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the performance of a2dp music
+ streaming and wifi throughput when device acts as a slave.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_047
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_slave_role_with_udp_dl(self):
+ """Starts UDP-downlink traffic with music streaming to a2dp headset.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the performance of a2dp music
+ streaming and wifi throughput when device acts as a slave.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_048
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_avrcp_controls_slave_role_tcp_ul(self):
+ """Starts TCP-uplink traffic with music streaming and avrcp controls.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the performance of a2dp music streaming and
+ avrcp controls when device is in slave role.
+
+ 1. Run TCP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_049
+ """
+ if not self.music_streaming_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_avrcp_controls_slave_role_tcp_dl(self):
+ """Starts TCP-downlink traffic with music streaming and avrcp controls.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the performance of a2dp music streaming and
+ avrcp controls when device is in slave role.
+
+ 1. Run TCP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_050
+ """
+ if not self.music_streaming_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_avrcp_controls_slave_role_udp_ul(self):
+ """Starts UDP-uplink traffic with music streaming and avrcp controls.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the performance of a2dp music streaming and
+ avrcp controls when device is in slave role.
+
+ 1. Run UDP-uplink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_051
+ """
+ if not self.music_streaming_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_performance_a2dp_streaming_avrcp_controls_slave_role_udp_dl(self):
+ """Starts UDP-downlink traffic with music streaming and avrcp controls.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the performance of a2dp music streaming and
+ avrcp controls when device is in slave role.
+
+ 1. Run UDP-downlink traffic.
+ 2. Start media streaming to a2dp headset.
+ 3. Check all avrcp related controls.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_052
+ """
+ if not self.music_streaming_avrcp_controls_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/slave_role/performance_tests/WlanWithHFPPerformanceSlaveTest.py b/acts/tests/google/coex/slave_role/performance_tests/WlanWithHFPPerformanceSlaveTest.py
new file mode 100644
index 0000000..1ff111f
--- /dev/null
+++ b/acts/tests/google/coex/slave_role/performance_tests/WlanWithHFPPerformanceSlaveTest.py
@@ -0,0 +1,246 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.bluez_test_utils import BluezUtils
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_constants import bluetooth_profiles
+from acts.test_utils.coex.coex_constants import CALL_WAIT_TIME
+from acts.test_utils.coex.coex_test_utils import multithread_func
+from acts.test_utils.coex.coex_test_utils import setup_tel_config
+from acts.test_utils.tel.tel_test_utils import initiate_call
+
+
+class WlanWithHFPPerformanceSlaveTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["sim_conf_file"]
+ self.unpack_userparams(req_params)
+ self.ag_phone_number, self.re_phone_number = setup_tel_config(
+ self.pri_ad, self.sec_ad, self.sim_conf_file)
+ self.device_id = str(self.pri_ad.droid.bluetoothGetLocalAddress())
+ self.dbus = BluezUtils()
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.pri_ad.droid.bluetoothMakeDiscoverable()
+ if not self.dbus.find_device(self.device_id):
+ self.log.info("Device is not discoverable")
+ return False
+ self.pri_ad.droid.bluetoothStartPairingHelper(True)
+ if not self.dbus.pair_bluetooth_device():
+ self.log.info("Pairing failed")
+ return False
+ if not self.dbus.connect_bluetooth_device(
+ bluetooth_profiles["HFP_AG"], bluetooth_profiles["A2DP_SRC"]):
+ self.log.info("Connection failed")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.dbus.remove_bluetooth_device(self.device_id)
+
+ def initiate_call_from_hf_with_iperf(self):
+ """Wrapper function to start iperf and initiate call"""
+ self.run_iperf_and_get_result()
+ if not self.dbus.initiate_and_disconnect_call_from_hf(
+ self.re_phone_number, self.iperf["duration"]):
+ return False
+ return self.teardown_result()
+
+ def initiate_call_avrcp_controls_with_iperf(self):
+ """Wrapper function to start iperf, initiate call from hf and answer
+ call from secondary device.
+ """
+ initiate_call(self.log, self.sec_ad, self.ag_phone_number)
+ time.sleep(CALL_WAIT_TIME)
+ self.run_iperf_and_get_result()
+ tasks = [(self.dbus.answer_call, (self.iperf["duration"],)),
+ (self.dbus.avrcp_actions, (self.device_id,))]
+ if not multithread_func(self.log, tasks):
+ return False
+ return self.teardown_result()
+
+ def test_performance_hfp_call_slave_role_tcp_ul(self):
+ """Starts TCP-uplink traffic with hfp connection.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the performance hfp connection
+ call and wifi throughput when device is in slave role.
+
+ Steps:.
+ 1. Start TCP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_053
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_slave_role_tcp_dl(self):
+ """Starts TCP-downlink traffic with hfp connection.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the performance hfp connection
+ call and wifi throughput when device is in slave role.
+
+ Steps:.
+ 1. Start TCP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_054
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_slave_role_udp_ul(self):
+ """Starts UDP-uplink traffic with hfp connection.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the performance hfp connection
+ call and wifi throughput when device is in slave role.
+
+ Steps:.
+ 1. Start UDP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_055
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_slave_role_udp_dl(self):
+ """Starts UDP-downlink traffic with hfp connection.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the performance hfp connection
+ call and wifi throughput when device is in slave role.
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_056
+ """
+ if not self.initiate_call_from_hf_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_avrcp_controls_slave_role_tcp_ul(self):
+ """Starts TCP-uplink traffic with hfp connection and check volume.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the performance hfp connection,
+ call, volume change and wifi throughput when device is in slave role.
+
+ Steps:.
+ 1. Start TCP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+ 3. Change call volume when device is in call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_057
+ """
+ if not self.initiate_call_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_avrcp_controls_slave_role_tcp_dl(self):
+ """Starts TCP-downlink traffic with hfp connection and check volume.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the performance hfp connection,
+ call, volume change and wifi throughput when device is in slave role.
+
+ Steps:.
+ 1. Start TCP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+ 3. Change call volume when device is in call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_058
+ """
+ if not self.initiate_call_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_avrcp_controls_slave_role_udp_ul(self):
+ """Starts UDP-uplink traffic with hfp connection and check volume.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the performance hfp connection,
+ call, volume change and wifi throughput when device is in slave role.
+
+ Steps:.
+ 1. Start UDP-uplink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+ 3. Change call volume when device is in call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_059
+ """
+ if not self.initiate_call_avrcp_controls_with_iperf():
+ return False
+ return True
+
+ def test_performance_hfp_call_avrcp_controls_slave_role_udp_dl(self):
+ """Starts UDP-downlink traffic with hfp connection and check volume.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the performance hfp connection,
+ call, volume change and wifi throughput when device is in slave role.
+
+ Steps:.
+ 1. Start UDP-downlink traffic.
+ 2. Initiate call from HF and disconnect call from primary device.
+ 3. Change call volume when device is in call.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_060
+ """
+ if not self.initiate_call_avrcp_controls_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/stress_tests/CoexA2dpStressTest.py b/acts/tests/google/coex/stress_tests/CoexA2dpStressTest.py
new file mode 100644
index 0000000..51e4407
--- /dev/null
+++ b/acts/tests/google/coex/stress_tests/CoexA2dpStressTest.py
@@ -0,0 +1,443 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import connect_dev_to_headset
+from acts.test_utils.coex.coex_test_utils import disconnect_headset_from_dev
+from acts.test_utils.coex.coex_test_utils import music_play_and_check
+from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+
+
+class CoexA2dpStressTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["iterations"]
+ self.unpack_userparams(req_params)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.audio_receiver.pairing_mode()
+ if not pair_and_connect_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.A2DP.value])):
+ self.log.error("Failed to pair and connect to headset")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.audio_receiver.clean_up()
+
+ def connect_disconnect_headset(self):
+ """Initiates connection to paired headset and disconnects headset.
+
+ Returns:
+ True if successful False otherwise.
+ """
+ for i in range(self.iterations):
+ self.log.info("Headset connect/disconnect iteration={}".format(i))
+ self.pri_ad.droid.bluetoothConnectBonded(
+ self.audio_receiver.mac_address)
+ time.sleep(2)
+ self.pri_ad.droid.bluetoothDisconnectConnected(
+ self.audio_receiver.mac_address)
+ return True
+
+ def connect_disconnect_a2dp_headset(self):
+ """Connect and disconnect a2dp profile on headset for multiple
+ iterations.
+
+ Steps:
+ 1.Connect a2dp profile on headset.
+ 2.Disconnect a2dp profile on headset.
+ 3.Repeat step 1 and 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ for i in range(self.iterations):
+ if not connect_dev_to_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ {BtEnum.BluetoothProfile.A2DP.value}):
+ self.log.error("Failure to connect A2dp headset.")
+ return False
+
+ if not disconnect_headset_from_dev(
+ self.pri_ad, self.audio_receiver.mac_address,
+ [BtEnum.BluetoothProfile.A2DP.value]):
+ self.log.error("Could not disconnect {}".format(
+ self.audio_receiver.mac_address))
+ return False
+ return True
+
+ def connect_disconnect_headset_with_iperf(self):
+ """Wrapper function to start iperf traffic and connect/disconnect
+ to headset for N iterations.
+ """
+ self.run_iperf_and_get_result()
+ if not self.connect_disconnect_headset():
+ return False
+ return self.teardown_result()
+
+ def connect_disconnect_a2dp_headset_with_iperf(self):
+ """Wrapper function to start iperf traffic and connect/disconnect
+ to a2dp headset for N iterations.
+ """
+ self.run_iperf_and_get_result()
+ if not self.connect_disconnect_a2dp_headset():
+ return False
+ return self.teardown_result()
+
+ def music_streaming_with_iperf(self):
+ """Wrapper function to start iperf traffic and music streaming."""
+ self.run_iperf_and_get_result()
+ if not music_play_and_check(
+ self.pri_ad, self.audio_receiver.mac_address,
+ self.music_file_to_play, self.iperf["duration"]):
+ return False
+ return self.teardown_result()
+
+ def test_stress_connect_disconnect_headset_with_tcp_ul(self):
+ """Stress test for connect/disconnect headset.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Connect and disconnect headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_013
+ """
+ if not self.connect_disconnect_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_headset_with_tcp_dl(self):
+ """Stress test for connect/disconnect headset.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Connect and disconnect headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_014
+ """
+ if not self.connect_disconnect_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_headset_with_udp_ul(self):
+ """Stress test for connect/disconnect headset.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Connect and disconnect headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_015
+ """
+ if not self.connect_disconnect_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_headset_with_udp_dl(self):
+ """Stress test for connect/disconnect headset.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Connect and disconnect headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_016
+ """
+ if not self.connect_disconnect_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_a2dp_long_duration_with_tcp_ul(self):
+ """Stress test to stream music to headset continuously for 12 hours.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the integrity of audio streaming for 12 hours.
+
+ Steps:
+ 1. Start TCP uplink traffic.
+ 2. Start music streaming to headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_017
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_stress_a2dp_long_duration_with_tcp_dl(self):
+ """Stress test to stream music to headset continuously for 12 hours.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the integrity of audio streaming for 12 hours.
+
+ Steps:
+ 1. Start TCP downlink traffic.
+ 2. Start music streaming to headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_018
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_stress_a2dp_long_duration_with_udp_ul(self):
+ """Stress test to stream music to headset continuously for 12 hours.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the integrity of audio streaming for 12 hours.
+
+ Steps:
+ 1. Start UDP uplink traffic.
+ 2. Start music streaming to headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_019
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_stress_a2dp_long_duration_with_udp_dl(self):
+ """Stress test to stream music to headset continuously for 12 hours.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the integrity of audio streaming for 12 hours.
+
+ Steps:
+ 1. Start UDP downlink traffic.
+ 2. Start music streaming to headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_020
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_a2dp_profile_with_tcp_ul(self):
+ """Stress test for connect/disconnect a2dp headset.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with a2dp profile.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Connect and disconnect headset with a2dp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_029
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_a2dp_profile_with_tcp_dl(self):
+ """Stress test for connect/disconnect a2dp headset.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with a2dp profile.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Connect and disconnect headset with a2dp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_030
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_a2dp_profile_with_udp_ul(self):
+ """Stress test for connect/disconnect a2dp headset.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with a2dp profile.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Connect and disconnect headset with a2dp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_031
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_a2dp_profile_with_udp_dl(self):
+ """Stress test for connect/disconnect a2dp headset.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with a2dp profile.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Connect and disconnect headset with a2dp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_032
+ """
+ if not self.connect_disconnect_a2dp_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_headset_with_tcp_bidirectional(self):
+ """Stress test for connect/disconnect headset.
+
+ This test starts TCP-bidirectional traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset.
+
+ Steps:
+ 1. Run TCP-bidirectional traffic.
+ 2. Connect and disconnect headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_057
+ """
+ if not self.connect_disconnect_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_headset_with_udp_bidirectional(self):
+ """Stress test for connect/disconnect headset.
+
+ This test starts UDP-bidirectional traffic between host machin and
+ android device and test the integrity of connection and disconnection
+ to headset.
+
+ Steps:
+ 1. Run UDP-bidirectional traffic.
+ 2. Connect and disconnect headset.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_058
+ """
+ if not self.connect_disconnect_headset_with_iperf():
+ return False
+ return True
+
+ def test_stress_a2dp_long_duration_with_tcp_bidirectional(self):
+ """Stress test to stream music to headset continuously for 12 hours.
+
+ This test starts TCP-bidirectional traffic between host machin and
+ android device and test the integrity of audio streaming for 12 hours.
+
+ Steps:
+ 1. Start TCP bidirectional traffic.
+ 2. Start music streaming to headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_065
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
+
+ def test_stress_a2dp_long_duration_with_udp_bidirectional(self):
+ """Stress test to stream music to headset continuously for 12 hours.
+
+ This test starts UDP-bidirectional traffic between host machin and
+ android device and test the integrity of audio streaming for 12 hours.
+
+ Steps:
+ 1. Start UDP bidirectional traffic.
+ 2. Start music streaming to headset.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_066
+ """
+ if not self.music_streaming_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/stress_tests/CoexBasicStressTest.py b/acts/tests/google/coex/stress_tests/CoexBasicStressTest.py
new file mode 100644
index 0000000..bc87b80
--- /dev/null
+++ b/acts/tests/google/coex/stress_tests/CoexBasicStressTest.py
@@ -0,0 +1,447 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import toggle_bluetooth
+from acts.test_utils.coex.coex_test_utils import device_discoverable
+
+
+class CoexBasicStressTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["iterations"]
+ self.unpack_userparams(req_params)
+
+ def start_stop_classic_discovery_with_iperf(self):
+ """Starts and stop bluetooth discovery for 1000 iterations.
+
+ Steps:
+ 1. Start Discovery on Primary device.
+ 2. Stop Discovery on Secondary device.
+ 3. Repeat step 1 and 2.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ self.run_iperf_and_get_result()
+ for i in range(self.iterations):
+ self.log.info("Inquiry iteration {}".format(i))
+ if not self.pri_ad.droid.bluetoothStartDiscovery():
+ self.log.error("Bluetooth discovery failed.")
+ return False
+ time.sleep(2)
+ if not self.pri_ad.droid.bluetoothCancelDiscovery():
+ self.log.error("Bluetooth cancel discovery failed.")
+ return False
+ return self.teardown_result()
+
+ def check_device_discoverability_with_iperf(self):
+ """Checks if primary device is visible from secondary device.
+
+ Steps:
+ 1. Start discovery on primary device.
+ 2. Discover primary device from Secondary device.
+ 3. Repeat step 1 and 2.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ self.run_iperf_and_get_result()
+ for i in range(self.iterations):
+ self.log.info("Discovery iteration = {}".format(i))
+ if not device_discoverable(self.pri_ad, self.sec_ad):
+ self.log.error("Primary device could not be discovered.")
+ return False
+ return self.teardown_result()
+
+ def toogle_bluetooth_with_iperf(self):
+ """Wrapper function to start iperf traffic and toggling bluetooth."""
+ self.run_iperf_and_get_result()
+ if not toggle_bluetooth(self.pri_ad, self.iterations):
+ return False
+ return self.teardown_result()
+
+ def test_stress_toggle_bluetooth_with_tcp_ul(self):
+ """Stress test toggling bluetooth on and off.
+
+ This test is to start TCP-uplink traffic between host machine
+ and android device and test the integrity of toggling bluetooth
+ on and off.
+
+ Steps:
+ 1. Starts TCP-uplink traffic.
+ 2. Toggle bluetooth state on and off for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_001
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_stress_toggle_bluetooth_with_tcp_dl(self):
+ """Stress test toggling bluetooth on and off.
+
+ This test is to start TCP-downlink traffic between host machine
+ and android device and test the integrity of toggling bluetooth
+ on and off.
+
+ Steps:
+ 1. Starts TCP-downlink traffic.
+ 2. Toggle bluetooth state on and off for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_002
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_stress_toggle_bluetooth_with_udp_ul(self):
+ """Stress test toggling bluetooth on and off.
+
+ This test is to start UDP-uplink traffic between host machine
+ and android device and test the integrity of toggling bluetooth
+ on and off.
+
+ Steps:
+ 1. Starts UDP-uplink traffic.
+ 2. Toggle bluetooth state on and off for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_003
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_stress_toggle_bluetooth_with_udp_dl(self):
+ """Stress test toggling bluetooth on and off.
+
+ This test is to start UDP-downlink traffic between host machine
+ and android device and test the integrity of toggling bluetooth
+ on and off.
+
+ Steps:
+ 1. Starts UDP-downlink traffic.
+ 2. Toggle bluetooth state on and off for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_004
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_stress_bluetooth_discovery_with_tcp_ul(self):
+ """Stress test on bluetooth discovery.
+
+ This test is to start TCP-uplink traffic between host machine
+ and android device and test the integrity of start and stop discovery.
+
+ Steps:
+ 1. Starts TCP-uplink traffic.
+ 2. Performs start and stop discovery in quick succession.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_005
+ """
+ if not self.start_stop_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_stress_bluetooth_discovery_with_tcp_dl(self):
+ """Stress test on bluetooth discovery.
+
+ This test is to start TCP-downlink traffic between host machine
+ and android device and test the integrity of start and stop discovery.
+
+ Steps:
+ 1. Starts TCP-downlink traffic.
+ 2. Performs start and stop discovery in quick succession.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_006
+ """
+ if not self.start_stop_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_stress_bluetooth_discovery_with_udp_ul(self):
+ """Stress test on bluetooth discovery.
+
+ This test is to start UDP-uplink traffic between host machine
+ and android device and test the integrity of start and stop discovery.
+
+ Steps:
+ 1. Starts UDP-uplink traffic.
+ 2. Performs start and stop discovery in quick succession.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_007
+ """
+ if not self.start_stop_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_stress_bluetooth_discovery_with_udp_dl(self):
+ """Stress test on bluetooth discovery.
+
+ This test is to start UDP-downlink traffic between host machine
+ and android device and test the integrity of start and stop discovery.
+
+ Steps:
+ 1. Starts UDP-downlink traffic.
+ 2. Performs start and stop discovery in quick succession.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_008
+ """
+ if not self.start_stop_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_stress_primary_device_visibility_with_tcp_ul(self):
+ """Stress test on device visibility from secondary device.
+
+ This test is to start TCP-uplink traffic between host machine
+ and android device and stress test on device discoverability from
+ secondary device
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 2. Make primary device visible.
+ 3. Check if primary device name is visible from secondary device.
+ 4. Repeat step 2 and 3 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_009
+ """
+ if not self.check_device_discoverability_with_iperf():
+ return False
+ return True
+
+ def test_stress_primary_device_visibility_with_tcp_dl(self):
+ """Stress test on device visibility from secondary device.
+
+ This test is to start TCP-downlink traffic between host machine
+ and android device and stress test on device discoverability from
+ secondary device
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 2. Make primary device visible.
+ 3. Check if primary device name is visible from secondary device.
+ 4. Repeat step 2 and 3 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_010
+ """
+ if not self.check_device_discoverability_with_iperf():
+ return False
+ return True
+
+ def test_stress_primary_device_visibility_with_udp_ul(self):
+ """Stress test on device visibility from secondary device.
+
+ This test is to start UDP-uplink traffic between host machine
+ and android device and stress test on device discoverability from
+ secondary device
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 2. Make primary device visible.
+ 3. Check if primary device name is visible from secondary device.
+ 4. Repeat step 2 and 3 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_011
+ """
+ if not self.check_device_discoverability_with_iperf():
+ return False
+ return True
+
+ def test_stress_primary_device_visibility_with_udp_dl(self):
+ """Stress test on device visibility from secondary device.
+
+ This test is to start UDP-downlink traffic between host machine
+ and android device and stress test on device discoverability from
+ secondary device
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+ 2. Make primary device visible.
+ 3. Check if primary device name is visible from secondary device.
+ 4. Repeat step 2 and 3 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_012
+ """
+ if not self.check_device_discoverability_with_iperf():
+ return False
+ return True
+
+ def test_stress_toggle_bluetooth_with_tcp_bidirectional(self):
+ """Stress test toggling bluetooth on and off.
+
+ This test is to start TCP-bidirectional traffic between host machine
+ and android device and test the integrity of toggling bluetooth
+ on and off.
+
+ Steps:
+ 1. Starts TCP-bidirectional traffic.
+ 2. Toggle bluetooth state on and off for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_051
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_stress_toggle_bluetooth_with_udp_bidirectional(self):
+ """Stress test toggling bluetooth on and off.
+
+ This test is to start UDP-bidirectional traffic between host machine
+ and android device and test the integrity of toggling bluetooth
+ on and off.
+
+ Steps:
+ 1. Starts TCP-bidirectional traffic.
+ 2. Toggle bluetooth state on and off for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_052
+ """
+ if not self.toogle_bluetooth_with_iperf():
+ return False
+ return True
+
+ def test_stress_bluetooth_discovery_with_tcp_bidirectional(self):
+ """Stress test on bluetooth discovery.
+
+ This test is to start TCP-bidirectional traffic between host machine
+ and android device and test the integrity of start and stop discovery.
+
+ Steps:
+ 1. Starts TCP-bidirectional traffic.
+ 2. Performs start and stop discovery in quick succession.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_053
+ """
+ if not self.start_stop_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_stress_bluetooth_discovery_with_udp_bidirectional(self):
+ """Stress test on bluetooth discovery.
+
+ This test is to start UDP-bidirectional traffic between host machine
+ and android device and test the integrity of start and stop discovery.
+
+ Steps:
+ 1. Start wlan traffic with UDP-bidirectional.
+ 2. Start bluetooth discovery for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_054
+ """
+ if not self.start_stop_classic_discovery_with_iperf():
+ return False
+ return True
+
+ def test_stress_primary_device_visiblity_tcp_bidirectional(self):
+ """Stress test on device visibility from secondary device.
+
+ This test is to start TCP-bidirectional traffic between host machine
+ and android device and stress test on device discoverability from
+ secondary device
+
+ Steps:
+ 1. Start TCP-bidirectional traffic.
+ 2. Make primary device visible.
+ 3. Check if primary device name is visible from secondary device.
+ 4. Repeat step 2 and 3 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_055
+ """
+ if not self.check_device_discoverability_with_iperf():
+ return False
+ return True
+
+ def test_stress_primary_device_visiblity_udp_bidirectional(self):
+ """Stress test on device visibility from secondary device.
+
+ This test is to start UDP-bidirectional traffic between host machine
+ and android device and stress test on device discoverability from
+ secondary device
+
+ Steps:
+ 1. Start UDP-bidirectional traffic.
+ 2. Make primary device visible.
+ 3. Check if primary device name is visible from secondary device.
+ 4. Repeat step 2 and 3 for n iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_056
+ """
+ if not self.check_device_discoverability_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/stress_tests/CoexBtMultiProfileStressTest.py b/acts/tests/google/coex/stress_tests/CoexBtMultiProfileStressTest.py
new file mode 100644
index 0000000..8c770e8
--- /dev/null
+++ b/acts/tests/google/coex/stress_tests/CoexBtMultiProfileStressTest.py
@@ -0,0 +1,180 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import disconnect_headset_from_dev
+from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+
+
+class CoexBtMultiProfileStressTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ self.receiver = self.relay_devices[1]
+ req_params = ["iterations"]
+ self.unpack_userparams(req_params)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.audio_receiver.pairing_mode()
+ self.receiver.setup()
+ self.receiver.power_on()
+ self.receiver.pairing_mode()
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.audio_receiver.clean_up()
+ self.receiver.clean_up()
+
+ def initiate_classic_connection_to_multiple_devices(self):
+ """Initiates multiple BR/EDR connections.
+
+ Steps:
+ 1. Initiate A2DP Connection.
+ 2. Initiate HFP Connection.
+ 3. Disconnect A2DP Connection.
+ 4. Disconnect HFP Connection.
+ 5. Repeat step 1 to 4.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ for i in range(self.iterations):
+ if not pair_and_connect_headset(
+ self.pri_ad, self.receiver.mac_address,
+ {BtEnum.BluetoothProfile.A2DP.value}):
+ self.log.error("Failed to connect A2DP Profile.")
+ return False
+ time.sleep(2)
+
+ if not pair_and_connect_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ {BtEnum.BluetoothProfile.HEADSET.value}):
+ self.log.error("Failed to connect HEADSET profile.")
+ return False
+ time.sleep(2)
+
+ if not disconnect_headset_from_dev(
+ self.pri_ad, self.receiver.mac_address,
+ [BtEnum.BluetoothProfile.A2DP.value]):
+ self.log.error("Could not disconnect {}".format(
+ self.receiver.mac_address))
+ return False
+
+ if not disconnect_headset_from_dev(
+ self.pri_ad, self.audio_receiver.mac_address,
+ [BtEnum.BluetoothProfile.HEADSET.value]):
+ self.log.error("Could not disconnect {}".format(
+ self.audio_receiver.mac_address))
+ return False
+ return True
+
+ def initiate_classic_connection_with_iperf(self):
+ """Wrapper function to initiate bluetooth classic connection to
+ multiple devices.
+ """
+ self.run_iperf_and_get_result()
+ if not self.initiate_classic_connection_to_multiple_devices():
+ return False
+ return self.teardown_result()
+
+ def test_stress_multiple_connection_with_tcp_ul(self):
+ """ Connects multiple headsets with wlan traffic over TCP-uplink.
+
+ This test is to perform connect and disconnect with A2DP and HFP
+ profiles on two different bluetooth devices.
+
+ Steps:
+ 1. Run wlan traffic over TCP-uplink.
+ 2. Initiate connect and disconnect to multiple profiles from primary
+ device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_037
+ """
+ if not self.initiate_classic_connection_with_iperf():
+ return False
+ return True
+
+ def test_stress_multiple_connection_with_tcp_dl(self):
+ """ Connects multiple headsets with wlan traffic over TCP-downlink.
+
+ This test is to perform connect and disconnect with A2DP and HFP
+ profiles on two different bluetooth devices.
+
+ Steps:
+ 1. Run wlan traffic over TCP-downlink.
+ 2. Initiate connect and disconnect to multiple profiles from primary
+ device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_038
+ """
+ if not self.initiate_classic_connection_with_iperf():
+ return False
+ return True
+
+ def test_stress_multiple_connection_with_udp_ul(self):
+ """ Connects multiple headsets with wlan traffic over UDP-uplink.
+
+ This test is to perform connect and disconnect with A2DP and HFP
+ profiles on two different bluetooth devices.
+
+ Steps:
+ 1. Run wlan traffic over UDP-uplink.
+ 2. Initiate connect and disconnect to multiple profiles from primary
+ device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_039
+ """
+ if not self.initiate_classic_connection_with_iperf():
+ return False
+ return True
+
+ def test_stress_multiple_connection_with_udp_dl(self):
+ """ Connects multiple headsets with wlan traffic over UDP-downlink.
+
+ This test is to perform connect and disconnect with A2DP and HFP
+ profiles.
+
+ Steps:
+ 1. Run wlan traffic over UDP-downlink.
+ 2. Initiate connect and disconnect to multiple profiles from primary
+ device.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_040
+ """
+ if not self.initiate_classic_connection_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/coex/stress_tests/CoexHfpStressTest.py b/acts/tests/google/coex/stress_tests/CoexHfpStressTest.py
new file mode 100644
index 0000000..390cba1
--- /dev/null
+++ b/acts/tests/google/coex/stress_tests/CoexHfpStressTest.py
@@ -0,0 +1,636 @@
+# /usr/bin/env python3.4
+#
+# 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.
+
+import time
+
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.coex.CoexBaseTest import CoexBaseTest
+from acts.test_utils.coex.coex_test_utils import connect_dev_to_headset
+from acts.test_utils.coex.coex_test_utils import disconnect_headset_from_dev
+from acts.test_utils.coex.coex_constants import AUDIO_ROUTE_BLUETOOTH
+from acts.test_utils.coex.coex_constants import AUDIO_ROUTE_SPEAKER
+from acts.test_utils.coex.coex_test_utils import initiate_disconnect_from_hf
+from acts.test_utils.coex.coex_test_utils import pair_and_connect_headset
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_voice_utils import set_audio_route
+
+
+class CoexHfpStressTest(CoexBaseTest):
+
+ def __init__(self, controllers):
+ CoexBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ CoexBaseTest.setup_class(self)
+ req_params = ["iterations"]
+ self.unpack_userparams(req_params)
+
+ def setup_test(self):
+ CoexBaseTest.setup_test(self)
+ self.audio_receiver.pairing_mode()
+ if not pair_and_connect_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ set([BtEnum.BluetoothProfile.HEADSET.value])):
+ self.log.error("Failed to pair and connect to headset")
+ return False
+
+ def teardown_test(self):
+ clear_bonded_devices(self.pri_ad)
+ CoexBaseTest.teardown_test(self)
+ self.audio_receiver.clean_up()
+
+ def connect_disconnect_hfp_headset(self):
+ """Connect and disconnect hfp profile on headset for multiple
+ iterations.
+
+ Steps:
+ 1.Connect hfp profile on headset.
+ 2.Disconnect hfp profile on headset.
+ 3.Repeat step 1 and 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ for i in range(self.iterations):
+ if not connect_dev_to_headset(
+ self.pri_ad, self.audio_receiver.mac_address,
+ {BtEnum.BluetoothProfile.HEADSET.value}):
+ self.log.error("Failure to connect HFP headset.")
+ return False
+
+ if not disconnect_headset_from_dev(
+ self.pri_ad, self.audio_receiver.mac_address,
+ {BtEnum.BluetoothProfile.HEADSET.value}):
+ self.log.error("Could not disconnect {}".format(
+ self.audio_receiver.mac_address))
+ return False
+ return True
+
+ def initiate_call_from_hf_disconnect_from_ag(self):
+ """Initiates call from HF and disconnect call from ag for multiple
+ iterations.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ for i in range(self.iterations):
+ if not self.audio_receiver.initiate_call_from_hf():
+ self.log.error("Failed to initiate call.")
+ return False
+ time.sleep(5)
+ if not hangup_call(self.log, self.pri_ad):
+ self.log.error("Failed to hang up the call.")
+ return False
+ return True
+
+ def route_audio_from_hf_to_speaker(self):
+ """Route audio from HF to primary device inbuilt speakers and
+ vice_versa.
+
+ Steps:
+ 1. Initiate call from HF.
+ 2. Toggle audio from HF to speaker and vice-versa from N iterations.
+ 3. Hangup call from primary device.
+
+ Returns:
+ True if successful, False otherwise.
+ """
+ if not self.audio_receiver.initiate_call_from_hf():
+ self.log.error("Failed to initiate call.")
+ return False
+ for i in range(self.iterations):
+ self.log.info("DUT speaker iteration = {}".format(i))
+ if not set_audio_route(self.log, self.pri_ad, AUDIO_ROUTE_SPEAKER):
+ self.log.error("Failed switching to primary device speaker.")
+ hangup_call(self.log, self.pri_ad)
+ return False
+ time.sleep(2)
+ if not set_audio_route(self.log, self.pri_ad,
+ AUDIO_ROUTE_BLUETOOTH):
+ self.log.error("Failed switching to bluetooth headset.")
+ hangup_call(self.log, self.pri_ad)
+ return False
+ if not hangup_call(self.log, self.pri_ad):
+ self.log.error("Failed to hang up the call.")
+ return False
+ return True
+
+ def connect_disconnect_hfp_headset_with_iperf(self):
+ """Wrapper function to start iperf traffic and connect/disconnect
+ to a2dp headset for N iterations.
+ """
+ self.run_iperf_and_get_result()
+ if not self.connect_disconnect_hfp_headset():
+ return False
+ return self.teardown_result()
+
+ def hfp_long_duration_with_iperf(self):
+ """Wrapper function to start iperf traffic and initiate hfp call."""
+ self.run_iperf_and_get_result()
+ if not initiate_disconnect_from_hf(
+ self.audio_receiver, self.pri_ad, self.sec_ad,
+ self.iperf["duration"]):
+ return False
+ return self.teardown_result()
+
+ def initiate_call_multiple_times_with_iperf(self):
+ """Wrapper function to start iperf traffic and initiate call and
+ disconnect call simultaneously.
+ """
+ self.run_iperf_and_get_result()
+ if not self.initiate_call_from_hf_disconnect_from_ag():
+ return False
+ return self.teardown_result()
+
+ def route_audio_from_hf_to_speaker_with_iperf(self):
+ """Wrapper function to start iperf traffic and route audio from
+ headset to speaker.
+ """
+ self.run_iperf_and_get_result()
+ if not self.route_audio_from_hf_to_speaker():
+ return False
+ return self.teardown_result()
+
+ def test_stress_hfp_long_duration_with_tcp_ul(self):
+ """Stress test on hfp call continuously for 12 hours.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the integrity of hfp connection for 12 hours.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 2. Initiate call.
+ 3. Verify call status.
+ 4. Disconnect call.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_021
+ """
+ if not self.hfp_long_duration_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_long_duration_with_tcp_dl(self):
+ """Stress test on hfp call continuously for 12 hours.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the integrity of hfp connection for 12 hours.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 2. Initiate call.
+ 3. Verify call status.
+ 4. Disconnect call.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_022
+ """
+ if not self.hfp_long_duration_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_long_duration_with_udp_ul(self):
+ """Stress test on hfp call continuously for 12 hours.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the integrity of hfp connection for 12 hours.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 2. Initiate call.
+ 3. Verify call status.
+ 4. Disconnect call.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_023
+ """
+ if not self.hfp_long_duration_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_long_duration_with_udp_dl(self):
+ """Stress test on hfp call continuously for 12 hours.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the integrity of hfp connection for 12 hours.
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+ 2. Initiate call.
+ 3. Verify call status.
+ 4. Disconnect call.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_024
+ """
+ if not self.hfp_long_duration_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_call_multiple_times_with_tcp_ul(self):
+ """Stress test for initiate and disconnect hfp call.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the integrity of hfp call.
+
+ Steps:
+ 1. Start TCP-uplink traffic.
+ 2. Initiate call from HF
+ 3. Verify status of call
+ 4. Disconnect from AG.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_025
+ """
+ if not self.initiate_call_multiple_times_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_call_multiple_times_with_tcp_dl(self):
+ """Stress test for initiate and disconnect hfp call.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the integrity of hfp call.
+
+ Steps:
+ 1. Start TCP-downlink traffic.
+ 2. Initiate call from HF
+ 3. Verify status of call
+ 4. Disconnect from AG.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_026
+ """
+ if not self.initiate_call_multiple_times_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_call_multiple_times_with_udp_ul(self):
+ """Stress test for initiate and disconnect hfp call.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the integrity of hfp call.
+
+ Steps:
+ 1. Start UDP-uplink traffic.
+ 2. Initiate call from HF
+ 3. Verify status of call
+ 4. Disconnect from AG.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_027
+ """
+ if not self.initiate_call_multiple_times_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_call_multiple_times_with_udp_dl(self):
+ """Stress test for initiate and disconnect hfp call.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the integrity of hfp call.
+
+ Steps:
+ 1. Start UDP-downlink traffic.
+ 2. Initiate call from HF
+ 3. Verify status of call
+ 4. Disconnect from AG.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_028
+ """
+ if not self.initiate_call_multiple_times_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_hfp_profile_with_tcp_ul(self):
+ """Stress test for connect/disconnect hfp headset.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with hfp profile.
+
+ Steps:
+ 1. Run TCP-uplink traffic.
+ 2. Connect and disconnect headset with hfp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_033
+ """
+ if not self.connect_disconnect_hfp_headset():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_hfp_profile_with_tcp_dl(self):
+ """Stress test for connect/disconnect hfp headset.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with hfp profile.
+
+ Steps:
+ 1. Run TCP-downlink traffic.
+ 2. Connect and disconnect headset with hfp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_034
+ """
+ if not self.connect_disconnect_hfp_headset():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_hfp_profile_with_udp_ul(self):
+ """Stress test for connect/disconnect hfp headset.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with hfp profile.
+
+ Steps:
+ 1. Run UDP-uplink traffic.
+ 2. Connect and disconnect headset with hfp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_035
+ """
+ if not self.connect_disconnect_hfp_headset():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_hfp_profile_with_udp_dl(self):
+ """Stress test for connect/disconnect hfp headset.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with hfp profile.
+
+ Steps:
+ 1. Run UDP-downlink traffic.
+ 2. Connect and disconnect headset with hfp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_036
+ """
+ if not self.connect_disconnect_hfp_headset():
+ return False
+ return True
+
+ def test_stress_audio_routing_with_tcp_ul(self):
+ """Stress to route audio from HF to primary device speaker.
+
+ This test is to start TCP-uplink traffic between host machine and
+ android device and test the integrity of audio routing between
+ bluetooth headset and android device inbuilt speaker.
+
+ Steps:
+ 1. Starts TCP-uplink traffic.
+ 2. Route audio from hf to speaker and vice-versa.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_037
+ """
+ if not self.route_audio_from_hf_to_speaker_with_iperf():
+ return False
+ return True
+
+ def test_stress_audio_routing_with_tcp_dl(self):
+ """Stress to route audio from HF to primary device speaker.
+
+ This test is to start TCP-downlink traffic between host machine and
+ android device and test the integrity of audio routing between
+ bluetooth headset and android device inbuilt speaker.
+
+ Steps:
+ 1. Starts TCP-downlink traffic.
+ 2. Route audio from hf to speaker and vice-versa.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_038
+ """
+ if not self.route_audio_from_hf_to_speaker_with_iperf():
+ return False
+ return True
+
+ def test_stress_audio_routing_with_udp_ul(self):
+ """Stress to route audio from HF to primary device speaker.
+
+ This test is to start UDP-uplink traffic between host machine and
+ android device and test the integrity of audio routing between
+ bluetooth headset and android device inbuilt speaker.
+
+ Steps:
+ 1. Starts UDP-uplink traffic.
+ 2. Route audio from hf to speaker and vice-versa.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_039
+ """
+ if not self.route_audio_from_hf_to_speaker_with_iperf():
+ return False
+ return True
+
+ def test_stress_audio_routing_with_udp_dl(self):
+ """Stress to route audio from HF to primary device speaker.
+
+ This test is to start UDP-downlink traffic between host machine and
+ android device and test the integrity of audio routing between
+ bluetooth headset and android device inbuilt speaker.
+
+ Steps:
+ 1. Starts UDP-downlink traffic.
+ 2. Route audio from hf to speaker and vice-versa.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_040
+ """
+ if not self.route_audio_from_hf_to_speaker_with_iperf():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_hfp_with_tcp_bidirectional(self):
+ """Stress test for connect/disconnect headset.
+
+ This test is to start TCP-bidirectional traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with hfp profile.
+
+ Steps:
+ 1. Run TCP-bidirectional traffic.
+ 2. Connect and disconnect headset with hfp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_067
+ """
+ if not self.connect_disconnect_hfp_headset():
+ return False
+ return True
+
+ def test_stress_connect_disconnect_hfp_with_udp_bidirectional(self):
+ """Stress test for connect/disconnect headset.
+
+ This test is to start UDP-bidirectional traffic between host machine and
+ android device and test the integrity of connection and disconnection
+ to headset with hfp profile.
+
+ Steps:
+ 1. Run UDP-bidirectional traffic.
+ 2. Connect and disconnect headset with hfp profile.
+ 3. Repeat step 2 for N iterations.
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_068
+ """
+ if not self.connect_disconnect_hfp_headset():
+ return False
+ return True
+
+ def test_stress_hfp_long_duration_with_tcp_bidirectional(self):
+ """Stress test on hfp call continuously for 12 hours.
+
+ This test is to start TCP-bidirectional traffic between host machine and
+ android device and test the integrity of hfp connection for 12 hours.
+
+ Steps:
+ 1. Start TCP-bidirectional traffic.
+ 2. Initiate call.
+ 3. Verify call status.
+ 4. Disconnect call.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_069
+ """
+ if not self.hfp_long_duration_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_long_duration_with_udp_bidirectional(self):
+ """Stress test on hfp call continuously for 12 hours.
+
+ This test is to start UDP-bidirectional traffic between host machine and
+ android device and test the integrity of hfp connection for 12 hours.
+
+ Steps:
+ 1. Start UDP-bidirectional traffic.
+ 2. Initiate call.
+ 3. Verify call status.
+ 4. Disconnect call.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_070
+ """
+ if not self.hfp_long_duration_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_call_multiple_times_with_tcp_bidirectional(self):
+ """Stress test for initiate and disconnect hfp call.
+
+ This test is to start TCP-bidirectional traffic between host machine and
+ android device and test the integrity of hfp call.
+
+ Steps:
+ 1. Start TCP-bidirectional traffic.
+ 2. Initiate call from HF
+ 3. Verify status of call
+ 4. Disconnect from AG.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_071
+ """
+ if not self.initiate_call_multiple_times_with_iperf():
+ return False
+ return True
+
+ def test_stress_hfp_call_multiple_times_with_udp_bidirectional(self):
+ """Stress test for initiate and disconnect hfp call.
+
+ This test is to start UDP-bidirectional traffic between host machine and
+ android device and test the integrity of hfp call.
+
+ Steps:
+ 1. Start UDP-bidirectional traffic.
+ 2. Initiate call from HF
+ 3. Verify status of call
+ 4. Disconnect from AG.
+ 5. Repeat steps 2 to 4 for N iterations
+
+ Returns:
+ True if successful, False otherwise.
+
+ Test Id: Bt_CoEx_Stress_072
+ """
+ if not self.initiate_call_multiple_times_with_iperf():
+ return False
+ return True
diff --git a/acts/tests/google/net/DataCostTest.py b/acts/tests/google/net/DataCostTest.py
new file mode 100644
index 0000000..4a82948
--- /dev/null
+++ b/acts/tests/google/net/DataCostTest.py
@@ -0,0 +1,204 @@
+#
+# Copyright 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.
+
+import logging
+import os
+import random
+import socket
+import threading
+import time
+
+from acts import asserts
+from acts import base_test
+from acts import test_runner
+from acts.controllers import adb
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
+from acts.test_utils.tel.tel_test_utils import verify_http_connection
+from acts.test_utils.tel.tel_test_utils import _check_file_existance
+from acts.test_utils.tel.tel_test_utils import _generate_file_directory_and_file_name
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_NONE as NONE
+from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_HANDOVER as HANDOVER
+from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_RELIABILITY as RELIABILITY
+from acts.test_utils.net.connectivity_const import MULTIPATH_PREFERENCE_PERFORMANCE as PERFORMANCE
+
+DOWNLOAD_PATH = "/sdcard/Download/"
+RELIABLE = RELIABILITY | HANDOVER
+
+class DataCostTest(base_test.BaseTestClass):
+ """ Tests for Wifi Tethering """
+
+ def setup_class(self):
+ """ Setup devices for tethering and unpack params """
+
+ req_params = ("wifi_network", "download_file")
+ self.unpack_userparams(req_params)
+
+ for ad in self.android_devices:
+ wutils.reset_wifi(ad)
+ ad.droid.telephonyToggleDataConnection(True)
+ wait_for_cell_data_connection(self.log, ad, True)
+ asserts.assert_true(
+ verify_http_connection(self.log, ad),
+ "HTTP verification failed on cell data connection")
+
+ def teardown_class(self):
+ for ad in self.android_devices:
+ wutils.reset_wifi(ad)
+ ad.droid.telephonyToggleDataConnection(True)
+
+ """ Helper functions """
+
+ def _get_total_data_usage_for_device(self, ad, conn_type, sub_id):
+ """ Get total data usage in MB for device
+
+ Args:
+ ad: Android device object
+ conn_type: MOBILE/WIFI data usage
+ sub_id: subscription id
+
+ Returns:
+ Data usage in MB
+ """
+ # end time should be in milli seconds and at least 2 hours more than the
+ # actual end time. NetStats:bucket is of size 2 hours and to ensure to
+ # get the most recent data usage, end_time should be +2hours
+ end_time = int(time.time() * 1000) + 2 * 1000 * 60 * 60
+ data_usage = ad.droid.connectivityQuerySummaryForDevice(
+ conn_type, sub_id, 0, end_time)
+ data_usage /= 1000.0 * 1000.0 # convert data_usage to MB
+ self.log.info("Total data usage is: %s" % data_usage)
+ return data_usage
+
+ def _check_if_multipath_preference_valid(self, val, exp):
+ """ Check if multipath value is same as expected
+
+ Args:
+ val: multipath preference for the network
+ exp: expected multipath preference value
+ """
+ if exp == NONE:
+ asserts.assert_true(val == exp, "Multipath value should be 0")
+ else:
+ asserts.assert_true(val >= exp,
+ "Multipath value should be at least %s" % exp)
+
+ def _verify_multipath_preferences(self,
+ ad,
+ wifi_pref,
+ cell_pref,
+ wifi_network,
+ cell_network):
+ """ Verify mutlipath preferences for wifi and cell networks
+
+ Args:
+ ad: Android device object
+ wifi_pref: Expected multipath value for wifi network
+ cell_pref: Expected multipath value for cell network
+ wifi_network: Wifi network id on the device
+ cell_network: Cell network id on the device
+ """
+ wifi_multipath = \
+ ad.droid.connectivityGetMultipathPreferenceForNetwork(wifi_network)
+ cell_multipath = \
+ ad.droid.connectivityGetMultipathPreferenceForNetwork(cell_network)
+ self.log.info("WiFi multipath preference: %s" % wifi_multipath)
+ self.log.info("Cell multipath preference: %s" % cell_multipath)
+ self.log.info("Checking multipath preference for wifi")
+ self._check_if_multipath_preference_valid(wifi_multipath, wifi_pref)
+ self.log.info("Checking multipath preference for cell")
+ self._check_if_multipath_preference_valid(cell_multipath, cell_pref)
+
+ """ Test Cases """
+
+ @test_tracker_info(uuid="e86c8108-3e84-4668-bae4-e5d2c8c27910")
+ def test_multipath_preference_low_data_limit(self):
+ """ Verify multipath preference when mobile data limit is low
+
+ Steps:
+ 1. DUT has WiFi and LTE data
+ 2. Set mobile data usage limit to low value
+ 3. Verify that multipath preference is 0 for cell network
+ """
+ # set vars
+ ad = self.android_devices[0]
+ sub_id = str(ad.droid.telephonyGetSubscriberId())
+ cell_network = ad.droid.connectivityGetActiveNetwork()
+ self.log.info("cell network %s" % cell_network)
+ wutils.wifi_connect(ad, self.wifi_network)
+ wifi_network = ad.droid.connectivityGetActiveNetwork()
+ self.log.info("wifi network %s" % wifi_network)
+
+ # verify mulipath preference values
+ self._verify_multipath_preferences(
+ ad, RELIABLE, RELIABLE, wifi_network, cell_network)
+
+ # set low data limit on mobile data
+ total_pre = self._get_total_data_usage_for_device(ad, 0, sub_id)
+ self.log.info("Setting data usage limit to %sMB" % (total_pre + 5))
+ ad.droid.connectivitySetDataUsageLimit(
+ sub_id, int((total_pre + 5) * 1000.0 * 1000.0))
+ self.log.info("Setting data warning limit to %sMB" % (total_pre + 5))
+ ad.droid.connectivitySetDataWarningLimit(
+ sub_id, int((total_pre + 5) * 1000.0 * 1000.0))
+
+ # reset data limit
+ ad.droid.connectivityFactoryResetNetworkPolicies(sub_id)
+ ad.droid.connectivitySetDataWarningLimit(sub_id, -1)
+
+ # verify multipath preference values
+ self._verify_multipath_preferences(
+ ad, RELIABLE, NONE, wifi_network, cell_network)
+
+ @test_tracker_info(uuid="a2781411-d880-476a-9f40-2c67e0f97db9")
+ def test_multipath_preference_data_download(self):
+ """ Verify multipath preference when large file is downloaded
+
+ Steps:
+ 1. DUT has WiFi and LTE data
+ 2. WiFi is active network
+ 3. Download large file over cell network
+ 4. Verify multipath preference on cell network is 0
+ """
+ # set vars
+ ad = self.android_devices[1]
+ cell_network = ad.droid.connectivityGetActiveNetwork()
+ self.log.info("cell network %s" % cell_network)
+ wutils.wifi_connect(ad, self.wifi_network)
+ wifi_network = ad.droid.connectivityGetActiveNetwork()
+ self.log.info("wifi network %s" % wifi_network)
+
+ # verify multipath preference for wifi and cell networks
+ self._verify_multipath_preferences(
+ ad, RELIABLE, RELIABLE, wifi_network, cell_network)
+
+ # download file with cell network
+ ad.droid.connectivityNetworkOpenConnection(cell_network,
+ self.download_file)
+ file_folder, file_name = _generate_file_directory_and_file_name(
+ self.download_file, DOWNLOAD_PATH)
+ file_path = os.path.join(file_folder, file_name)
+ self.log.info("File path: %s" % file_path)
+ if _check_file_existance(ad, file_path):
+ self.log.info("File exists. Removing file %s" % file_name)
+ ad.adb.shell("rm -rf %s%s" % (DOWNLOAD_PATH, file_name))
+
+ # verify multipath preference values
+ self._verify_multipath_preferences(
+ ad, RELIABLE, NONE, wifi_network, cell_network)
+
+ # TODO gmoturu@: Need to add tests that use the mobility rig and test when
+ # the WiFi signal is poor and data signal is good.
diff --git a/acts/tests/google/net/DataUsageTest.py b/acts/tests/google/net/DataUsageTest.py
new file mode 100644
index 0000000..0e636ac
--- /dev/null
+++ b/acts/tests/google/net/DataUsageTest.py
@@ -0,0 +1,390 @@
+#
+# Copyright 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.
+
+import time
+
+from acts import asserts
+from acts import base_test
+from acts.controllers import adb
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.net import connectivity_const as cconst
+from acts.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
+from acts.test_utils.tel.tel_test_utils import http_file_download_by_chrome
+from acts.test_utils.tel.tel_test_utils import verify_http_connection
+from acts.test_utils.tel import tel_test_utils as ttutils
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+conn_test_class = "com.android.tests.connectivity.uid.ConnectivityTestActivity"
+android_os_class = "com.quicinc.cne.CNEService.CNEServiceApp"
+instr_cmd = "am instrument -w -e command grant-all \
+ com.android.permissionutils/.PermissionInstrumentation"
+
+HOUR_IN_MILLIS = 1000 * 60 * 60
+BYTE_TO_MB_ANDROID = 1000.0 * 1000.0
+BYTE_TO_MB = 1024.0 * 1024.0
+DOWNLOAD_PATH = "/sdcard/download/"
+DATA_USG_ERR = 2.2
+DATA_ERR = 0.2
+TIMEOUT = 2 * 60
+INC_DATA = 10
+
+
+class DataUsageTest(base_test.BaseTestClass):
+ """ Data usage tests """
+
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.tests = ("test_mobile_data_usage_downlink",
+ "test_wifi_data_usage_downlink",
+ "test_wifi_tethering_mobile_data_usage_downlink",
+ "test_data_usage_limit_downlink",
+ "test_wifi_tethering_data_usage_limit_downlink",)
+
+ def setup_class(self):
+ """ Setup devices for tests and unpack params """
+ self.dut = self.android_devices[0]
+ self.tethered_devices = self.android_devices[1:]
+ wutils.reset_wifi(self.dut)
+ self.dut.droid.telephonyToggleDataConnection(True)
+ wait_for_cell_data_connection(self.log, self.dut, True)
+ asserts.assert_true(
+ verify_http_connection(self.log, self.dut),
+ "HTTP verification failed on cell data connection")
+
+ # unpack user params
+ req_params = ("wifi_network", "download_file", "file_size", "network")
+ self.unpack_userparams(req_params)
+ self.file_path = DOWNLOAD_PATH + self.download_file.split('/')[-1]
+ self.file_size = int(self.file_size)
+ self.sub_id = str(self.dut.droid.telephonyGetSubscriberId())
+ self.android_os_uid = self.dut.droid.getUidForPackage(android_os_class)
+ self.conn_test_uid = self.dut.droid.getUidForPackage(conn_test_class)
+ for ad in self.android_devices:
+ try:
+ ad.adb.shell(instr_cmd)
+ except adb.AdbError:
+ self.log.warn("adb cmd %s failed on %s" % (instr_cmd, ad.serial))
+
+ # Set chrome browser start with no-first-run verification
+ # Give permission to read from and write to storage
+ commands = ["pm grant com.android.chrome "
+ "android.permission.READ_EXTERNAL_STORAGE",
+ "pm grant com.android.chrome "
+ "android.permission.WRITE_EXTERNAL_STORAGE",
+ "rm /data/local/chrome-command-line",
+ "am set-debug-app --persistent com.android.chrome",
+ 'echo "chrome --no-default-browser-check --no-first-run '
+ '--disable-fre" > /data/local/tmp/chrome-command-line']
+ for cmd in commands:
+ for dut in self.android_devices:
+ try:
+ dut.adb.shell(cmd)
+ except adb.AdbError:
+ self.log.warn("adb command %s failed on %s" % (cmd, dut.serial))
+
+ def teardown_class(self):
+ """ Reset devices """
+ wutils.reset_wifi(self.dut)
+
+ """ Helper functions """
+
+ def _download_data_through_app(self, ad):
+ """ Download data through app on DUT
+
+ Args:
+ 1. ad - DUT to download the file on
+
+ Returns:
+ True - if file download is successful
+ False - if file download is not successful
+ """
+ intent = self.dut.droid.createIntentForClassName(conn_test_class)
+ json_obj = {"url": self.download_file}
+ ad.droid.launchForResultWithIntent(intent, json_obj)
+ download_status = False
+ end_time = time.time() + TIMEOUT
+ while time.time() < end_time:
+ download_status = ttutils._check_file_existance(
+ ad, self.file_path, self.file_size * BYTE_TO_MB)
+ if download_status:
+ self.log.info("Delete file: %s", self.file_path)
+ ad.adb.shell("rm %s" % self.file_path, ignore_status=True)
+ break
+ time.sleep(8) # wait to check again if download is complete
+ return download_status
+
+ def _get_data_usage(self, ad, conn_type):
+ """ Get data usage
+
+ Args:
+ 1. ad - DUT to get data usage from
+ 2. conn_type - MOBILE/WIFI data usage
+
+ Returns:
+ Tuple of Android Os app, Conn UID app, Total data usages
+ """
+ aos = self._get_data_usage_for_uid_rx(ad, conn_type, self.android_os_uid)
+ app = self._get_data_usage_for_uid_rx(ad, conn_type, self.conn_test_uid)
+ tot = self._get_data_usage_for_device_rx(ad, conn_type)
+ self.log.info("Android Os data usage: %s" % aos)
+ self.log.info("Conn UID Test data usage: %s" % app)
+ self.log.info("Total data usage: %s" % tot)
+ return (aos, app, tot)
+
+ def _get_total_data_usage_for_device(self, conn_type):
+ """ Get total data usage in MB for device
+
+ Args:
+ 1. conn_type - MOBILE/WIFI data usage
+
+ Returns:
+ Data usage in MB
+ """
+ end_time = int(time.time() * 1000) + 2 * HOUR_IN_MILLIS
+ data_usage = self.dut.droid.connectivityQuerySummaryForDevice(
+ conn_type, self.sub_id, 0, end_time)
+ data_usage /= BYTE_TO_MB_ANDROID
+ self.log.info("Total data usage is: %s" % data_usage)
+ return data_usage
+
+ def _get_data_usage_for_uid_rx(self, ad, conn_type, uid):
+ """ Get data usage for UID in Rx Bytes
+
+ Args:
+ 1. ad - DUT to get data usage from
+ 2. conn_type - MOBILE/WIFI data usage
+ 3. uid - UID of the app
+
+ Returns:
+ Data usage in MB
+ """
+ subscriber_id = ad.droid.telephonyGetSubscriberId()
+ end_time = int(time.time() * 1000) + 2 * HOUR_IN_MILLIS
+ data_usage = ad.droid.connectivityQueryDetailsForUidRxBytes(
+ conn_type, subscriber_id, 0, end_time, uid)
+ return data_usage/BYTE_TO_MB_ANDROID
+
+ def _get_data_usage_for_device_rx(self, ad, conn_type):
+ """ Get total data usage in rx bytes for device
+
+ Args:
+ 1. ad - DUT to get data usage from
+ 2. conn_type - MOBILE/WIFI data usage
+
+ Returns:
+ Data usage in MB
+ """
+ subscriber_id = ad.droid.telephonyGetSubscriberId()
+ end_time = int(time.time() * 1000) + 2 * HOUR_IN_MILLIS
+ data_usage = ad.droid.connectivityQuerySummaryForDeviceRxBytes(
+ conn_type, subscriber_id, 0, end_time)
+ return data_usage/BYTE_TO_MB_ANDROID
+
+ """ Test Cases """
+
+ @test_tracker_info(uuid="b2d9b36c-3a1c-47ca-a9c1-755450abb20c")
+ def test_mobile_data_usage_downlink(self):
+ """ Verify mobile data usage
+
+ Steps:
+ 1. Get the current data usage of ConnUIDTest and Android OS apps
+ 2. DUT is on LTE data
+ 3. Download file of size xMB through ConnUIDTest app
+ 4. Verify that data usage of Android OS app did not change
+ 5. Verify that data usage of ConnUIDTest app increased by ~xMB
+ 6. Verify that data usage of device also increased by ~xMB
+ """
+ # disable wifi
+ wutils.wifi_toggle_state(self.dut, False)
+
+ # get pre mobile data usage
+ (aos_pre, app_pre, total_pre) = self._get_data_usage(self.dut,
+ cconst.TYPE_MOBILE)
+
+ # download file through app
+ self._download_data_through_app(self.dut)
+
+ # get new mobile data usage
+ (aos_pst, app_pst, total_pst) = self._get_data_usage(self.dut,
+ cconst.TYPE_MOBILE)
+
+ # verify data usage
+ aos_diff = aos_pst - aos_pre
+ app_diff = app_pst - app_pre
+ total_diff = total_pst - total_pre
+ self.log.info("Data usage of Android os increased by %s" % aos_diff)
+ self.log.info("Data usage of ConnUID app increased by %s" % app_diff)
+ self.log.info("Data usage on the device increased by %s" % total_diff)
+ return (aos_diff < DATA_ERR) and \
+ (self.file_size < app_diff < self.file_size + DATA_USG_ERR) and \
+ (self.file_size < total_diff < self.file_size + DATA_USG_ERR)
+
+ @test_tracker_info(uuid="72ddb42a-5942-4a6a-8b20-2181c41b2765")
+ def test_wifi_data_usage_downlink(self):
+ """ Verify wifi data usage
+
+ Steps:
+ 1. Get the current data usage of ConnUIDTest and Android OS apps
+ 2. DUT is on LTE data
+ 3. Download file of size xMB through ConnUIDTest app
+ 4. Verify that data usage of Android OS app did not change
+ 5. Verify that data usage of ConnUIDTest app increased by ~xMB
+ 6. Verify that data usage of device also increased by ~xMB
+ """
+ # connect to wifi network
+ wutils.wifi_connect(self.dut, self.wifi_network)
+
+ # get pre wifi data usage
+ (aos_pre, app_pre, total_pre) = self._get_data_usage(self.dut,
+ cconst.TYPE_WIFI)
+
+ # download file through app
+ self._download_data_through_app(self.dut)
+
+ # get new mobile data usage
+ (aos_pst, app_pst, total_pst) = self._get_data_usage(self.dut,
+ cconst.TYPE_WIFI)
+
+ # verify data usage
+ aos_diff = aos_pst - aos_pre
+ app_diff = app_pst - app_pre
+ total_diff = total_pst - total_pre
+ self.log.info("Data usage of Android os increased by %s" % aos_diff)
+ self.log.info("Data usage of ConnUID app increased by %s" % app_diff)
+ self.log.info("Data usage on the device increased by %s" % total_diff)
+ return (aos_diff < DATA_ERR) and \
+ (self.file_size < app_diff < self.file_size + DATA_USG_ERR) and \
+ (self.file_size < total_diff < self.file_size + DATA_USG_ERR)
+
+ @test_tracker_info(uuid="fe1390e5-635c-49a9-b050-032e66f52f40")
+ def test_wifi_tethering_mobile_data_usage_downlink(self):
+ """ Verify mobile data usage with tethered device
+
+ Steps:
+ 1. Start wifi hotspot and connect tethered device to it
+ 2. Get the data usage on hotspot device
+ 3. Download data on tethered device
+ 4. Get the new data usage on hotspot device
+ 5. Verify that hotspot device's data usage increased by downloaded file size
+ """
+ # connect device to wifi hotspot
+ ad = self.tethered_devices[0]
+ wutils.start_wifi_tethering(self.dut,
+ self.network[wutils.WifiEnums.SSID_KEY],
+ self.network[wutils.WifiEnums.PWD_KEY],
+ ttutils.WIFI_CONFIG_APBAND_2G)
+ wutils.wifi_connect(ad, self.network)
+
+ # get pre mobile data usage
+ (aos_pre, app_pre, total_pre) = self._get_data_usage(self.dut,
+ cconst.TYPE_MOBILE)
+
+ # download file through app
+ self._download_data_through_app(ad)
+
+ # get new mobile data usage
+ (aos_pst, app_pst, total_pst) = self._get_data_usage(self.dut,
+ cconst.TYPE_MOBILE)
+
+ # stop wifi hotspot
+ wutils.stop_wifi_tethering(self.dut)
+
+ # verify data usage
+ aos_diff = aos_pst - aos_pre
+ app_diff = app_pst - app_pre
+ total_diff = total_pst - total_pre
+ self.log.info("Data usage of Android os increased by %s" % aos_diff)
+ self.log.info("Data usage of ConnUID app increased by %s" % app_diff)
+ self.log.info("Data usage on the device increased by %s" % total_diff)
+ return (aos_diff < DATA_ERR) and (app_diff < DATA_ERR) and \
+ (self.file_size < total_diff < self.file_size + DATA_USG_ERR)
+
+ @test_tracker_info(uuid="ac4750fd-20d9-451d-a85b-79fdbaa7da97")
+ def test_data_usage_limit_downlink(self):
+ """ Verify connectivity when data usage limit reached
+
+ Steps:
+ 1. Set the data usage limit to current data usage + 10MB
+ 2. Download 20MB data
+ 3. File download stops and data limit reached
+ 4. Device should lose internet connectivity
+ 5. Verify data usage limit
+ """
+ # get pre mobile data usage
+ total_pre = self._get_total_data_usage_for_device(cconst.TYPE_MOBILE)
+
+ # set data usage limit to current usage limit + 10MB
+ self.log.info("Setting data usage limit to %sMB" % (total_pre + INC_DATA))
+ self.dut.droid.connectivitySetDataUsageLimit(
+ self.sub_id, str(int((total_pre + INC_DATA) * BYTE_TO_MB_ANDROID)))
+
+ # download file through app
+ http_file_download_by_chrome(
+ self.dut, self.download_file, self.file_size, timeout=120)
+ total_pst = self._get_total_data_usage_for_device(cconst.TYPE_MOBILE)
+
+ # verify data usage
+ connectivity_status = wutils.validate_connection(self.dut)
+ self.dut.droid.connectivityFactoryResetNetworkPolicies(self.sub_id)
+ self.log.info("Expected data usage: %s" % (total_pre + INC_DATA))
+ self.log.info("Actual data usage: %s" % total_pst)
+ asserts.assert_true(
+ not connectivity_status,
+ "Device has internet connectivity after reaching data limit")
+ return total_pst - total_pre - INC_DATA < DATA_USG_ERR
+
+ @test_tracker_info(uuid="7c9ab330-9645-4030-bb1e-dcce126944a2")
+ def test_wifi_tethering_data_usage_limit_downlink(self):
+ """ Verify connectivity when data usage limit reached
+
+ Steps:
+ 1. Set the data usage limit to current data usage + 10MB
+ 2. Start wifi tethering and connect a dut to the SSID
+ 3. Download 20MB data on tethered device
+ 4. File download stops and data limit reached
+ 5. Verify data usage limit
+ """
+ # connect device to wifi hotspot
+ ad = self.tethered_devices[0]
+ wutils.toggle_wifi_off_and_on(self.dut)
+ wutils.start_wifi_tethering(self.dut,
+ self.network[wutils.WifiEnums.SSID_KEY],
+ self.network[wutils.WifiEnums.PWD_KEY],
+ ttutils.WIFI_CONFIG_APBAND_2G)
+ wutils.wifi_connect(ad, self.network)
+
+ # get pre mobile data usage
+ total_pre = self._get_total_data_usage_for_device(cconst.TYPE_MOBILE)
+
+ # set data usage limit to current usage limit + 10MB
+ self.log.info("Setting data usage limit to %sMB" % (total_pre + INC_DATA))
+ self.dut.droid.connectivitySetDataUsageLimit(
+ self.sub_id, str(int((total_pre + INC_DATA) * BYTE_TO_MB_ANDROID)))
+
+ # download file from tethered device
+ http_file_download_by_chrome(
+ ad, self.download_file, self.file_size, timeout=120)
+ total_pst = self._get_total_data_usage_for_device(cconst.TYPE_MOBILE)
+
+ # verify data usage
+ connectivity_status = wutils.validate_connection(ad)
+ self.dut.droid.connectivityFactoryResetNetworkPolicies(self.sub_id)
+ wutils.stop_wifi_tethering(self.dut)
+ self.log.info("Expected data usage: %s" % (total_pre + INC_DATA))
+ self.log.info("Actual data usage: %s" % total_pst)
+ asserts.assert_true(
+ not connectivity_status,
+ "Device has internet connectivity after reaching data limit")
+ return total_pst - total_pre - INC_DATA < DATA_USG_ERR
diff --git a/acts/tests/google/net/DnsOverTlsTest.py b/acts/tests/google/net/DnsOverTlsTest.py
new file mode 100644
index 0000000..46a6c01
--- /dev/null
+++ b/acts/tests/google/net/DnsOverTlsTest.py
@@ -0,0 +1,370 @@
+#
+# Copyright 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.
+
+import logging
+import os
+import random
+import socket
+import threading
+import time
+
+from acts import asserts
+from acts import base_test
+from acts import test_runner
+from acts.controllers import adb
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
+from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
+from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
+from acts.test_utils.tel.tel_test_utils import verify_http_connection
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+from scapy.all import TCP
+from scapy.all import UDP
+from scapy.all import rdpcap
+
+DNS_QUAD9 = "dns.quad9.net"
+PRIVATE_DNS_MODE_OFF = "off"
+PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic"
+PRIVATE_DNS_MODE_STRICT = "hostname"
+RST = 0x04
+
+class DnsOverTlsTest(base_test.BaseTestClass):
+ """ Tests for Wifi Tethering """
+
+ def setup_class(self):
+ """ Setup devices for tethering and unpack params """
+
+ self.dut = self.android_devices[0]
+ wutils.reset_wifi(self.dut)
+ self.dut.droid.telephonyToggleDataConnection(True)
+ wait_for_cell_data_connection(self.log, self.dut, True)
+ asserts.assert_true(
+ verify_http_connection(self.log, self.dut),
+ "HTTP verification failed on cell data connection")
+ req_params = ("wifi_network_with_dns_tls", "wifi_network_no_dns_tls",
+ "ping_hosts")
+ self.unpack_userparams(req_params)
+ self.tcpdump_pid = None
+ self.tcpdump_file = None
+
+ """ Helper functions """
+
+ def _start_tcp_dump(self, ad):
+ """ Start tcpdump on the give dut
+
+ Args:
+ 1. ad: dut to run tcpdump on
+ """
+ if self.tcpdump_pid:
+ stop_adb_tcpdump(ad, self.tcpdump_pid, pull_tcpdump=False)
+ self.tcpdump_pid = start_adb_tcpdump(ad, self.test_name, mask='all')
+
+ def _stop_tcp_dump(self, ad):
+ """ Stop tcpdump and pull it to the test run logs
+
+ Args:
+ 1. ad: dut to pull tcpdump from
+ """
+ file_name = ad.adb.shell("ls /sdcard/tcpdump")
+ file_name = os.path.join(ad.log_path, "TCPDUMP_%s" % ad.serial,
+ file_name.split('/')[-1])
+ if self.tcpdump_pid:
+ stop_adb_tcpdump(ad, self.tcpdump_pid, pull_tcpdump=True)
+ self.tcpdump_pid = None
+ return os.path.join(ad.log_path, file_name)
+
+ def _verify_dns_queries_over_tls(self, pcap_file, tls=True):
+ """ Verify if DNS queries were over TLS or not
+
+ Args:
+ 1. pcap_file: tcpdump file
+ 2. tls: if queries should be over TLS or port 853
+ """
+ try:
+ packets = rdpcap(pcap_file)
+ except Scapy_Exception:
+ asserts.fail("Not a valid pcap file")
+ for pkt in packets:
+ summary = "%s" % pkt.summary()
+ if tls and UDP in pkt and pkt[UDP].dport == 53 and \
+ "connectivitycheck.gstatic.com." not in summary and \
+ "www.google.com" not in summary:
+ asserts.fail("Found query to port 53: %s" % summary)
+ elif not tls and TCP in pkt and pkt[TCP].dport == 853 and \
+ not pkt[TCP].flags:
+ asserts.fail("Found query to port 853: %s" % summary)
+
+ def _verify_rst_packets(self, pcap_file):
+ """ Verify if RST packets are found in the pcap file """
+ packets = rdpcap(pcap_file)
+ for pkt in packets:
+ if TCP in pkt and pkt[TCP].flags == RST:
+ asserts.fail("Found RST packets: %s" % pkt.summary())
+
+ def _test_private_dns_mode(self, network, dns_mode, use_tls,
+ hostname = None):
+ """ Test private DNS mode """
+ # connect to wifi
+ wutils.reset_wifi(self.dut)
+ if network:
+ wutils.wifi_connect(self.dut, network)
+ time.sleep(1) # wait till lte network becomes active - network = None
+
+ # start tcpdump on the device
+ self._start_tcp_dump(self.dut)
+
+ # set private dns mode
+ if dns_mode == PRIVATE_DNS_MODE_OFF:
+ self.dut.droid.setPrivateDnsMode(False)
+ elif hostname:
+ self.dut.droid.setPrivateDnsMode(True, hostname)
+ else:
+ self.dut.droid.setPrivateDnsMode(True)
+ mode = self.dut.droid.getPrivateDnsMode()
+ asserts.assert_true(mode == dns_mode,
+ "Failed to set private DNS mode to %s" % dns_mode)
+
+ # ping hosts should pass
+ for host in self.ping_hosts:
+ self.log.info("Pinging %s" % host)
+ asserts.assert_true(wutils.validate_connection(self.dut, host),
+ "Failed to ping host %s" % host)
+
+ # stop tcpdump
+ pcap_file = self._stop_tcp_dump(self.dut)
+ self.log.info("TCPDUMP file is: %s" % pcap_file)
+
+ # verify DNS queries
+ self._verify_dns_queries_over_tls(pcap_file, use_tls)
+
+ """ Test Cases """
+
+ @test_tracker_info(uuid="2957e61c-d333-45fb-9ff9-2250c9c8535a")
+ def test_private_dns_mode_off_wifi_no_dns_tls_server(self):
+ """ Verify private dns mode off
+
+ Steps:
+ 1. Set private dns mode off
+ 2. Connect to wifi network. DNS/TLS server is not set
+ 3. Verify ping works to differnt hostnames
+ 4. Verify that all queries go to port 53
+ """
+ self._test_private_dns_mode(self.wifi_network_no_dns_tls,
+ PRIVATE_DNS_MODE_OFF, False)
+
+ @test_tracker_info(uuid="ea036d22-25af-4df0-b6cc-0027bc1efbe9")
+ def test_private_dns_mode_off_wifi_with_dns_tls_server(self):
+ """ Verify private dns mode off
+
+ Steps:
+ 1. Set private dns mode off
+ 2. Connect to wifi network. DNS server is set to 9.9.9.9, 8.8.8.8
+ 3. Verify ping works to differnt hostnames
+ 4. Verify that all queries go to port 53
+ """
+ self._test_private_dns_mode(self.wifi_network_with_dns_tls,
+ PRIVATE_DNS_MODE_OFF, False)
+
+ @test_tracker_info(uuid="4227abf4-0a75-4b4d-968c-dfc63052f5db")
+ def test_private_dns_mode_opportunistic_wifi_no_dns_tls_server(self):
+ """ Verify private dns opportunistic mode
+
+ Steps:
+ 1. Set private dns mode to opportunistic
+ 2. Connect to wifi network. DNS/TLS server is not set
+ 3. Verify ping works to differnt hostnames
+ 4. Verify that all queries go to port 53
+ """
+ self._test_private_dns_mode(self.wifi_network_no_dns_tls,
+ PRIVATE_DNS_MODE_OPPORTUNISTIC, False)
+
+ @test_tracker_info(uuid="0c97cfef-4313-4346-b05b-395de63c5c3f")
+ def test_private_dns_mode_opportunistic_wifi_with_dns_tls_server(self):
+ """ Verify private dns opportunistic mode
+
+ Steps:
+ 1. Set private dns mode to opportunistic
+ 2. Connect to wifi network. DNS server is set to 9.9.9.9, 8.8.8.8
+ 3. Verify ping works to differnt hostnames
+ 4. Verify that all queries go to port 853
+ """
+ self._test_private_dns_mode(self.wifi_network_with_dns_tls,
+ PRIVATE_DNS_MODE_OPPORTUNISTIC, True)
+
+ @test_tracker_info(uuid="b70569f1-2613-49d0-be49-fd3464dde305")
+ def test_private_dns_mode_strict_wifi_no_dns_tls_server(self):
+ """ Verify private dns strict mode
+
+ Steps:
+ 1. Set private dns mode to strict
+ 2. Connect to wifi network. DNS/TLS server is not set
+ 3. Verify ping works to differnt hostnames
+ 4. Verify that all queries go to port 853
+ """
+ self._test_private_dns_mode(self.wifi_network_no_dns_tls,
+ PRIVATE_DNS_MODE_STRICT, True,
+ DNS_QUAD9)
+
+ @test_tracker_info(uuid="85738b52-823b-4c59-a0d5-219e2fab2929")
+ def test_private_dns_mode_strict_wifi_with_dns_tls_server(self):
+ """ Verify private dns strict mode
+
+ Steps:
+ 1. Set private dns mode to strict
+ 2. Connect to wifi network. DNS server is set to 9.9.9.9, 8.8.8.8
+ 3. Verify ping works to differnt hostnames
+ 4. Verify that all queries go to port 853
+ """
+ self._test_private_dns_mode(self.wifi_network_with_dns_tls,
+ PRIVATE_DNS_MODE_STRICT, True,
+ DNS_QUAD9)
+
+ @test_tracker_info(uuid="727e280a-d2bd-463f-b2a1-653d4b3f7f29")
+ def test_private_dns_mode_off_lte(self):
+ """ Verify private dns off mode
+
+ Steps:
+ 1. Set private dns mode to off
+ 2. Reset wifi and enable LTE on DUT
+ 3. Verify ping works to differnt hostnames
+ 4. Verify that all queries go to port 53
+ """
+ self._test_private_dns_mode(None, PRIVATE_DNS_MODE_OFF, False)
+
+ @test_tracker_info(uuid="b16f6e2c-a24f-4efe-9003-2bfaf28b8d5e")
+ def test_private_dns_mode_opportunistic_lte(self):
+ """ Verify private dns opportunistic mode
+
+ Steps:
+ 1. Set private dns mode to opportunistic mode
+ 2. Reset wifi and enable LTE on DUT
+ 3. Verify ping works to differnt hostnames
+ 4. Verify that all queries go to port 853
+ """
+ self._test_private_dns_mode(None, PRIVATE_DNS_MODE_OPPORTUNISTIC, True)
+
+ @test_tracker_info(uuid="edfa7bfe-3e52-46b4-9d72-7c6c300b3680")
+ def test_private_dns_mode_strict_lte(self):
+ """ Verify private dns strict mode
+
+ Steps:
+ 1. Set private dns mode to strict mode
+ 2. Reset wifi and enable LTE on DUT
+ 3. Verify ping works to differnt hostnames
+ 4. Verify that all queries go to port 853
+ """
+ self._test_private_dns_mode(None, PRIVATE_DNS_MODE_STRICT, True,
+ DNS_QUAD9)
+
+ @test_tracker_info(uuid="1426673a-7728-4df7-8de5-dfb3529ada62")
+ def test_dns_server_link_properties_strict_mode(self):
+ """ Verify DNS server in the link properties when set in strict mode
+
+ Steps:
+ 1. Set DNS server hostname in Private DNS settings (stict mode)
+ 2. Verify that DNS server set in settings is in link properties
+ 3. Verify for WiFi as well as LTE
+ """
+ # start tcpdump on device
+ self._start_tcp_dump(self.dut)
+
+ # set private DNS to strict mode
+ self.dut.droid.setPrivateDnsMode(True, DNS_QUAD9)
+ mode = self.dut.droid.getPrivateDnsMode()
+ specifier = self.dut.droid.getPrivateDnsSpecifier()
+ asserts.assert_true(
+ mode == PRIVATE_DNS_MODE_STRICT and specifier == DNS_QUAD9,
+ "Failed to set private DNS strict mode")
+
+ # connect DUT to wifi network
+ wutils.wifi_connect(self.dut, self.wifi_network_no_dns_tls)
+ for host in self.ping_hosts:
+ wutils.validate_connection(self.dut, host)
+
+ # DNS server in link properties for wifi network
+ link_prop = self.dut.droid.connectivityGetActiveLinkProperties()
+ dns_servers = link_prop['DnsServers']
+ wifi_dns_servers = [each for lst in dns_servers for each in lst]
+ self.log.info("Link prop: %s" % wifi_dns_servers)
+
+ # DUT is on LTE data
+ wutils.reset_wifi(self.dut)
+ time.sleep(1) # wait till lte network becomes active
+ for host in self.ping_hosts:
+ wutils.validate_connection(self.dut, host)
+
+ # DNS server in link properties for cell network
+ link_prop = self.dut.droid.connectivityGetActiveLinkProperties()
+ dns_servers = link_prop['DnsServers']
+ lte_dns_servers = [each for lst in dns_servers for each in lst]
+ self.log.info("Link prop: %s" % lte_dns_servers)
+
+ # stop tcpdump on device
+ pcap_file = self._stop_tcp_dump(self.dut)
+ self.log.info("TCPDUMP file is: %s" % pcap_file)
+
+ # Verify DNS server in link properties
+ asserts.assert_true(DNS_QUAD9 in wifi_dns_servers,
+ "Hostname not in link properties - wifi network")
+ asserts.assert_true(DNS_QUAD9 in lte_dns_servers,
+ "Hostname not in link properites - cell network")
+
+ @test_tracker_info(uuid="525a6f2d-9751-474e-a004-52441091e427")
+ def test_dns_over_tls_no_reset_packets(self):
+ """ Verify there are no TCP packets with RST flags
+
+ Steps:
+ 1. Enable opportunistic or strict mode
+ 2. Ping hosts and verify that there are TCP pkts with RST flags
+ """
+ # start tcpdump on device
+ self._start_tcp_dump(self.dut)
+
+ # set private DNS to opportunistic mode
+ self.dut.droid.setPrivateDnsMode(True)
+ mode = self.dut.droid.getPrivateDnsMode()
+ asserts.assert_true(mode == PRIVATE_DNS_MODE_OPPORTUNISTIC,
+ "Failed to set private DNS opportunistic mode")
+
+ # connect DUT to wifi network
+ wutils.wifi_connect(self.dut, self.wifi_network_with_dns_tls)
+ for host in self.ping_hosts:
+ wutils.validate_connection(self.dut, host)
+
+ # stop tcpdump on device
+ pcap_file = self._stop_tcp_dump(self.dut)
+ self.log.info("TCPDUMP file is: %s" % pcap_file)
+
+ # check that there no RST TCP packets
+ self._verify_rst_packets(pcap_file)
+
+ @test_tracker_info(uuid="af6e34f1-3ad5-4ab0-b3b9-53008aa08294")
+ def test_private_dns_mode_strict_invalid_hostnames(self):
+ """ Verify that invalid hostnames are not saved for strict mode
+
+ Steps:
+ 1. Set private DNS to strict mode with invalid hostname
+ 2. Verify that invalid hostname is not saved
+ """
+ invalid_hostnames = ["!%@&!*", "12093478129", "9.9.9.9", "sdkfjhasdf"]
+ for hostname in invalid_hostnames:
+ self.dut.droid.setPrivateDnsMode(True, hostname)
+ mode = self.dut.droid.getPrivateDnsMode()
+ specifier = self.dut.droid.getPrivateDnsSpecifier()
+ wutils.wifi_connect(self.dut, self.wifi_network_no_dns_tls)
+ asserts.assert_true(
+ mode == PRIVATE_DNS_MODE_STRICT and specifier != hostname,
+ "Able to set invalid private DNS strict mode")
diff --git a/acts/tests/google/net/IpSecTest.py b/acts/tests/google/net/IpSecTest.py
new file mode 100644
index 0000000..dc7fd42
--- /dev/null
+++ b/acts/tests/google/net/IpSecTest.py
@@ -0,0 +1,664 @@
+#
+# Copyright 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.
+
+from acts import asserts
+from acts import base_test
+from acts.controllers import adb
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.net import connectivity_const as cconst
+from acts.test_utils.net import ipsec_test_utils as iutils
+from acts.test_utils.net import socket_test_utils as sutils
+from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
+from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+import random
+
+WLAN = "wlan0"
+
+
+class IpSecTest(base_test.BaseTestClass):
+ """ Tests for UID networking """
+
+ def setup_class(self):
+ """ Setup devices for tests and unpack params """
+ self.dut_a = self.android_devices[0]
+ self.dut_b = self.android_devices[1]
+
+ req_params = ("wifi_network",)
+ self.unpack_userparams(req_params)
+ wutils.wifi_connect(self.dut_a, self.wifi_network)
+ wutils.wifi_connect(self.dut_b, self.wifi_network)
+
+ self.ipv4_dut_a = self.dut_a.droid.connectivityGetIPv4Addresses(WLAN)[0]
+ self.ipv4_dut_b = self.dut_b.droid.connectivityGetIPv4Addresses(WLAN)[0]
+ self.ipv6_dut_a = self.dut_a.droid.connectivityGetIPv6Addresses(WLAN)[0]
+ self.ipv6_dut_b = self.dut_b.droid.connectivityGetIPv6Addresses(WLAN)[0]
+
+ self.tcpdump_pid_a = None
+ self.tcpdump_file_a = None
+ self.tcpdump_pid_b = None
+ self.tcpdump_file_b = None
+
+ self.crypt_auth_combos = iutils.generate_random_crypt_auth_combo()
+
+ def teardown_class(self):
+ for ad in self.android_devices:
+ wutils.reset_wifi(ad)
+
+ def setup_test(self):
+ self.tcpdump_pid_a = start_adb_tcpdump(
+ self.dut_a, self.test_name, mask='all')
+ self.tcpdump_pid_b = start_adb_tcpdump(
+ self.dut_b, self.test_name, mask='all')
+
+ def teardown_test(self):
+ if self.tcpdump_pid_a:
+ stop_adb_tcpdump(
+ self.dut_a, self.tcpdump_pid_a, pull_tcpdump=True)
+ if self.tcpdump_pid_b:
+ stop_adb_tcpdump(
+ self.dut_b, self.tcpdump_pid_b, pull_tcpdump=True)
+ self.tcpdump_pid_a = None
+ self.tcpdump_pid_b = None
+
+ def on_fail(self, test_name, begin_time):
+ self.dut_a.take_bug_report(test_name, begin_time)
+ self.dut_b.take_bug_report(test_name, begin_time)
+
+ """ Helper functions begin """
+
+ def _test_spi_allocate_release_req_spi(self, dut, ip):
+ req_spi = random.randint(1, 999999999)
+ self.log.info("IP addr: %s" % ip)
+ self.log.info("Request SPI: %s" % req_spi)
+ key = dut.droid.ipSecAllocateSecurityParameterIndex(ip, req_spi)
+ spi = dut.droid.ipSecGetSecurityParameterIndex(key)
+ self.log.info("Got: %s" % spi)
+ dut.droid.ipSecReleaseSecurityParameterIndex(key)
+ asserts.assert_true(req_spi == spi, "Incorrect SPI")
+ spi = dut.droid.ipSecGetSecurityParameterIndex(key)
+ self.log.info("SPI after release: %s" % spi)
+ asserts.assert_true(spi == 0, "Release SPI failed")
+
+ def _test_spi_allocate_release_random_spi(self, dut, ip):
+ self.log.info("IP addr: %s" % ip)
+ key = dut.droid.ipSecAllocateSecurityParameterIndex(ip)
+ spi = dut.droid.ipSecGetSecurityParameterIndex(key)
+ self.log.info("Got: %s" % spi)
+ dut.droid.ipSecReleaseSecurityParameterIndex(key)
+ spi = dut.droid.ipSecGetSecurityParameterIndex(key)
+ self.log.info("SPI after release: %s" % spi)
+ asserts.assert_true(spi == 0, "Release SPI failed")
+
+ def _test_transport_mode_transform_file_descriptors(self,
+ domain,
+ sock_type,
+ udp_encap = False):
+ """Test transport mode tranform with android.system.Os sockets"""
+
+ # dut objects & ip addrs
+ dut_a = self.dut_a
+ dut_b = self.dut_b
+ port = random.randint(5000, 6000)
+ udp_encap_port = 4500
+ ip_a = self.ipv4_dut_a
+ ip_b = self.ipv4_dut_b
+ if domain == cconst.AF_INET6:
+ ip_a = self.ipv6_dut_a
+ ip_b = self.ipv6_dut_b
+ self.log.info("DUT_A IP addr: %s" % ip_a)
+ self.log.info("DUT_B IP addr: %s" % ip_b)
+ self.log.info("Port: %s" % port)
+
+ # create crypt and auth keys
+ cl, auth_algo, al, trunc_bits = random.choice(self.crypt_auth_combos)
+ crypt_key = iutils.make_key(cl)
+ auth_key = iutils.make_key(al)
+ crypt_algo = cconst.CRYPT_AES_CBC
+
+ # open sockets
+ fd_a = sutils.open_android_socket(dut_a, domain, sock_type, ip_a, port)
+ fd_b = sutils.open_android_socket(dut_b, domain, sock_type, ip_b, port)
+
+ # allocate SPIs
+ spi_keys_a = iutils.allocate_spis(dut_a, ip_a, ip_b)
+ in_spi = dut_a.droid.ipSecGetSecurityParameterIndex(spi_keys_a[0])
+ out_spi = dut_a.droid.ipSecGetSecurityParameterIndex(spi_keys_a[1])
+ spi_keys_b = iutils.allocate_spis(dut_b, ip_b, ip_a, out_spi, in_spi)
+
+ # open udp encap sockets
+ udp_encap_a = None
+ udp_encap_b = None
+ if udp_encap:
+ udp_encap_a = dut_a.droid.ipSecOpenUdpEncapsulationSocket(
+ udp_encap_port)
+ udp_encap_b = dut_b.droid.ipSecOpenUdpEncapsulationSocket(
+ udp_encap_port)
+ self.log.info("UDP Encap: %s" % udp_encap_a)
+ self.log.info("UDP Encap: %s" % udp_encap_b)
+
+ # create transforms
+ transforms_a = iutils.create_transport_mode_transforms(
+ dut_a, spi_keys_a, ip_a, ip_b, crypt_algo, crypt_key, auth_algo,
+ auth_key, trunc_bits, udp_encap_a)
+ transforms_b = iutils.create_transport_mode_transforms(
+ dut_b, spi_keys_b, ip_b, ip_a, crypt_algo, crypt_key, auth_algo,
+ auth_key, trunc_bits, udp_encap_b)
+
+ # apply transforms to socket
+ iutils.apply_transport_mode_transforms_file_descriptors(
+ dut_a, fd_a, transforms_a)
+ iutils.apply_transport_mode_transforms_file_descriptors(
+ dut_b, fd_b, transforms_b)
+
+ # listen, connect, accept if TCP socket
+ if sock_type == cconst.SOCK_STREAM:
+ server_fd = fd_b
+ self.log.info("Setup TCP connection")
+ dut_b.droid.listenSocket(fd_b)
+ dut_a.droid.connectSocket(fd_a, ip_b, port)
+ fd_b = dut_b.droid.acceptSocket(fd_b)
+ asserts.assert_true(fd_b, "accept() failed")
+
+ # Send message from one dut to another
+ sutils.send_recv_data_android_sockets(
+ dut_a, dut_b, fd_a, fd_b, ip_b, port)
+
+ # verify ESP packets
+ iutils.verify_esp_packets([dut_a, dut_b])
+
+ # remove transforms from socket
+ iutils.remove_transport_mode_transforms_file_descriptors(dut_a, fd_a)
+ iutils.remove_transport_mode_transforms_file_descriptors(dut_b, fd_b)
+
+ # destroy transforms
+ iutils.destroy_transport_mode_transforms(dut_a, transforms_a)
+ iutils.destroy_transport_mode_transforms(dut_b, transforms_b)
+
+ # close udp encap sockets
+ if udp_encap:
+ dut_a.droid.ipSecCloseUdpEncapsulationSocket(udp_encap_a)
+ dut_b.droid.ipSecCloseUdpEncapsulationSocket(udp_encap_b)
+
+ # release SPIs
+ iutils.release_spis(dut_a, spi_keys_a)
+ iutils.release_spis(dut_b, spi_keys_b)
+
+ # Send message from one dut to another
+ sutils.send_recv_data_android_sockets(
+ dut_a, dut_b, fd_a, fd_b, ip_b, port)
+
+ # close sockets
+ sutils.close_android_socket(dut_a, fd_a)
+ sutils.close_android_socket(dut_b, fd_b)
+ if sock_type == cconst.SOCK_STREAM:
+ sutils.close_android_socket(dut_b, server_fd)
+
+ def _test_transport_mode_transform_datagram_sockets(self,
+ domain,
+ udp_encap = False):
+ """ Test transport mode transform datagram sockets """
+
+ # dut objects and ip addrs
+ dut_a = self.dut_a
+ dut_b = self.dut_b
+ port = random.randint(5000, 6000)
+ udp_encap_port = 4500
+ ip_a = self.ipv4_dut_a
+ ip_b = self.ipv4_dut_b
+ if domain == cconst.AF_INET6:
+ ip_a = self.ipv6_dut_a
+ ip_b = self.ipv6_dut_b
+ self.log.info("DUT_A IP addr: %s" % ip_a)
+ self.log.info("DUT_B IP addr: %s" % ip_b)
+ self.log.info("Port: %s" % port)
+
+ # create crypt and auth keys
+ cl, auth_algo, al, trunc_bits = random.choice(self.crypt_auth_combos)
+ crypt_key = iutils.make_key(cl)
+ auth_key = iutils.make_key(al)
+ crypt_algo = cconst.CRYPT_AES_CBC
+
+ # open sockets
+ socket_a = sutils.open_datagram_socket(dut_a, ip_a, port)
+ socket_b = sutils.open_datagram_socket(dut_b, ip_b, port)
+
+ # allocate SPIs
+ spi_keys_a = iutils.allocate_spis(dut_a, ip_a, ip_b)
+ in_spi = dut_a.droid.ipSecGetSecurityParameterIndex(spi_keys_a[0])
+ out_spi = dut_a.droid.ipSecGetSecurityParameterIndex(spi_keys_a[1])
+ spi_keys_b = iutils.allocate_spis(dut_b, ip_b, ip_a, out_spi, in_spi)
+
+ # open udp encap sockets
+ udp_encap_a = None
+ udp_encap_b = None
+ if udp_encap:
+ udp_encap_a = dut_a.droid.ipSecOpenUdpEncapsulationSocket(
+ udp_encap_port)
+ udp_encap_b = dut_b.droid.ipSecOpenUdpEncapsulationSocket(
+ udp_encap_port)
+ self.log.info("UDP Encap: %s" % udp_encap_a)
+ self.log.info("UDP Encap: %s" % udp_encap_b)
+
+ # create transforms
+ transforms_a = iutils.create_transport_mode_transforms(
+ dut_a, spi_keys_a, ip_a, ip_b, crypt_algo, crypt_key, auth_algo,
+ auth_key, trunc_bits, udp_encap_a)
+ transforms_b = iutils.create_transport_mode_transforms(
+ dut_b, spi_keys_b, ip_b, ip_a, crypt_algo, crypt_key, auth_algo,
+ auth_key, trunc_bits, udp_encap_b)
+
+ # apply transforms
+ iutils.apply_transport_mode_transforms_datagram_socket(
+ dut_a, socket_a, transforms_a)
+ iutils.apply_transport_mode_transforms_datagram_socket(
+ dut_b, socket_b, transforms_b)
+
+ # send and verify message from one dut to another
+ sutils.send_recv_data_datagram_sockets(
+ dut_a, dut_b, socket_a, socket_b, ip_b, port)
+
+ # verify ESP packets
+ iutils.verify_esp_packets([dut_a, dut_b])
+
+ # remove transforms
+ iutils.remove_transport_mode_transforms_datagram_socket(dut_a, socket_a)
+ iutils.remove_transport_mode_transforms_datagram_socket(dut_b, socket_b)
+
+ # destroy transforms
+ iutils.destroy_transport_mode_transforms(dut_a, transforms_a)
+ iutils.destroy_transport_mode_transforms(dut_b, transforms_b)
+
+ # close udp encap sockets
+ if udp_encap:
+ dut_a.droid.ipSecCloseUdpEncapsulationSocket(udp_encap_a)
+ dut_b.droid.ipSecCloseUdpEncapsulationSocket(udp_encap_b)
+
+ # release SPIs
+ iutils.release_spis(dut_a, spi_keys_a)
+ iutils.release_spis(dut_b, spi_keys_b)
+
+ # Send and verify message from one dut to another
+ sutils.send_recv_data_datagram_sockets(
+ dut_a, dut_b, socket_a, socket_b, ip_b, port)
+
+ # close sockets
+ sutils.close_datagram_socket(dut_a, socket_a)
+ sutils.close_datagram_socket(dut_b, socket_b)
+
+ def _test_transport_mode_transform_sockets(self, domain, udp_encap = False):
+
+ # dut objects and ip addrs
+ dut_a = self.dut_a
+ dut_b = self.dut_b
+ port = random.randint(5000, 6000)
+ ip_a = self.ipv4_dut_a
+ ip_b = self.ipv4_dut_b
+ if domain == cconst.AF_INET6:
+ ip_a = self.ipv6_dut_a
+ ip_b = self.ipv6_dut_b
+ self.log.info("DUT_A IP addr: %s" % ip_a)
+ self.log.info("DUT_B IP addr: %s" % ip_b)
+ self.log.info("Port: %s" % port)
+ udp_encap_port = 4500
+
+ # open sockets
+ server_sock = sutils.open_server_socket(dut_b, ip_b, port)
+ sock_a, sock_b = sutils.open_connect_socket(
+ dut_a, dut_b, ip_a, ip_b, port, port, server_sock)
+
+ # create crypt and auth keys
+ cl, auth_algo, al, trunc_bits = random.choice(self.crypt_auth_combos)
+ crypt_key = iutils.make_key(cl)
+ auth_key = iutils.make_key(al)
+ crypt_algo = cconst.CRYPT_AES_CBC
+
+ # allocate SPIs
+ spi_keys_a = iutils.allocate_spis(dut_a, ip_a, ip_b)
+ in_spi = dut_a.droid.ipSecGetSecurityParameterIndex(spi_keys_a[0])
+ out_spi = dut_a.droid.ipSecGetSecurityParameterIndex(spi_keys_a[1])
+ spi_keys_b = iutils.allocate_spis(dut_b, ip_b, ip_a, out_spi, in_spi)
+
+ # open udp encap sockets
+ udp_encap_a = None
+ udp_encap_b = None
+ if udp_encap:
+ udp_encap_a = dut_a.droid.ipSecOpenUdpEncapsulationSocket(
+ udp_encap_port)
+ udp_encap_b = dut_b.droid.ipSecOpenUdpEncapsulationSocket(
+ udp_encap_port)
+ self.log.info("UDP Encap: %s" % udp_encap_a)
+ self.log.info("UDP Encap: %s" % udp_encap_b)
+
+ # create transforms
+ transforms_a = iutils.create_transport_mode_transforms(
+ dut_a, spi_keys_a, ip_a, ip_b, crypt_algo, crypt_key, auth_algo,
+ auth_key, trunc_bits, udp_encap_a)
+ transforms_b = iutils.create_transport_mode_transforms(
+ dut_b, spi_keys_b, ip_b, ip_a, crypt_algo, crypt_key, auth_algo,
+ auth_key, trunc_bits, udp_encap_b)
+
+ # apply transform to sockets
+ iutils.apply_transport_mode_transforms_socket(dut_a, sock_a, transforms_a)
+ iutils.apply_transport_mode_transforms_socket(dut_b, sock_b, transforms_b)
+
+ # send message from one dut to another
+ sutils.send_recv_data_sockets(dut_a, dut_b, sock_a, sock_b)
+
+ # verify esp packets
+ iutils.verify_esp_packets([dut_a, dut_b])
+
+ # remove transforms from socket
+ iutils.remove_transport_mode_transforms_socket(dut_a, sock_a)
+ iutils.remove_transport_mode_transforms_socket(dut_b, sock_b)
+
+ # destroy transforms
+ iutils.destroy_transport_mode_transforms(dut_a, transforms_a)
+ iutils.destroy_transport_mode_transforms(dut_b, transforms_b)
+
+ # close udp encap sockets
+ if udp_encap:
+ dut_a.droid.ipSecCloseUdpEncapsulationSocket(udp_encap_a)
+ dut_b.droid.ipSecCloseUdpEncapsulationSocket(udp_encap_b)
+
+ # release SPIs
+ iutils.release_spis(dut_a, spi_keys_a)
+ iutils.release_spis(dut_b, spi_keys_b)
+
+ # send message from one dut to another
+ sutils.send_recv_data_sockets(dut_a, dut_b, sock_a, sock_b)
+
+ # close sockets
+ sutils.close_socket(dut_a, sock_a)
+ sutils.close_socket(dut_b, sock_b)
+ sutils.close_server_socket(dut_b, server_sock)
+
+ """ Helper functions end """
+
+ """ Tests begin """
+
+ @test_tracker_info(uuid="877577e2-94e8-46d3-8fee-330327adba1e")
+ def test_spi_allocate_release_requested_spi_ipv4(self):
+ """
+ Steps:
+ 1. Request SPI for IPv4 dest addr
+ 2. SPI should be generated with the requested value
+ 3. Close SPI
+ """
+ self._test_spi_allocate_release_req_spi(self.dut_a, self.ipv4_dut_b)
+ self._test_spi_allocate_release_req_spi(self.dut_b, self.ipv4_dut_a)
+
+ @test_tracker_info(uuid="0be4c204-0e63-43fd-a7ff-8647eef21066")
+ def test_spi_allocate_release_requested_spi_ipv6(self):
+ """
+ Steps:
+ 1. Request SPI for IPv6 dest addr
+ 2. SPI should be generated with the requested value
+ 3. Close SPI
+ """
+ self._test_spi_allocate_release_req_spi(self.dut_a, self.ipv6_dut_b)
+ self._test_spi_allocate_release_req_spi(self.dut_b, self.ipv6_dut_a)
+
+ @test_tracker_info(uuid="75eea49c-5621-4410-839f-f6e7bdd792a0")
+ def test_spi_allocate_release_random_spi_ipv4(self):
+ """
+ Steps:
+ 1. Request SPI for IPv4 dest addr
+ 2. A random SPI should be generated
+ 3. Close SPI
+ """
+ self._test_spi_allocate_release_random_spi(self.dut_a, self.ipv4_dut_b)
+ self._test_spi_allocate_release_random_spi(self.dut_b, self.ipv4_dut_a)
+
+ @test_tracker_info(uuid="afad9e48-5573-4b3f-8dfd-c7a77eda4e1e")
+ def test_spi_allocate_release_random_spi_ipv6(self):
+ """
+ Steps:
+ 1. Request SPI for IPv6 dest addr
+ 2. A random SPI should be generated
+ 3. Close SPI
+ """
+ self._test_spi_allocate_release_random_spi(self.dut_a, self.ipv6_dut_b)
+ self._test_spi_allocate_release_random_spi(self.dut_b, self.ipv6_dut_a)
+
+ @test_tracker_info(uuid="97817f4d-159a-4692-93f7-a38b162a565e")
+ def test_allocate_release_multiple_random_spis_ipv4(self):
+ """
+ Steps:
+ 1. Request 10 SPIs at a time
+ 2. After 8, the remaining should return 0
+ """
+ res = True
+ spi_key_list = []
+ spi_list = []
+ for i in range(10):
+ spi_key = self.dut_a.droid.ipSecAllocateSecurityParameterIndex(
+ self.ipv4_dut_a)
+ spi_key_list.append(spi_key)
+ spi = self.dut_a.droid.ipSecGetSecurityParameterIndex(spi_key)
+ spi_list.append(spi)
+ if spi_list.count(0) > 2:
+ self.log.error("Valid SPIs is less than 8: %s" % spi_list)
+ res = False
+
+ spi_list = []
+ for key in spi_key_list:
+ self.dut_a.droid.ipSecReleaseSecurityParameterIndex(key)
+ spi = self.dut_a.droid.ipSecGetSecurityParameterIndex(key)
+ spi_list.append(spi)
+ if spi_list.count(0) != 10:
+ self.log.error("Could not release all SPIs")
+ res = False
+
+ return res
+
+ @test_tracker_info(uuid="efb22e2a-1c2f-43fc-85fa-5d9a5b8e2705")
+ def test_spi_allocate_release_requested_spi_ipv4_stress(self):
+ """
+ Steps:
+ 1. Request and release requested SPIs for IPv4
+ """
+ for i in range(15):
+ self._test_spi_allocate_release_req_spi(self.dut_a, self.ipv4_dut_b)
+ self._test_spi_allocate_release_req_spi(self.dut_b, self.ipv4_dut_a)
+
+ @test_tracker_info(uuid="589749b7-3e6c-4a19-8404-d4a725d63dfd")
+ def test_spi_allocate_release_requested_spi_ipv6_stress(self):
+ """
+ Steps:
+ 1. Request and release requested SPIs for IPv6
+ """
+ for i in range(15):
+ self._test_spi_allocate_release_req_spi(self.dut_a, self.ipv6_dut_b)
+ self._test_spi_allocate_release_req_spi(self.dut_b, self.ipv6_dut_a)
+
+ @test_tracker_info(uuid="4a48130a-3f69-4390-a587-20848dee4777")
+ def test_spi_allocate_release_random_spi_ipv4_stress(self):
+ """
+ Steps:
+ 1. Request and release random SPIs for IPv4
+ """
+ for i in range(15):
+ self._test_spi_allocate_release_random_spi(
+ self.dut_a, self.ipv4_dut_b)
+ self._test_spi_allocate_release_random_spi(
+ self.dut_b, self.ipv4_dut_a)
+
+ @test_tracker_info(uuid="b56f6b65-cd71-4462-a739-e29d60e90ae8")
+ def test_spi_allocate_release_random_spi_ipv6_stress(self):
+ """
+ Steps:
+ 1. Request and release random SPIs for IPv6
+ """
+ for i in range(15):
+ self._test_spi_allocate_release_random_spi(
+ self.dut_a, self.ipv6_dut_b)
+ self._test_spi_allocate_release_random_spi(
+ self.dut_b, self.ipv6_dut_a)
+
+ """ android.system.Os sockets """
+
+ @test_tracker_info(uuid="65c9ab9a-1128-4ba0-a1a1-a9feb99ce712")
+ def test_transport_mode_udp_ipv4_file_descriptors_no_nat_no_encap(self):
+ """
+ Steps:
+ 1. Encrypt a android.system.Os IPv4 UDP socket with transport mode
+ transform with UDP encap on a non-NAT network
+ 2. Verify encyption and decryption of packets
+ 3. Remove transform and verify that packets are unencrypted
+ """
+ self._test_transport_mode_transform_file_descriptors(cconst.AF_INET,
+ cconst.SOCK_DGRAM)
+
+ @test_tracker_info(uuid="66956266-f18b-490c-b859-08530c6745c9")
+ def test_transport_mode_udp_ipv4_file_descriptors_no_nat_with_encap(self):
+ """
+ Steps:
+ 1. Encrypt a android.system.Os IPv4 UDP socket with transport mode
+ transform with UDP encap on a non-NAT network
+ 2. Verify encyption and decryption of packets
+ 3. Remove transform and verify that packets are unencrypted
+ """
+ self._test_transport_mode_transform_file_descriptors(cconst.AF_INET,
+ cconst.SOCK_DGRAM,
+ True)
+
+ @test_tracker_info(uuid="467a1e7b-e508-439f-be24-a213bec4a9f0")
+ def test_transport_mode_tcp_ipv4_file_descriptors_no_nat_no_encap(self):
+ """
+ Steps:
+ 1. Encrypt a android.system.Os IPv4 TCP socket with transport mode
+ transform without UDP encap on a non-NAT network
+ 2. Verify encyption and decryption of packets
+ 3. Remove transform and verify that packets are unencrypted
+ """
+ self._test_transport_mode_transform_file_descriptors(cconst.AF_INET,
+ cconst.SOCK_STREAM)
+
+ @test_tracker_info(uuid="877a9516-2b1c-4b52-8931-3a9a55d57875")
+ def test_transport_mode_tcp_ipv4_file_descriptors_no_nat_with_encap(self):
+ """
+ Steps:
+ 1. Encrypt a android.system.Os IPv4 TCP socket with transport mode
+ transform with UDP encap on a non-NAT network
+ 2. Verify encyption and decryption of packets
+ 3. Remove transform and verify that packets are unencrypted
+ """
+ self._test_transport_mode_transform_file_descriptors(cconst.AF_INET,
+ cconst.SOCK_STREAM,
+ True)
+
+ @test_tracker_info(uuid="a2dd8dc7-99c8-4420-b586-b4cd68f4197e")
+ def test_transport_mode_udp_ipv6_file_descriptors(self):
+ """ Verify transport mode transform on android.Os datagram sockets ipv4
+
+ Steps:
+ 1. Encrypt a android.system.Os IPv6 UDP socket with transport mode
+ transform
+ 2. Verify that packets are encrypted and decrypted at the other end
+ 3. Remove tranform and verify that unencrypted packets are sent
+ """
+ self._test_transport_mode_transform_file_descriptors(cconst.AF_INET6,
+ cconst.SOCK_DGRAM)
+
+ @test_tracker_info(uuid="7bc49d79-ffa8-4946-a25f-11daed4061cc")
+ def test_transport_mode_tcp_ipv6_file_descriptors(self):
+ """ Verify transport mode on android.system.Os stream sockets ipv6
+
+ Steps:
+ 1. Encrypt a android.system.Os IPv6 TCP socket with transport mode
+ transform
+ 2. Verify that packets are encrypted and decrypted at the other end
+ 3. Remove tranform and verify that unencrypted packets are sent
+ """
+ self._test_transport_mode_transform_file_descriptors(cconst.AF_INET6,
+ cconst.SOCK_STREAM)
+
+ """ Datagram socket tests """
+
+ @test_tracker_info(uuid="7ff29fc9-41fd-44f4-81e9-ce8c3afd9304")
+ def test_transport_mode_udp_ipv4_datagram_sockets_no_nat_no_encap(self):
+ """ Verify transport mode transform on datagram sockets ipv6 - UDP
+
+ Steps:
+ 1. Encypt a DatagramSocket IPv6 socket with transport mode transform
+ without UDP encap on a non-NAT network
+ 2. Verify that packets are encrypted and decrypted at the other end
+ 3. Remove tranform and verify that unencrypted packets are sent
+ """
+ self._test_transport_mode_transform_datagram_sockets(cconst.AF_INET)
+
+ @test_tracker_info(uuid="552af945-a23a-49f2-9ceb-b0d1e7c1d50b")
+ def test_transport_mode_udp_ipv4_datagram_sockets_no_nat_with_encap(self):
+ """ Verify transport mode transform on datagram sockets ipv6 - UDP
+
+ Steps:
+ 1. Encypt a DatagramSocket IPv6 socket with transport mode transform
+ with UDP encap on a non-NAT network
+ 2. Verify that packets are encrypted and decrypted at the other end
+ 3. Remove tranform and verify that unencrypted packets are sent
+ """
+ self._test_transport_mode_transform_datagram_sockets(cconst.AF_INET, True)
+
+ @test_tracker_info(uuid="dd3a08c4-d393-46c4-b550-bc551ceb15f7")
+ def test_transport_mode_udp_ipv6_datagram_sockets(self):
+ """ Verify transport mode transform on datagram sockets ipv6 - UDP
+
+ Steps:
+ 1. Encypt a DatagramSocket IPv6 socket with transport mode transform
+ 2. Verify that packets are encrypted and decrypted at the other end
+ 3. Remove tranform and verify that unencrypted packets are sent
+ """
+ self._test_transport_mode_transform_datagram_sockets(cconst.AF_INET6)
+
+ """ Socket & server socket tests """
+
+ @test_tracker_info(uuid="f851b5da-790a-45c6-8968-1a347ef30cf2")
+ def test_transport_mode_tcp_ipv4_sockets_no_nat_no_encap(self):
+ """ Verify transport mode transform on sockets ipv4 - TCP
+
+ Steps:
+ 1. Encypt a Socket IPv4 socket with transport mode transform without
+ UDP encap on a non-NAT network
+ 2. Verify that packets are encrypted and decrypted at the other end
+ 3. Remove tranform and verify that unencrypted packets are sent
+ """
+ self._test_transport_mode_transform_sockets(cconst.AF_INET)
+
+ @test_tracker_info(uuid="11982874-8625-40c1-aae5-8327217bb3c4")
+ def test_transport_mode_tcp_ipv4_sockets_no_nat_with_encap(self):
+ """ Verify transport mode transform on sockets ipv4 - TCP
+
+ Steps:
+ 1. Encypt a Socket IPv4 socket with transport mode transform with
+ UDP encap on a non-NAT network
+ 2. Verify that packets are encrypted and decrypted at the other end
+ 3. Remove tranform and verify that unencrypted packets are sent
+ """
+ self._test_transport_mode_transform_sockets(cconst.AF_INET, True)
+
+ @test_tracker_info(uuid="ba8f98c7-2123-4082-935f-a5fd5e6fb461")
+ def test_transport_mode_tcp_ipv6_sockets(self):
+ """ Verify transport mode transform on sockets ipv6 - TCP
+
+ Steps:
+ 1. Encypt a Socket IPv6 socket with transport mode transform
+ 2. Verify that packets are encrypted and decrypted at the other end
+ 3. Remove tranform and verify that unencrypted packets are sent
+ """
+ self._test_transport_mode_transform_sockets(cconst.AF_INET6)
+
+ """ Tests end """
diff --git a/acts/tests/google/net/arduino/connect_wifi/connect_wifi.ino b/acts/tests/google/net/arduino/connect_wifi/connect_wifi.ino
new file mode 100644
index 0000000..14bd196
--- /dev/null
+++ b/acts/tests/google/net/arduino/connect_wifi/connect_wifi.ino
@@ -0,0 +1,93 @@
+#include <ESP8266WiFi.h>
+#include <ESP8266Ping.h>
+#include <WiFiUdp.h>
+
+const char* ssid = "wifi_tethering_test";
+const char* password = "password";
+
+unsigned int localPort = 8888;
+char packetBuffer[UDP_TX_PACKET_MAX_SIZE];
+WiFiUDP Udp;
+
+
+void setup() {
+ delay(1000); // wait for a second to read from serial port after flashing
+ Serial.begin(9600);
+ Serial.println("connect: setup(): CALL: Setup Begin");
+ Serial.println("connect: setup(): INFO: Setting baud rate to 9600");
+
+ wifiStatus();
+ connectWifi();
+
+ Udp.begin(localPort);
+ Serial.println("connect: setup(): CALL: Setup End");
+}
+
+void loop() {
+ wifiStatus();
+ udpPackets();
+}
+
+void connectWifi() {
+ Serial.println("connect: connectWifi(): CALL: Connect Begin");
+ WiFi.begin(ssid, password);
+ while (WiFi.status() != WL_CONNECTED) {
+ Serial.println("connect: setup(): INFO: WiFi disconnected");
+ delay(1000);
+ }
+ Serial.println("connect: connectWifi(): CALL: Connect End");
+}
+
+void wifiStatus() {
+ Serial.println("connect: wifiStatus(): CALL: Status Begin");
+ Serial.println("connect: wifiStatus(): INFO: WiFi connected");
+ Serial.print("connect: wifiStatus(): STATUS: ");
+ Serial.println(WiFi.status());
+ Serial.print("connect: wifiStatus(): IP: ");
+ Serial.println(WiFi.localIP());
+ Serial.print("connect: wifiStatus(): SSID: ");
+ Serial.println(WiFi.SSID());
+ bool ret = Ping.ping("www.google.com", 3);
+ Serial.print("connect: wifiStatus(): PING: ");
+ if (ret) {
+ Serial.println("1");
+ } else {
+ Serial.println("0");
+ }
+
+ delay(250);
+ Serial.println("connect: wifiStatus(): CALL: Status End");
+}
+
+void udpPackets() {
+ Serial.println("connect: udpPackets(): CALL: UDP Begin");
+ int packetSize = Udp.parsePacket();
+ while(packetSize) {
+ Serial.print("connect: udpPackets(): PKTSZ: ");
+ Serial.println(packetSize);
+ Serial.print("connect: udpPackets(): REMOTEIP: ");
+ IPAddress remote = Udp.remoteIP();
+ for (int i =0; i < 4; i++) {
+ Serial.print(remote[i], DEC);
+ if (i < 3) {
+ Serial.print(".");
+ }
+ }
+ Serial.println("");
+ Serial.print("connect: udpPackets(): REMOTEPORT: ");
+ Serial.println(Udp.remotePort());
+
+ // read the packet into packetBufffer
+ Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);
+ Serial.print("connect: udpPackets(): RECV: ");
+ Serial.println(packetBuffer);
+
+ // send the same message back
+ Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
+ Udp.write(packetBuffer);
+ Udp.endPacket();
+
+ packetSize = Udp.parsePacket();
+ }
+ Serial.println("connect: udpPackets(): CALL: UDP End");
+}
diff --git a/acts/tests/google/net/arduino/disconnect_wifi/disconnect_wifi.ino b/acts/tests/google/net/arduino/disconnect_wifi/disconnect_wifi.ino
new file mode 100644
index 0000000..cacef95
--- /dev/null
+++ b/acts/tests/google/net/arduino/disconnect_wifi/disconnect_wifi.ino
@@ -0,0 +1,64 @@
+#include <ESP8266WiFi.h>
+
+void setup() {
+ delay(1000); // wait for a second to read from serial port after flashing
+ Serial.begin(9600);
+ Serial.println("disconnect: setup(): CALL: Setup Begin");
+ Serial.println("disconnect: setup(): INFO: Setting baud rate to 9600");
+
+ wifiStatus();
+ disconnectWifi();
+
+ Serial.println("disconnect: setup(): CALL: Setup End");
+}
+
+void loop() {
+ wifiStatus();
+ scanNetworks();
+}
+
+void disconnectWifi() {
+ Serial.println("disconnect: setup(): CALL: Disconnect Begin");
+ WiFi.disconnect();
+ while (WiFi.status() == WL_CONNECTED) {
+ Serial.println("disconnect: setup(): INFO: WiFi connected");
+ delay(1000);
+ }
+ Serial.println("disconnect: setup(): CALL: Disconnect End");
+}
+
+void wifiStatus() {
+ Serial.println("disconnect: wifiStatus(): CALL: Status Begin");
+ Serial.println("disconnect: loop(): INFO: WiFi disconnected");
+ Serial.print("disconnect: wifiStatus(): STATUS: ");
+ Serial.println(WiFi.status());
+ Serial.print("disconnect: wifiStatus(): IP: ");
+ Serial.println(WiFi.localIP());
+ Serial.print("disconnect: wifiStatus(): SSID: ");
+ Serial.println(WiFi.SSID());
+ delay(1000);
+ Serial.println("disconnect: wifiStatus(): CALL: Status End");
+}
+
+void scanNetworks() {
+ Serial.println("disconnect: scanNetworks(): CALL: Scan Begin");
+ int n = WiFi.scanNetworks();
+ if (n == 0) {
+ Serial.println("disconnect: scanNetworks(): INFO: No networks found");
+ Serial.println("disconnect: scanNetworks(): COUNT: 0");
+ } else {
+ Serial.println("disconnect: scanNetworks(): INFO: WiFi Networks Found");
+ Serial.print("COUNT: ");
+ Serial.println(n);
+
+ for (int i = 0; i < n; ++i) {
+ Serial.print("SSID: ");
+ Serial.println(WiFi.SSID(i));
+ Serial.print("RSSI: ");
+ Serial.println(WiFi.RSSI(i));
+ }
+ }
+
+ delay(5000); // Wait a bit before scanning again
+ Serial.println("disconnect: scanNetworks(): CALL: Scan End");
+}
diff --git a/acts/tests/google/nfc/NfcBasicFunctionalityTest.py b/acts/tests/google/nfc/NfcBasicFunctionalityTest.py
index 8715fdf..5d531d1 100644
--- a/acts/tests/google/nfc/NfcBasicFunctionalityTest.py
+++ b/acts/tests/google/nfc/NfcBasicFunctionalityTest.py
@@ -23,30 +23,46 @@
class NfcBasicFunctionalityTest(BaseTestClass):
nfc_on_event = "NfcStateOn"
nfc_off_event = "NfcStateOff"
- timeout = 1
+ timeout = 5
def setup_class(self):
self.dut = self.android_devices[0]
+ self._ensure_nfc_enabled(self.dut)
self.dut.droid.nfcStartTrackingStateChange()
+ self.dut.adb.shell("setprop nfc.app_log_level 255")
+ self.dut.adb.shell("setprop nfc.enable_protocol_log 255")
+ self.dut.adb.shell("setprop nfc.nxp_log_level_global 5")
+ self.dut.adb.shell("setprop nfc.nxp_log_level_extns 5")
+ self.dut.adb.shell("setprop nfc.nxp_log_level_hal 5")
+ self.dut.adb.shell("setprop nfc.nxp_log_level_nci 5")
+ self.dut.adb.shell("setprop nfc.nxp_log_level_tml 5")
+ self.dut.adb.shell("setprop nfc.nxp_log_level_dnld 5")
+ self._ensure_nfc_disabled(self.dut)
return True
def _ensure_nfc_enabled(self, dut):
end_time = time.time() + 10
- while (not dut.droid.nfcIsEnabled() and end_time > time.time()):
+ while end_time > time.time():
try:
- dut.ed.pop_event(nfc_on_event, self.timeout)
+ dut.ed.pop_event(self.nfc_on_event, self.timeout)
+ self.log.info("Event {} found".format(self.nfc_on_event))
+ return True
except Exception as err:
- self.log.debug("Event not yet found")
- return dut.droid.nfcIsEnabled()
+ self.log.debug(
+ "Event {} not yet found".format(self.nfc_on_event))
+ return False
def _ensure_nfc_disabled(self, dut):
end_time = time.time() + 10
- while (dut.droid.nfcIsEnabled() and end_time > time.time()):
+ while end_time > time.time():
try:
- dut.ed.pop_event(nfc_off_event, self.timeout)
+ dut.ed.pop_event(self.nfc_off_event, self.timeout)
+ self.log.info("Event {} found".format(self.nfc_off_event))
+ return True
except Exception as err:
- self.log.debug("Event not yet found")
- return not dut.droid.nfcIsEnabled()
+ self.log.debug(
+ "Event {} not yet found".format(self.nfc_off_event))
+ return False
def setup_test(self):
# Every test starts with the assumption that NFC is enabled
@@ -59,6 +75,9 @@
return False
return True
+ def on_fail(self, test_name, begin_time):
+ self.dut.take_bug_report(test_name, begin_time)
+
@test_tracker_info(uuid='d57fcdd8-c56c-4ab0-81fb-e2218b100de9')
def test_nfc_toggle_state_100_iterations(self):
"""Test toggling NFC state 100 times.
diff --git a/acts/tests/google/power/bt/PowerBTbaselineTest.py b/acts/tests/google/power/bt/PowerBTbaselineTest.py
index 89eceaa..b7e5745 100644
--- a/acts/tests/google/power/bt/PowerBTbaselineTest.py
+++ b/acts/tests/google/power/bt/PowerBTbaselineTest.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3.4
#
-# Copyright 2017 - The Android Open Source Project
+# Copyright 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.
@@ -14,152 +14,54 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import logging
-import os
-import time
-
from acts import base_test
-from acts import utils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-from acts.test_utils.bt import bt_power_test_utils as btutils
from acts.test_decorators import test_tracker_info
+import acts.test_utils.power.PowerBTBaseTest as PBtBT
-class PowerBTbaselineTest(base_test.BaseTestClass):
+class PowerBTbaselineTest(PBtBT.PowerBTBaseTest):
def __init__(self, controllers):
base_test.BaseTestClass.__init__(self, controllers)
- def setup_class(self):
+ def bt_baseline_test_func(self):
+ """Base function for BT baseline measurement.
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- req_params = ['btbaseline_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.btbaseline_params)
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon = self.monsoons[0]
- self.mon.set_max_current(wputils.MONSOON_MAX_CURRENT)
- self.mon.set_voltage(wputils.PHONE_BATTERY_VOLTAGE)
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
-
- # Reset BT to factory defaults
- self.dut.droid.bluetoothFactoryReset()
- time.sleep(2)
-
- def teardown_class(self):
- """Clean up the test class after all tests finish running
-
+ Steps:
+ 1. Sets the phone in airplane mode, disables gestures and location
+ 2. Turns ON/OFF BT, BLE and screen according to test conditions
+ 3. Measures the power consumption
+ 4. Asserts pass/fail criteria based on measured power
"""
- self.mon.usb('on')
- self.dut.droid.bluetoothFactoryReset()
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters.
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- def measure_power(self):
- """Measures current consumption and evaluates pass/fail criteria
-
- """
- # Measure current and plot
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
-
- # Compute pass or fail check
- wputils.pass_fail_check(self, avg_current)
+ # Decode the test params from test name
+ attrs = ['screen_status', 'bt_status', 'ble_status', 'scan_status']
+ indices = [2, 4, 6, 7]
+ self.decode_test_configs(attrs, indices)
+ # Setup the phoen at desired state
+ self.phone_setup_for_BT(self.test_configs.bt_status,
+ self.test_configs.ble_status,
+ self.test_configs.screen_status)
+ if self.test_configs.scan_status == 'connectable':
+ self.dut.droid.bluetoothMakeConnectable()
+ elif self.test_configs.scan_status == 'discoverable':
+ self.dut.droid.bluetoothMakeDiscoverable(
+ self.mon_info.duration + self.mon_info.offset)
+ self.measure_power_and_validate()
# Test cases- Baseline
@test_tracker_info(uuid='3f8ac0cb-f20d-4569-a58e-6009c89ea049')
- def test_bt_ON_screen_OFF_connectable(self):
- """Measures baseline power when BT is toggled ON and screen is OFF
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. Turns ON BT (i.e., page scan) and turns screen OFF
- 4. Measures the power consumption
- 5. Asserts pass/fail criteria based on measured power
- """
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'ON', 'ON', 'OFF')
-
- # This is the default mode: devices enters paging periodically
- self.dut.droid.bluetoothMakeConnectable()
-
- # Measure power
- self.measure_power()
+ def test_screen_OFF_bt_ON_ble_ON_connectable(self):
+ self.bt_baseline_test_func()
@test_tracker_info(uuid='d54a992e-37ed-460a-ada7-2c51941557fd')
- def test_bt_ON_screen_OFF_discoverable(self):
- """Measures baseline power when BT is discoverable and screen is OFF
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. Sets phone discoverable (i.e., inquiry scan) and turns screen OFF
- 4. Measures the power consumption
- 5. Asserts pass/fail criteria based on measured power
- """
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'ON', 'ON', 'OFF')
-
- # Device will enter Inquiry state
- duration = self.mon_info['duration'] + self.mon_info['offset']
- self.dut.droid.bluetoothMakeDiscoverable(duration)
-
- # Measure power
- self.measure_power()
+ def test_screen_OFF_bt_ON_ble_ON_discoverable(self):
+ self.bt_baseline_test_func()
@test_tracker_info(uuid='8f4c36b5-b18e-4aa5-9fe5-aafb729c1034')
- def test_bt_ON_screen_ON_connectable(self):
- """Measures baseline power when BT is toggled ON and screen is ON
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. Turns ON BT (i.e., page scan) and turns screen ON
- 4. Measures the power consumption
- 5. Asserts pass/fail criteria based on measured power
- """
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'ON', 'ON', 'ON')
-
- # This is the default mode: devices enters paging periodically
- self.dut.droid.bluetoothMakeConnectable()
-
- # Measure power
- self.measure_power()
+ def test_screen_ON_bt_ON_ble_ON_connectable(self):
+ self.bt_baseline_test_func()
@test_tracker_info(uuid='7128356f-67d8-46b3-9d6b-1a4c9a7a1745')
- def test_bt_ON_screen_ON_discoverable(self):
- """Measures baseline power when BT is discoverable and screen is ON
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. Sets phone discoverable (i.e., inquiry scan) and turns screen ON
- 4. Measures the power consumption
- 5. Asserts pass/fail criteria based on measured power
- """
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'ON', 'ON', 'ON')
-
- # Device will enter Inquiry state
- duration = self.mon_info['duration'] + self.mon_info['offset']
- self.dut.droid.bluetoothMakeDiscoverable(duration)
-
- # Measure power
- self.measure_power()
+ def test_screen_ON_bt_ON_ble_ON_discoverable(self):
+ self.bt_baseline_test_func()
diff --git a/acts/tests/google/power/bt/PowerBTscanTest.py b/acts/tests/google/power/bt/PowerBTscanTest.py
index 59818e1..0ef622c 100644
--- a/acts/tests/google/power/bt/PowerBTscanTest.py
+++ b/acts/tests/google/power/bt/PowerBTscanTest.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3.4
#
-# Copyright 2017 - The Android Open Source Project
+# Copyright 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.
@@ -14,75 +14,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import logging
-import os
-import time
-
-from acts import base_test
-from acts import utils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.bt import bt_power_test_utils as btutils
+import acts.test_utils.power.PowerBTBaseTest as PBtBT
from acts.test_decorators import test_tracker_info
-class PowerBTscanTest(base_test.BaseTestClass):
- def __init__(self, controllers):
-
- base_test.BaseTestClass.__init__(self, controllers)
-
- def setup_class(self):
-
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- req_params = ['btscan_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.btscan_params)
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon = self.monsoons[0]
- self.mon.set_max_current(wputils.MONSOON_MAX_CURRENT)
- self.mon.set_voltage(wputils.PHONE_BATTERY_VOLTAGE)
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
-
- # Start PMC app.
- self.log.info('Start PMC app...')
- self.dut.adb.shell(btutils.START_PMC_CMD)
- self.dut.adb.shell(btutils.PMC_VERBOSE_CMD)
-
- # Reset BT to factory defaults
- self.dut.droid.bluetoothFactoryReset()
- time.sleep(2)
-
- def teardown_test(self):
- """Tear down necessary objects/settings after test finishes
-
- """
- self.dut.adb.shell(btutils.BLE_LOCATION_SCAN_DISABLE)
-
- def teardown_class(self):
- """Clean up the test class after all tests finish running
-
- """
- self.mon.usb('on')
- self.dut.droid.bluetoothFactoryReset()
-
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters.
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- def scan_ble_measure_power(self, scan_mode):
- """Starts a generic BLE scan and measures the power
+class PowerBTscanTest(PBtBT.PowerBTBaseTest):
+ def ble_scan_base_func(self):
+ """Base function to start a generic BLE scan and measures the power
Steps:
1. Sets the phone in airplane mode, disables gestures and location
@@ -90,97 +28,58 @@
3. Sends the adb shell command to PMC to start scan
4. Measures the power consumption
5. Asserts pass/fail criteria based on measured power
-
- Args:
- scan_mode: BLE scan type (e.g., low_power)
"""
- # Start BLE scan
- btutils.start_pmc_ble_scan(self.dut, scan_mode,
- self.mon_info['offset'],
- self.mon_info['duration'])
-
- # Measure current and plot
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
-
- # Compute pass or fail check
- wputils.pass_fail_check(self, avg_current)
+ # Decode the test params from test name
+ attrs = ['screen_status', 'bt_status', 'ble_status', 'scan_mode']
+ indices = [2, 4, 6, -1]
+ self.decode_test_configs(attrs, indices)
+ if self.test_configs.scan_mode == 'lowpower':
+ scan_mode = 'low_power'
+ elif self.test_configs.scan_mode == 'lowlatency':
+ scan_mode = 'low_latency'
+ else:
+ scan_mode = self.test_configs.scan_mode
+ self.phone_setup_for_BT(self.test_configs.bt_status,
+ self.test_configs.ble_status,
+ self.test_configs.screen_status)
+ self.start_pmc_ble_scan(scan_mode, self.mon_info.offset,
+ self.mon_info.duration)
+ self.measure_power_and_validate()
# Test Cases: BLE Scans + Filtered scans
@test_tracker_info(uuid='e9a36161-1d0c-4b9a-8bd8-80fef8cdfe28')
- def test_ble_screen_ON_default_scan(self):
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'ON', 'ON', 'ON')
-
- # Start scan and measure power
- self.scan_ble_measure_power(ble_scan_settings_modes['balanced'])
+ def test_screen_ON_bt_ON_ble_ON_default_scan_balanced(self):
+ self.ble_scan_base_func()
@test_tracker_info(uuid='5fa61bf4-5f04-40bf-af52-6644b534d02e')
- def test_ble_screen_OFF_filter_scan_opport(self):
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'ON', 'ON', 'OFF')
-
- # Start BLE scan and measure power
- self.scan_ble_measure_power(ble_scan_settings_modes['opportunistic'])
+ def test_screen_OFF_bt_ON_ble_ON_filter_scan_opportunistic(self):
+ self.ble_scan_base_func()
@test_tracker_info(uuid='512b6cde-be83-43b0-b799-761380ba69ff')
- def test_ble_screen_OFF_filter_scan_low_power(self):
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'ON', 'ON', 'OFF')
-
- # Start BLE scan and measure power
- self.scan_ble_measure_power(ble_scan_settings_modes['low_power'])
+ def test_screen_OFF_bt_ON_ble_ON_filter_scan_lowpower(self):
+ self.ble_scan_base_func()
@test_tracker_info(uuid='3a526838-ae7b-4cdb-bc29-89a5503d2306')
- def test_ble_screen_OFF_filter_scan_balanced(self):
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'ON', 'ON', 'OFF')
-
- # Start BLE scan and measure power
- self.scan_ble_measure_power(ble_scan_settings_modes['balanced'])
+ def test_screen_OFF_bt_ON_ble_ON_filter_scan_balanced(self):
+ self.ble_scan_base_func()
@test_tracker_info(uuid='03a57cfd-4269-4a09-8544-84f878d2e801')
- def test_ble_screen_OFF_filter_scan_low_lat(self):
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'ON', 'ON', 'OFF')
-
- # Start BLE scan and measure power
- self.scan_ble_measure_power(ble_scan_settings_modes['low_latency'])
+ def test_screen_OFF_bt_ON_ble_ON_filter_scan_lowlatency(self):
+ self.ble_scan_base_func()
# Test Cases: Background scans
@test_tracker_info(uuid='20145317-e362-4bfd-9860-4ceddf764784')
- def test_ble_screen_ON_backgnd_scan_low_lat(self):
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'OFF', 'ON', 'ON')
-
- # Start BLE scan and measure power
- self.scan_ble_measure_power(ble_scan_settings_modes['low_latency'])
+ def test_screen_ON_bt_OFF_ble_ON_background_scan_lowlatency(self):
+ self.ble_scan_base_func()
@test_tracker_info(uuid='00a53dc3-2c33-43c4-b356-dba93249b823')
- def test_ble_screen_ON_backgnd_scan_low_power(self):
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'OFF', 'ON', 'ON')
-
- # Start BLE scan and measure power
- self.scan_ble_measure_power(ble_scan_settings_modes['low_power'])
+ def test_screen_ON_bt_OFF_ble_ON_background_scan_lowpower(self):
+ self.ble_scan_base_func()
@test_tracker_info(uuid='b7185d64-631f-4b18-8d0b-4e14b80db375')
- def test_ble_screen_OFF_filter_backgnd_scan_low_lat(self):
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'OFF', 'ON', 'OFF')
-
- # Start BLE scan and measure power
- self.scan_ble_measure_power(ble_scan_settings_modes['low_latency'])
+ def test_screen_OFF_bt_OFF_ble_ON_background_scan_lowlatency(self):
+ self.ble_scan_base_func()
@test_tracker_info(uuid='93eb05da-a577-409c-8208-6af1899a10c2')
- def test_ble_screen_OFF_filter_backgnd_scan_low_power(self):
- # Set the phone in the desired state
- btutils.phone_setup_for_BT(self.dut, 'OFF', 'ON', 'OFF')
-
- # Start BLE scan and measure power
- self.scan_ble_measure_power(ble_scan_settings_modes['low_power'])
+ def test_screen_OFF_bt_OFF_ble_ON_background_scan_lowpower(self):
+ self.ble_scan_base_func()
diff --git a/acts/tests/google/power/coex/PowerCoexBaseTest.py b/acts/tests/google/power/coex/PowerCoexBaseTest.py
deleted file mode 100644
index d4cae36..0000000
--- a/acts/tests/google/power/coex/PowerCoexBaseTest.py
+++ /dev/null
@@ -1,224 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - 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.
-
-import logging
-import os
-
-from acts import base_test
-from acts import utils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-from acts.controllers.ap_lib import hostapd_constants as hc
-from acts.test_decorators import test_tracker_info
-
-
-class PowerCoexBaseTest(base_test.BaseTestClass):
- def __init__(self, controllers):
-
- base_test.BaseTestClass.__init__(self, controllers)
-
- def setup_class(self):
-
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- self.access_point = self.access_points[0]
- req_params = ['coexbaseline_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.coexbaseline_params)
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon = self.monsoons[0]
- self.mon.set_max_current(wputils.MONSOON_MAX_CURRENT)
- self.mon.set_voltage(wputils.PHONE_BATTERY_VOLTAGE)
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- self.num_atten = self.attenuators[0].instrument.num_atten
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- elif 'attenuator_setting' in file:
- self.attenuation_file = file
- elif 'network_config' in file:
- self.network_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
- self.atten_level = wputils.unpack_custom_file(self.attenuation_file,
- self.TAG)
- self.networks = wputils.unpack_custom_file(self.network_file)
- self.main_network = self.networks['main_network']
-
- def teardown_class(self):
- """Clean up the test class after all tests finish running
-
- """
- self.mon.usb('on')
- self.access_point.close() # Just as a precaution
-
- def teardown_test(self):
- """Tear down necessary objects/settings after test finishes
-
- """
- if self.brconfigs:
- self.access_point.bridge.teardown(self.brconfigs)
- self.access_point.close()
-
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters.
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- def measure_power(self):
- """Measures current consumption and evaluates pass/fail criteria
-
- """
- # Measure current and plot
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
-
- # Compute pass or fail check
- wputils.pass_fail_check(self, avg_current)
-
- @test_tracker_info(uuid='f3fc6667-73d8-4fb5-bdf3-0253e52043b1')
- def test_wifi_discon_bt_on_screen_off(self):
- """Measure power when WiFi is ON (disconnected) and BT is toggled ON
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. Turns ON BT and WiFi (disconnected)
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self, bt_on='ON', wifi_on='ON', screen_status='OFF')
- self.measure_power()
-
- @test_tracker_info(uuid='1bec36d1-f7b2-4a4b-9f5d-dfb5ed985649')
- def test_wifi_2G_bt_on_screen_off(self):
- """Measure power when WiFi is connected to 2G and BT is ON
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. Turns ON BT and WiFi is connected to 2.4 GHz
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_2G],
- screen_status='OFF')
- self.measure_power()
-
- @test_tracker_info(uuid='88170cad-8336-4dff-8e53-3cc693d01b72')
- def test_wifi_5G_bt_on_screen_off(self):
- """Measure power when WiFi is connected to 5G and BT is ON
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. Turns ON BT and WiFi is connected to 5 GHz
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_5G],
- screen_status='OFF')
- self.measure_power()
-
- @test_tracker_info(uuid='b82e59a9-9b27-4ba2-88f6-48d7917066f4')
- def test_bt_on_cellular_verizon_on_screen_off(self):
- """Measure power when BT and cellular (Verizon) are ON
-
- Steps:
- 1. Disables gestures and location
- 2. Turns ON BT and cellular (Verizon)
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='OFF',
- screen_status='OFF',
- regular_mode=True)
- self.measure_power()
-
- @test_tracker_info(uuid='6409a02e-d63a-4c46-a210-1d5f1b006556')
- def test_cellular_verizon_on_wifi_5G_screen_off(self):
- """Measure power when WiFi is connected to 5G and cellular is ON
-
- Steps:
- 1. Disables gestures and location
- 2. Connect Wifi to 5 GHz and have cellular idle (Verizon)
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='OFF',
- wifi_on='ON',
- network=self.main_network[hc.BAND_5G],
- screen_status='OFF',
- regular_mode=True)
- self.measure_power()
-
- @test_tracker_info(uuid='6f22792f-b304-4804-853d-e41484d442ab')
- def test_cellular_verizon_on_wifi_2G_screen_off(self):
- """Measure power when WiFi is connected to 2G and cellular is ON
-
- Steps:
- 1. Disables gestures and location
- 2. Connect Wifi to 2.4 GHz and have cellular idle (Verizon)
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='OFF',
- wifi_on='ON',
- network=self.main_network[hc.BAND_2G],
- screen_status='OFF',
- regular_mode=True)
- self.measure_power()
-
- @test_tracker_info(uuid='11bb1683-4544-46b4-ad4a-875e31323729')
- def test_cellular_verizon_on_bt_on_wifi_5G_screen_off(self):
- """Measure power when WiFi is connected to 5G, BT and cellular are ON
-
- Steps:
- 1. Disables gestures and location
- 2. Connect Wifi to 5 GHz and turn BT and cellular ON
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_5G],
- screen_status='OFF',
- regular_mode=True)
- self.measure_power()
diff --git a/acts/tests/google/power/coex/PowerCoexScanTest.py b/acts/tests/google/power/coex/PowerCoexScanTest.py
deleted file mode 100644
index 210b5a9..0000000
--- a/acts/tests/google/power/coex/PowerCoexScanTest.py
+++ /dev/null
@@ -1,431 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - 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.
-
-import logging
-import math
-import os
-import time
-
-from acts import base_test
-from acts import utils
-from acts.controllers.ap_lib import hostapd_constants as hc
-from acts.test_decorators import test_tracker_info
-from acts.test_utils.bt import bt_power_test_utils as btutils
-from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-
-
-class PowerCoexScanTest(base_test.BaseTestClass):
- def __init__(self, controllers):
-
- base_test.BaseTestClass.__init__(self, controllers)
-
- def setup_class(self):
-
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- self.access_point = self.access_points[0]
- req_params = ['coexscan_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.coexscan_params)
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon = self.monsoons[0]
- self.mon.set_max_current(wputils.MONSOON_MAX_CURRENT)
- self.mon.set_voltage(wputils.PHONE_BATTERY_VOLTAGE)
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- self.num_atten = self.attenuators[0].instrument.num_atten
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- elif 'attenuator_setting' in file:
- self.attenuation_file = file
- elif 'network_config' in file:
- self.network_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
- self.atten_level = wputils.unpack_custom_file(self.attenuation_file,
- self.TAG)
- self.networks = wputils.unpack_custom_file(self.network_file)
- self.main_network = self.networks['main_network']
-
- # Start PMC app.
- self.log.info('Start PMC app...')
- self.dut.adb.shell(btutils.START_PMC_CMD)
- self.dut.adb.shell(btutils.PMC_VERBOSE_CMD)
-
- def setup_test(self):
-
- iterations = math.floor((self.mon_duration + self.mon_offset + 10) /
- self.wifi_scan_interval)
-
- self.PERIODIC_WIFI_SCAN = (
- 'am instrument -w -r -e scan-interval \"%d\" -e scan-iterations'
- ' \"%d\" -e class com.google.android.platform.powertests.'
- 'WifiTests#testGScanAllChannels com.google.android.platform.'
- 'powertests/android.test.InstrumentationTestRunner > /dev/null &' %
- (self.wifi_scan_interval, iterations))
-
- def teardown_class(self):
- """Clean up the test class after all tests finish running
-
- """
- self.mon.usb('on')
- self.access_point.close() # Just as a precaution
-
- def teardown_test(self):
- """Tear down necessary objects/settings after test finishes
-
- """
- self.dut.adb.shell(btutils.BLE_LOCATION_SCAN_DISABLE)
- if self.brconfigs:
- self.access_point.bridge.teardown(self.brconfigs)
- self.access_point.close()
-
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- def measure_power(self):
- """Measures current consumption and evaluates pass/fail criteria
-
- """
- # Measure current and plot
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
-
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
-
- # Compute pass or fail check
- wputils.pass_fail_check(self, avg_current)
-
- @test_tracker_info(uuid='a998dd2b-f5f1-4361-b5da-83e42a69e80b')
- def test_ble_scan_balan_wifi_2G_screen_on(self):
- """Measure power when WiFi is connected to 2.4 GHz and BLE is scanning
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. BLE starts a balanced scan and WiFi is connected to 2.4 GHz
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_2G],
- screen_status='ON')
-
- # Start BLE scan
- btutils.start_pmc_ble_scan(
- self.dut, ble_scan_settings_modes['opportunistic'],
- self.mon_info['offset'], self.mon_info['duration'])
-
- # Measure power
- self.measure_power()
-
- @test_tracker_info(uuid='87146825-787a-4ea7-9622-30e9286c8a76')
- def test_filter_ble_scan_low_power_wifi_2G_screen_off(self):
- """Measure power when WiFi is connected to 2.4 GHz and BLE is scanning
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. BLE starts a filtered low power scan and WiFi is connected to 2G
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_2G],
- screen_status='OFF')
-
- # Start BLE scan
- btutils.start_pmc_ble_scan(
- self.dut, ble_scan_settings_modes['low_power'],
- self.mon_info['offset'], self.mon_info['duration'])
-
- # Measure power
- self.measure_power()
-
- @test_tracker_info(uuid='2e645deb-b744-4272-8578-5d4cb159d5aa')
- def test_filter_ble_scan_low_power_wifi_5G_screen_off(self):
- """Measure power when WiFi is connected to 5 GHz and BLE is scanning
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. BLE starts a filtered low power scan and WiFi is connected to 5G
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_5G],
- screen_status='OFF')
-
- # Start BLE scan
- btutils.start_pmc_ble_scan(
- self.dut, ble_scan_settings_modes['low_power'],
- self.mon_info['offset'], self.mon_info['duration'])
-
- # Measure power
- self.measure_power()
-
- @test_tracker_info(uuid='d458bc41-f1c8-4ed6-a7b5-0bec34780dda')
- def test_wifi_scan_bt_on_screen_off(self):
- """Measure power when WiFi is scanning and BT is doing a page scan
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. WiFi is scanning and BT is doing a page scan
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_2G],
- screen_status='OFF')
-
- # Start WiFi connectivity scans
- self.dut.adb.shell_nb(self.PERIODIC_WIFI_SCAN)
- self.log.info('Started connectivity scans:')
- self.log.info(self.PERIODIC_WIFI_SCAN)
-
- # Measure power
- self.measure_power()
-
- @test_tracker_info(uuid='6d9c0e8e-6a0f-458b-84d2-7d60fc254170')
- def test_wifi_scan_ble_filter_low_power_scan_screen_off(self):
- """Measure power when WiFi is scanning and BLE is scanning
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. WiFi is scanning and BLE is doing a low power filtered scan
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_2G],
- screen_status='OFF')
-
- # Start BLE scan
- btutils.start_pmc_ble_scan(
- self.dut, ble_scan_settings_modes['low_power'],
- self.mon_info['offset'], self.mon_info['duration'])
- time.sleep(2)
-
- # Start WiFi connectivity scans
- self.dut.adb.shell_nb(self.PERIODIC_WIFI_SCAN)
- self.log.info('Started connectivity scans:')
- self.log.info(self.PERIODIC_WIFI_SCAN)
-
- # Measure power
- self.measure_power()
-
- @test_tracker_info(uuid='ba52317f-426a-4688-a0a5-1394bcc7b092')
- def test_wifi_scan_ble_filter_low_lat_scan_screen_off(self):
- """Measure power when WiFi is scanning and BLE is scanning
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. WiFi is scanning and BLE is doing a low latency filtered scan
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_2G],
- screen_status='OFF')
-
- # Start BLE scan
- btutils.start_pmc_ble_scan(
- self.dut, ble_scan_settings_modes['low_latency'],
- self.mon_info['offset'], self.mon_info['duration'])
- time.sleep(2)
-
- # Start WiFi connectivity scans
- self.dut.adb.shell_nb(self.PERIODIC_WIFI_SCAN)
- self.log.info('Started connectivity scans:')
- self.log.info(self.PERIODIC_WIFI_SCAN)
-
- # Measure power
- self.measure_power()
-
- @test_tracker_info(uuid='b4c63eac-bc77-4e76-afff-ade98dde4411')
- def test_wifi_pno_scan_ble_filter_low_lat_scan_screen_off(self):
- """Measure power when WiFi disconnected (PNO scan) and BLE is scanning
-
- Steps:
- 1. Sets the phone in airplane mode, disables gestures and location
- 2. WiFi is disconnected (PNOsscan) and BLE is doing a low latency
- filtered scan
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='ON',
- wifi_on='ON',
- network=self.main_network[hc.BAND_2G],
- screen_status='OFF')
-
- # Start BLE scan
- btutils.start_pmc_ble_scan(
- self.dut, ble_scan_settings_modes['low_latency'],
- self.mon_info['offset'], self.mon_info['duration'])
- time.sleep(1)
-
- # Set attenuator to make WiFi disconnect and start PNO scans
- self.log.info('Set attenuation so device loses connection')
- [
- self.attenuators[i].set_atten(
- self.atten_level[self.current_test_name][i])
- for i in range(self.num_atten)
- ]
-
- # Measure power
- self.measure_power()
-
- @test_tracker_info(uuid='798796dc-960c-42b2-a835-2b2aefa028d5')
- def test_cellular_verizon_on_wifi_scan_screen_off(self):
- """Measure power when cellular is ON, WiFi is scanning and BT is OFF
-
- Steps:
- 1. Disables gestures and location
- 2. WiFi is scanning and cellular is idle (Verizon)
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='OFF',
- wifi_on='ON',
- network=self.main_network[hc.BAND_5G],
- screen_status='OFF',
- regular_mode=True)
-
- # Start WiFi connectivity scans
- self.dut.adb.shell_nb(self.PERIODIC_WIFI_SCAN)
- self.log.info('Started connectivity scans:')
- self.log.info(self.PERIODIC_WIFI_SCAN)
-
- # Measure power
- self.measure_power()
-
- @test_tracker_info(uuid='6ae44d84-0e68-4524-99b2-d3bfbd2253b8')
- def test_cellular_on_wifi_scan_ble_backgnd_scan_low_power_screen_off(self):
- """Measure power when cellular is ON, WiFi and BLE are scanning
-
- Steps:
- 1. Disables gestures and location
- 2. WiFi is scanning and cellular is idle (Verizon) and BLE is doing
- a low power background scan
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='OFF',
- wifi_on='ON',
- network=self.main_network[hc.BAND_5G],
- screen_status='OFF',
- regular_mode=True)
-
- # Start BLE background scans
- self.dut.adb.shell(btutils.BLE_LOCATION_SCAN_ENABLE)
- time.sleep(1)
- self.dut.droid.bluetoothEnableBLE()
- time.sleep(2)
- self.dut.log.info('BLE is ON')
- btutils.start_pmc_ble_scan(
- self.dut, ble_scan_settings_modes['low_power'],
- self.mon_info['offset'], self.mon_info['duration'])
- time.sleep(2)
-
- # Start WiFi connectivity scans
- self.dut.adb.shell_nb(self.PERIODIC_WIFI_SCAN)
- self.log.info('Started connectivity scans:')
- self.log.info(self.PERIODIC_WIFI_SCAN)
-
- # Measure power
- self.measure_power()
-
- @test_tracker_info(uuid='2cb915a3-6319-4ac4-9e4d-9325b3b731c8')
- def test_cellular_on_wifi_scan_ble_backgnd_scan_low_lat_screen_off(self):
- """Measure power when cellular is ON, WiFi and BLE are scanning
-
- Steps:
- 1. Disables gestures and location
- 2. WiFi is scanning and cellular is idle (Verizon) and BLE is doing
- a low latency background scan
- 3. Measures the power consumption
- 4. Asserts pass/fail criteria based on measured power
- """
- # Set phone in the desired wireless mode
- self.brconfigs = wputils.setup_phone_wireless(
- test_class=self,
- bt_on='OFF',
- wifi_on='ON',
- network=self.main_network[hc.BAND_2G],
- screen_status='OFF',
- regular_mode=True)
-
- # Start BLE background scans
- self.dut.adb.shell(btutils.BLE_LOCATION_SCAN_ENABLE)
- time.sleep(1)
- self.dut.droid.bluetoothEnableBLE()
- time.sleep(2)
- self.dut.log.info('BLE is ON')
- btutils.start_pmc_ble_scan(
- self.dut, ble_scan_settings_modes['low_latency'],
- self.mon_info['offset'], self.mon_info['duration'])
- time.sleep(2)
-
- # Start WiFi connectivity scans
- self.dut.adb.shell_nb(self.PERIODIC_WIFI_SCAN)
- self.log.info('Started connectivity scans:')
- self.log.info(self.PERIODIC_WIFI_SCAN)
-
- # Measure power
- self.measure_power()
diff --git a/acts/tests/google/power/coex/PowerCoexbaselineTest.py b/acts/tests/google/power/coex/PowerCoexbaselineTest.py
new file mode 100644
index 0000000..19d7763
--- /dev/null
+++ b/acts/tests/google/power/coex/PowerCoexbaselineTest.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import acts.test_utils.power.PowerCoexBaseTest as PCoBT
+from acts.test_decorators import test_tracker_info
+
+
+class PowerCoexbaselineTest(PCoBT.PowerCoexBaseTest):
+ def coex_baseline_test_func(self):
+ """Base function to do coex baseline tests.
+
+ Steps:
+ 1. Set the phone into desired state (WiFi, BT/BLE, cellular)
+ 2. Measures the power consumption
+ 3. Asserts pass/fail criteria based on measured power
+ """
+ attrs = [
+ 'screen_status', 'wifi_status', 'wifi_band', 'bt_status',
+ 'ble_status', 'cellular_status', 'cellular_band'
+ ]
+ indices = [2, 4, 6, 8, 10, 12, 14]
+ self.decode_test_configs(attrs, indices)
+ self.coex_test_phone_setup(
+ self.test_configs.screen_status, self.test_configs.wifi_status,
+ self.test_configs.wifi_band, self.test_configs.bt_status,
+ self.test_configs.ble_status, self.test_configs.cellular_status,
+ self.test_configs.cellular_band)
+ self.measure_power_and_validate()
+
+ @test_tracker_info(uuid='f3fc6667-73d8-4fb5-bdf3-0253e52043b1')
+ def test_screen_OFF_WiFi_ON_band_None_bt_ON_ble_ON_cellular_OFF_band_None(
+ self):
+ self.coex_baseline_test_func()
+
+ @test_tracker_info(uuid='1bec36d1-f7b2-4a4b-9f5d-dfb5ed985649')
+ def test_screen_OFF_WiFi_Connected_band_2g_bt_ON_ble_ON_cellular_OFF_band_None(
+ self):
+ self.coex_baseline_test_func()
+
+ @test_tracker_info(uuid='88170cad-8336-4dff-8e53-3cc693d01b72')
+ def test_screen_OFF_WiFi_Connected_band_5g_bt_ON_ble_ON_cellular_OFF_band_None(
+ self):
+ self.coex_baseline_test_func()
+
+ @test_tracker_info(uuid='b82e59a9-9b27-4ba2-88f6-48d7917066f4')
+ def test_screen_OFF_WiFi_OFF_band_None_bt_ON_ble_ON_cellular_ON_band_Verizon(
+ self):
+ self.coex_baseline_test_func()
+
+ @test_tracker_info(uuid='6409a02e-d63a-4c46-a210-1d5f1b006556')
+ def test_screen_OFF_WiFi_Connected_band_5g_bt_OFF_ble_OFF_cellular_ON_band_Verizon(
+ self):
+ self.coex_baseline_test_func()
+
+ @test_tracker_info(uuid='6f22792f-b304-4804-853d-e41484d442ab')
+ def test_screen_OFF_WiFi_Connected_band_2g_bt_OFF_ble_OFF_cellular_ON_band_Verizon(
+ self):
+ self.coex_baseline_test_func()
+
+ @test_tracker_info(uuid='11bb1683-4544-46b4-ad4a-875e31323729')
+ def test_screen_OFF_WiFi_Connected_band_5g_bt_ON_ble_ON_cellular_ON_band_Verizon(
+ self):
+ self.coex_baseline_test_func()
diff --git a/acts/tests/google/power/coex/PowerCoexscanTest.py b/acts/tests/google/power/coex/PowerCoexscanTest.py
new file mode 100644
index 0000000..3d1f1e3
--- /dev/null
+++ b/acts/tests/google/power/coex/PowerCoexscanTest.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import math
+import acts.test_utils.power.PowerCoexBaseTest as PCoBT
+from acts.test_decorators import test_tracker_info
+
+
+class PowerCoexscanTest(PCoBT.PowerCoexBaseTest):
+ def setup_class(self):
+
+ super().setup_class()
+ iterations = math.floor((self.mon_duration + self.mon_offset + 10) /
+ self.wifi_scan_interval)
+
+ self.PERIODIC_WIFI_SCAN = (
+ 'am instrument -w -r -e scan-interval \"%d\" -e scan-iterations'
+ ' \"%d\" -e class com.google.android.platform.powertests.'
+ 'WifiTests#testGScanAllChannels com.google.android.platform.'
+ 'powertests/android.test.InstrumentationTestRunner > /dev/null &' %
+ (self.wifi_scan_interval, iterations))
+
+ def coex_scan_test_func(self):
+ """Base test function for coex scan tests.
+
+ Steps:
+ 1. Setup phone in correct state
+ 2. Start desired scans
+ 3. Measure power and validate result
+ """
+ attrs = [
+ 'screen_status', 'wifi_status', 'wifi_band', 'wifi_scan',
+ 'bt_status', 'ble_status', 'ble_scan_mode', 'cellular_status',
+ 'cellular_band'
+ ]
+ indices = [2, 4, 6, 8, 10, 12, 15, 17, 19]
+ self.decode_test_configs(attrs, indices)
+ if self.test_configs.ble_scan_mode == 'lowpower':
+ ble_scan_mode = 'low_power'
+ elif self.test_configs.ble_scan_mode == 'lowlatency':
+ ble_scan_mode = 'low_latency'
+ else:
+ ble_scan_mode = self.test_configs.ble_scan_mode
+ self.phone_setup_for_BT(self.test_configs.bt_status,
+ self.test_configs.ble_status,
+ self.test_configs.screen_status)
+ self.coex_scan_setup(self.test_configs.wifi_scan, ble_scan_mode,
+ self.PERIODIC_WIFI_SCAN)
+ self.measure_power_and_validate()
+
+ @test_tracker_info(uuid='a998dd2b-f5f1-4361-b5da-83e42a69e80b')
+ def test_screen_ON_WiFi_Connected_band_2g_scan_OFF_bt_ON_ble_ON_default_scan_balanced_cellular_OFF_band_None(
+ self):
+ self.coex_scan_test_func()
+
+ @test_tracker_info(uuid='87146825-787a-4ea7-9622-30e9286c8a76')
+ def test_screen_OFF_WiFi_Connected_band_2g_scan_OFF_bt_ON_ble_ON_filtered_scan_lowpower_cellular_OFF_band_None(
+ self):
+ self.coex_scan_test_func()
+
+ @test_tracker_info(uuid='2e645deb-b744-4272-8578-5d4cb159d5aa')
+ def test_screen_OFF_WiFi_Connected_band_5g_scan_OFF_bt_ON_ble_ON_filtered_scan_lowpower_cellular_OFF_band_None(
+ self):
+ self.coex_scan_test_func()
+
+ @test_tracker_info(uuid='d458bc41-f1c8-4ed6-a7b5-0bec34780dda')
+ def test_screen_OFF_WiFi_Disconnected_band_2g_scan_ON_bt_ON_ble_ON_page_scan_None_cellular_OFF_band_None(
+ self):
+ self.coex_scan_test_func()
+
+ @test_tracker_info(uuid='6d9c0e8e-6a0f-458b-84d2-7d60fc254170')
+ def test_screen_OFF_WiFi_Disconnected_band_2g_scan_ON_bt_ON_ble_ON_filtered_scan_lowpower_cellular_OFF_band_None(
+ self):
+ self.coex_scan_test_func()
+
+ @test_tracker_info(uuid='ba52317f-426a-4688-a0a5-1394bcc7b092')
+ def test_screen_OFF_WiFi_Disconnected_band_2g_scan_ON_bt_ON_ble_ON_filtered_scan_lowlatency_cellular_OFF_band_None(
+ self):
+ self.coex_scan_test_func()
+
+ @test_tracker_info(uuid='b4c63eac-bc77-4e76-afff-ade98dde4411')
+ def test_screen_OFF_WiFi_Connected_band_2g_scan_PNO_bt_ON_ble_ON_filtered_scan_lowlatency_cellular_OFF_band_None(
+ self):
+ self.coex_scan_test_func()
+
+ @test_tracker_info(uuid='798796dc-960c-42b2-a835-2b2aefa028d5')
+ def test_screen_OFF_WiFi_Disconnected_band_5g_scan_ON_bt_OFF_ble_OFF_no_scan_None_cellular_ON_band_Verizon(
+ self):
+ self.coex_scan_test_func()
+
+ @test_tracker_info(uuid='6ae44d84-0e68-4524-99b2-d3bfbd2253b8')
+ def test_screen_OFF_WiFi_Disconnected_band_5g_scan_ON_bt_OFF_ble_ON_background_scan_lowpower_cellular_ON_band_Verizon(
+ self):
+ self.coex_scan_test_func()
+
+ @test_tracker_info(uuid='2cb915a3-6319-4ac4-9e4d-9325b3b731c8')
+ def test_screen_OFF_WiFi_Disconnected_band_2g_scan_ON_bt_OFF_ble_ON_background_scan_lowlatency_cellular_ON_band_Verizon(
+ self):
+ self.coex_scan_test_func()
diff --git a/acts/tests/google/power/wifi/PowerWiFibaselineTest.py b/acts/tests/google/power/wifi/PowerWiFibaselineTest.py
new file mode 100644
index 0000000..213c81a
--- /dev/null
+++ b/acts/tests/google/power/wifi/PowerWiFibaselineTest.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+from acts.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_decorators import test_tracker_info
+
+
+class PowerWiFibaselineTest(PWBT.PowerWiFiBaseTest):
+ """Power baseline tests for rockbottom state.
+ Rockbottom for wifi on/off, screen on/off, everything else turned off
+
+ """
+
+ def rockbottom_test_func(self):
+ """Test function for baseline rockbottom tests.
+
+ Decode the test config from the test name, set device to desired state.
+ Measure power and validate results.
+ """
+
+ attrs = ['screen_status', 'wifi_status']
+ indices = [3, 5]
+ self.decode_test_configs(attrs, indices)
+ if self.test_configs.wifi_status == 'ON':
+ wutils.wifi_toggle_state(self.dut, True)
+ if self.test_configs.screen_status == 'OFF':
+ self.dut.droid.goToSleepNow()
+ self.dut.log.info('Screen is OFF')
+ self.measure_power_and_validate()
+
+ # Test cases
+ @test_tracker_info(uuid='e7ab71f4-1e14-40d2-baec-cde19a3ac859')
+ def test_rockbottom_screen_OFF_wifi_OFF(self):
+
+ self.rockbottom_test_func()
+
+ @test_tracker_info(uuid='167c847d-448f-4c7c-900f-82c552d7d9bb')
+ def test_rockbottom_screen_OFF_wifi_ON(self):
+
+ self.rockbottom_test_func()
+
+ @test_tracker_info(uuid='2cd25820-8548-4e60-b0e3-63727b3c952c')
+ def test_rockbottom_screen_ON_wifi_OFF(self):
+
+ self.rockbottom_test_func()
+
+ @test_tracker_info(uuid='d7d90a1b-231a-47c7-8181-23814c8ff9b6')
+ def test_rockbottom_screen_ON_wifi_ON(self):
+
+ self.rockbottom_test_func()
diff --git a/acts/tests/google/power/wifi/PowerWiFidtimTest.py b/acts/tests/google/power/wifi/PowerWiFidtimTest.py
new file mode 100644
index 0000000..c4a1090
--- /dev/null
+++ b/acts/tests/google/power/wifi/PowerWiFidtimTest.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import time
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts.test_utils.wifi import wifi_power_test_utils as wputils
+
+
+class PowerWiFidtimTest(PWBT.PowerWiFiBaseTest):
+ def dtim_test_func(self, dtim_max=10):
+ """A reusable function for DTIM test.
+ Covering different DTIM value, with screen ON or OFF and 2g/5g network
+
+ Args:
+ dtim: the value for DTIM set on the phone
+ screen_status: screen on or off
+ network: a dict of information for the network to connect
+ """
+ attrs = ['screen_status', 'wifi_band', 'dtim']
+ indices = [2, 4, 6]
+ self.decode_test_configs(attrs, indices)
+ # Initialize the dut to rock-bottom state
+ rebooted = wputils.change_dtim(
+ self.dut,
+ gEnableModulatedDTIM=int(self.test_configs.dtim),
+ gMaxLIModulatedDTIM=dtim_max)
+ if rebooted:
+ self.dut_rockbottom()
+ self.dut.log.info('DTIM value of the phone is now {}'.format(
+ self.test_configs.dtim))
+ self.setup_ap_connection(
+ self.main_network[self.test_configs.wifi_band])
+ if self.test_configs.screen_status == 'OFF':
+ self.dut.droid.goToSleepNow()
+ self.dut.log.info('Screen is OFF')
+ time.sleep(5)
+ self.measure_power_and_validate()
+
+ # Test cases
+ @test_tracker_info(uuid='2a70a78b-93a8-46a6-a829-e1624b8239d2')
+ def test_screen_OFF_band_2g_dtim_1(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='b6c4114d-984a-4269-9e77-2bec0e4b6e6f')
+ def test_screen_OFF_band_2g_dtim_2(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='2ae5bc29-3d5f-4fbb-9ff6-f5bd499a9d6e')
+ def test_screen_OFF_band_2g_dtim_4(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='b37fa75f-6166-4247-b15c-adcda8c7038e')
+ def test_screen_OFF_band_2g_dtim_5(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='384d3b0f-4335-4b00-8363-308ec27a150c')
+ def test_screen_ON_band_2g_dtim_1(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='79d0f065-2c46-4400-b02c-5ad60e79afea')
+ def test_screen_ON_band_2g_dtim_4(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='5e2f73cb-7e4e-4a25-8fd5-c85adfdf466e')
+ def test_screen_OFF_band_5g_dtim_1(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='017f57c3-e133-461d-80be-d025d1491d8a')
+ def test_screen_OFF_band_5g_dtim_2(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='b84a1cb3-9573-4bfd-9875-0f33cb171cc5')
+ def test_screen_OFF_band_5g_dtim_4(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='75644df4-2cc8-4bbd-8985-0656a4f9d056')
+ def test_screen_OFF_band_5g_dtim_5(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='327af44d-d9e7-49e0-9bda-accad6241dc7')
+ def test_screen_ON_band_5g_dtim_1(self):
+ self.dtim_test_func()
+
+ @test_tracker_info(uuid='8b32585f-2517-426b-a2c9-8087093cf991')
+ def test_screen_ON_band_5g_dtim_4(self):
+ self.dtim_test_func()
diff --git a/acts/tests/google/power/wifi/PowerWiFimulticastTest.py b/acts/tests/google/power/wifi/PowerWiFimulticastTest.py
new file mode 100644
index 0000000..f43f6a5
--- /dev/null
+++ b/acts/tests/google/power/wifi/PowerWiFimulticastTest.py
@@ -0,0 +1,328 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import time
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts.test_utils.wifi import wifi_power_test_utils as wputils
+from acts.controllers import packet_sender as pkt_utils
+
+RA_SHORT_LIFETIME = 3
+RA_LONG_LIFETIME = 1000
+DNS_LONG_LIFETIME = 300
+DNS_SHORT_LIFETIME = 3
+
+
+class PowerWiFimulticastTest(PWBT.PowerWiFiBaseTest):
+ def set_connection(self):
+ """Setup connection between AP and client.
+
+ Setup connection between AP and phone, change DTIMx1 and get information
+ such as IP addresses to prepare packet construction.
+
+ """
+ attrs = ['screen_status', 'wifi_band']
+ indices = [2, 4]
+ self.decode_test_configs(attrs, indices)
+ # Change DTIMx1 on the phone to receive all Multicast packets
+ rebooted = wputils.change_dtim(
+ self.dut, gEnableModulatedDTIM=1, gMaxLIModulatedDTIM=10)
+ self.dut.log.info('DTIM value of the phone is now DTIMx1')
+ if rebooted:
+ self.dut_rockbottom()
+
+ self.setup_ap_connection(
+ self.main_network[self.test_configs.wifi_band])
+ # Wait for DHCP with timeout of 60 seconds
+ wputils.wait_for_dhcp(self.pkt_sender.interface)
+
+ # Set the desired screen status
+ if self.test_configs.screen_status == 'OFF':
+ self.dut.droid.goToSleepNow()
+ self.dut.log.info('Screen is OFF')
+ time.sleep(5)
+
+ def sendPacketAndMeasure(self, packet):
+ """Packet injection template function
+
+ Args:
+ packet: packet to be sent/inject
+ """
+ # Start sending packets and measure power
+ self.pkt_sender.start_sending(packet, self.interval)
+ self.measure_power_and_validate()
+
+ # Test cases - screen OFF
+ @test_tracker_info(uuid='b5378aaf-7949-48ac-95fb-ee94c85d49c3')
+ def test_screen_OFF_band_5g_directed_arp(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.ArpGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='3b5d348d-70bf-483d-8736-13da569473aa')
+ def test_screen_OFF_band_5g_misdirected_arp(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.ArpGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.ipv4_dst_fake)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='8e534d3b-5a25-429a-a1bb-8119d7d28b5a')
+ def test_screen_OFF_band_5g_directed_ns(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.NsGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='536d716d-f30b-4d20-9976-e2cbc36c3415')
+ def test_screen_OFF_band_5g_misdirected_ns(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.NsGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.ipv6_dst_fake)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='5eed3174-8e94-428e-8527-19a9b5a90322')
+ def test_screen_OFF_band_5g_ra_short(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(RA_SHORT_LIFETIME)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='67867bae-f1c5-44a4-9bd0-2b832ac8059c')
+ def test_screen_OFF_band_5g_ra_long(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(RA_LONG_LIFETIME)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='db19bc94-3513-45c4-b3a5-d6219649d0bb')
+ def test_screen_OFF_band_5g_directed_dhcp_offer(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.DhcpOfferGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='a8059869-40ee-4cf3-a957-4b7aed03fcf9')
+ def test_screen_OFF_band_5g_misdirected_dhcp_offer(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.DhcpOfferGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.mac_dst_fake, self.ipv4_dst_fake)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='6e663f0a-3eb5-46f6-a79e-311baebd5d2a')
+ def test_screen_OFF_band_5g_ra_rnds_short(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(
+ RA_LONG_LIFETIME, enableDNS=True, dns_lifetime=DNS_SHORT_LIFETIME)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='84d2f1ff-bd4f-46c6-9b06-826d9b14909c')
+ def test_screen_OFF_band_5g_ra_rnds_long(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(
+ RA_LONG_LIFETIME, enableDNS=True, dns_lifetime=DNS_LONG_LIFETIME)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='4a17e74f-3e7f-4e90-ac9e-884a7c13cede')
+ def test_screen_OFF_band_5g_directed_ping6(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Ping6Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='ab249e0d-58ba-4b55-8a81-e1e4fb04780a')
+ def test_screen_OFF_band_5g_misdirected_ping6(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Ping6Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.ipv6_dst_fake, pkt_utils.MAC_BROADCAST)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='e37112e6-5c35-4c89-8d15-f5a44e69be0b')
+ def test_screen_OFF_band_5g_directed_ping4(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Ping4Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='afd4a011-63a9-46c3-8a75-13f515ba8475')
+ def test_screen_OFF_band_5g_misdirected_ping4(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Ping4Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.ipv4_dst_fake, pkt_utils.MAC_BROADCAST)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='03f0e845-fd66-4120-a79d-5eb64d49b6cd')
+ def test_screen_OFF_band_5g_mdns6(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Mdns6Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='dcbb0aec-512d-48bd-b743-024697ce511b')
+ def test_screen_OFF_band_5g_mdns4(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Mdns4Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ # Test cases: screen ON
+ @test_tracker_info(uuid='b9550149-bf36-4f86-9b4b-6e900756a90e')
+ def test_screen_ON_band_5g_directed_arp(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.ArpGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='406dffae-104e-46cb-9ec2-910aac7aca39')
+ def test_screen_ON_band_5g_misdirected_arp(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.ArpGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.ipv4_dst_fake)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='be4cb543-c710-4041-a770-819e82a6d164')
+ def test_screen_ON_band_5g_directed_ns(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.NsGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='de21d24f-e03e-47a1-8bbb-11953200e870')
+ def test_screen_ON_band_5g_misdirected_ns(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.NsGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.ipv6_dst_fake)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='b424a170-5095-4b47-82eb-50f7b7fdf35d')
+ def test_screen_ON_band_5g_ra_short(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(RA_SHORT_LIFETIME)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='ab627e59-2ee8-4c0d-970b-eeb1d1cecdc1')
+ def test_screen_ON_band_5g_ra_long(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(RA_LONG_LIFETIME)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='ee6514ab-1814-44b9-ba01-63f77ba77c34')
+ def test_screen_ON_band_5g_directed_dhcp_offer(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.DhcpOfferGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='eaebfe98-32da-4ebc-bca7-3b7026d99a4f')
+ def test_screen_ON_band_5g_misdirected_dhcp_offer(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.DhcpOfferGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.mac_dst_fake, self.ipv4_dst_fake)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='f0e2193f-bf6a-441b-b9c1-bb7b65787cd5')
+ def test_screen_ON_band_5g_ra_rnds_short(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(
+ RA_LONG_LIFETIME, enableDNS=True, dns_lifetime=DNS_SHORT_LIFETIME)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='62b99cd7-75bf-45be-b93f-bb037a13b3e2')
+ def test_screen_ON_band_5g_ra_rnds_long(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(
+ RA_LONG_LIFETIME, enableDNS=True, dns_lifetime=DNS_LONG_LIFETIME)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='4088af4c-a64b-4fc1-848c-688936cc6c12')
+ def test_screen_ON_band_5g_directed_ping6(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Ping6Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='3179e327-e6ac-4dae-bb8a-f3940f21094d')
+ def test_screen_ON_band_5g_misdirected_ping6(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Ping6Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.ipv6_dst_fake, pkt_utils.MAC_BROADCAST)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='90c70e8a-74fd-4878-89c6-5e15c3ede318')
+ def test_screen_ON_band_5g_directed_ping4(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Ping4Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='dcfabbc7-a7e1-4a92-a38d-8ebe7aa2e063')
+ def test_screen_ON_band_5g_misdirected_ping4(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Ping4Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate(self.ipv4_dst_fake, pkt_utils.MAC_BROADCAST)
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='117814db-f94d-4239-a7ab-033482b1da52')
+ def test_screen_ON_band_5g_mdns6(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Mdns6Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
+
+ @test_tracker_info(uuid='ce6ad7e2-21f3-4e68-9c0d-d0e14e0a7c53')
+ def test_screen_ON_band_5g_mdns4(self):
+ self.set_connection()
+ self.pkt_gen_config = wputils.create_pkt_config(self)
+ pkt_gen = pkt_utils.Mdns4Generator(**self.pkt_gen_config)
+ packet = pkt_gen.generate()
+ self.sendPacketAndMeasure(packet)
diff --git a/acts/tests/google/power/wifi/PowerWiFiroamingTest.py b/acts/tests/google/power/wifi/PowerWiFiroamingTest.py
new file mode 100644
index 0000000..6f9dcdd
--- /dev/null
+++ b/acts/tests/google/power/wifi/PowerWiFiroamingTest.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import time
+from acts import utils
+from acts.controllers.ap_lib import hostapd_constants as hc
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts.test_utils.wifi import wifi_constants as wc
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi import wifi_power_test_utils as wputils
+
+
+class PowerWiFiroamingTest(PWBT.PowerWiFiBaseTest):
+
+ # Test cases
+ @test_tracker_info(uuid='392622d3-0c5c-4767-afa2-abfb2058b0b8')
+ def test_screenoff_roaming(self):
+ """Test roaming power consumption with screen off.
+ Change the attenuation level to trigger roaming between two APs
+
+ """
+ self.log.info('Set attenuation to connect device to both APs')
+ self.set_attenuation(self.atten_level['zero_atten'])
+ # Setup both APs
+ network_main = self.main_network[hc.BAND_2G]
+ network_aux = self.aux_network[hc.BAND_2G]
+ self.brconfigs_aux = self.setup_ap_connection(network_aux)
+ self.brconfigs_main = self.setup_ap_connection(network_main)
+ self.dut.droid.goToSleepNow()
+ time.sleep(5)
+ # Set attenuator to trigger roaming
+ self.dut.log.info('Trigger roaming now')
+ self.set_attenuation(self.atten_level[self.current_test_name])
+ self.measure_power_and_validate()
+
+ @test_tracker_info(uuid='2fec5208-043a-410a-8fd2-6784d70a3587')
+ def test_screenoff_fastroaming(self):
+
+ # Setup the aux AP
+ network_main = self.main_network[hc.BAND_2G]
+ network_aux = self.aux_network[hc.BAND_2G]
+ # Set the same SSID for the AUX AP for fastroaming purpose
+ network_aux[wc.SSID] = network_main[wc.SSID]
+ # Set attenuator to connect the phone to the aux AP
+ self.log.info('Set attenuation to connect device to the aux AP')
+ self.set_attenuation(self.atten_level[wc.AP_AUX])
+ self.brconfigs_aux = self.setup_ap_connection(network_aux)
+ # Set attenuator to connect the phone to main AP
+ self.log.info('Set attenuation to connect device to the main AP')
+ self.set_attenuation(self.atten_level[wc.AP_MAIN])
+ self.brconfigs_main = self.setup_ap_connection(network_main)
+ time.sleep(5)
+ self.dut.droid.goToSleepNow()
+ # Trigger fastroaming
+ self.dut.log.info('Trigger fastroaming now')
+ self.set_attenuation(self.atten_level[wc.AP_AUX])
+ self.measure_power_and_validate()
+
+ @test_tracker_info(uuid='a0459b7c-74ce-4adb-8e55-c5365bc625eb')
+ def test_screenoff_toggle_between_AP(self):
+
+ # Set attenuator to connect phone to both networks
+ self.log.info('Set attenuation to connect device to both APs')
+ self.set_attenuation(self.atten_level[self.current_test_name])
+ # Connect to both APs
+ network_main = self.main_network[hc.BAND_2G]
+ network_aux = self.aux_network[hc.BAND_2G]
+ self.brconfigs_main = self.setup_ap_connection(network_main)
+ self.brconfigs_aux = self.setup_ap_connection(network_aux)
+ self.mon_info.duration = self.toggle_interval
+ self.dut.droid.goToSleepNow()
+ time.sleep(5)
+ # Toggle between two networks
+ begin_time = utils.get_current_epoch_time()
+ for i in range(self.toggle_times):
+ self.dut.log.info('Connecting to %s' % network_main[wc.SSID])
+ self.dut.droid.wifiConnect(network_main)
+ file_path, avg_current = wputils.monsoon_data_collect_save(
+ self.dut, self.mon_info, self.current_test_name)
+ self.dut.log.info('Connecting to %s' % network_aux[wc.SSID])
+ self.dut.droid.wifiConnect(network_aux)
+ file_path, avg_current = wputils.monsoon_data_collect_save(
+ self.dut, self.mon_info, self.current_test_name)
+ [plot, dt] = wputils.monsoon_data_plot(self.mon_info, file_path)
+ avg_current = dt.source.data['y0'][0]
+ # Take Bugreport
+ if self.bug_report:
+ self.dut.take_bug_report(self.test_name, begin_time)
+ # Path fail check
+ wputils.pass_fail_check(self, avg_current)
+
+ @test_tracker_info(uuid='e5ff95c0-b17e-425c-a903-821ba555a9b9')
+ def test_screenon_toggle_between_AP(self):
+
+ # Set attenuator to connect phone to both networks
+ self.log.info('Set attenuation to connect device to both APs')
+ self.set_attenuation(self.atten_level[self.current_test_name])
+ # Connect to both APs
+ network_main = self.main_network[hc.BAND_2G]
+ network_aux = self.aux_network[hc.BAND_2G]
+ self.brconfigs_main = self.setup_ap_connection(network_main)
+ self.brconfigs_aux = self.setup_ap_connection(network_aux)
+ self.mon_info.duration = self.toggle_interval
+ time.sleep(5)
+ # Toggle between two networks
+ begin_time = utils.get_current_epoch_time()
+ for i in range(self.toggle_times):
+ self.dut.log.info('Connecting to %s' % network_main[wc.SSID])
+ self.dut.droid.wifiConnect(network_main)
+ file_path, avg_current = wputils.monsoon_data_collect_save(
+ self.dut, self.mon_info, self.current_test_name)
+ self.dut.log.info('Connecting to %s' % network_aux[wc.SSID])
+ self.dut.droid.wifiConnect(network_aux)
+ file_path, avg_current = wputils.monsoon_data_collect_save(
+ self.dut, self.mon_info, self.current_test_name)
+ [plot, dt] = wputils.monsoon_data_plot(self.mon_info, file_path)
+ avg_current = dt.source.data['y0'][0]
+ # Take Bugreport
+ if self.bug_report:
+ self.dut.take_bug_report(self.test_name, begin_time)
+ # Path fail check
+ wputils.pass_fail_check(self, avg_current)
+
+ @test_tracker_info(uuid='a16ae337-326f-4d09-990f-42232c3c0dc4')
+ def test_screenoff_wifi_wedge(self):
+
+ # Set attenuator to connect phone to both networks
+ self.log.info('Set attenuation to connect device to both APs')
+ self.set_attenuation(self.atten_level['zero_atten'])
+ # Connect to both APs
+ network_main = self.main_network[hc.BAND_2G]
+ network_aux = self.aux_network[hc.BAND_2G]
+ self.brconfigs_main = self.setup_ap_connection(network_main)
+ self.brconfigs_aux = self.setup_ap_connection(network_aux)
+ self.log.info('Forget network {}'.format(network_aux[wc.SSID]))
+ wutils.wifi_forget_network(self.dut, network_aux[wc.SSID])
+ self.log.info('Set attenuation to trigger wedge condition')
+ self.set_attenuation(self.atten_level[self.current_test_name])
+ self.dut.droid.goToSleepNow()
+ self.measure_power_and_validate()
diff --git a/acts/tests/google/power/wifi/PowerWiFiscanTest.py b/acts/tests/google/power/wifi/PowerWiFiscanTest.py
new file mode 100644
index 0000000..5b8de6c
--- /dev/null
+++ b/acts/tests/google/power/wifi/PowerWiFiscanTest.py
@@ -0,0 +1,181 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import time
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+UNLOCK_SCREEN = 'input keyevent 82'
+
+
+class PowerWiFiscanTest(PWBT.PowerWiFiBaseTest):
+ def setup_class(self):
+ super().setup_class()
+ # Setup scan command
+ SINGLE_SHOT_SCAN = (
+ 'am instrument -w -r -e min_scan_count \"700\"'
+ ' -e WifiScanTest-testWifiSingleShotScan %d'
+ ' -e class com.google.android.platform.powertests.'
+ 'WifiScanTest#testWifiSingleShotScan'
+ ' com.google.android.platform.powertests/'
+ 'android.test.InstrumentationTestRunner > /dev/null &' %
+ (self.mon_duration + self.mon_offset + 10))
+ BACKGROUND_SCAN = ('am instrument -w -r -e min_scan_count \"1\" -e '
+ 'WifiScanTest-testWifiBackgroundScan %d -e class '
+ 'com.google.android.platform.powertests.WifiScan'
+ 'Test#testWifiBackgroundScan com.google.android.'
+ 'platform.powertests/android.test.Instrumentation'
+ 'TestRunner > /dev/null &' %
+ (self.mon_duration + self.mon_offset + 10))
+ WIFI_SCAN = ('am instrument -w -r -e min_scan_count \"1\" -e '
+ 'WifiScanTest-testWifiScan %d -e class '
+ 'com.google.android.platform.powertests.WifiScanTest#'
+ 'testWifiScan com.google.android.platform.powertests/'
+ 'android.test.InstrumentationTestRunner > /dev/null &' %
+ (self.mon_duration + self.mon_offset + 10))
+ self.APK_SCAN_CMDS = {
+ 'singleshot': SINGLE_SHOT_SCAN,
+ 'background': BACKGROUND_SCAN,
+ 'wifi': WIFI_SCAN
+ }
+
+ def scan_setup(self):
+ """Setup for scan based on the type of scan.
+
+ Trigger the desired scan.
+ """
+ self.log.info('Trigger {} scans'.format(self.test_configs.scan_mode))
+ if self.test_configs.scan_type == 'apk':
+ atten_setting = self.test_configs.wifi_band + '_' + self.test_configs.rssi
+ self.set_attenuation(self.atten_level[atten_setting])
+ self.dut.adb.shell_nb(
+ self.APK_SCAN_CMDS[self.test_configs.scan_mode])
+ else:
+ self.mon_info.offset = 0
+ if self.test_configs.scan_mode == 'pno':
+ self.log.info('Set attenuation to trigger PNO scan')
+ self.set_attenuation(self.atten_level['max_atten'])
+ elif self.test_configs.scan_mode == 'connectivity':
+ self.dut.droid.wakeUpNow()
+ self.log.info(
+ 'Now turn on screen to trigger connectivity scans')
+ self.dut.adb.shell(UNLOCK_SCREEN)
+ elif self.test_configs.scan_mode == 'roaming':
+ atten_setting = self.test_configs.wifi_band + '_roaming'
+ self.set_attenuation(self.atten_level[atten_setting])
+
+ def wifi_scan_test_func(self):
+
+ attrs = [
+ 'screen_status', 'wifi_status', 'wifi_band', 'rssi', 'scan_type',
+ 'scan_mode'
+ ]
+ indices = [2, 4, 6, 8, 10, 11]
+ self.decode_test_configs(attrs, indices)
+ if self.test_configs.wifi_status == 'Disconnected':
+ self.setup_ap_connection(
+ self.main_network[self.test_configs.wifi_band], connect=False)
+ elif self.test_configs.wifi_status == 'Connected':
+ self.setup_ap_connection(
+ self.main_network[self.test_configs.wifi_band])
+ else:
+ wutils.wifi_toggle_state(self.dut, True)
+ if self.test_configs.screen_status == 'OFF':
+ self.dut.droid.goToSleepNow()
+ self.dut.log.info('Screen is OFF')
+ time.sleep(2)
+ self.scan_setup()
+ self.measure_power_and_validate()
+
+ # Test cases
+ # Power.apk triggered scans
+ @test_tracker_info(uuid='e5539b01-e208-43c6-bebf-6f1e73d8d8cb')
+ def test_screen_OFF_WiFi_Disconnected_band_2g_RSSI_high_scan_apk_singleshot(
+ self):
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='14c5a762-95bc-40ea-9fd4-27126df7d86c')
+ def test_screen_OFF_WiFi_Disconnected_band_2g_RSSI_low_scan_apk_singleshot(
+ self):
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='a6506600-c567-43b5-9c25-86b505099b97')
+ def test_screen_OFF_WiFi_Disconnected_band_2g_RSSI_none_scan_apk_singleshot(
+ self):
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='1a458248-1159-4c8e-a39f-92fc9e69c4dd')
+ def test_screen_OFF_WiFi_Disconnected_band_5g_RSSI_high_scan_apk_singleshot(
+ self):
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='bd4da426-a621-4131-9f89-6e5a77f321d2')
+ def test_screen_OFF_WiFi_Disconnected_band_5g_RSSI_low_scan_apk_singleshot(
+ self):
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='288b3add-8925-4803-81c0-53debf157ffc')
+ def test_screen_OFF_WiFi_Disconnected_band_5g_RSSI_none_scan_apk_singleshot(
+ self):
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='f401c66c-e515-4f51-8ef2-2a03470d8ff2')
+ def test_screen_OFF_WiFi_Disconnected_band_5g_RSSI_high_scan_apk_background(
+ self):
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='fe38c1c7-937c-42c0-9381-98356639df8f')
+ def test_screen_OFF_WiFi_Disconnected_band_2g_RSSI_high_scan_apk_wifi(
+ self):
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='8eedefd1-3a08-4ac2-ba55-5eb438def3d4')
+ def test_screen_OFF_WiFi_Disconnected_band_5g_RSSI_high_scan_apk_wifi(
+ self):
+ self.wifi_scan_test_func()
+
+ # Firmware/framework scans
+ @test_tracker_info(uuid='ff5ea952-ee31-4968-a190-82935ce7a8cb')
+ def test_screen_OFF_WiFi_ON_band_5g_RSSI_high_scan_system_connectivity(
+ self):
+ """WiFi disconected, turn on Screen to trigger connectivity scans.
+
+ """
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='9a836e5b-8128-4dd2-8e96-e79177810bdd')
+ def test_screen_OFF_WiFi_Connected_band_2g_RSSI_high_scan_system_connectivity(
+ self):
+ """WiFi connected to 2g, turn on screen to trigger connectivity scans.
+
+ """
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='51e3c4f1-742b-45af-afd5-ae3552a03272')
+ def test_screen_OFF_WiFi_Connected_band_2g_RSSI_high_scan_system_roaming(
+ self):
+ """WiFi connected to 2g, low RSSI to be below roaming threshold.
+
+ """
+ self.wifi_scan_test_func()
+
+ @test_tracker_info(uuid='a16ae337-326f-4d09-990f-42232c3c0dc4')
+ def test_screen_OFF_WiFi_Connected_band_2g_RSSI_high_scan_system_pno(self):
+ """WiFi connected to 2g, trigger pno scan.
+
+ """
+ self.wifi_scan_test_func()
diff --git a/acts/tests/google/power/wifi/PowerWiFitrafficTest.py b/acts/tests/google/power/wifi/PowerWiFitrafficTest.py
new file mode 100644
index 0000000..05a7f8a
--- /dev/null
+++ b/acts/tests/google/power/wifi/PowerWiFitrafficTest.py
@@ -0,0 +1,293 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import time
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.power import PowerWiFiBaseTest as PWBT
+from acts.test_utils.wifi import wifi_power_test_utils as wputils
+
+TEMP_FILE = '/sdcard/Download/tmp.log'
+
+
+class PowerWiFitrafficTest(PWBT.PowerWiFiBaseTest):
+ def iperf_power_test_func(self):
+ """Test function for iperf power measurement at different RSSI level.
+
+ """
+ # Decode the test configs from test name
+ attrs = [
+ 'screen_status', 'band', 'traffic_direction', 'traffic_type',
+ 'signal_level', 'oper_mode', 'bandwidth'
+ ]
+ indices = [3, 4, 5, 6, 7, 9, 10]
+ self.decode_test_configs(attrs, indices)
+
+ # Set attenuator to desired level
+ self.log.info('Set attenuation to desired RSSI level')
+ atten_setting = self.test_configs.band + '_' + self.test_configs.signal_level
+ self.set_attenuation(self.atten_level[atten_setting])
+
+ # Setup AP and connect dut to the network
+ self.setup_ap_connection(
+ network=self.main_network[self.test_configs.band],
+ bandwidth=int(self.test_configs.bandwidth))
+ # Wait for DHCP on the ethernet port and get IP as Iperf server address
+ # Time out in 60 seconds if not getting DHCP address
+ self.iperf_server_address = wputils.wait_for_dhcp(
+ self.pkt_sender.interface)
+
+ time.sleep(5)
+ if self.test_configs.screen_status == 'OFF':
+ self.dut.droid.goToSleepNow()
+ self.RSSI = wputils.get_wifi_rssi(self.dut)
+
+ # Run IPERF
+ self.setup_iperf()
+ self.iperf_server.start()
+ wputils.run_iperf_client_nonblocking(
+ self.dut, self.iperf_server_address, self.iperf_args)
+
+ self.measure_power_and_validate()
+
+ def setup_iperf(self):
+ """Setup iperf command and arguments.
+
+ """
+ # Construct the iperf command based on the test params
+ iperf_args = '-i 1 -t {} -p {} -J'.format(self.iperf_duration,
+ self.iperf_server.port)
+ if self.test_configs.traffic_type == "UDP":
+ iperf_args = iperf_args + "-u -b 2g"
+ if self.test_configs.traffic_direction == "DL":
+ iperf_args = iperf_args + ' -R'
+ self.use_client_output = True
+ else:
+ self.use_client_output = False
+ # Parse the client side data to a file saved on the phone
+ self.iperf_args = iperf_args + ' > %s' % TEMP_FILE
+
+ # Screen off TCP test cases
+ @test_tracker_info(uuid='93f79f74-88d9-4781-bff0-8899bed1c336')
+ def test_traffic_screen_OFF_2g_DL_TCP_high_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='147eff45-97d7-47c0-b306-f84d9adecd9b')
+ def test_traffic_screen_OFF_2g_DL_TCP_medium_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='5982268b-57e4-40bf-848e-fee80fabf9d7')
+ def test_traffic_screen_OFF_2g_DL_TCP_low_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='c71a8c77-d355-4a82-b9f1-7cc8b888abd8')
+ def test_traffic_screen_OFF_5g_DL_TCP_high_RSSI_VHT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='307945a6-32b7-42d0-a26c-d439f1599963')
+ def test_traffic_screen_OFF_5g_DL_TCP_medium_RSSI_VHT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='e9a900a1-e263-45ad-bdf3-9c463f761d3c')
+ def test_traffic_screen_OFF_5g_DL_TCP_low_RSSI_VHT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='1d1d9a06-98e1-486e-a1db-2102708161ec')
+ def test_traffic_screen_OFF_5g_DL_TCP_high_RSSI_VHT_40(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='feeaad15-6893-4d49-aaf6-bf9802780f5d')
+ def test_traffic_screen_OFF_5g_DL_TCP_medium_RSSI_VHT_40(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='f378679a-1c20-43a1-bff6-a6a5482a8e3d')
+ def test_traffic_screen_OFF_5g_DL_TCP_low_RSSI_VHT_40(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='6a05f133-49e5-4436-ba84-0746f04021ef')
+ def test_traffic_screen_OFF_5g_DL_TCP_high_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='750bf1c3-2099-4b89-97dd-18f8e72df462')
+ def test_traffic_screen_OFF_5g_DL_TCP_medium_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='1ea458af-1ae0-40ee-853d-ac57b51d3eda')
+ def test_traffic_screen_OFF_5g_DL_TCP_low_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='43d9b146-3547-4a27-9d79-c9341c32ccda')
+ def test_traffic_screen_OFF_2g_UL_TCP_high_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='f00a868b-c8b1-4b36-8136-b39b5c2396a7')
+ def test_traffic_screen_OFF_2g_UL_TCP_medium_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='cd0c37ac-23fe-4dd1-9130-ccb2dfa71020')
+ def test_traffic_screen_OFF_2g_UL_TCP_low_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='f9173d39-b46d-4d80-a5a5-7966f5eed9de')
+ def test_traffic_screen_OFF_5g_UL_TCP_high_RSSI_VHT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='cf77e1dc-30bc-4df9-88be-408f1fddc24f')
+ def test_traffic_screen_OFF_5g_UL_TCP_medium_RSSI_VHT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='48f91745-22dc-47c9-ace6-c2719df651d6')
+ def test_traffic_screen_OFF_5g_UL_TCP_low_RSSI_VHT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='18456aa7-62f0-4560-a7dc-4d7e01f6aca5')
+ def test_traffic_screen_OFF_5g_UL_TCP_high_RSSI_VHT_40(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='8ad237d7-f5e1-45e1-a4a2-a010628a4db9')
+ def test_traffic_screen_OFF_5g_UL_TCP_medium_RSSI_VHT_40(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='3e29173f-b950-4a41-a7f6-6cc0731bf477')
+ def test_traffic_screen_OFF_5g_UL_TCP_low_RSSI_VHT_40(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='3d4cdb21-a1b0-4011-9956-ca0b7a9f3bec')
+ def test_traffic_screen_OFF_5g_UL_TCP_high_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='8427d3f0-9418-4b5c-aea9-7509e5959ce6')
+ def test_traffic_screen_OFF_5g_UL_TCP_medium_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='5ac91734-0323-464b-b04a-c7d3d7ff8cdf')
+ def test_traffic_screen_OFF_5g_UL_TCP_low_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ # Screen off UDP tests - only check 5g VHT 80
+ @test_tracker_info(uuid='1ab4a4e2-bce2-4ff8-be9d-f8ed2bb617cd')
+ def test_traffic_screen_OFF_5g_DL_UDP_high_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='a2c66d63-e93f-42aa-a021-0c6cdfdc87b8')
+ def test_traffic_screen_OFF_5g_DL_UDP_medium_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='68e6f92a-ae15-4e76-81e7-a7b491e181fe')
+ def test_traffic_screen_OFF_5g_DL_UDP_low_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='258500f4-f177-43df-82a7-a64d66e90720')
+ def test_traffic_screen_OFF_5g_UL_UDP_high_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='3d2d3d45-575d-4080-86f9-b32a96963032')
+ def test_traffic_screen_OFF_5g_UL_UDP_medium_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='a17c7d0b-58ca-47b5-9f32-0b7a3d7d3d9d')
+ def test_traffic_screen_OFF_5g_UL_UDP_low_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ # Screen on point check
+ @test_tracker_info(uuid='c1c71639-4463-4999-8f5d-7d9153402c79')
+ def test_traffic_screen_ON_2g_DL_TCP_high_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='40daebc4-45a2-4299-b724-e8cb917b86e8')
+ def test_traffic_screen_ON_5g_DL_TCP_high_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='2e286f36-1a47-4895-a0e8-a161d6a9fd9f')
+ def test_traffic_screen_ON_2g_UL_TCP_high_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='9f6b52cb-b48a-4382-8061-3d3a511a261a')
+ def test_traffic_screen_ON_5g_UL_TCP_high_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='59d79274-15cf-446b-a567-655c07f8a778')
+ def test_traffic_screen_ON_2g_DL_UDP_high_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='02891671-48cc-4186-9a95-3e02671477d0')
+ def test_traffic_screen_ON_5g_DL_UDP_high_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='02821540-7b08-4e4f-a1f1-b455fd4cec6e')
+ def test_traffic_screen_ON_2g_UL_UDP_high_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='59ea06ac-3ac8-4ecc-abb1-bcde34f47358')
+ def test_traffic_screen_ON_2g_UL_UDP_medium_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='0cbbd849-7b59-4143-95e7-92cf1fd955dc')
+ def test_traffic_screen_ON_2g_UL_UDP_low_RSSI_HT_20(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='d84f11d8-41a9-4ce8-a351-ebb0379d56c1')
+ def test_traffic_screen_ON_5g_UL_UDP_high_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='01b6087c-b39a-441d-90e9-da659aa0db7f')
+ def test_traffic_screen_ON_5g_UL_UDP_medium_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
+
+ @test_tracker_info(uuid='7e16dcaa-128f-4874-ab52-2f43e25e6da8')
+ def test_traffic_screen_ON_5g_UL_UDP_low_RSSI_VHT_80(self):
+
+ self.iperf_power_test_func()
diff --git a/acts/tests/google/power/wifi/PowerbaselineTest.py b/acts/tests/google/power/wifi/PowerbaselineTest.py
deleted file mode 100644
index f74f639..0000000
--- a/acts/tests/google/power/wifi/PowerbaselineTest.py
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - 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.
-
-import logging
-import os
-from acts import base_test
-from acts import utils
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-from acts.test_decorators import test_tracker_info
-
-
-class PowerbaselineTest(base_test.BaseTestClass):
- """Power baseline tests for rockbottom state.
- Rockbottom for wifi on/off, screen on/off, everything else turned off
-
- """
-
- def __init__(self, controllers):
-
- base_test.BaseTestClass.__init__(self, controllers)
-
- def setup_class(self):
-
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- req_params = ['baselinetest_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.baselinetest_params)
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon = self.monsoons[0]
- self.mon.set_max_current(8.0)
- self.mon.set_voltage(4.2)
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
-
- def teardown_class(self):
- """Tearing down the entire test class.
-
- """
- self.log.info('Tearing down the test class')
- self.mon.usb('on')
-
- def teardown_test(self):
- """Tearing down the test case.
-
- """
- self.log.info('Tearing down the test')
- self.mon.usb('on')
-
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters.
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- def rockbottom_test_func(self, screen_status, wifi_status):
- """Test function for baseline rockbottom tests.
-
- Args:
- screen_status: screen on or off
- wifi_status: wifi enable or disable, on/off, not connected even on
- """
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- if wifi_status == 'ON':
- wutils.wifi_toggle_state(self.dut, True)
- if screen_status == 'OFF':
- self.dut.droid.goToSleepNow()
- self.dut.log.info('Screen is OFF')
- # Collecting current measurement data and plot
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- wputils.pass_fail_check(self, avg_current)
-
- # Test cases
- @test_tracker_info(uuid='e7ab71f4-1e14-40d2-baec-cde19a3ac859')
- def test_rockbottom_screenoff_wifidisabled(self):
-
- self.rockbottom_test_func('OFF', 'OFF')
-
- @test_tracker_info(uuid='167c847d-448f-4c7c-900f-82c552d7d9bb')
- def test_rockbottom_screenoff_wifidisconnected(self):
-
- self.rockbottom_test_func('OFF', 'ON')
-
- @test_tracker_info(uuid='2cd25820-8548-4e60-b0e3-63727b3c952c')
- def test_rockbottom_screenon_wifidisabled(self):
-
- self.rockbottom_test_func('ON', 'OFF')
-
- @test_tracker_info(uuid='d7d90a1b-231a-47c7-8181-23814c8ff9b6')
- def test_rockbottom_screenon_wifidisconnected(self):
-
- self.rockbottom_test_func('ON', 'ON')
diff --git a/acts/tests/google/power/wifi/PowerdtimTest.py b/acts/tests/google/power/wifi/PowerdtimTest.py
deleted file mode 100644
index 1a91e95..0000000
--- a/acts/tests/google/power/wifi/PowerdtimTest.py
+++ /dev/null
@@ -1,200 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - 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.
-
-import logging
-import os
-import time
-from acts import base_test
-from acts import utils
-from acts.controllers.ap_lib import hostapd_constants as hc
-from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-
-
-class PowerdtimTest(base_test.BaseTestClass):
- def __init__(self, controllers):
-
- base_test.BaseTestClass.__init__(self, controllers)
-
- def setup_class(self):
-
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- self.access_point = self.access_points[0]
- req_params = ['dtimtest_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.dtimtest_params)
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon = self.monsoons[0]
- self.mon.set_max_current(8.0)
- self.mon.set_voltage(4.2)
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- self.num_atten = self.attenuators[0].instrument.num_atten
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- elif 'attenuator_setting' in file:
- self.attenuation_file = file
- elif 'network_config' in file:
- self.network_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
- self.atten_level = wputils.unpack_custom_file(self.attenuation_file,
- self.TAG)
- self.networks = wputils.unpack_custom_file(self.network_file)
- self.main_network = self.networks['main_network']
-
- def teardown_test(self):
- """Tear down necessary objects after test case is finished.
-
- Bring down the AP interface and connect device back on.
- """
- self.log.info('Tearing down the test case')
- self.access_point.bridge.teardown(self.brconfigs)
- self.access_point.close()
- self.mon.usb('on')
-
- def teardown_class(self):
-
- self.log.info('Tearing down the test class')
- self.access_point.close()
- self.mon.usb('on')
-
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters.
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- def dtim_test_func(self, dtim, screen_status, network, dtim_max=10):
- """A reusable function for DTIM test.
- Covering different DTIM value, with screen ON or OFF and 2g/5g network
-
- Args:
- dtim: the value for DTIM set on the phone
- screen_status: screen on or off
- network: a dict of information for the network to connect
- """
- # Initialize the dut to rock-bottom state
- wputils.change_dtim(
- self.dut, gEnableModulatedDTIM=dtim, gMaxLIModulatedDTIM=dtim_max)
- self.dut.log.info('DTIM value of the phone is now {}'.format(dtim))
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- [
- self.attenuators[i].set_atten(self.atten_level['zero_atten'][i])
- for i in range(self.num_atten)
- ]
- self.log.info('Set attenuation level to connect to the AP')
- # Set up the AP
- self.brconfigs = wputils.ap_setup(self.access_point, network, 20)
- wutils.wifi_connect(self.dut, network)
- if screen_status == 'OFF':
- self.dut.droid.goToSleepNow()
- self.dut.log.info('Screen is OFF')
- time.sleep(5)
- # Collect power data and plot
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Pass and fail check
- wputils.pass_fail_check(self, avg_current)
-
- # Test cases
- @test_tracker_info(uuid='2a70a78b-93a8-46a6-a829-e1624b8239d2')
- def test_2g_screenoff_dtimx1(self):
- network = self.main_network[hc.BAND_2G]
- self.dtim_test_func(1, 'OFF', network)
-
- @test_tracker_info(uuid='b6c4114d-984a-4269-9e77-2bec0e4b6e6f')
- def test_2g_screenoff_dtimx2(self):
- network = self.main_network[hc.BAND_2G]
- self.dtim_test_func(2, 'OFF', network)
-
- @test_tracker_info(uuid='2ae5bc29-3d5f-4fbb-9ff6-f5bd499a9d6e')
- def test_2g_screenoff_dtimx4(self):
- network = self.main_network[hc.BAND_2G]
- self.dtim_test_func(4, 'OFF', network)
-
- @test_tracker_info(uuid='b37fa75f-6166-4247-b15c-adcda8c7038e')
- def test_2g_screenoff_dtimx5(self):
- network = self.main_network[hc.BAND_2G]
- self.dtim_test_func(5, 'OFF', network)
-
- @test_tracker_info(uuid='384d3b0f-4335-4b00-8363-308ec27a150c')
- def test_2g_screenon_dtimx1(self):
- """With screen on, modulated dtim isn't wokring, always DTIMx1.
- So not running through all DTIM cases
-
- """
- network = self.main_network[hc.BAND_2G]
- self.dtim_test_func(1, 'ON', network)
-
- @test_tracker_info(uuid='79d0f065-2c46-4400-b02c-5ad60e79afea')
- def test_2g_screenon_dtimx4(self):
- """Run only extra DTIMx4 for screen on to compare with DTIMx1.
- They should be the same if everything is correct.
-
- """
- network = self.main_network[hc.BAND_2G]
- self.dtim_test_func(4, 'ON', network)
-
- @test_tracker_info(uuid='5e2f73cb-7e4e-4a25-8fd5-c85adfdf466e')
- def test_5g_screenoff_dtimx1(self):
- network = self.main_network[hc.BAND_5G]
- self.dtim_test_func(1, 'OFF', network)
-
- @test_tracker_info(uuid='017f57c3-e133-461d-80be-d025d1491d8a')
- def test_5g_screenoff_dtimx2(self):
- network = self.main_network[hc.BAND_5G]
- self.dtim_test_func(2, 'OFF', network)
-
- @test_tracker_info(uuid='b84a1cb3-9573-4bfd-9875-0f33cb171cc5')
- def test_5g_screenoff_dtimx4(self):
- network = self.main_network[hc.BAND_5G]
- self.dtim_test_func(4, 'OFF', network)
-
- @test_tracker_info(uuid='75644df4-2cc8-4bbd-8985-0656a4f9d056')
- def test_5g_screenoff_dtimx5(self):
- network = self.main_network[hc.BAND_5G]
- self.dtim_test_func(5, 'OFF', network)
-
- @test_tracker_info(uuid='327af44d-d9e7-49e0-9bda-accad6241dc7')
- def test_5g_screenon_dtimx1(self):
- """With screen on, modulated dtim isn't wokring, always DTIMx1.
- So not running through all DTIM cases
-
- """
- network = self.main_network[hc.BAND_5G]
- self.dtim_test_func(1, 'ON', network)
-
- @test_tracker_info(uuid='8b32585f-2517-426b-a2c9-8087093cf991')
- def test_5g_screenon_dtimx4(self):
- """Run only extra DTIMx4 for screen on to compare with DTIMx1.
- They should be the same if everything is correct.
-
- """
- network = self.main_network[hc.BAND_5G]
- self.dtim_test_func(4, 'ON', network)
diff --git a/acts/tests/google/power/wifi/PowermulticastTest.py b/acts/tests/google/power/wifi/PowermulticastTest.py
deleted file mode 100644
index 52dea86..0000000
--- a/acts/tests/google/power/wifi/PowermulticastTest.py
+++ /dev/null
@@ -1,451 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - 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.
-
-import logging
-import os
-import time
-
-from acts import base_test
-from acts import utils
-from acts.controllers.ap_lib import hostapd_constants as hc
-from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-from acts.controllers import packet_sender as pkt_utils
-
-RA_SHORT_LIFETIME = 3
-RA_LONG_LIFETIME = 1000
-DNS_LONG_LIFETIME = 300
-DNS_SHORT_LIFETIME = 3
-
-
-class PowermulticastTest(base_test.BaseTestClass):
- def __init__(self, controllers):
-
- base_test.BaseTestClass.__init__(self, controllers)
-
- def setup_class(self):
-
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- self.access_point = self.access_points[0]
- req_params = ['multicast_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.multicast_params)
- self.num_atten = self.attenuators[0].instrument.num_atten
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon = self.monsoons[0]
- self.mon.set_max_current(8.0)
- self.mon.set_voltage(4.2)
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- self.pkt_sender = self.packet_senders[0]
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- elif 'attenuator_setting' in file:
- self.attenuation_file = file
- elif 'network_config' in file:
- self.network_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
- self.atten_level = wputils.unpack_custom_file(self.attenuation_file,
- self.TAG)
- self.networks = wputils.unpack_custom_file(self.network_file)
- self.main_network = self.networks['main_network']
-
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters.
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- def teardown_test(self):
- """Tear down necessary objects after test case is finished.
-
- Bring down the AP interface, delete the bridge interface, stop the
- packet sender, and reset the ethernet interface for the packet sender
- """
- self.log.info('Tearing down the test case')
- self.pkt_sender.stop_sending(ignore_status=True)
- self.access_point.bridge.teardown(self.brconfigs)
- self.access_point.close()
- wputils.reset_host_interface(self.pkt_sender.interface)
- self.mon.usb('on')
-
- def teardown_class(self):
- """Clean up the test class after tests finish running
-
- """
- self.log.info('Tearing down the test class')
- self.mon.usb('on')
- self.pkt_sender.stop_sending(ignore_status=True)
- self.access_point.close()
-
- def set_connection(self, screen_status, network):
- """Setup connection between AP and client.
-
- Setup connection between AP and phone, change DTIMx1 and get information
- such as IP addresses to prepare packet construction.
-
- Args:
- screen_status: screen on or off
- network: network selection, 2g/5g
- """
- # Change DTIMx1 on the phone to receive all Multicast packets
- wputils.change_dtim(
- self.dut, gEnableModulatedDTIM=1, gMaxLIModulatedDTIM=10)
- self.dut.log.info('DTIM value of the phone is now DTIMx1')
-
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
-
- # Set attenuation and connect to AP
- for attn in range(self.num_atten):
- self.attenuators[attn].set_atten(
- self.atten_level['zero_atten'][attn])
- self.log.info('Set attenuation level to all zero')
- iface_eth = self.pkt_sender.interface
- self.brconfigs = wputils.ap_setup(self.access_point, network)
- wutils.wifi_connect(self.dut, network)
-
- # Wait for DHCP with timeout of 60 seconds
- wputils.wait_for_dhcp(iface_eth)
-
- # Set the desired screen status
- if screen_status == 'OFF':
- self.dut.droid.goToSleepNow()
- self.dut.log.info('Screen is OFF')
- time.sleep(5)
-
- def sendPacketAndMeasure(self, packet):
- """Packet injection template function
-
- Args:
- packet: packet to be sent/inject
- """
- # Start sending packets
- self.pkt_sender.start_sending(packet, self.interval)
-
- # Measure current and plot
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
-
- # Compute pass or fail check
- wputils.pass_fail_check(self, avg_current)
-
- # Test cases - screen OFF
- @test_tracker_info(uuid='b5378aaf-7949-48ac-95fb-ee94c85d49c3')
- def test_screenoff_directed_arp(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.ArpGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='3b5d348d-70bf-483d-8736-13da569473aa')
- def test_screenoff_misdirected_arp(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.ArpGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.ipv4_dst_fake)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='8e534d3b-5a25-429a-a1bb-8119d7d28b5a')
- def test_screenoff_directed_ns(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.NsGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='536d716d-f30b-4d20-9976-e2cbc36c3415')
- def test_screenoff_misdirected_ns(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.NsGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.ipv6_dst_fake)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='5eed3174-8e94-428e-8527-19a9b5a90322')
- def test_screenoff_ra_short(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(RA_SHORT_LIFETIME)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='67867bae-f1c5-44a4-9bd0-2b832ac8059c')
- def test_screenoff_ra_long(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(RA_LONG_LIFETIME)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='db19bc94-3513-45c4-b3a5-d6219649d0bb')
- def test_screenoff_directed_dhcp_offer(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.DhcpOfferGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='a8059869-40ee-4cf3-a957-4b7aed03fcf9')
- def test_screenoff_misdirected_dhcp_offer(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.DhcpOfferGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.mac_dst_fake, self.ipv4_dst_fake)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='6e663f0a-3eb5-46f6-a79e-311baebd5d2a')
- def test_screenoff_ra_rnds_short(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(
- RA_LONG_LIFETIME, enableDNS=True, dns_lifetime=DNS_SHORT_LIFETIME)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='84d2f1ff-bd4f-46c6-9b06-826d9b14909c')
- def test_screenoff_ra_rnds_long(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(
- RA_LONG_LIFETIME, enableDNS=True, dns_lifetime=DNS_LONG_LIFETIME)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='4a17e74f-3e7f-4e90-ac9e-884a7c13cede')
- def test_screenoff_directed_ping6(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Ping6Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='ab249e0d-58ba-4b55-8a81-e1e4fb04780a')
- def test_screenoff_misdirected_ping6(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Ping6Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.ipv6_dst_fake, pkt_utils.MAC_BROADCAST)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='e37112e6-5c35-4c89-8d15-f5a44e69be0b')
- def test_screenoff_directed_ping4(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Ping4Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='afd4a011-63a9-46c3-8a75-13f515ba8475')
- def test_screenoff_misdirected_ping4(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Ping4Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.ipv4_dst_fake, pkt_utils.MAC_BROADCAST)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='03f0e845-fd66-4120-a79d-5eb64d49b6cd')
- def test_screenoff_mdns6(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Mdns6Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='dcbb0aec-512d-48bd-b743-024697ce511b')
- def test_screenoff_mdns4(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('OFF', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Mdns4Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- # Test cases: screen ON
- @test_tracker_info(uuid='b9550149-bf36-4f86-9b4b-6e900756a90e')
- def test_screenon_directed_arp(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.ArpGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='406dffae-104e-46cb-9ec2-910aac7aca39')
- def test_screenon_misdirected_arp(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.ArpGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.ipv4_dst_fake)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='be4cb543-c710-4041-a770-819e82a6d164')
- def test_screenon_directed_ns(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.NsGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='de21d24f-e03e-47a1-8bbb-11953200e870')
- def test_screenon_misdirected_ns(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.NsGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.ipv6_dst_fake)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='b424a170-5095-4b47-82eb-50f7b7fdf35d')
- def test_screenon_ra_short(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(RA_SHORT_LIFETIME)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='ab627e59-2ee8-4c0d-970b-eeb1d1cecdc1')
- def test_screenon_ra_long(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(RA_LONG_LIFETIME)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='ee6514ab-1814-44b9-ba01-63f77ba77c34')
- def test_screenon_directed_dhcp_offer(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.DhcpOfferGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='eaebfe98-32da-4ebc-bca7-3b7026d99a4f')
- def test_screenon_misdirected_dhcp_offer(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.DhcpOfferGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.mac_dst_fake, self.ipv4_dst_fake)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='f0e2193f-bf6a-441b-b9c1-bb7b65787cd5')
- def test_screenon_ra_rnds_short(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(
- RA_LONG_LIFETIME, enableDNS=True, dns_lifetime=DNS_SHORT_LIFETIME)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='62b99cd7-75bf-45be-b93f-bb037a13b3e2')
- def test_screenon_ra_rnds_long(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.RaGenerator(**self.pkt_gen_config)
- packet = pkt_gen.generate(
- RA_LONG_LIFETIME, enableDNS=True, dns_lifetime=DNS_LONG_LIFETIME)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='4088af4c-a64b-4fc1-848c-688936cc6c12')
- def test_screenon_directed_ping6(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Ping6Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='3179e327-e6ac-4dae-bb8a-f3940f21094d')
- def test_screenon_misdirected_ping6(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Ping6Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.ipv6_dst_fake, pkt_utils.MAC_BROADCAST)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='90c70e8a-74fd-4878-89c6-5e15c3ede318')
- def test_screenon_directed_ping4(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Ping4Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='dcfabbc7-a7e1-4a92-a38d-8ebe7aa2e063')
- def test_screenon_misdirected_ping4(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Ping4Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate(self.ipv4_dst_fake, pkt_utils.MAC_BROADCAST)
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='117814db-f94d-4239-a7ab-033482b1da52')
- def test_screenon_mdns6(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Mdns6Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
-
- @test_tracker_info(uuid='ce6ad7e2-21f3-4e68-9c0d-d0e14e0a7c53')
- def test_screenon_mdns4(self):
- network = self.main_network[hc.BAND_5G]
- self.set_connection('ON', network)
- self.pkt_gen_config = wputils.create_pkt_config(self)
- pkt_gen = pkt_utils.Mdns4Generator(**self.pkt_gen_config)
- packet = pkt_gen.generate()
- self.sendPacketAndMeasure(packet)
diff --git a/acts/tests/google/power/wifi/PowerroamingTest.py b/acts/tests/google/power/wifi/PowerroamingTest.py
deleted file mode 100644
index 1b71583..0000000
--- a/acts/tests/google/power/wifi/PowerroamingTest.py
+++ /dev/null
@@ -1,304 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - 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.
-
-import logging
-import os
-import time
-from acts import base_test
-from acts import utils
-from acts.controllers.ap_lib import hostapd_constants as hc
-from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_constants as wc
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-
-
-class PowerroamingTest(base_test.BaseTestClass):
- def __init__(self, controllers):
-
- base_test.BaseTestClass.__init__(self, controllers)
-
- def setup_class(self):
-
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- self.access_point_main = self.access_points[0]
- self.access_point_aux = self.access_points[1]
- req_params = ['roamingtest_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.roamingtest_params)
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon = self.monsoons[0]
- self.mon.set_max_current(8.0)
- self.mon.set_voltage(4.2)
- self.mon_duration_all = self.mon_duration
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- self.num_atten = self.attenuators[0].instrument.num_atten
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- elif 'attenuator_setting' in file:
- self.attenuation_file = file
- elif 'network_config' in file:
- self.network_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
- self.atten_level = wputils.unpack_custom_file(self.attenuation_file,
- self.TAG)
- self.networks = wputils.unpack_custom_file(self.network_file)
- self.main_network = self.networks['main_network']
- self.aux_network = self.networks['aux_network']
-
- def teardown_test(self):
- """Tear down necessary objects after test case is finished.
-
- Bring down all AP interfaces
- """
- self.log.info('Tearing down the test case')
- self.mon.usb('on')
- self.access_point_main.bridge.teardown(self.brconfigs_main)
- self.access_point_aux.bridge.teardown(self.brconfigs_aux)
- for ap in self.access_points:
- ap.close()
-
- def teardown_class(self):
-
- self.log.info('Tearing down the test class')
- self.mon.usb('on')
-
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters.
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- # Test cases
- @test_tracker_info(uuid='392622d3-0c5c-4767-afa2-abfb2058b0b8')
- def test_screenoff_roaming(self):
- """Test roaming power consumption with screen off.
- Change the attenuation level to trigger roaming between two APs
-
- """
- # Setup both APs
- network_main = self.main_network[hc.BAND_2G]
- self.brconfigs_main = wputils.ap_setup(self.access_point_main,
- network_main)
- network_aux = self.aux_network[hc.BAND_2G]
- self.brconfigs_aux = wputils.ap_setup(self.access_point_aux,
- network_aux)
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- # Set attenuator and add two networks to the phone
- self.log.info('Set attenuation to connect device to both APs')
- [
- self.attenuators[i].set_atten(self.atten_level['zero_atten'][i])
- for i in range(self.num_atten)
- ]
- wutils.wifi_connect(self.dut, network_aux)
- time.sleep(5)
- wutils.wifi_connect(self.dut, network_main)
- self.dut.droid.goToSleepNow()
- time.sleep(5)
- # Set attenuator to trigger roaming
- self.dut.log.info('Trigger roaming now')
- [
- self.attenuators[i].set_atten(
- self.atten_level[self.current_test_name][i])
- for i in range(self.num_atten)
- ]
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Path fail check
- wputils.pass_fail_check(self, avg_current)
-
- @test_tracker_info(uuid='2fec5208-043a-410a-8fd2-6784d70a3587')
- def test_screenoff_fastroaming(self):
-
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- # Setup the aux AP
- network_main = self.main_network[hc.BAND_2G]
- network_aux = self.aux_network[hc.BAND_2G]
- # Set the same SSID for the AUX AP for fastroaming purpose
- network_aux[wc.SSID] = network_main[wc.SSID]
- self.brconfigs_aux = wputils.ap_setup(self.access_point_aux,
- network_aux)
- # Set attenuator and add two networks to the phone
- self.log.info('Set attenuation to connect device to the aux AP')
- [
- self.attenuators[i].set_atten(self.atten_level[wc.AP_AUX][i])
- for i in range(self.num_atten)
- ]
- wutils.wifi_connect(self.dut, network_aux)
- time.sleep(5)
- # Setup the main AP
- self.brconfigs_main = wputils.ap_setup(self.access_point_main,
- network_main)
- # Set attenuator to connect the phone to main AP
- self.log.info('Set attenuation to connect device to the main AP')
- [
- self.attenuators[i].set_atten(self.atten_level[wc.AP_MAIN][i])
- for i in range(self.num_atten)
- ]
- wutils.wifi_connect(self.dut, network_main)
- time.sleep(5)
- self.dut.droid.goToSleepNow()
- # Trigger fastroaming
- self.dut.log.info('Trigger fastroaming now')
- [
- self.attenuators[i].set_atten(self.atten_level[wc.AP_AUX][i])
- for i in range(self.num_atten)
- ]
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Path fail check
- wputils.pass_fail_check(self, avg_current)
-
- @test_tracker_info(uuid='a0459b7c-74ce-4adb-8e55-c5365bc625eb')
- def test_screenoff_toggle_between_AP(self):
-
- # Setup both APs
- network_main = self.main_network[hc.BAND_2G]
- self.brconfigs_main = wputils.ap_setup(self.access_point_main,
- network_main)
- network_aux = self.aux_network[hc.BAND_2G]
- self.brconfigs_aux = wputils.ap_setup(self.access_point_aux,
- network_aux)
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- self.mon_info['duration'] = self.toggle_interval
- self.dut.droid.goToSleepNow()
- time.sleep(5)
- self.log.info('Set attenuation to connect device to both APs')
- [
- self.attenuators[i].set_atten(
- self.atten_level[self.current_test_name][i])
- for i in range(self.num_atten)
- ]
- # Toggle between two networks
- begin_time = utils.get_current_epoch_time()
- for i in range(self.toggle_times):
- self.dut.log.info('Connecting to %s' % network_main[wc.SSID])
- self.dut.droid.wifiConnect(network_main)
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- self.dut.log.info('Connecting to %s' % network_aux[wc.SSID])
- self.dut.droid.wifiConnect(network_aux)
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- [plot, dt] = wputils.monsoon_data_plot(self.mon_info, file_path)
- avg_current = dt.source.data['y0'][0]
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Path fail check
- wputils.pass_fail_check(self, avg_current)
-
- @test_tracker_info(uuid='e5ff95c0-b17e-425c-a903-821ba555a9b9')
- def test_screenon_toggle_between_AP(self):
-
- # Setup both APs
- network_main = self.main_network[hc.BAND_5G]
- self.brconfigs_main = wputils.ap_setup(self.access_point_main,
- network_main)
- network_aux = self.aux_network[hc.BAND_5G]
- self.brconfigs_aux = wputils.ap_setup(self.access_point_aux,
- network_aux)
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- self.mon_info['duration'] = self.toggle_interval
- self.log.info('Set attenuation to connect device to both APs')
- [
- self.attenuators[i].set_atten(
- self.atten_level[self.current_test_name][i])
- for i in range(self.num_atten)
- ]
- # Toggle between two networks
- begin_time = utils.get_current_epoch_time()
- for i in range(self.toggle_times):
- self.dut.log.info('Connecting to %s' % network_main[wc.SSID])
- self.dut.droid.wifiConnect(network_main)
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- self.dut.log.info('Connecting to %s' % network_aux[wc.SSID])
- self.dut.droid.wifiConnect(network_aux)
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- [plot, dt] = wputils.monsoon_data_plot(self.mon_info, file_path)
- avg_current = dt.source.data['y0'][0]
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Path fail check
- wputils.pass_fail_check(self, avg_current)
-
- @test_tracker_info(uuid='a16ae337-326f-4d09-990f-42232c3c0dc4')
- def test_screenoff_wifi_wedge(self):
-
- # Setup both APs
- network_main = self.main_network[hc.BAND_2G]
- self.brconfigs_main = wputils.ap_setup(self.access_point_main,
- network_main)
- network_aux = self.aux_network[hc.BAND_2G]
- self.brconfigs_aux = wputils.ap_setup(self.access_point_aux,
- network_aux)
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- # Set attenuator to connect phone to both networks
- self.log.info('Set attenuation to connect device to both APs')
- [
- self.attenuators[i].set_atten(self.atten_level['zero_atten'][i])
- for i in range(self.num_atten)
- ]
- wutils.wifi_connect(self.dut, network_main)
- wutils.wifi_connect(self.dut, network_aux)
- self.log.info('Forget network {}'.format(network_aux[wc.SSID]))
- wutils.wifi_forget_network(self.dut, network_aux[wc.SSID])
- self.log.info('Set attenuation to trigger wedge condition')
- [
- self.attenuators[i].set_atten(
- self.atten_level[self.current_test_name][i])
- for i in range(self.num_atten)
- ]
- self.dut.droid.goToSleepNow()
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Path fail check
- wputils.pass_fail_check(self, avg_current)
diff --git a/acts/tests/google/power/wifi/PowerscanTest.py b/acts/tests/google/power/wifi/PowerscanTest.py
deleted file mode 100644
index 1c26a90..0000000
--- a/acts/tests/google/power/wifi/PowerscanTest.py
+++ /dev/null
@@ -1,314 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - 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.
-
-import logging
-import os
-import time
-from acts import base_test
-from acts import utils
-from acts.controllers.ap_lib import hostapd_constants as hc
-from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-
-UNLOCK_SCREEN = 'input keyevent 82'
-
-
-class PowerscanTest(base_test.BaseTestClass):
- def __init__(self, controllers):
-
- base_test.BaseTestClass.__init__(self, controllers)
-
- def setup_class(self):
-
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- self.access_point = self.access_points[0]
- req_params = ['scantest_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.scantest_params)
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon = self.monsoons[0]
- self.mon.set_max_current(8.0)
- self.mon.set_voltage(4.2)
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- self.num_atten = self.attenuators[0].instrument.num_atten
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- elif 'attenuator_setting' in file:
- self.attenuation_file = file
- elif 'network_config' in file:
- self.network_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
- self.atten_level = wputils.unpack_custom_file(self.attenuation_file,
- self.TAG)
- self.networks = wputils.unpack_custom_file(self.network_file)
- self.main_network = self.networks['main_network']
-
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters.
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- def setup_test(self):
-
- self.SINGLE_SHOT_SCAN = (
- 'am instrument -w -r -e min_scan_count \"700\"'
- ' -e WifiScanTest-testWifiSingleShotScan %d'
- ' -e class com.google.android.platform.powertests.'
- 'WifiScanTest#testWifiSingleShotScan'
- ' com.google.android.platform.powertests/'
- 'android.test.InstrumentationTestRunner > /dev/null &' %
- (self.mon_duration + self.mon_offset + 10))
- self.BACKGROUND_SCAN = (
- 'am instrument -w -r -e min_scan_count \"1\" -e '
- 'WifiScanTest-testWifiBackgroundScan %d -e class '
- 'com.google.android.platform.powertests.WifiScan'
- 'Test#testWifiBackgroundScan com.google.android.'
- 'platform.powertests/android.test.Instrumentation'
- 'TestRunner > /dev/null &' %
- (self.mon_duration + self.mon_offset + 10))
- self.WIFI_SCAN = (
- 'am instrument -w -r -e min_scan_count \"1\" -e '
- 'WifiScanTest-testWifiScan %d -e class '
- 'com.google.android.platform.powertests.WifiScanTest#'
- 'testWifiScan com.google.android.platform.powertests/'
- 'android.test.InstrumentationTestRunner > /dev/null &' %
- (self.mon_duration + self.mon_offset + 10))
-
- def teardown_test(self):
- """Tear down necessary objects after test case is finished.
-
- Bring down the AP interface and connect device back online
- """
- self.log.info('Tearing down the test case')
- self.access_point.bridge.teardown(self.brconfigs)
- self.access_point.close()
- self.mon.usb('on')
-
- def teardown_class(self):
-
- self.log.info('Tearing down the test class')
- self.access_point.close()
- self.mon.usb('on')
-
- def powrapk_scan_test_func(self, scan_command, band):
- """Test function for power.apk triggered scans.
- Args:
- scan_command: the adb shell command to trigger scans
-
- """
- network = self.main_network[band]
- self.brconfigs = wputils.ap_setup(self.access_point, network)
- self.log.info('Set attenuation to get high RSSI at {}'.format(band))
- [
- self.attenuators[i].set_atten(
- self.atten_level[self.current_test_name][i])
- for i in range(self.num_atten)
- ]
- self.mon_info['offset'] == 0
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- self.log.info('Wait for {} seconds'.format(self.settle_wait_time))
- time.sleep(self.settle_wait_time)
- self.log.info('Running power apk command to trigger scans')
- self.dut.adb.shell_nb(scan_command)
- self.dut.droid.goToSleepNow()
- # Collect power data and plot
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Path fail check
- wputils.pass_fail_check(self, avg_current)
-
- # Test cases
- @test_tracker_info(uuid='e5539b01-e208-43c6-bebf-6f1e73d8d8cb')
- def test_single_shot_scan_2g_highRSSI(self):
-
- self.powrapk_scan_test_func(self.SINGLE_SHOT_SCAN, hc.BAND_2G)
-
- @test_tracker_info(uuid='14c5a762-95bc-40ea-9fd4-27126df7d86c')
- def test_single_shot_scan_2g_lowRSSI(self):
-
- self.powrapk_scan_test_func(self.SINGLE_SHOT_SCAN, hc.BAND_2G)
-
- @test_tracker_info(uuid='a6506600-c567-43b5-9c25-86b505099b97')
- def test_single_shot_scan_2g_noAP(self):
-
- self.powrapk_scan_test_func(self.SINGLE_SHOT_SCAN, hc.BAND_2G)
-
- @test_tracker_info(uuid='1a458248-1159-4c8e-a39f-92fc9e69c4dd')
- def test_single_shot_scan_5g_highRSSI(self):
-
- self.powrapk_scan_test_func(self.SINGLE_SHOT_SCAN, hc.BAND_5G)
-
- @test_tracker_info(uuid='bd4da426-a621-4131-9f89-6e5a77f321d2')
- def test_single_shot_scan_5g_lowRSSI(self):
-
- self.powrapk_scan_test_func(self.SINGLE_SHOT_SCAN, hc.BAND_5G)
-
- @test_tracker_info(uuid='288b3add-8925-4803-81c0-53debf157ffc')
- def test_single_shot_scan_5g_noAP(self):
-
- self.powrapk_scan_test_func(self.SINGLE_SHOT_SCAN, hc.BAND_5G)
-
- @test_tracker_info(uuid='f401c66c-e515-4f51-8ef2-2a03470d8ff2')
- def test_background_scan(self):
-
- self.powrapk_scan_test_func(self.BACKGROUND_SCAN, hc.BAND_5G)
-
- @test_tracker_info(uuid='fe38c1c7-937c-42c0-9381-98356639df8f')
- def test_wifi_scan_2g(self):
-
- self.powrapk_scan_test_func(self.WIFI_SCAN, hc.BAND_2G)
-
- @test_tracker_info(uuid='8eedefd1-3a08-4ac2-ba55-5eb438def3d4')
- def test_wifi_scan_5g(self):
-
- self.powrapk_scan_test_func(self.WIFI_SCAN, hc.BAND_5G)
-
- @test_tracker_info(uuid='ff5ea952-ee31-4968-a190-82935ce7a8cb')
- def test_scan_wifidisconnected_turnonscreen(self):
-
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- self.dut.droid.goToSleepNow()
- self.log.info('Screen is OFF')
- time.sleep(5)
- self.dut.droid.wakeUpNow()
- self.log.info('Now turn on screen to trigger scans')
- self.dut.adb.shell(UNLOCK_SCREEN)
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- wputils.pass_fail_check(self, avg_current)
-
- @test_tracker_info(uuid='9a836e5b-8128-4dd2-8e96-e79177810bdd')
- def test_scan_wificonnected_turnonscreen(self):
-
- network = self.main_network[hc.BAND_2G]
- self.brconfigs = wputils.ap_setup(self.access_point, network)
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- # Set attenuators to connect main AP
- [
- self.attenuators[i].set_atten(
- self.atten_level[self.current_test_name][i])
- for i in range(self.num_atten)
- ]
- wutils.wifi_connect(self.dut, network)
- time.sleep(10)
- self.dut.droid.goToSleepNow()
- self.log.info('Screen is OFF')
- time.sleep(5)
- self.dut.droid.wakeUpNow()
- self.log.info('Now turn on screen to trigger scans')
- self.dut.adb.shell(UNLOCK_SCREEN)
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Path fail check
- wputils.pass_fail_check(self, avg_current)
-
- @test_tracker_info(uuid='51e3c4f1-742b-45af-afd5-ae3552a03272')
- def test_scan_screenoff_below_rssi_threshold(self):
-
- network = self.main_network[hc.BAND_2G]
- self.brconfigs = wputils.ap_setup(self.access_point, network)
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- # Set attenuator and add main network to the phone
- self.log.info('Set attenuation so device connection has medium RSSI')
- [
- self.attenuators[i].set_atten(self.atten_level['zero_atten'][i])
- for i in range(self.num_atten)
- ]
- wutils.wifi_connect(self.dut, network)
- self.dut.droid.goToSleepNow()
- time.sleep(20)
- # Set attenuator to make RSSI below threshold
- self.log.info('Set attenuation to drop RSSI below threhold')
- [
- self.attenuators[i].set_atten(
- self.atten_level[self.current_test_name][i])
- for i in range(self.num_atten)
- ]
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Path fail check
- wputils.pass_fail_check(self, avg_current)
-
- @test_tracker_info(uuid='a16ae337-326f-4d09-990f-42232c3c0dc4')
- def test_scan_screenoff_lost_wificonnection(self):
-
- network = self.main_network[hc.BAND_5G]
- self.brconfigs = wputils.ap_setup(self.access_point, network)
- # Initialize the dut to rock-bottom state
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
- # Set attenuator and add main network to the phone
- self.log.info('Set attenuation so device connection has medium RSSI')
- [
- self.attenuators[i].set_atten(self.atten_level['zero_atten'][i])
- for i in range(self.num_atten)
- ]
- wutils.wifi_connect(self.dut, network)
- self.dut.droid.goToSleepNow()
- time.sleep(5)
- # Set attenuator to make RSSI below threshold
- self.log.info('Set attenuation so device loses connection')
- [
- self.attenuators[i].set_atten(
- self.atten_level[self.current_test_name][i])
- for i in range(self.num_atten)
- ]
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
- wputils.monsoon_data_plot(self.mon_info, file_path)
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Path fail check
- wputils.pass_fail_check(self, avg_current)
diff --git a/acts/tests/google/power/wifi/PowertrafficTest.py b/acts/tests/google/power/wifi/PowertrafficTest.py
deleted file mode 100644
index 83f99dd..0000000
--- a/acts/tests/google/power/wifi/PowertrafficTest.py
+++ /dev/null
@@ -1,409 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - 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.
-
-import logging
-import math
-import os
-import time
-from acts import base_test
-from acts import utils
-from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.wifi import wifi_power_test_utils as wputils
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-import acts.controllers.iperf_server as ipf
-
-TEMP_FILE = '/sdcard/Download/tmp.log'
-
-
-class PowertrafficTest(base_test.BaseTestClass):
- def __init__(self, controllers):
-
- WifiBaseTest.__init__(self, controllers)
-
- def setup_class(self):
-
- self.log = logging.getLogger()
- self.dut = self.android_devices[0]
- req_params = ['traffictest_params', 'custom_files']
- self.unpack_userparams(req_params)
- self.unpack_testparams(self.traffictest_params)
- self.num_atten = self.attenuators[0].instrument.num_atten
- self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
- self.mon_duration = self.iperf_duration - 10
- self.mon = self.monsoons[0]
- self.mon.set_max_current(8.0)
- self.mon.set_voltage(4.2)
- self.mon.attach_device(self.dut)
- self.mon_info = wputils.create_monsoon_info(self)
- self.iperf_server = self.iperf_servers[0]
- self.access_point = self.access_points[0]
- self.pkt_sender = self.packet_senders[0]
- for file in self.custom_files:
- if 'pass_fail_threshold' in file:
- self.threshold_file = file
- elif 'attenuator_setting' in file:
- self.attenuation_file = file
- elif 'network_config' in file:
- self.network_file = file
- self.threshold = wputils.unpack_custom_file(self.threshold_file,
- self.TAG)
- self.atten_level = wputils.unpack_custom_file(self.attenuation_file,
- self.TAG)
- self.networks = wputils.unpack_custom_file(self.network_file)
- self.main_network = self.networks['main_network']
-
- def teardown_test(self):
- """Tear down necessary objects after test case is finished.
-
- Bring down the AP interface, delete the bridge interface, stop IPERF
- server and reset the ethernet interface for iperf traffic
- """
- self.log.info('Tearing down the test case')
- self.iperf_server.stop()
- self.access_point.bridge.teardown(self.brconfigs)
- self.access_point.close()
- wputils.reset_host_interface(self.pkt_sender.interface)
- self.mon.usb('on')
-
- def teardown_class(self):
- """Clean up the test class after tests finish running
-
- """
- self.log.info('Tearing down the test class')
- self.mon.usb('on')
- self.access_point.close()
-
- def unpack_testparams(self, bulk_params):
- """Unpack all the test specific parameters.
-
- Args:
- bulk_params: dict with all test specific params in the config file
- """
- for key in bulk_params.keys():
- setattr(self, key, bulk_params[key])
-
- def iperf_power_test_func(self):
- """Test function for iperf power measurement at different RSSI level.
-
- Args:
- screen_status: screen ON or OFF
- band: desired band for AP to operate on
- """
- # Decode test parameters for the current test
- test_params = self.current_test_name.split('_')
- screen_status = test_params[2][6:]
- band = test_params[3]
- traffic_direction = test_params[4]
- traffic_type = test_params[5]
- signal_level = test_params[6][:-4]
- oper_mode = test_params[7]
- bandwidth = int(test_params[8])
-
- # Set device to rockbottom first
- wputils.dut_rockbottom(self.dut)
- wutils.wifi_toggle_state(self.dut, True)
-
- # Set up the AP
- network = self.main_network[band]
- self.brconfigs = wputils.ap_setup(self.access_point, network,
- bandwidth)
-
- # Wait for DHCP on the ethernet port and get IP as Iperf server address
- # Time out in 60 seconds if not getting DHCP address
- iface_eth = self.pkt_sender.interface
- self.iperf_server_address = wputils.wait_for_dhcp(iface_eth)
-
- # Set attenuator to desired level
- self.log.info('Set attenuation to desired RSSI level')
- atten_setting = band + '_' + signal_level
- for i in range(self.num_atten):
- attenuation = self.atten_level[atten_setting][i]
- self.attenuators[i].set_atten(attenuation)
-
- # Connect the phone to the AP
- wutils.wifi_connect(self.dut, network)
- time.sleep(5)
- if screen_status == 'off':
- self.dut.droid.goToSleepNow()
- RSSI = wputils.get_wifi_rssi(self.dut)
-
- # Construct the iperf command based on the test params
- iperf_args = '-i 1 -t {} -p {} -J'.format(self.iperf_duration,
- self.iperf_server.port)
- if traffic_type == "UDP":
- iperf_args = iperf_args + "-u -b 2g"
- if traffic_direction == "DL":
- iperf_args = iperf_args + ' -R'
- use_client_output = True
- else:
- use_client_output = False
- # Parse the client side data to a file saved on the phone
- iperf_args = iperf_args + ' > %s' % TEMP_FILE
-
- # Run IPERF
- self.iperf_server.start()
- wputils.run_iperf_client_nonblocking(
- self.dut, self.iperf_server_address, iperf_args)
-
- # Collect power data and plot
- begin_time = utils.get_current_epoch_time()
- file_path, avg_current = wputils.monsoon_data_collect_save(
- self.dut, self.mon_info, self.current_test_name)
-
- # Get IPERF results
- RESULTS_DESTINATION = os.path.join(self.iperf_server.log_path,
- "iperf_client_output_{}.log".format(
- self.current_test_name))
- PULL_FILE = '{} {}'.format(TEMP_FILE, RESULTS_DESTINATION)
- self.dut.adb.pull(PULL_FILE)
- # Calculate the average throughput
- if use_client_output:
- iperf_file = RESULTS_DESTINATION
- else:
- iperf_file = self.iperf_server.log_files[-1]
- try:
- iperf_result = ipf.IPerfResult(iperf_file)
- throughput = (math.fsum(iperf_result.instantaneous_rates[:-1]) /
- len(iperf_result.instantaneous_rates[:-1])) * 8
- self.log.info("The average throughput is {}".format(throughput))
- except:
- self.log.warning(
- "ValueError: Cannot get iperf result. Setting to 0")
- throughput = 0
-
- # Monsoon Power data plot with IPerf throughput information
- tag = '_RSSI_{0:d}dBm_Throughput_{1:.2f}Mbps'.format(RSSI, throughput)
- wputils.monsoon_data_plot(self.mon_info, file_path, tag)
-
- # Take Bugreport
- if bool(self.bug_report) == True:
- self.dut.take_bug_report(self.test_name, begin_time)
- # Pass and fail check
- wputils.pass_fail_check(self, avg_current)
-
- # Screen off TCP test cases
- @test_tracker_info(uuid='93f79f74-88d9-4781-bff0-8899bed1c336')
- def test_traffic_screenoff_2g_DL_TCP_highRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='147eff45-97d7-47c0-b306-f84d9adecd9b')
- def test_traffic_screenoff_2g_DL_TCP_mediumRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='5982268b-57e4-40bf-848e-fee80fabf9d7')
- def test_traffic_screenoff_2g_DL_TCP_lowRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='c71a8c77-d355-4a82-b9f1-7cc8b888abd8')
- def test_traffic_screenoff_5g_DL_TCP_highRSSI_VHT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='307945a6-32b7-42d0-a26c-d439f1599963')
- def test_traffic_screenoff_5g_DL_TCP_mediumRSSI_VHT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='e9a900a1-e263-45ad-bdf3-9c463f761d3c')
- def test_traffic_screenoff_5g_DL_TCP_lowRSSI_VHT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='1d1d9a06-98e1-486e-a1db-2102708161ec')
- def test_traffic_screenoff_5g_DL_TCP_highRSSI_VHT_40(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='feeaad15-6893-4d49-aaf6-bf9802780f5d')
- def test_traffic_screenoff_5g_DL_TCP_mediumRSSI_VHT_40(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='f378679a-1c20-43a1-bff6-a6a5482a8e3d')
- def test_traffic_screenoff_5g_DL_TCP_lowRSSI_VHT_40(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='6a05f133-49e5-4436-ba84-0746f04021ef')
- def test_traffic_screenoff_5g_DL_TCP_highRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='750bf1c3-2099-4b89-97dd-18f8e72df462')
- def test_traffic_screenoff_5g_DL_TCP_mediumRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='1ea458af-1ae0-40ee-853d-ac57b51d3eda')
- def test_traffic_screenoff_5g_DL_TCP_lowRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='43d9b146-3547-4a27-9d79-c9341c32ccda')
- def test_traffic_screenoff_2g_UL_TCP_highRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='f00a868b-c8b1-4b36-8136-b39b5c2396a7')
- def test_traffic_screenoff_2g_UL_TCP_mediumRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='cd0c37ac-23fe-4dd1-9130-ccb2dfa71020')
- def test_traffic_screenoff_2g_UL_TCP_lowRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='f9173d39-b46d-4d80-a5a5-7966f5eed9de')
- def test_traffic_screenoff_5g_UL_TCP_highRSSI_VHT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='cf77e1dc-30bc-4df9-88be-408f1fddc24f')
- def test_traffic_screenoff_5g_UL_TCP_mediumRSSI_VHT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='48f91745-22dc-47c9-ace6-c2719df651d6')
- def test_traffic_screenoff_5g_UL_TCP_lowRSSI_VHT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='18456aa7-62f0-4560-a7dc-4d7e01f6aca5')
- def test_traffic_screenoff_5g_UL_TCP_highRSSI_VHT_40(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='8ad237d7-f5e1-45e1-a4a2-a010628a4db9')
- def test_traffic_screenoff_5g_UL_TCP_mediumRSSI_VHT_40(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='3e29173f-b950-4a41-a7f6-6cc0731bf477')
- def test_traffic_screenoff_5g_UL_TCP_lowRSSI_VHT_40(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='3d4cdb21-a1b0-4011-9956-ca0b7a9f3bec')
- def test_traffic_screenoff_5g_UL_TCP_highRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='8427d3f0-9418-4b5c-aea9-7509e5959ce6')
- def test_traffic_screenoff_5g_UL_TCP_mediumRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='5ac91734-0323-464b-b04a-c7d3d7ff8cdf')
- def test_traffic_screenoff_5g_UL_TCP_lowRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- # Screen off UDP tests - only check 5g VHT 80
- @test_tracker_info(uuid='1ab4a4e2-bce2-4ff8-be9d-f8ed2bb617cd')
- def test_traffic_screenoff_5g_DL_UDP_highRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='a2c66d63-e93f-42aa-a021-0c6cdfdc87b8')
- def test_traffic_screenoff_5g_DL_UDP_mediumRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='68e6f92a-ae15-4e76-81e7-a7b491e181fe')
- def test_traffic_screenoff_5g_DL_UDP_lowRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='258500f4-f177-43df-82a7-a64d66e90720')
- def test_traffic_screenoff_5g_UL_UDP_highRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='3d2d3d45-575d-4080-86f9-b32a96963032')
- def test_traffic_screenoff_5g_UL_UDP_mediumRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='a17c7d0b-58ca-47b5-9f32-0b7a3d7d3d9d')
- def test_traffic_screenoff_5g_UL_UDP_lowRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- # Screen on point check
- @test_tracker_info(uuid='c1c71639-4463-4999-8f5d-7d9153402c79')
- def test_traffic_screenon_2g_DL_TCP_highRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='40daebc4-45a2-4299-b724-e8cb917b86e8')
- def test_traffic_screenon_5g_DL_TCP_highRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='2e286f36-1a47-4895-a0e8-a161d6a9fd9f')
- def test_traffic_screenon_2g_UL_TCP_highRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='9f6b52cb-b48a-4382-8061-3d3a511a261a')
- def test_traffic_screenon_5g_UL_TCP_highRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='59d79274-15cf-446b-a567-655c07f8a778')
- def test_traffic_screenon_2g_DL_UDP_highRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='02891671-48cc-4186-9a95-3e02671477d0')
- def test_traffic_screenon_5g_DL_UDP_highRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='02821540-7b08-4e4f-a1f1-b455fd4cec6e')
- def test_traffic_screenon_2g_UL_UDP_highRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='59ea06ac-3ac8-4ecc-abb1-bcde34f47358')
- def test_traffic_screenon_2g_UL_UDP_mediumRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='0cbbd849-7b59-4143-95e7-92cf1fd955dc')
- def test_traffic_screenon_2g_UL_UDP_lowRSSI_HT_20(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='d84f11d8-41a9-4ce8-a351-ebb0379d56c1')
- def test_traffic_screenon_5g_UL_UDP_highRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='01b6087c-b39a-441d-90e9-da659aa0db7f')
- def test_traffic_screenon_5g_UL_UDP_mediumRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
-
- @test_tracker_info(uuid='7e16dcaa-128f-4874-ab52-2f43e25e6da8')
- def test_traffic_screenon_5g_UL_UDP_lowRSSI_VHT_80(self):
-
- self.iperf_power_test_func()
diff --git a/acts/tests/google/tel/lab/TelLabCmasTest.py b/acts/tests/google/tel/lab/TelLabCmasTest.py
index 9ac76fc..0da53f1 100644
--- a/acts/tests/google/tel/lab/TelLabCmasTest.py
+++ b/acts/tests/google/tel/lab/TelLabCmasTest.py
@@ -44,6 +44,7 @@
from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.anritsu_utils import set_post_sim_params
from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
@@ -59,6 +60,7 @@
from acts.test_utils.tel.tel_test_utils import ensure_network_rat
from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_decorators import test_tracker_info
@@ -88,10 +90,11 @@
return True
def setup_test(self):
+ if getattr(self, "qxdm_log", True):
+ start_qxdm_loggers(self.log, self.android_devices)
ensure_phones_idle(self.log, self.android_devices)
toggle_airplane_mode(self.log, self.ad, True)
- self.ad.adb.shell("setprop net.lte.ims.volte.provisioned 1",
- ignore_status=True)
+ self.ad.adb.shell("logcat -c -b all", ignore_status=True)
return True
def teardown_test(self):
@@ -118,6 +121,9 @@
[self.bts1] = set_simulation_func(self.anritsu, self.user_params,
self.ad.sim_card)
set_usim_parameters(self.anritsu, self.ad.sim_card)
+ if rat == RAT_LTE:
+ set_post_sim_params(self.anritsu, self.user_params,
+ self.ad.sim_card)
self.anritsu.start_simulation()
if rat == RAT_LTE:
@@ -134,6 +140,7 @@
preferred_network_setting = NETWORK_MODE_GSM_ONLY
rat_family = RAT_FAMILY_GSM
elif rat == RAT_1XRTT:
+ self.ad.droid.telephonyToggleDataConnection(False)
preferred_network_setting = NETWORK_MODE_CDMA
rat_family = RAT_FAMILY_CDMA2000
else:
@@ -157,8 +164,14 @@
self.log, self.ad, self.anritsu,
next(TelLabCmasTest.SERIAL_NO), message_id,
warning_message):
- self.log.error("Phone {} Failed to receive CMAS message"
- .format(self.ad.serial))
+ self.log.warning("Phone {} Failed to receive CMAS message"
+ .format(self.ad.serial))
+ # Another check of logcat before confirming failure
+ if self.ad.search_logcat(warning_message):
+ self.ad.log.info(
+ "Confirmed from Logcat - User received %s",
+ warning_message)
+ return True
return False
else:
if not cmas_receive_verify_message_cdma1x(
@@ -166,8 +179,13 @@
next(TelLabCmasTest.SERIAL_NO), message_id,
warning_message, c2k_response_type, c2k_severity,
c2k_urgency, c2k_certainty):
- self.log.error("Phone {} Failed to receive CMAS message"
- .format(self.ad.serial))
+ self.log.warning("Phone {} Failed to receive CMAS message"
+ .format(self.ad.serial))
+ if self.ad.search_logcat(warning_message):
+ self.ad.log.info(
+ "Confirmed from Logcat - User received %s",
+ warning_message)
+ return True
return False
except AnritsuError as e:
self.log.error("Error in connection with Anritsu Simulator: " +
diff --git a/acts/tests/google/tel/lab/TelLabDataRoamingTest.py b/acts/tests/google/tel/lab/TelLabDataRoamingTest.py
index 59d0964..71c5ed5 100644
--- a/acts/tests/google/tel/lab/TelLabDataRoamingTest.py
+++ b/acts/tests/google/tel/lab/TelLabDataRoamingTest.py
@@ -18,6 +18,7 @@
"""
import time
+from acts.test_decorators import test_tracker_info
from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import BtsServiceState
@@ -25,12 +26,15 @@
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.anritsu_utils import set_post_sim_params
from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
from acts.test_utils.tel.tel_test_utils import ensure_network_rat
from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
from acts.test_utils.tel.tel_test_utils import toggle_cell_data_roaming
+from acts.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.utils import adb_shell_ping
PING_DURATION = 5 # Number of packets to ping
@@ -46,6 +50,8 @@
self.md8475a_ip_address = self.user_params[
"anritsu_md8475a_ip_address"]
self.wlan_option = self.user_params.get("anritsu_wlan_option", False)
+ if self.ad.sim_card == "VzW12349":
+ set_preferred_apn_by_adb(self.ad, "VZWINTERNET")
def setup_class(self):
try:
@@ -57,10 +63,12 @@
return True
def setup_test(self):
+ if getattr(self, "qxdm_log", True):
+ start_qxdm_loggers(self.log, self.android_devices)
ensure_phones_idle(self.log, self.android_devices)
toggle_airplane_mode(self.log, self.ad, True)
- self.ad.adb.shell("setprop net.lte.ims.volte.provisioned 1",
- ignore_status=True)
+ self.ad.adb.shell(
+ "setprop net.lte.ims.volte.provisioned 1", ignore_status=True)
return True
def teardown_test(self):
@@ -73,39 +81,62 @@
self.anritsu.disconnect()
return True
- def LTE_WCDMA_data_roaming(self, mcc, mnc):
+ def phone_setup_data_roaming(self):
+ return ensure_network_rat(
+ self.log,
+ self.ad,
+ NETWORK_MODE_LTE_GSM_WCDMA,
+ RAT_FAMILY_LTE,
+ toggle_apm_after_setting=True)
+
+ def LTE_WCDMA_data_roaming(self, mcc, mnc, lte_band, wcdma_band):
try:
[self.bts1, self.bts2] = set_system_model_lte_wcdma(
self.anritsu, self.user_params, self.ad.sim_card)
set_usim_parameters(self.anritsu, self.ad.sim_card)
+ set_post_sim_params(self.anritsu, self.user_params,
+ self.ad.sim_card)
self.bts1.mcc = mcc
self.bts1.mnc = mnc
self.bts2.mcc = mcc
self.bts2.mnc = mnc
- self.bts2.packet_rate = BtsPacketRate.WCDMA_DLHSAUTO_REL7_ULHSAUTO
+ self.bts1.band = lte_band
+ self.bts2.band = wcdma_band
+ self.bts2.packet_rate = BtsPacketRate.WCDMA_DLHSAUTO_REL8_ULHSAUTO
self.anritsu.start_simulation()
self.bts2.service_state = BtsServiceState.SERVICE_STATE_OUT
self.log.info("Toggle Mobile Data On")
self.ad.droid.telephonyToggleDataConnection(True)
- if not ensure_network_rat(
- self.log,
- self.ad,
- NETWORK_MODE_LTE_GSM_WCDMA,
- RAT_FAMILY_LTE,
- toggle_apm_after_setting=True):
- self.log.error(
- "Failed to set rat family {}, preferred network:{}".format(
- RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
- return False
+
+ if not self.phone_setup_data_roaming():
+ self.log.warning("phone_setup_func failed. Rebooting UE")
+ self.ad.reboot()
+ time.sleep(30)
+ if self.ad.sim_card == "VzW12349":
+ set_preferred_apn_by_adb(self.ad, "VZWINTERNET")
+ if not self.phone_setup_data_roaming():
+ self.log.error(
+ "Failed to set rat family {}, preferred network:{}".
+ format(RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+ return False
+
toggle_cell_data_roaming(self.ad, True)
self.anritsu.wait_for_registration_state(1) # for BTS1 LTE
time.sleep(TIME_TO_WAIT_BEFORE_PING)
- if not adb_shell_ping(self.ad, PING_DURATION, PING_TARGET):
- self.log.error(
- "Test Fail: Phone {} can not ping {} with Data Roaming On"
- .format(self.ad.serial, PING_TARGET))
- return False
+ for i in range(3):
+ self.ad.log.info("Verify internet connection - attempt %d",
+ i + 1)
+ result = adb_shell_ping(self.ad, PING_DURATION, PING_TARGET)
+ if result:
+ self.ad.log.info("PING SUCCESS")
+ break
+ elif i == 2:
+ self.log.error(
+ "Test Fail: Phone {} can not ping {} with Data Roaming On"
+ .format(self.ad.serial, PING_TARGET))
+ return False
+
toggle_cell_data_roaming(self.ad, False)
time.sleep(TIME_TO_WAIT_BEFORE_PING)
if adb_shell_ping(self.ad, PING_DURATION, PING_TARGET):
@@ -124,11 +155,19 @@
self.anritsu.wait_for_registration_state(2) # for BTS2 WCDMA
time.sleep(TIME_TO_WAIT_BEFORE_PING)
- if not adb_shell_ping(self.ad, PING_DURATION, PING_TARGET):
- self.log.error(
- "Test Fail: Phone {} can not ping {} with Data Roaming On"
- .format(self.ad.serial, PING_TARGET))
- return False
+ for i in range(3):
+ self.ad.log.info("Verify internet connection - attempt %d",
+ i + 1)
+ result = adb_shell_ping(self.ad, PING_DURATION, PING_TARGET)
+ if result:
+ self.ad.log.info("PING SUCCESS")
+ break
+ elif i == 2:
+ self.log.error(
+ "Test Fail: Phone {} can not ping {} with Data Roaming On"
+ .format(self.ad.serial, PING_TARGET))
+ return False
+
toggle_cell_data_roaming(self.ad, False)
time.sleep(TIME_TO_WAIT_BEFORE_PING)
if adb_shell_ping(self.ad, PING_DURATION, PING_TARGET):
@@ -148,6 +187,7 @@
""" Tests Begin """
+ @test_tracker_info(uuid="46d49bff-9671-4ab0-a90d-b49d870af6f0")
@TelephonyBaseTest.tel_test_wrap
def test_data_roaming_optus(self):
"""Data roaming test for Optus LTE and WCDMA networks
@@ -171,8 +211,10 @@
Returns:
True if pass; False if fail
"""
- return self.LTE_WCDMA_data_roaming("505", "90F")
+ return self.LTE_WCDMA_data_roaming(
+ mcc="505", mnc="02F", lte_band="3", wcdma_band="1")
+ @test_tracker_info(uuid="68a6313c-d95a-4cae-8e35-2fdf3c94df56")
@TelephonyBaseTest.tel_test_wrap
def test_data_roaming_telus(self):
"""Data roaming test for Telus LTE and WCDMA networks
@@ -196,8 +238,10 @@
Returns:
True if pass; False if fail
"""
- return self.LTE_WCDMA_data_roaming("302", "86F")
+ return self.LTE_WCDMA_data_roaming(
+ mcc="302", mnc="220", lte_band="4", wcdma_band="2")
+ @test_tracker_info(uuid="16de850a-6511-42d4-8d8f-d800477aba6b")
@TelephonyBaseTest.tel_test_wrap
def test_data_roaming_vodafone(self):
"""Data roaming test for Vodafone LTE and WCDMA networks
@@ -221,8 +265,10 @@
Returns:
True if pass; False if fail
"""
- return self.LTE_WCDMA_data_roaming("234", "15F")
+ return self.LTE_WCDMA_data_roaming(
+ mcc="234", mnc="15F", lte_band="20", wcdma_band="1")
+ @test_tracker_info(uuid="e9050f3d-b53c-4a87-9363-b88a842a3479")
@TelephonyBaseTest.tel_test_wrap
def test_data_roaming_o2(self):
"""Data roaming test for O2 LTE and WCDMA networks
@@ -246,8 +292,10 @@
Returns:
True if pass; False if fail
"""
- return self.LTE_WCDMA_data_roaming("234", "02F")
+ return self.LTE_WCDMA_data_roaming(
+ mcc="234", mnc="02F", lte_band="20", wcdma_band="1")
+ @test_tracker_info(uuid="a3f56da1-6a51-45b0-8016-3a492661e1f4")
@TelephonyBaseTest.tel_test_wrap
def test_data_roaming_orange(self):
"""Data roaming test for Orange LTE and WCDMA networks
@@ -271,8 +319,10 @@
Returns:
True if pass; False if fail
"""
- return self.LTE_WCDMA_data_roaming("234", "33F")
+ return self.LTE_WCDMA_data_roaming(
+ mcc="234", mnc="33F", lte_band="20", wcdma_band="1")
+ @test_tracker_info(uuid="dcde16c1-730c-41ee-ad29-286f4962c66f")
@TelephonyBaseTest.tel_test_wrap
def test_data_roaming_idea(self):
"""Data roaming test for Idea LTE and WCDMA networks
@@ -296,6 +346,7 @@
Returns:
True if pass; False if fail
"""
- return self.LTE_WCDMA_data_roaming("404", "24F")
+ return self.LTE_WCDMA_data_roaming(
+ mcc="404", mnc="24F", lte_band="3", wcdma_band="1")
""" Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabDataTest.py b/acts/tests/google/tel/lab/TelLabDataTest.py
index ee7d24c..abb2002 100644
--- a/acts/tests/google/tel/lab/TelLabDataTest.py
+++ b/acts/tests/google/tel/lab/TelLabDataTest.py
@@ -22,6 +22,7 @@
import logging
import os
+from acts.test_decorators import test_tracker_info
from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import BtsBandwidth
@@ -35,12 +36,14 @@
from acts.test_utils.tel.anritsu_utils import sms_mo_send
from acts.test_utils.tel.anritsu_utils import sms_mt_receive_verify
from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.anritsu_utils import set_post_sim_params
from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
from acts.test_utils.tel.tel_defines import RAT_1XRTT
from acts.test_utils.tel.tel_defines import RAT_GSM
from acts.test_utils.tel.tel_defines import RAT_LTE
@@ -56,6 +59,7 @@
from acts.test_utils.tel.tel_test_utils import ensure_network_generation
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
from acts.test_utils.tel.tel_test_utils import iperf_test_by_adb
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.utils import adb_shell_ping
from acts.utils import rand_ascii_str
@@ -64,6 +68,7 @@
DEFAULT_PING_DURATION = 30
+
class TelLabDataTest(TelephonyBaseTest):
SETTLING_TIME = 30
SERIAL_NO = cb_serial_number()
@@ -97,10 +102,10 @@
return True
def setup_test(self):
+ if getattr(self, "qxdm_log", True):
+ start_qxdm_loggers(self.log, self.android_devices)
ensure_phones_idle(self.log, self.android_devices)
toggle_airplane_mode(self.log, self.ad, True)
- self.ad.adb.shell(
- "setprop net.lte.ims.volte.provisioned 1", ignore_status=True)
return True
def teardown_test(self):
@@ -118,6 +123,8 @@
[self.bts1] = set_simulation_func(self.anritsu, self.user_params,
self.ad.sim_card)
set_usim_parameters(self.anritsu, self.ad.sim_card)
+ set_post_sim_params(self.anritsu, self.user_params,
+ self.ad.sim_card)
if self.lte_bandwidth == 20:
self.bts1.bandwidth = BtsBandwidth.LTE_BANDWIDTH_20MHz
elif self.lte_bandwidth == 15:
@@ -130,7 +137,7 @@
self.anritsu.start_simulation()
if rat == RAT_LTE:
- preferred_network_setting = NETWORK_MODE_LTE_GSM_WCDMA
+ preferred_network_setting = NETWORK_MODE_LTE_CDMA_EVDO
rat_family = RAT_FAMILY_LTE
elif rat == RAT_WCDMA:
preferred_network_setting = NETWORK_MODE_GSM_UMTS
@@ -158,10 +165,6 @@
self.anritsu.wait_for_registration_state()
time.sleep(self.SETTLING_TIME)
- if not ensure_network_generation(self.log, self.ad, GEN_4G,
- NETWORK_SERVICE_DATA):
- self.log.error("Device not in 4G Connected Mode.")
- return False
# Fetch IP address of the host machine
cmd = "|".join(("ifconfig", "grep eth0 -A1", "grep inet",
@@ -198,7 +201,7 @@
else:
self.log.error("iperf failed to Destination.")
self.log.info("Iteration %d Failed", iteration)
- if float(current_power) < -55.0 :
+ if float(current_power) < -55.0:
return True
else:
return False
@@ -226,6 +229,7 @@
""" Tests Begin """
+ @test_tracker_info(uuid="df40279a-46dc-40ee-9205-bce2d0fba7e8")
@TelephonyBaseTest.tel_test_wrap
def test_lte_pings_iperf(self):
""" Test Pings functionality on LTE
diff --git a/acts/tests/google/tel/lab/TelLabEmergencyCallTest.py b/acts/tests/google/tel/lab/TelLabEmergencyCallTest.py
index 2a3a3d8..c46ddd8 100644
--- a/acts/tests/google/tel/lab/TelLabEmergencyCallTest.py
+++ b/acts/tests/google/tel/lab/TelLabEmergencyCallTest.py
@@ -36,6 +36,7 @@
from acts.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.anritsu_utils import set_post_sim_params
from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
from acts.test_utils.tel.tel_defines import DEFAULT_EMERGENCY_CALL_NUMBER
from acts.test_utils.tel.tel_defines import EMERGENCY_CALL_NUMBERS
@@ -57,16 +58,22 @@
from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
from acts.test_utils.tel.tel_test_utils import toggle_volte
+from acts.test_utils.tel.tel_test_utils import check_apm_mode_on_by_serial
+from acts.test_utils.tel.tel_test_utils import set_apm_mode_on_by_serial
+from acts.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_decorators import test_tracker_info
+from acts.utils import exe_cmd
+
class TelLabEmergencyCallTest(TelephonyBaseTest):
def __init__(self, controllers):
TelephonyBaseTest.__init__(self, controllers)
try:
- self.stress_test_number = int(self.user_params[
- "stress_test_number"])
+ self.stress_test_number = int(
+ self.user_params["stress_test_number"])
self.log.info("Executing {} calls per test in stress test mode".
format(self.stress_test_number))
except KeyError:
@@ -91,6 +98,25 @@
self.log.warning("Unknown Emergency Number {}".format(
self.emergency_call_number))
+ # Check for all adb devices on the linux machine, and set APM ON
+ cmd = "|".join(("adb devices", "grep -i device$", "cut -f1"))
+ output = exe_cmd(cmd)
+ list_of_devices = output.decode("utf-8").split("\n")
+ if len(list_of_devices) > 1:
+ for i in range(len(list_of_devices) - 1):
+ self.log.info("Serial %s", list_of_devices[i])
+ if check_apm_mode_on_by_serial(self.ad, list_of_devices[i]):
+ self.log.info("Device is already in APM ON")
+ else:
+ self.log.info("Device is not in APM, turning it ON")
+ set_apm_mode_on_by_serial(self.ad, list_of_devices[i])
+ if check_apm_mode_on_by_serial(self.ad,
+ list_of_devices[i]):
+ self.log.info("Device is now in APM ON")
+
+ if self.ad.sim_card == "VzW12349":
+ set_preferred_apn_by_adb(self.ad, "VZWINTERNET")
+
def setup_class(self):
try:
self.anritsu = MD8475A(self.md8475a_ip_address, self.log,
@@ -101,10 +127,15 @@
return True
def setup_test(self):
+ if getattr(self, "qxdm_log", True):
+ start_qxdm_loggers(self.log, self.android_devices)
ensure_phone_default_state(self.log, self.ad, check_subscription=False)
toggle_airplane_mode_by_adb(self.log, self.ad, True)
- self.ad.adb.shell("setprop net.lte.ims.volte.provisioned 1",
- ignore_status=True)
+ try:
+ if self.ad.sim_card == "VzW12349":
+ self.ad.droid.imsSetVolteProvisioning(True)
+ except Exception as e:
+ self.ad.log.error(e)
# get a handle to virtual phone
self.virtualPhoneHandle = self.anritsu.get_VirtualPhone()
return True
@@ -135,6 +166,9 @@
set_simulation_func(self.anritsu, self.user_params,
self.ad.sim_card)
set_usim_parameters(self.anritsu, self.ad.sim_card)
+ if is_ims_call or srvcc or csfb_type:
+ set_post_sim_params(self.anritsu, self.user_params,
+ self.ad.sim_card)
self.virtualPhoneHandle.auto_answer = (VirtualPhoneAutoAnswer.ON,
2)
if csfb_type:
@@ -168,12 +202,16 @@
elif srvcc == "InCall":
self.anritsu.start_simulation()
self.anritsu.send_command("IMSSTARTVN 1")
+ self.anritsu.send_command("IMSSTARTVN 2")
+ self.anritsu.send_command("IMSSTARTVN 3")
check_ims_reg = True
check_ims_calling = True
else:
self.anritsu.start_simulation()
- if is_ims_call:
+ if is_ims_call or csfb_type:
self.anritsu.send_command("IMSSTARTVN 1")
+ self.anritsu.send_command("IMSSTARTVN 2")
+ self.anritsu.send_command("IMSSTARTVN 3")
iterations = 1
if self.stress_test_number > 0:
@@ -181,8 +219,8 @@
successes = 0
for i in range(1, iterations + 1):
if self.stress_test_number:
- self.log.info("Running iteration {} of {}".format(
- i, iterations))
+ self.log.info(
+ "Running iteration {} of {}".format(i, iterations))
# FIXME: There's no good reason why this must be true;
# I can only assume this was done to work around a problem
self.ad.droid.telephonyToggleDataConnection(False)
@@ -191,13 +229,21 @@
sim_model = (self.anritsu.get_simulation_model()).split(",")
no_of_bts = len(sim_model)
for i in range(2, no_of_bts + 1):
- self.anritsu.send_command("OUTOFSERVICE OUT,BTS{}".format(
- i))
+ self.anritsu.send_command(
+ "OUTOFSERVICE OUT,BTS{}".format(i))
if phone_setup_func is not None:
if not phone_setup_func(self.ad):
- self.log.error("phone_setup_func failed.")
- continue
+ self.log.warning(
+ "phone_setup_func failed. Rebooting UE")
+ self.ad.reboot()
+ time.sleep(30)
+ if self.ad.sim_card == "VzW12349":
+ set_preferred_apn_by_adb(self.ad, "VZWINTERNET")
+ if not phone_setup_func(self.ad):
+ self.log.error("phone_setup_func failed.")
+ continue
+
if is_wait_for_registration:
self.anritsu.wait_for_registration_state()
@@ -207,8 +253,8 @@
continue
for i in range(2, no_of_bts + 1):
- self.anritsu.send_command("OUTOFSERVICE IN,BTS{}".format(
- i))
+ self.anritsu.send_command(
+ "OUTOFSERVICE IN,BTS{}".format(i))
time.sleep(WAIT_TIME_ANRITSU_REG_AND_CALL)
if srlte_csfb or srvcc:
@@ -249,6 +295,7 @@
return True
def _phone_setup_lte_wcdma(self, ad):
+ toggle_volte(self.log, ad, False)
return ensure_network_rat(
self.log,
ad,
@@ -313,17 +360,17 @@
@test_tracker_info(uuid="f5c93228-3b43-48a3-b509-796d41625171")
@TelephonyBaseTest.tel_test_wrap
def test_emergency_call_lte_wcdma_csfb_redirection(self):
- """ Test Emergency call functionality on LTE (CSFB to WCDMA).
+ """ Test Emergency call functionality on LTE.
CSFB type is REDIRECTION
Steps:
1. Setup CallBox on LTE and WCDMA network, make sure DUT register on LTE network.
- 2. Make an emergency call to 911. Make sure DUT CSFB to WCDMA.
+ 2. Make an emergency call to 911. Make sure DUT does not CSFB to WCDMA.
3. Make sure Anritsu receives the call and accept.
4. Tear down the call.
Expected Results:
- 2. Emergency call succeed. DUT CSFB to WCDMA.
+ 2. Emergency call succeed. DUT does not CSFB to WCDMA.
3. Anritsu can accept the call.
4. Tear down call succeed.
@@ -334,22 +381,23 @@
set_system_model_lte_wcdma,
self._phone_setup_lte_wcdma,
emergency_number=self.emergency_call_number,
- csfb_type=CsfbType.CSFB_TYPE_REDIRECTION)
+ csfb_type=CsfbType.CSFB_TYPE_REDIRECTION,
+ is_ims_call=True)
@test_tracker_info(uuid="8deb6b21-2cb0-4241-bcad-6cd62a340b07")
@TelephonyBaseTest.tel_test_wrap
def test_emergency_call_lte_wcdma_csfb_handover(self):
- """ Test Emergency call functionality on LTE (CSFB to WCDMA).
+ """ Test Emergency call functionality on LTE.
CSFB type is HANDOVER
Steps:
1. Setup CallBox on LTE and WCDMA network, make sure DUT register on LTE network.
- 2. Make an emergency call to 911. Make sure DUT CSFB to WCDMA.
+ 2. Make an emergency call to 911. Make sure DUT does not CSFB to WCDMA.
3. Make sure Anritsu receives the call and accept.
4. Tear down the call.
Expected Results:
- 2. Emergency call succeed. DUT CSFB to WCDMA.
+ 2. Emergency call succeed. DUT does not CSFB to WCDMA.
3. Anritsu can accept the call.
4. Tear down call succeed.
@@ -360,7 +408,8 @@
set_system_model_lte_wcdma,
self._phone_setup_lte_wcdma,
emergency_number=self.emergency_call_number,
- csfb_type=CsfbType.CSFB_TYPE_HANDOVER)
+ csfb_type=CsfbType.CSFB_TYPE_HANDOVER,
+ is_ims_call=True)
@test_tracker_info(uuid="52b6b783-de77-497d-87e0-63c930e6c9bb")
@TelephonyBaseTest.tel_test_wrap
diff --git a/acts/tests/google/tel/lab/TelLabEtwsTest.py b/acts/tests/google/tel/lab/TelLabEtwsTest.py
index 3c469b4..0576c9b 100644
--- a/acts/tests/google/tel/lab/TelLabEtwsTest.py
+++ b/acts/tests/google/tel/lab/TelLabEtwsTest.py
@@ -30,6 +30,7 @@
from acts.test_utils.tel.anritsu_utils import set_system_model_lte
from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.anritsu_utils import set_post_sim_params
from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
@@ -45,6 +46,7 @@
from acts.test_utils.tel.tel_test_utils import ensure_network_rat
from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_decorators import test_tracker_info
@@ -76,10 +78,10 @@
return True
def setup_test(self):
+ if getattr(self, "qxdm_log", True):
+ start_qxdm_loggers(self.log, self.android_devices)
ensure_phones_idle(self.log, self.android_devices)
toggle_airplane_mode(self.log, self.ad, True)
- self.ad.adb.shell("setprop net.lte.ims.volte.provisioned 1",
- ignore_status=True)
return True
def teardown_test(self):
@@ -97,6 +99,9 @@
[self.bts1] = set_simulation_func(self.anritsu, self.user_params,
self.ad.sim_card)
set_usim_parameters(self.anritsu, self.ad.sim_card)
+ if rat == RAT_LTE:
+ set_post_sim_params(self.anritsu, self.user_params,
+ self.ad.sim_card)
self.anritsu.start_simulation()
if rat == RAT_LTE:
diff --git a/acts/tests/google/tel/lab/TelLabMobilityTest.py b/acts/tests/google/tel/lab/TelLabMobilityTest.py
index 8ce7b7e..12e570b 100644
--- a/acts/tests/google/tel/lab/TelLabMobilityTest.py
+++ b/acts/tests/google/tel/lab/TelLabMobilityTest.py
@@ -18,6 +18,7 @@
"""
import time
+from acts.test_decorators import test_tracker_info
from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
from acts.controllers.anritsu_lib.md8475a import MD8475A
from acts.controllers.anritsu_lib.md8475a import BtsNumber
@@ -31,6 +32,7 @@
from acts.test_utils.tel.anritsu_utils import set_system_model_lte_1x
from acts.test_utils.tel.anritsu_utils import set_system_model_lte_evdo
from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.anritsu_utils import set_post_sim_params
from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
@@ -51,6 +53,8 @@
from acts.test_utils.tel.tel_test_utils import toggle_volte
from acts.test_utils.tel.tel_test_utils import run_multithread_func
from acts.test_utils.tel.tel_test_utils import iperf_test_by_adb
+from acts.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.utils import adb_shell_ping
@@ -58,7 +62,7 @@
from acts.controllers import iperf_server
from acts.utils import exe_cmd
-DEFAULT_CALL_NUMBER = "0123456789"
+DEFAULT_CALL_NUMBER = "+11234567891"
DEFAULT_PING_DURATION = 5
WAITTIME_BEFORE_HANDOVER = 20
WAITTIME_AFTER_HANDOVER = 20
@@ -77,6 +81,8 @@
self.ip_server = self.iperf_servers[0]
self.port_num = self.ip_server.port
self.log.info("Iperf Port is %s", self.port_num)
+ if self.ad.sim_card == "VzW12349":
+ set_preferred_apn_by_adb(self.ad, "VZWINTERNET")
def setup_class(self):
try:
@@ -89,6 +95,8 @@
def setup_test(self):
try:
+ if getattr(self, "qxdm_log", True):
+ start_qxdm_loggers(self.log, self.android_devices)
self.ad.droid.telephonyFactoryReset()
except Exception as e:
self.ad.log.error(e)
@@ -115,7 +123,7 @@
phone_idle_func_after_registration=None,
volte=True,
iperf=True,
- all_bands=True,
+ all_bands=False,
is_wait_for_registration=True,
voice_number=DEFAULT_CALL_NUMBER,
teardown_side=CALL_TEARDOWN_PHONE,
@@ -124,91 +132,108 @@
bts = set_simulation_func(self.anritsu, self.user_params,
self.ad.sim_card)
set_usim_parameters(self.anritsu, self.ad.sim_card)
-
+ set_post_sim_params(self.anritsu, self.user_params,
+ self.ad.sim_card)
self.anritsu.start_simulation()
self.anritsu.send_command("IMSSTARTVN 1")
-
- self.ad.droid.telephonyToggleDataConnection(False)
-
+ self.anritsu.send_command("IMSSTARTVN 2")
+ self.anritsu.send_command("IMSSTARTVN 3")
# turn off all other BTS to ensure UE registers on BTS1
- sim_model = (self.anritsu.get_simulation_model()).split(",")
- no_of_bts = len(sim_model)
+ simmodel = self.anritsu.get_simulation_model().split(',')
+ no_of_bts = len(simmodel)
for i in range(2, no_of_bts + 1):
self.anritsu.send_command("OUTOFSERVICE OUT,BTS{}".format(i))
if phone_setup_func is not None:
if not phone_setup_func(self.ad):
- self.log.error("phone_setup_func failed.")
-
+ self.log.warning("phone_setup_func failed. Rebooting UE")
+ self.ad.reboot()
+ time.sleep(30)
+ if self.ad.sim_card == "VzW12349":
+ set_preferred_apn_by_adb(self.ad, "VZWINTERNET")
+ if not phone_setup_func(self.ad):
+ self.log.error("phone_setup_func failed.")
if is_wait_for_registration:
self.anritsu.wait_for_registration_state()
-
if phone_idle_func_after_registration:
if not phone_idle_func_after_registration(self.log, self.ad):
self.log.error("phone_idle_func failed.")
-
for i in range(2, no_of_bts + 1):
self.anritsu.send_command("OUTOFSERVICE IN,BTS{}".format(i))
-
time.sleep(WAIT_TIME_ANRITSU_REG_AND_CALL)
-
- if iperf:
+ if iperf: # setup iPerf server
server_ip = self.iperf_setup()
if not server_ip:
self.log.error("iperf server can not be reached by ping")
return False
-
- if volte:
+ if volte: # make a VoLTE MO call
if not make_ims_call(self.log, self.ad, self.anritsu,
voice_number):
self.log.error("Phone {} Failed to make volte call to {}"
.format(self.ad.serial, voice_number))
return False
+ if all_bands and (simmodel[1] == "WCDMA"):
+ band = []
+ for rat in simmodel[:2]:
+ band.append(self.anritsu.get_supported_bands(rat))
+ self.log.info("UE reported LTE bands are {}".format(band[0]))
+ self.log.info("UE reported WCDMA bands are {}".format(band[1]))
+ current_lte_band = bts[0].band
+ # move current LTE band to the last in the list
+ band[0].remove(current_lte_band)
+ band[0].append(current_lte_band)
+ n = max(len(band[0]), len(band[1]))
+ else:
+ n = 1 # n is the number of LTE->WCDMA->LTE handovers
- if not iperf: # VoLTE only
- result = handover_tc(self.log, self.anritsu,
- WAITTIME_BEFORE_HANDOVER, BtsNumber.BTS1,
- BtsNumber.BTS2)
- time.sleep(WAITTIME_AFTER_HANDOVER)
- else: # with iPerf
- iperf_task = (self._iperf_task, (
- server_ip,
- WAITTIME_BEFORE_HANDOVER + WAITTIME_AFTER_HANDOVER - 10))
- ho_task = (handover_tc,
- (self.log, self.anritsu, WAITTIME_BEFORE_HANDOVER,
- BtsNumber.BTS1, BtsNumber.BTS2))
- result = run_multithread_func(self.log, [ho_task, iperf_task])
- if not result[1]:
- self.log.error("iPerf failed.")
- return False
+ for i in range(n):
+ if all_bands:
+ bts[1].band = band[1][i % len(band[1])]
+ if not iperf: # VoLTE only
+ result = handover_tc(self.log, self.anritsu,
+ WAITTIME_BEFORE_HANDOVER,
+ BtsNumber.BTS1, BtsNumber.BTS2)
+ time.sleep(WAITTIME_AFTER_HANDOVER)
+ else: # with iPerf
+ iperf_task = (self._iperf_task,
+ (server_ip, WAITTIME_BEFORE_HANDOVER +
+ WAITTIME_AFTER_HANDOVER - 10))
+ ho_task = (handover_tc, (self.log, self.anritsu,
+ WAITTIME_BEFORE_HANDOVER,
+ BtsNumber.BTS1, BtsNumber.BTS2))
+ result = run_multithread_func(self.log,
+ [ho_task, iperf_task])
+ if not result[1]:
+ self.log.error("iPerf failed.")
+ return False
- self.log.info("handover test case result code {}.".format(result[
- 0]))
+ self.log.info(
+ "handover test case result code {}.".format(result[0]))
+ if volte:
+ # check if the phone stay in call
+ if not self.ad.droid.telecomIsInCall():
+ self.log.error("Call is already ended in the phone.")
+ return False
- if volte:
- # check if the phone stay in call
- if not self.ad.droid.telecomIsInCall():
- self.log.error("Call is already ended in the phone.")
- return False
-
- if not tear_down_call(self.log, self.ad, self.anritsu):
- self.log.error("Phone {} Failed to tear down"
- .format(self.ad.serial, voice_number))
- return False
-
- simmodel = self.anritsu.get_simulation_model().split(',')
- if simmodel[1] == "WCDMA" and iperf:
- iperf_task = (self._iperf_task, (
- server_ip,
- WAITTIME_BEFORE_HANDOVER + WAITTIME_AFTER_HANDOVER - 10))
- ho_task = (handover_tc,
- (self.log, self.anritsu, WAITTIME_BEFORE_HANDOVER,
- BtsNumber.BTS2, BtsNumber.BTS1))
- result = run_multithread_func(self.log, [ho_task, iperf_task])
- if not result[1]:
- self.log.error("iPerf failed.")
- return False
- self.log.info("handover test case result code {}.".format(
- result[0]))
+ if not tear_down_call(self.log, self.ad, self.anritsu):
+ self.log.error("Phone {} Failed to tear down"
+ .format(self.ad.serial, voice_number))
+ return False
+ if simmodel[1] == "WCDMA" and iperf:
+ if all_bands:
+ bts[0].band = band[0][i % len(band[0])]
+ iperf_task = (self._iperf_task,
+ (server_ip, WAITTIME_BEFORE_HANDOVER +
+ WAITTIME_AFTER_HANDOVER - 10))
+ ho_task = (handover_tc, (self.log, self.anritsu,
+ WAITTIME_BEFORE_HANDOVER,
+ BtsNumber.BTS2, BtsNumber.BTS1))
+ result = run_multithread_func(self.log,
+ [ho_task, iperf_task])
+ if not result[1]:
+ self.log.error("iPerf failed.")
+ return False
+ self.log.info(
+ "handover test case result code {}.".format(result[0]))
except AnritsuError as e:
self.log.error("Error in connection with Anritsu Simulator: " +
@@ -313,6 +338,7 @@
""" Tests Begin """
+ @test_tracker_info(uuid="bd014822-2c09-4503-9e01-594513ea6808")
@TelephonyBaseTest.tel_test_wrap
def test_volte_iperf_handover(self):
""" Test VoLTE to VoLTE Inter-Freq handover with iPerf data
@@ -340,6 +366,7 @@
volte=True,
iperf=True)
+ @test_tracker_info(uuid="a5a15947-40eb-4a70-b652-0b52a548c3c1")
@TelephonyBaseTest.tel_test_wrap
def test_volte_handover(self):
""" Test VoLTE to VoLTE Inter-Freq handover without iPerf data
@@ -365,6 +392,7 @@
volte=True,
iperf=False)
+ @test_tracker_info(uuid="382521d9-d991-49bc-8347-2e766ec0db74")
@TelephonyBaseTest.tel_test_wrap
def test_iperf_handover(self):
""" Test Inter-Freq handover with iPerf data
@@ -389,22 +417,24 @@
volte=False,
iperf=True)
+ @test_tracker_info(uuid="d255a58b-8697-4d0a-9bc0-1e7ffa4cccaf")
@TelephonyBaseTest.tel_test_wrap
def test_volte_iperf_handover_wcdma(self):
- """ Test VoLTE to VoLTE Inter-Freq handover with iPerf data
+ """ Test VoLTE to WCDMA to LTE handover with iPerf data
Steps:
- 1. Setup CallBox for 2 LTE cells with 2 different bands.
+ 1. Setup CallBox for LTE and WCDMA simulation.
2. Turn on DUT and enable VoLTE. Make an voice call to DEFAULT_CALL_NUMBER.
3. Check if VoLTE voice call connected successfully.
4. Start iPerf data transfer
- 5. Handover the call to BTS2 and check if the call is still up.
+ 5. SRVCC to WCDMA and check if the call is still up.
6. Check iPerf data throughput
7. Tear down the call.
+ 8. Handover back to LTE with iPerf
Expected Results:
1. VoLTE Voice call is made successfully.
2. After handover, the call is not dropped.
- 3. Tear down call succeed.
+ 3. iPerf continue after handover
Returns:
True if pass; False if fail
@@ -416,11 +446,12 @@
volte=True,
iperf=True)
+ @test_tracker_info(uuid="28bc2e85-602e-4143-afe7-6dd442bef5c8")
@TelephonyBaseTest.tel_test_wrap
def test_volte_handover_wcdma(self):
- """ Test VoLTE to VoLTE Inter-Freq handover with iPerf data
+ """ Test VoLTE to WCDMA handover (SRVCC)
Steps:
- 1. Setup CallBox for 2 LTE cells with 2 different bands.
+ 1. Setup CallBox for LTE and WCDMA simulation.
2. Turn on DUT and enable VoLTE. Make an voice call to DEFAULT_CALL_NUMBER.
3. Check if VoLTE voice call connected successfully.
4. Start iPerf data transfer
@@ -443,22 +474,24 @@
volte=True,
iperf=False)
+ @test_tracker_info(uuid="3ef15650-8e44-4b75-b809-8d7dec5a41e3")
@TelephonyBaseTest.tel_test_wrap
def test_iperf_handover_wcdma(self):
- """ Test VoLTE to VoLTE Inter-Freq handover with iPerf data
+ """ Test LTE to WCDMA to LTE handovers with iPerf data
Steps:
- 1. Setup CallBox for 2 LTE cells with 2 different bands.
- 2. Turn on DUT and enable VoLTE. Make an voice call to DEFAULT_CALL_NUMBER.
- 3. Check if VoLTE voice call connected successfully.
- 4. Start iPerf data transfer
- 5. Handover the call to BTS2 and check if the call is still up.
- 6. Check iPerf data throughput
- 7. Tear down the call.
+ 1. Setup CallBox for LTE and WCDMA simulation.
+ 2. Turn on DUT and register on LTE BTS.
+ 3. Start iPerf data transfer
+ 4. Handover to WCDMA.
+ 5. Stop and check iPerf data throughput
+ 6. Start iPerf data transfer
+ 7. Handover to WCDMA.
+ 8. Stop and check iPerf data throughput
+
Expected Results:
- 1. VoLTE Voice call is made successfully.
- 2. After handover, the call is not dropped.
- 3. Tear down call succeed.
+ 1. Each handover is successful
+ 2. After each handover, the iPerf continues successfully.
Returns:
True if pass; False if fail
@@ -470,4 +503,38 @@
volte=False,
iperf=True)
+ @test_tracker_info(uuid="2bfad82d-1797-474b-9bf7-c14602b061cd")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_iperf_handover_wcdma_all_bands(self):
+ """ Test LTE->WCDMA->LTE handovers through all bands UE supports with iPerf data
+ Steps:
+ 1. Setup CallBox for LTE and WCDMA simulation.
+ 2. Turn on DUT and register on LTE BTS.
+ 3. Query MD8475A for UE supported bands contained in UE Capability Information
+ 4. Set target WCDMA band with first band in WCDMA supported band list
+ 5. Start iPerf data transfer
+ 6. Handover to WCDMA.
+ 7. Stop and check iPerf data throughput
+ 8. Set target LTE band with first band in LTE supported band list
+ 9. Start iPerf data transfer
+ 10. Handover to LTE.
+ 11. Stop and check iPerf data throughput
+ 12. Repeat step 4-11 with second WCDMA/LTE bands in supported band lists
+ 13. Repeat step 12 until all bands are tested. Reuse the begining of the shorter list to match the longer list.
+
+ Expected Results:
+ 1. Each handover is successful
+ 2. After each handover, the iPerf continues successfully.
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.active_handover(
+ set_system_model_lte_wcdma,
+ self._phone_setup_volte,
+ phone_idle_volte,
+ volte=False,
+ iperf=True,
+ all_bands=True)
+
""" Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabNeighborCellTest.py b/acts/tests/google/tel/lab/TelLabNeighborCellTest.py
index c616777..0a2cf8e 100644
--- a/acts/tests/google/tel/lab/TelLabNeighborCellTest.py
+++ b/acts/tests/google/tel/lab/TelLabNeighborCellTest.py
@@ -19,6 +19,7 @@
import math
import time
+from acts.test_decorators import test_tracker_info
from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
from acts.controllers.anritsu_lib.md8475a import CTCHSetup
from acts.controllers.anritsu_lib.md8475a import BtsBandwidth
@@ -593,6 +594,7 @@
""" Tests Begin """
+ @test_tracker_info(uuid="17a42861-abb5-480b-9139-89219fa304b2")
@TelephonyBaseTest.tel_test_wrap
def test_2lte_intra_freq_ncell_away_close(self):
""" Test phone moving away from Neighbor Intra Freq cell then
@@ -639,6 +641,7 @@
return self.lte_intra_freq_ncell(self.ad, pcid, init_pwr, power_seq)
+ @test_tracker_info(uuid="117f404b-fb78-474a-86ba-209e6a54c9a8")
@TelephonyBaseTest.tel_test_wrap
def test_2lte_intra_freq_scell_away_close(self):
""" Test phone moving away from serving cell then close back while
@@ -686,8 +689,9 @@
return self.lte_intra_freq_ncell(self.ad, pcid, init_pwr, power_seq)
+ @test_tracker_info(uuid="d1eec95f-40e9-4099-a669-9a88e56049ca")
@TelephonyBaseTest.tel_test_wrap
- def test_2lte_intra_freq_ncell_away_close(self):
+ def test_2lte_intra_freq_ncell_away_close_2(self):
""" Test phone moving away from serving cell and close to neighbor
Intra Freq cell, then back and forth
@@ -719,6 +723,7 @@
return self.lte_intra_freq_ncell(self.ad, pcid, init_pwr, power_seq)
+ @test_tracker_info(uuid="c642a85b-4970-429c-81c4-f635392879be")
@TelephonyBaseTest.tel_test_wrap
def test_2lte_intra_freq_2cell_synced(self):
""" Test phone moving away and back to both serving cell and neighbor
@@ -751,6 +756,7 @@
return self.lte_intra_freq_ncell(self.ad, pcid, init_pwr, power_seq)
+ @test_tracker_info(uuid="9144fab6-c7e1-4de2-a01d-7a15c117ec70")
@TelephonyBaseTest.tel_test_wrap
def test_3lte_intra_freq_scell_reversed(self):
""" Test phone moving away and back between 2 neighbor cells while maintain
@@ -786,6 +792,7 @@
return self.lte_intra_freq_ncell(self.ad, pcid, init_pwr, power_seq)
+ @test_tracker_info(uuid="7bfbea72-e6fa-45ae-bf7e-b9b42063abe7")
@TelephonyBaseTest.tel_test_wrap
def test_3lte_intra_freq_3cell_synced(self):
""" Test phone moving away and back to both serving cell and neighbor
@@ -818,6 +825,7 @@
return self.lte_intra_freq_ncell(self.ad, pcid, init_pwr, power_seq)
+ @test_tracker_info(uuid="b4577ae1-6435-4a15-9449-e02013dfb032")
@TelephonyBaseTest.tel_test_wrap
def test_ncells_intra_lte_0_cells(self):
""" Test Number of neighbor cells reported by Phone when no neighbor
@@ -870,6 +878,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="fe2cc07b-9676-41ab-b7ff-112d3ef84980")
@TelephonyBaseTest.tel_test_wrap
def test_ncells_intra_lte_1_cells(self):
""" Test Number of neighbor cells reported by Phone when one neighbor
@@ -945,6 +954,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="8abc7903-4ea7-407a-946b-455d7f767c3e")
@TelephonyBaseTest.tel_test_wrap
def test_ncells_intra_lte_2_cells(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -1031,6 +1041,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="623b3d16-bc48-4353-abc3-054ca6351a97")
@TelephonyBaseTest.tel_test_wrap
def test_ncells_intra_lte_3_cells(self):
""" Test Number of neighbor cells reported by Phone when three neighbor
@@ -1131,6 +1142,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="3e094e3d-e7b7-447a-9a7a-8060c5b17e88")
@TelephonyBaseTest.tel_test_wrap
def test_ncells_intra_lte_4_cells(self):
""" Test Number of neighbor cells reported by Phone when four neighbor
@@ -1250,6 +1262,7 @@
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="7e9a9c30-9284-4440-b85e-f94b83e0373f")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_lte_intrafreq_0_tmo(self):
""" Test Number of neighbor cells reported by Phone when no neighbor
@@ -1303,6 +1316,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="13bd7000-5a45-43f5-9e54-001e0aa09262")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_lte_intrafreq_1_tmo(self):
""" Test Number of neighbor cells reported by Phone when one neighbor
@@ -1377,6 +1391,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="5dca3a16-73a0-448a-a35d-22ebd253a570")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_lte_intrafreq_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -1471,6 +1486,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="860152de-8aa0-422e-b5b0-28bf244076f4")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_lte_intrafreq_3_tmo(self):
""" Test Number of neighbor cells reported by Phone when three neighbor
@@ -1583,6 +1599,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="8c5b63ba-1322-47b6-adce-5224cbc0995a")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_lte_interfreq_1_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -1661,6 +1678,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="97853501-a328-4706-bb3f-c5e708b1ccb8")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_lte_interfreq_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -1759,6 +1777,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="74bd528c-e1c5-476d-9ee0-ebfc7bbc5de1")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_lte_interband_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -1856,6 +1875,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="6289e3e4-9316-4b82-bd0b-dde53f26da0d")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_lte_interrat_1_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -1933,6 +1953,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="9be4e4a8-f79a-4283-9a85-371a9bddfa5d")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_lte_interrat_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -2029,6 +2050,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="14db7a3d-b18b-4b87-9d84-fb0c00d3971e")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_wcdma_intrafreq_0_tmo(self):
""" Test Number of neighbor cells reported by Phone when no neighbor
@@ -2081,6 +2103,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="1a227d1e-9991-4646-b51a-8156f24485da")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_wcdma_intrafreq_1_tmo(self):
""" Test Number of neighbor cells reported by Phone when one neighbor
@@ -2156,6 +2179,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="170689a0-0db1-4a14-8b87-5a1b6c9b8581")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_wcdma_intrafreq_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -2250,6 +2274,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="3ec77512-4d5b-40c9-b733-cf358f999e15")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_wcdma_intrafreq_3_tmo(self):
""" Test Number of neighbor cells reported by Phone when three neighbor
@@ -2364,6 +2389,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="6f39e4a5-81da-4f47-8022-f22d82ff6f31")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_wcdma_interfreq_1_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -2442,6 +2468,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="992d9ffb-2538-447b-b7e8-f40061063686")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_wcdma_interfreq_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -2537,6 +2564,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="60cb8c15-3cb3-4ead-9e59-a8aee819e9ef")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_wcdma_interband_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -2633,6 +2661,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="daa29f27-f67b-47ee-9a30-1c9572eedf2f")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_wcdma_interrat_1_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -2723,6 +2752,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="08e5d666-fae6-48a3-b03b-de7b7b3f5982")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_wcdma_interrat_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -2819,6 +2849,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="bebbe764-4c8c-4aaf-81b9-c61509a9695e")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_gsm_intrafreq_0_tmo(self):
""" Test Number of neighbor cells reported by Phone when no neighbor
@@ -2869,6 +2900,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="861dd399-d6f6-4e9f-9e8d-0718966ea45a")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_gsm_intrafreq_1_tmo(self):
""" Test Number of neighbor cells reported by Phone when one neighbor
@@ -2943,6 +2975,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="58627a33-45bd-436d-85b2-1ca711f56794")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_gsm_intrafreq_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -3035,6 +3068,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="3ff3439a-2e45-470a-a2d6-c63e37379f19")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_gsm_intrafreq_3_tmo(self):
""" Test Number of neighbor cells reported by Phone when three neighbor
@@ -3145,6 +3179,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="0cac1370-144e-40a4-b6bc-66691926f898")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_gsm_interfreq_1_tmo(self):
""" Test Number of neighbor cells reported by Phone when one neighbor
@@ -3220,6 +3255,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="5f0367dd-08b5-4871-a784-51a0f76e229b")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_gsm_interfreq_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -3313,6 +3349,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="b195153f-f6a0-4ec4-bb53-29c30ec0a034")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_gsm_interband_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when two neighbor
@@ -3404,6 +3441,7 @@
time.sleep(self._SETTLING_TIME)
return self._verify_cell_info(self.ad, expected_cell_info_stats)
+ @test_tracker_info(uuid="209f62c1-7950-447c-9101-abe930da20ba")
@TelephonyBaseTest.tel_test_wrap
def test_neighbor_cell_reporting_gsm_interrat_2_tmo(self):
""" Test Number of neighbor cells reported by Phone when no neighbor
diff --git a/acts/tests/google/tel/lab/TelLabPowerDataTest.py b/acts/tests/google/tel/lab/TelLabPowerDataTest.py
new file mode 100644
index 0000000..9ebd85a
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabPowerDataTest.py
@@ -0,0 +1,244 @@
+#/usr/bin/env python3.4
+#
+# Copyright 2016 - 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.
+"""
+Sanity tests for voice tests in telephony
+"""
+import time, os
+
+from acts.test_utils.tel.anritsu_utils import make_ims_call
+from acts.test_utils.tel.anritsu_utils import tear_down_call
+from acts.test_utils.tel.tel_test_utils import iperf_test_by_adb
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.TelephonyLabPowerTest import TelephonyLabPowerTest
+from acts.utils import adb_shell_ping
+from acts.controllers import iperf_server
+from acts.utils import exe_cmd
+import json
+
+DEFAULT_PING_DURATION = 10
+IPERF_DURATION = 30
+IPERF_LOG_FILE_PATH = "/sdcard/iperf.txt"
+
+DEFAULT_CALL_NUMBER = "+11234567891"
+WAIT_TIME_VOLTE = 5
+
+
+class TelLabPowerDataTest(TelephonyLabPowerTest):
+ # TODO Keep if we want to add more in here for this class.
+ def __init__(self, controllers):
+ TelephonyLabPowerTest.__init__(self, controllers)
+ self.ip_server = self.iperf_servers[0]
+ self.port_num = self.ip_server.port
+ self.log.info("Iperf Port is %s", self.port_num)
+ self.log.info("End of __init__ class of TelLabPowerDataTest")
+
+ # May not need
+ def teardown_class(self):
+ # Always take down the simulation
+ TelephonyLabPowerTest.teardown_class(self)
+
+ def iperf_setup(self):
+ # Fetch IP address of the host machine
+ cmd = "|".join(("ifconfig", "grep eth0 -A1", "grep inet",
+ "cut -d ':' -f2", "cut -d ' ' -f 1"))
+ destination_ip = exe_cmd(cmd)
+ destination_ip = (destination_ip.decode("utf-8")).split("\n")[0]
+ self.log.info("Dest IP is %s", destination_ip)
+ time.sleep(1)
+ if not adb_shell_ping(
+ self.ad, DEFAULT_PING_DURATION, destination_ip,
+ loss_tolerance=95):
+ self.log.error("Pings failed to Destination.")
+ return False
+
+ return destination_ip
+
+ def _iperf_task(self, destination_ip, duration):
+ self.log.info("Starting iPerf task")
+ self.ip_server.start()
+ tput_dict = {"Uplink": 0, "Downlink": 0}
+ if iperf_test_by_adb(
+ self.log,
+ self.ad,
+ destination_ip,
+ self.port_num,
+ True, # reverse
+ duration,
+ rate_dict=tput_dict,
+ blocking=False,
+ log_file_path=IPERF_LOG_FILE_PATH):
+ return True
+ else:
+ self.log.error("iperf failed to Destination.")
+ self.ip_server.stop()
+ return False
+
+ def power_iperf_test(self, olvl, rflvl, sch_mode="DYNAMIC", volte=False):
+ if volte:
+ # make a VoLTE MO call
+ self.log.info("DEFAULT_CALL_NUMBER = " + DEFAULT_CALL_NUMBER)
+ if not make_ims_call(self.log, self.ad, self.anritsu,
+ DEFAULT_CALL_NUMBER):
+ self.log.error("Phone {} Failed to make volte call to {}"
+ .format(self.ad.serial, DEFAULT_CALL_NUMBER))
+ return False
+ self.log.info("wait for %d seconds" % WAIT_TIME_VOLTE)
+ time.sleep(WAIT_TIME_VOLTE)
+
+ server_ip = self.iperf_setup()
+ if not server_ip:
+ self.log.error("iperf server can not be reached by ping")
+ return False
+
+ self._iperf_task(server_ip, IPERF_DURATION)
+ self.log.info("Wait for 10 secconds before power measurement")
+ time.sleep(10)
+ self.power_test(olvl, rflvl, sch_mode)
+
+ result = self.ad.adb.shell("cat {}".format(IPERF_LOG_FILE_PATH))
+ if result is not None:
+ data_json = json.loads(''.join(result))
+ rx_rate = data_json['end']['sum_received']['bits_per_second']
+ xfer_time = data_json['end']['sum_received']['seconds']
+ self.ad.log.info('iPerf3 transfer time was %ssecs', xfer_time)
+ self.ad.log.info('iPerf3 download speed is %sbps', rx_rate)
+
+ if volte:
+ # check if the phone is still in call, then tear it down
+ if not self.ad.droid.telecomIsInCall():
+ self.log.error("Call is already ended in the phone.")
+ return False
+ if not tear_down_call(self.log, self.ad, self.anritsu):
+ self.log.error("Phone {} Failed to tear down VoLTE call"
+ .format(self.ad.serial))
+ return False
+
+ return True
+
+ """ Tests Begin """
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_data_power_n30_n30(self):
+ """ Test power consumption for iPerf data @ DL/UL -30/-30dBm
+ Steps:
+ 1. Assume UE already in Communication mode.
+ 2. Initiate iPerf data transfer.
+ 3. Set DL/UL power and Dynamic scheduling.
+ 4. Measure power consumption.
+
+ Expected Results:
+ 1. power consumption measurement is successful
+ 2. measurement results is saved accordingly
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.power_iperf_test(-30, -30)
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_data_power_n50_n10(self):
+ """ Test power consumption for iPerf data @ DL/UL -50/-10dBm
+ Steps:
+ 1. Assume UE already in Communication mode.
+ 2. Initiate iPerf data transfer.
+ 3. Set DL/UL power and Dynamic scheduling.
+ 4. Measure power consumption.
+
+ Expected Results:
+ 1. power consumption measurement is successful
+ 2. measurement results is saved accordingly
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.power_iperf_test(-50, -10)
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_data_power_n70_10(self):
+ """ Test power consumption for iPerf data @ DL/UL -70/+10dBm
+ Steps:
+ 1. Assume UE already in Communication mode.
+ 2. Initiate iPerf data transfer.
+ 3. Set DL/UL power and Dynamic scheduling.
+ 4. Measure power consumption.
+
+ Expected Results:
+ 1. power consumption measurement is successful
+ 2. measurement results is saved accordingly
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.power_iperf_test(-70, 10)
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_data_volte_power_n30_n30(self):
+ """ Test power consumption for iPerf data and volte @ DL/UL -30/-30dBm
+ Steps:
+ 1. Assume UE already in Communication mode.
+ 2. Make MO VoLTE call.
+ 3. Initiate iPerf data transfer.
+ 4. Set DL/UL power and Dynamic scheduling.
+ 5. Measure power consumption.
+
+ Expected Results:
+ 1. power consumption measurement is successful
+ 2. measurement results is saved accordingly
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.power_iperf_test(-30, -30, volte=True)
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_data_volte_power_n50_n10(self):
+ """ Test power consumption for iPerf data and volte @ DL/UL -50/-10dBm
+ Steps:
+ 1. Assume UE already in Communication mode.
+ 2. Make MO VoLTE call.
+ 3. Initiate iPerf data transfer.
+ 4. Set DL/UL power and Dynamic scheduling.
+ 5. Measure power consumption.
+
+ Expected Results:
+ 1. power consumption measurement is successful
+ 2. measurement results is saved accordingly
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.power_iperf_test(-50, -10, volte=True)
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_data_volte_power_n70_10(self):
+ """ Test power consumption for iPerf data and volte @ DL/UL -70/+10dBm
+ Steps:
+ 1. Assume UE already in Communication mode.
+ 2. Make MO VoLTE call.
+ 3. Initiate iPerf data transfer.
+ 4. Set DL/UL power and Dynamic scheduling.
+ 5. Measure power consumption.
+
+ Expected Results:
+ 1. power consumption measurement is successful
+ 2. measurement results is saved accordingly
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.power_iperf_test(-70, 10, volte=True)
+
+ """ Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabPowerVoLTETest.py b/acts/tests/google/tel/lab/TelLabPowerVoLTETest.py
new file mode 100644
index 0000000..2b88059
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabPowerVoLTETest.py
@@ -0,0 +1,121 @@
+#/usr/bin/env python3.4
+#
+# Copyright 2016 - 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.
+"""
+Sanity tests for voice tests in telephony
+"""
+import time
+
+from acts.test_utils.tel.anritsu_utils import make_ims_call
+from acts.test_utils.tel.anritsu_utils import tear_down_call
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.TelephonyLabPowerTest import TelephonyLabPowerTest
+
+DEFAULT_CALL_NUMBER = "+11234567891"
+WAIT_TIME_VOLTE = 5
+
+
+class TelLabPowerVoLTETest(TelephonyLabPowerTest):
+
+ # TODO Keep if we want to add more in here for this class.
+ def __init__(self, controllers):
+ TelephonyLabPowerTest.__init__(self, controllers)
+
+ def setup_class(self):
+ self.log.info("entering setup_class TelLabPowerVoLTETest")
+ TelephonyLabPowerTest.setup_class(self)
+ self.log.info("Making MO VoLTE call")
+ # make a VoLTE MO call
+ self.log.info("DEFAULT_CALL_NUMBER = " + DEFAULT_CALL_NUMBER)
+ if not make_ims_call(self.log, self.ad, self.anritsu,
+ DEFAULT_CALL_NUMBER):
+ self.log.error("Phone {} Failed to make volte call to {}"
+ .format(self.ad.serial, DEFAULT_CALL_NUMBER))
+ return False
+ self.log.info("wait for %d seconds" % WAIT_TIME_VOLTE)
+ time.sleep(WAIT_TIME_VOLTE)
+ return True
+
+ def teardown_test(self):
+ # check if the phone stay in call
+ if not self.ad.droid.telecomIsInCall():
+ self.log.error("Call is already ended in the phone.")
+ return False
+ self.log.info("End of teardown_test()")
+ return True
+
+ def teardown_class(self):
+ if not tear_down_call(self.log, self.ad, self.anritsu):
+ self.log.error("Phone {} Failed to tear down"
+ .format(self.ad.serial))
+ return False
+ # Always take down the simulation
+ TelephonyLabPowerTest.teardown_class(self)
+ return True
+
+ """ Tests Begin """
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_volte_power_n40_n20(self):
+ """ Measure power consumption of a VoLTE call with DL/UL -40/-20dBm
+ Steps:
+ 1. Assume a VoLTE call is already in place by Setup_Class.
+ 2. Set DL/UL power and Dynamic scheduling
+ 3. Measure power consumption.
+
+ Expected Results:
+ 1. power consumption measurement is successful
+ 2. measurement results is saved accordingly
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.power_test(-40, -20)
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_volte_power_n60_0(self):
+ """ Measure power consumption of a VoLTE call with DL/UL -60/0dBm
+ Steps:
+ 1. Assume a VoLTE call is already in place by Setup_Class.
+ 2. Set DL/UL power and Dynamic scheduling
+ 3. Measure power consumption.
+
+ Expected Results:
+ 1. power consumption measurement is successful
+ 2. measurement results is saved accordingly
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.power_test(-60, 0)
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_volte_power_n80_20(self):
+ """ Measure power consumption of a VoLTE call with DL/UL -80/+20dBm
+ Steps:
+ 1. Assume a VoLTE call is already in place by Setup_Class.
+ 2. Set DL/UL power and Dynamic scheduling
+ 3. Measure power consumption.
+
+ Expected Results:
+ 1. power consumption measurement is successful
+ 2. measurement results is saved accordingly
+
+ Returns:
+ True if pass; False if fail
+ """
+ return self.power_test(-80, 20)
+
+ """ Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabVoiceTest.py b/acts/tests/google/tel/lab/TelLabVoiceTest.py
index 92af3c5..24b91f9 100644
--- a/acts/tests/google/tel/lab/TelLabVoiceTest.py
+++ b/acts/tests/google/tel/lab/TelLabVoiceTest.py
@@ -18,6 +18,7 @@
"""
import time
+from acts.test_decorators import test_tracker_info
from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
from acts.controllers.anritsu_lib.md8475a import CsfbType
from acts.controllers.anritsu_lib.md8475a import MD8475A
@@ -36,6 +37,7 @@
from acts.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.anritsu_utils import set_post_sim_params
from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
@@ -54,6 +56,8 @@
from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
from acts.test_utils.tel.tel_test_utils import toggle_volte
+from acts.test_utils.tel.tel_test_utils import set_preferred_apn_by_adb
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
@@ -64,8 +68,8 @@
def __init__(self, controllers):
TelephonyBaseTest.__init__(self, controllers)
try:
- self.stress_test_number = int(self.user_params[
- "stress_test_number"])
+ self.stress_test_number = int(
+ self.user_params["stress_test_number"])
self.log.info("Executing {} calls per test in stress test mode".
format(self.stress_test_number))
except KeyError:
@@ -86,6 +90,9 @@
self.log.info("Using provided voice call number: {}".format(
self.voice_call_number))
+ if self.ad.sim_card == "VzW12349":
+ set_preferred_apn_by_adb(self.ad, "VZWINTERNET")
+
def setup_class(self):
try:
self.anritsu = MD8475A(self.md8475a_ip_address, self.log,
@@ -97,12 +104,14 @@
def setup_test(self):
try:
+ if getattr(self, "qxdm_log", True):
+ start_qxdm_loggers(self.log, self.android_devices)
self.ad.droid.telephonyFactoryReset()
+ if self.ad.sim_card == "VzW12349":
+ self.ad.droid.imsSetVolteProvisioning(True)
except Exception as e:
self.ad.log.error(e)
toggle_airplane_mode_by_adb(self.log, self.ad, True)
- self.ad.adb.shell("setprop net.lte.ims.volte.provisioned 1",
- ignore_status=True)
# get a handle to virtual phone
self.virtualPhoneHandle = self.anritsu.get_VirtualPhone()
return True
@@ -133,6 +142,9 @@
set_simulation_func(self.anritsu, self.user_params,
self.ad.sim_card)
set_usim_parameters(self.anritsu, self.ad.sim_card)
+ if is_ims_call or srvcc or csfb_type:
+ set_post_sim_params(self.anritsu, self.user_params,
+ self.ad.sim_card)
self.virtualPhoneHandle.auto_answer = (VirtualPhoneAutoAnswer.ON,
2)
if srvcc != None:
@@ -140,12 +152,16 @@
self.anritsu.send_command("IMSCSCFAUTOANSWER 1,DISABLE")
self.anritsu.start_simulation()
self.anritsu.send_command("IMSSTARTVN 1")
+ self.anritsu.send_command("IMSSTARTVN 2")
+ self.anritsu.send_command("IMSSTARTVN 3")
check_ims_reg = True
check_ims_calling = True
else:
self.anritsu.start_simulation()
- if is_ims_call:
+ if is_ims_call or csfb_type:
self.anritsu.send_command("IMSSTARTVN 1")
+ self.anritsu.send_command("IMSSTARTVN 2")
+ self.anritsu.send_command("IMSSTARTVN 3")
iterations = 1
if self.stress_test_number > 0:
@@ -153,8 +169,8 @@
successes = 0
for i in range(1, iterations + 1):
if self.stress_test_number:
- self.log.info("Running iteration {} of {}".format(
- i, iterations))
+ self.log.info(
+ "Running iteration {} of {}".format(i, iterations))
# FIXME: There's no good reason why this must be true;
# I can only assume this was done to work around a problem
self.ad.droid.telephonyToggleDataConnection(False)
@@ -163,13 +179,21 @@
sim_model = (self.anritsu.get_simulation_model()).split(",")
no_of_bts = len(sim_model)
for i in range(2, no_of_bts + 1):
- self.anritsu.send_command("OUTOFSERVICE OUT,BTS{}".format(
- i))
+ self.anritsu.send_command(
+ "OUTOFSERVICE OUT,BTS{}".format(i))
if phone_setup_func is not None:
if not phone_setup_func(self.ad):
- self.log.error("phone_setup_func failed.")
- continue
+ self.log.warning(
+ "phone_setup_func failed. Rebooting UE")
+ self.ad.reboot()
+ time.sleep(30)
+ if self.ad.sim_card == "VzW12349":
+ set_preferred_apn_by_adb(self.ad, "VZWINTERNET")
+ if not phone_setup_func(self.ad):
+ self.log.error("phone_setup_func failed.")
+ continue
+
if is_wait_for_registration:
self.anritsu.wait_for_registration_state()
@@ -179,8 +203,8 @@
continue
for i in range(2, no_of_bts + 1):
- self.anritsu.send_command("OUTOFSERVICE IN,BTS{}".format(
- i))
+ self.anritsu.send_command(
+ "OUTOFSERVICE IN,BTS{}".format(i))
time.sleep(WAIT_TIME_ANRITSU_REG_AND_CALL)
if srvcc:
@@ -225,6 +249,7 @@
return True
def _phone_setup_lte_wcdma(self, ad):
+ toggle_volte(self.log, ad, False)
return ensure_network_rat(
self.log,
ad,
@@ -283,6 +308,7 @@
""" Tests Begin """
+ @test_tracker_info(uuid="56c42e16-3936-4c51-8b8b-4866f54cc0bc")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_lte_wcdma_csfb_redirection(self):
""" Test Voice call functionality on LTE (CSFB to WCDMA).
@@ -308,6 +334,7 @@
voice_number=self.voice_call_number,
csfb_type=CsfbType.CSFB_TYPE_REDIRECTION)
+ @test_tracker_info(uuid="dcc1428f-9b7d-4064-8313-f1f5e428e0c7")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_lte_wcdma_csfb_handover(self):
""" Test Voice call functionality on LTE (CSFB to WCDMA).
@@ -333,6 +360,7 @@
voice_number=self.voice_call_number,
csfb_type=CsfbType.CSFB_TYPE_HANDOVER)
+ @test_tracker_info(uuid="e250b134-d5e9-48ca-b224-eb0e07648275")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_lte_1x_csfb(self):
""" Test Voice call functionality on LTE (CSFB to 1x).
@@ -356,6 +384,7 @@
self._phone_setup_lte_1x,
voice_number=self.voice_call_number)
+ @test_tracker_info(uuid="fcbe0f5d-51c2-46c8-8ff3-2daa1d91b936")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_wcdma(self):
""" Test Voice call functionality on WCDMA
@@ -379,6 +408,7 @@
self._phone_setup_wcdma,
voice_number=self.voice_call_number)
+ @test_tracker_info(uuid="077f851b-2c8e-4b1d-adc2-0326d3346157")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_gsm(self):
""" Test Voice call functionality on GSM
@@ -402,6 +432,7 @@
self._phone_setup_gsm,
voice_number=self.voice_call_number)
+ @test_tracker_info(uuid="80376fb3-44fc-43b7-be99-2ccd3bd2913e")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_1x(self):
""" Test Voice call functionality on CDMA 1X
@@ -425,6 +456,7 @@
self._phone_setup_1x,
voice_number=self.voice_call_number)
+ @test_tracker_info(uuid="1f8ae218-042d-4114-9fc7-4401a503b3b4")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_1x_evdo(self):
""" Test Voice call functionality on CDMA 1X with EVDO
@@ -448,6 +480,7 @@
self._phone_setup_1x,
voice_number=self.voice_call_number)
+ @test_tracker_info(uuid="7641ffc0-c1b3-42b8-92d6-00ae53719f8d")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_volte_wcdma_srvcc(self):
""" Test Voice call functionality,
@@ -475,6 +508,7 @@
voice_number=self.voice_call_number,
wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+ @test_tracker_info(uuid="0d63e797-b4bc-4094-98c3-70060e5ea91b")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_volte_gsm_srvcc(self):
""" Test Voice call functionality,
@@ -502,6 +536,7 @@
voice_number=self.voice_call_number,
wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+ @test_tracker_info(uuid="f73f6cc0-79c8-47b3-9867-ea7390dfee41")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_volte_wcdma_asrvcc(self):
""" Test Voice call functionality,
@@ -528,6 +563,7 @@
srvcc="Alert",
voice_number=self.voice_call_number)
+ @test_tracker_info(uuid="823e8e10-58bd-476d-ba4b-ec436ac424fb")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_volte_gsm_asrvcc(self):
""" Test Voice call functionality,
@@ -555,6 +591,7 @@
voice_number=self.voice_call_number,
wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+ @test_tracker_info(uuid="cd066cb1-6d12-4e29-90b9-f44054f00a00")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_volte_wcdma_asrvcc_mt(self):
""" Test Voice call functionality,
@@ -582,6 +619,7 @@
mo=False,
voice_number=self.voice_call_number)
+ @test_tracker_info(uuid="b23ebec3-7e5c-4aca-a749-e34307c56d58")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_volte_gsm_asrvcc_mt(self):
""" Test Voice call functionality,
@@ -610,6 +648,7 @@
voice_number=self.voice_call_number,
wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+ @test_tracker_info(uuid="b81b3a0e-a7e3-4b30-889f-7c015bdc6980")
@TelephonyBaseTest.tel_test_wrap
def test_voice_call_volte(self):
""" Test Voice call functionality on VoLTE
diff --git a/acts/tests/google/tel/live/TelLiveDataTest.py b/acts/tests/google/tel/live/TelLiveDataTest.py
index b199dd9..c0034c0 100644
--- a/acts/tests/google/tel/live/TelLiveDataTest.py
+++ b/acts/tests/google/tel/live/TelLiveDataTest.py
@@ -26,6 +26,9 @@
from queue import Empty
from acts.test_utils.tel.tel_subscription_utils import \
get_subid_from_slot_index
+from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
@@ -60,6 +63,8 @@
from acts.test_utils.tel.tel_data_utils import wifi_tethering_cleanup
from acts.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
from acts.test_utils.tel.tel_test_utils import active_file_download_test
+from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
+from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
from acts.test_utils.tel.tel_test_utils import call_setup_teardown
from acts.test_utils.tel.tel_test_utils import check_is_wifi_connected
from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
@@ -68,15 +73,19 @@
from acts.test_utils.tel.tel_test_utils import \
ensure_network_generation_for_subscription
from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts.test_utils.tel.tel_test_utils import get_mobile_data_usage
from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
from acts.test_utils.tel.tel_test_utils import get_network_rat_for_subscription
from acts.test_utils.tel.tel_test_utils import hangup_call
from acts.test_utils.tel.tel_test_utils import multithread_func
+from acts.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit
from acts.test_utils.tel.tel_test_utils import set_call_state_listen_level
+from acts.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
from acts.test_utils.tel.tel_test_utils import setup_sim
from acts.test_utils.tel.tel_test_utils import stop_wifi_tethering
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
from acts.test_utils.tel.tel_test_utils import toggle_volte
+from acts.test_utils.tel.tel_test_utils import verify_http_connection
from acts.test_utils.tel.tel_test_utils import verify_internet_connection
from acts.test_utils.tel.tel_test_utils import verify_incall_state
from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
@@ -87,6 +96,7 @@
wait_for_data_attach_for_subscription
from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
from acts.test_utils.tel.tel_test_utils import wifi_reset
+from acts.test_utils.tel.tel_test_utils import wait_for_state
from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
@@ -101,6 +111,7 @@
from acts.utils import disable_doze
from acts.utils import enable_doze
from acts.utils import rand_ascii_str
+from acts.utils import adb_shell_ping
class TelLiveDataTest(TelephonyBaseTest):
@@ -109,11 +120,35 @@
self.stress_test_number = self.get_stress_test_number()
self.wifi_network_ssid = self.user_params.get(
- "wifi_network_ssid") or self.user_params.get("wifi_network_ssid_2g")
+ "wifi_network_ssid") or self.user_params.get(
+ "wifi_network_ssid_2g")
self.wifi_network_pass = self.user_params.get(
- "wifi_network_pass") or self.user_params.get("wifi_network_pass_2g")
- self.provider = self.android_devices[-1]
- self.clients = self.android_devices[:-1]
+ "wifi_network_pass") or self.user_params.get(
+ "wifi_network_pass_2g")
+ self.provider = self.android_devices[0]
+ self.clients = self.android_devices[1:]
+
+ def setup_test(self):
+ TelephonyBaseTest.setup_test(self)
+ self.number_of_devices = 1
+ try:
+ self.tcpdump_proc = [None, None]
+ self.tcpdump_proc[0] = start_adb_tcpdump(
+ self.android_devices[0], self.test_name, mask="all")
+ except Exception as e:
+ self.log.warning("Failed to start tcpdump collection", e)
+ pass
+
+ def on_fail(self, test_name, begin_time):
+ self.log.info("Inside Teardown Test")
+ TelephonyBaseTest.on_fail(self, test_name, begin_time)
+ try:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(self.android_devices[0], self.tcpdump_proc[0],
+ True, self.test_name)
+ except Exception as e:
+ self.log.warning("Failed to stop tcpdump collection", e)
+ pass
@test_tracker_info(uuid="1b0354f3-8668-4a28-90a5-3b3d2b2756d3")
@TelephonyBaseTest.tel_test_wrap
@@ -276,10 +311,10 @@
True stress pass rate is higher than MINIMUM_SUCCESS_RATE.
False otherwise.
"""
- ads = self.android_devices
MINIMUM_SUCCESS_RATE = .95
success_count = 0
fail_count = 0
+ self.number_of_devices = 2
for i in range(1, self.stress_test_number + 1):
@@ -370,6 +405,7 @@
class _LocalException(Exception):
pass
+ self.number_of_devices = 2
ad_list = [self.android_devices[0], self.android_devices[1]]
ensure_phones_idle(self.log, ad_list)
@@ -392,12 +428,17 @@
wifi_toggle_state(self.log, self.android_devices[0], False)
self.android_devices[0].droid.telephonyToggleDataConnection(True)
if (not wait_for_cell_data_connection(self.log,
- self.android_devices[0], True) or
- not verify_internet_connection(self.log,
- self.android_devices[0])):
+ self.android_devices[0], True)
+ or not verify_internet_connection(self.log,
+ self.android_devices[0])):
self.log.error("Data not available on cell")
return False
+ self.log.info(
+ "b/69431819, sending data to increase NW threshold limit")
+ adb_shell_ping(
+ self.android_devices[0], count=30, timeout=60, loss_tolerance=100)
+
try:
self.log.info("Step2 Initiate call and accept.")
if call_direction == DIRECTION_MOBILE_ORIGINATED:
@@ -414,8 +455,8 @@
if simultaneous_voice_data:
self.log.info("Step3 Verify internet.")
time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
- if not verify_internet_connection(self.log,
- self.android_devices[0]):
+ if not verify_internet_connection(
+ self.log, self.android_devices[0], retries=3):
raise _LocalException("Internet Inaccessible when Enabled")
self.log.info("Step4 Turn off data and verify not connected.")
@@ -435,8 +476,8 @@
if not wait_for_cell_data_connection(
self.log, self.android_devices[0], True):
raise _LocalException("Failed to Re-Enable Cellular Data")
- if not verify_internet_connection(self.log,
- self.android_devices[0]):
+ if not verify_internet_connection(
+ self.log, self.android_devices[0], retries=3):
raise _LocalException("Internet Inaccessible when Enabled")
else:
self.log.info("Step3 Verify no Internet and skip step 4-5.")
@@ -445,15 +486,15 @@
raise _LocalException("Internet Accessible.")
self.log.info("Step6 Verify phones still in call and Hang up.")
- if not verify_incall_state(self.log, [
- self.android_devices[0], self.android_devices[1]
- ], True):
+ if not verify_incall_state(
+ self.log,
+ [self.android_devices[0], self.android_devices[1]], True):
return False
if not hangup_call(self.log, self.android_devices[0]):
self.log.error("Failed to hang up call")
return False
- if not verify_internet_connection(self.log,
- self.android_devices[0]):
+ if not verify_internet_connection(
+ self.log, self.android_devices[0], retries=3):
raise _LocalException("Internet Inaccessible when Enabled")
except _LocalException as e:
@@ -603,7 +644,6 @@
True stress pass rate is higher than MINIMUM_SUCCESS_RATE.
False otherwise.
"""
- ads = self.android_devices
MINIMUM_SUCCESS_RATE = .95
success_count = 0
fail_count = 0
@@ -611,7 +651,7 @@
for i in range(1, self.stress_test_number + 1):
ensure_phones_default_state(
- self.log, [self.android_devices[0], self.android_devices[1]])
+ self.log, [self.android_devices[0]])
wifi_reset(self.log, self.android_devices[0])
wifi_toggle_state(self.log, self.android_devices[0], False)
@@ -646,15 +686,15 @@
True stress pass rate is higher than MINIMUM_SUCCESS_RATE.
False otherwise.
"""
- ads = self.android_devices
MINIMUM_SUCCESS_RATE = .95
success_count = 0
fail_count = 0
+ self.number_of_devices = 1
for i in range(1, self.stress_test_number + 1):
ensure_phones_default_state(
- self.log, [self.android_devices[0], self.android_devices[1]])
+ self.log, [self.android_devices[0]])
wifi_reset(self.log, self.android_devices[0])
wifi_toggle_state(self.log, self.android_devices[0], False)
@@ -689,38 +729,511 @@
True if success.
False if failed.
"""
+ self.number_of_devices = None
ensure_phones_idle(self.log, self.android_devices)
+ wifi_toggle_state(self.log, self.provider, False)
if network_generation:
if not ensure_network_generation(
self.log, self.provider, network_generation,
MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
- self.provider.log.error("Device failed to connect to %s.",
+ self.provider.log.error("Provider failed to connect to %s.",
network_generation)
return False
- self.log.info("Airplane Off, Wifi Off, Data On.")
+ self.provider.log.info(
+ "Set provider Airplane Off, Wifi Off, Bluetooth Off, Data On.")
toggle_airplane_mode(self.log, self.provider, False)
- wifi_toggle_state(self.log, self.provider, False)
self.provider.droid.telephonyToggleDataConnection(True)
- for ad in self.clients:
- ad.droid.telephonyToggleDataConnection(False)
+ self.provider.log.info("Provider disable wifi")
+ wifi_toggle_state(self.log, self.provider, False)
+ # Turn off active SoftAP if any.
+ if self.provider.droid.wifiIsApEnabled():
+ self.provider.log.info("Disable provider wifi tethering")
+ stop_wifi_tethering(self.log, self.provider)
+ self.provider.log.info("Provider disable bluetooth")
+ disable_bluetooth(self.provider.droid)
+ for ad in self.clients:
+ ad.log.info(
+ "Set client Airplane Off, Wifi Off, Bluetooth Off, Data Off.")
+ toggle_airplane_mode(self.log, ad, False)
+ ad.log.info("Client disable data")
+ ad.droid.telephonyToggleDataConnection(False)
+ ad.log.info("Client disable bluetooth")
+ disable_bluetooth(ad.droid)
+ ad.log.info("Client disable wifi")
+ wifi_toggle_state(self.log, ad, False)
if not wait_for_cell_data_connection(self.log, self.provider, True):
self.provider.log.error(
"Provider failed to enable data connection.")
return False
self.log.info("Verify internet")
- if not verify_internet_connection(self.log, self.provider):
- self.provider.log.error("Data not available on cell.")
+ if not self._test_internet_connection(
+ client_status=False, provider_status=True):
+ self.log.error("Internet connection check failed before tethering")
return False
- # Turn off active SoftAP if any.
- if self.provider.droid.wifiIsApEnabled():
- stop_wifi_tethering(self.log, self.provider)
-
return True
+ def _enable_bluetooth_tethering_connection(self, provider, clients):
+ for ad in [self.provider] + self.clients:
+ if not bluetooth_enabled_check(ad):
+ ad.log.info("Bluetooth is not enabled")
+ return False
+ else:
+ ad.log.info("Bluetooth is enabled")
+
+ for client in self.clients:
+ if not (pair_pri_to_sec(self.provider, client)):
+ client.log.error("Client failed to pair with provider")
+ return False
+ else:
+ client.log.info("Client paired with provider")
+ self.provider.log.info("Provider enabling bluetooth tethering")
+ try:
+ provider.droid.bluetoothPanSetBluetoothTethering(True)
+ except Exception as e:
+ provider.log.error(
+ "Faile to enable provider Bluetooth tethering with %s", e)
+ return False
+
+ if wait_for_state(provider.droid.bluetoothPanIsTetheringOn, True):
+ provider.log.info("Provider Bluetooth tethering is enabled.")
+ else:
+ provider.log.error(
+ "Failed to enable provider Bluetooth tethering.")
+ provider.log.error("bluetoothPanIsTetheringOn = %s",
+ provider.droid.bluetoothPanIsTetheringOn())
+ return False
+ time.sleep(5)
+ for client in clients:
+ client.droid.bluetoothConnectBonded(
+ provider.droid.bluetoothGetLocalAddress())
+ time.sleep(20)
+ return True
+
+ def _test_internet_connection(self,
+ client_status=True,
+ provider_status=True):
+ client_retry = 10 if client_status else 1
+ for client in self.clients:
+ if not verify_http_connection(
+ self.log,
+ client,
+ retry=client_retry,
+ expected_state=client_status):
+ client.log.error("client internet connection state is not %s",
+ client_status)
+ return False
+ else:
+ client.log.info("client internet connection state is %s",
+ client_status)
+ if not verify_http_connection(
+ self.log, self.provider, retry=3,
+ expected_state=provider_status):
+ self.provider.log.error(
+ "provider internet connection is not %s" % provider_status)
+ return False
+ else:
+ self.provider.log.info(
+ "provider internet connection is %s" % provider_status)
+ return True
+
+ def _verify_bluetooth_tethering_connection(self,
+ change_rat=None,
+ toggle_data=False,
+ toggle_tethering=False,
+ voice_call=False,
+ toggle_bluetooth=True):
+ """Setups up a bluetooth tethering conenction between two android devices.
+
+ Returns:
+ True if PAN connection and verification is successful,
+ false if unsuccessful.
+ """
+ if not self._enable_bluetooth_tethering_connection(
+ self.provider, self.clients):
+ return False
+ if not self._test_internet_connection():
+ self.log.error("Internet connection check failed")
+ return False
+ if voice_call:
+ self.log.info("====== Voice call test =====")
+ for caller, callee in [(self.provider, self.clients[0]),
+ (self.clients[0], self.provider)]:
+ if not call_setup_teardown(
+ self.log, caller, callee, ad_hangup=None):
+ self.log.error("Setup Call Failed.")
+ hangup_call(self.log, caller)
+ return False
+ self.log.info("Verify data.")
+ if not verify_http_connection(
+ self.log, self.clients[0], retry=0):
+ self.clients[0].log.warning(
+ "client internet connection state is not on")
+ else:
+ self.clients[0].log.info(
+ "client internet connection state is on")
+ hangup_call(self.log, caller)
+ if not verify_http_connection(
+ self.log, self.clients[0], retry=0):
+ self.clients[0].log.warning(
+ "client internet connection state is not on")
+ return False
+ else:
+ self.clients[0].log.info(
+ "client internet connection state is on")
+ if toggle_tethering:
+ self.log.info("====== Toggling provider bluetooth tethering =====")
+ self.provider.log.info("Disable bluetooth tethering")
+ self.provider.droid.bluetoothPanSetBluetoothTethering(False)
+ if not self._test_internet_connection(False, True):
+ self.log.error(
+ "Internet connection check failed after disable tethering")
+ return False
+ self.provider.log.info("Enable bluetooth tethering")
+ if not self._enable_bluetooth_tethering_connection(
+ self.provider, self.clients):
+ self.provider.log.error(
+ "Fail to re-enable bluetooth tethering")
+ return False
+ if not self._test_internet_connection(True, True):
+ self.log.error(
+ "Internet connection check failed after enable tethering")
+ return False
+ if toggle_bluetooth:
+ self.log.info("====== Toggling provider bluetooth =====")
+ self.provider.log.info("Disable provider bluetooth")
+ disable_bluetooth(self.provider.droid)
+ if not self._test_internet_connection(False, True):
+ self.log.error(
+ "Internet connection check failed after disable bluetooth")
+ return False
+ if not self._enable_bluetooth_tethering_connection(
+ self.provider, self.clients):
+ self.provider.log.error(
+ "Fail to re-enable bluetooth tethering")
+ return False
+ if not self._test_internet_connection(True, True):
+ self.log.error(
+ "Internet connection check failed after enable bluetooth")
+ return False
+ if toggle_data:
+ self.log.info("===== Toggling provider data connection =====")
+ self.provider.log.info("Disable provider data connection")
+ self.provider.droid.telephonyToggleDataConnection(False)
+
+ if not self._test_internet_connection(False, False):
+ return False
+ self.provider.log.info("Enable provider data connection")
+ self.provider.droid.telephonyToggleDataConnection(True)
+ if not wait_for_cell_data_connection(self.log, self.provider,
+ True):
+ self.provider.log.error(
+ "Provider failed to enable data connection.")
+ return False
+ if not self._test_internet_connection(True, True):
+ self.log.error(
+ "Internet connection check failed after enable data")
+ return False
+ if change_rat:
+ self.log.info("===== Change provider RAT to %s =====", change_rat)
+ if not ensure_network_generation(
+ self.log,
+ self.provider,
+ change_rat,
+ voice_or_data=NETWORK_SERVICE_DATA,
+ toggle_apm_after_setting=False):
+ self.provider.log.error("Provider failed to reselect to %s.",
+ change_rat)
+ return False
+ if not self._test_internet_connection(True, True):
+ self.log.error(
+ "Internet connection check failed after RAT change to %s",
+ change_rat)
+ return False
+ return True
+
+ @test_tracker_info(uuid="2d945656-22f7-4610-9a84-40ce04d603a4")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_4g_to_bluetooth(self):
+ """Bluetooth Tethering test: LTE to Bluetooth Tethering
+
+ 1. DUT in LTE mode, idle.
+ 2. DUT start Bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Toggle provider bluetooth connection
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_4G):
+ self.log.error("Verify 4G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection()
+
+ @test_tracker_info(uuid="8d2ae56b-c2c1-4c32-9b8e-5044007b5b90")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_4g_to_bluetooth_with_voice_call(self):
+ """Bluetooth Tethering test: LTE to Bluetooth Tethering
+
+ 1. DUT in LTE mode, idle.
+ 2. DUT start Bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Verify provider and client are able to make or receive phone call
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_4G):
+ self.log.error("Verify 4G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection(
+ toggle_tethering=False, toggle_bluetooth=False, voice_call=True)
+
+ @test_tracker_info(uuid="b4617727-fa83-4451-89d7-7e574c0a0938")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_4g_to_bluetooth_toggle_data(self):
+ """Bluetooth Tethering test: LTE to Bluetooth Tethering
+
+ 1. DUT in LTE mode, idle.
+ 2. DUT start Bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Toggle provider data connection
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_4G):
+ self.log.error("Verify 4G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection(
+ toggle_tethering=False, toggle_bluetooth=False, toggle_data=True)
+
+ @test_tracker_info(uuid="6a0f6001-609d-41f2-ad09-c8ae19f73ac8")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_4g_to_bluetooth_toggle_tethering(self):
+ """Bluetooth Tethering test: LTE to Bluetooth Tethering
+
+ 1. DUT in LTE mode, idle.
+ 2. DUT start Bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Toggle provider bluetooth tethering
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_4G):
+ self.log.error("Verify 4G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection(
+ toggle_tethering=True, toggle_bluetooth=False, toggle_data=False)
+
+ @test_tracker_info(uuid="b1abc1ac-8018-4956-a17e-bf2ceaf264ea")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_3g_to_bluetooth(self):
+ """Bluetooth Tethering test: 3G to Bluetoothing Tethering
+
+ 1. DUT in 3G mode, idle.
+ 2. DUT start bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Toggle provider bluetooth connection
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_3G):
+ self.log.error("Verify 3G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection()
+
+ @test_tracker_info(uuid="69793745-0c49-4cef-9879-d372e3a3f4c7")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_3g_to_bluetooth_with_voice_call(self):
+ """Bluetooth Tethering test: 3G to Bluetooth Tethering
+
+ 1. DUT in 3G mode, idle.
+ 2. DUT start Bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Verify provider and client are able to make or receive phone call
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_3G):
+ self.log.error("Verify 3G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection(
+ toggle_tethering=False, toggle_bluetooth=False, voice_call=True)
+
+ @test_tracker_info(uuid="4275ee69-dfdf-4f47-82c5-4224fceee761")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_3g_to_bluetooth_toggle_data(self):
+ """Bluetooth Tethering test: 3G to Bluetoothing Tethering
+
+ 1. DUT in 3G mode, idle.
+ 2. DUT start bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Toggle provider data connection
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_3G):
+ self.log.error("Verify 3G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection(
+ toggle_tethering=False, toggle_bluetooth=False, toggle_data=True)
+
+ @test_tracker_info(uuid="db0e0f27-1a4f-4301-832d-b66415e289f3")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_2g_to_bluetooth(self):
+ """Bluetooth Tethering test: 2G to Bluetooth Tethering
+
+ 1. DUT in 2G mode, idle.
+ 2. DUT start bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Toggle provider bluetooth connection
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_2G):
+ self.log.error("Verify 3G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection()
+
+ @test_tracker_info(uuid="584e9fa5-a38e-47cd-aa33-fcf8d72c423e")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_2g_to_bluetooth_with_voice_call(self):
+ """Bluetooth Tethering test: 2G to Bluetooth Tethering
+
+ 1. DUT in 2G mode, idle.
+ 2. DUT start Bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Verify provider and client are able to make or receive phone call
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_2G):
+ self.log.error("Verify 2G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection(
+ toggle_tethering=False, toggle_bluetooth=False, voice_call=True)
+
+ @test_tracker_info(uuid="be3e74f9-3dc8-4b72-8a33-32bff0868a44")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_2g_to_bluetooth_toggle_data(self):
+ """Bluetooth Tethering test: 2G to Bluetooth Tethering
+
+ 1. DUT in 2G mode, idle.
+ 2. DUT start Bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Toggle provider data connection
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_2G):
+ self.log.error("Verify 4G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection(
+ toggle_tethering=False, toggle_bluetooth=False, toggle_data=True)
+
+ @test_tracker_info(uuid="4a106549-0bfa-4c8f-8e66-edec93fabadf")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_rat_from_4g_to_3g_bluetooth(self):
+ """Bluetooth Tethering test: 2G to Bluetooth Tethering
+
+ 1. DUT in 4G mode, idle.
+ 2. DUT start bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Change provider RAT to 3G
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_4G):
+ self.log.error("Verify 3G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection(
+ toggle_tethering=False,
+ toggle_bluetooth=False,
+ toggle_data=False,
+ change_rat=RAT_3G)
+
+ @test_tracker_info(uuid="eaa5b61b-f054-437f-ae82-8d80f6487785")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_tethering_rat_from_4g_to_2g_bluetooth(self):
+ """Bluetooth Tethering test: 2G to Bluetooth Tethering
+
+ 1. DUT in 4G mode, idle.
+ 2. DUT start bluetooth Tethering
+ 3. PhoneB disable data, connect to DUT's softAP
+ 4. Verify Internet access on DUT and PhoneB
+ 5. Change provider RAT to 2G
+ 6. Verify Internet access on DUT and PhoneB
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not self._test_setup_tethering(RAT_4G):
+ self.log.error("Verify 3G Internet access failed.")
+ return False
+
+ return self._verify_bluetooth_tethering_connection(
+ toggle_tethering=False,
+ toggle_bluetooth=False,
+ toggle_data=False,
+ change_rat=RAT_2G)
+
@test_tracker_info(uuid="912a11a3-14b3-4928-885f-cea69f14a571")
@TelephonyBaseTest.tel_test_wrap
def test_tethering_4g_to_2gwifi(self):
@@ -735,14 +1248,13 @@
True if success.
False if failed.
"""
- ads = self.android_devices
if not self._test_setup_tethering(RAT_4G):
self.log.error("Verify 4G Internet access failed.")
return False
return wifi_tethering_setup_teardown(
self.log,
- self.provider, [self.clients[0]],
+ self.provider, self.clients,
ap_band=WIFI_CONFIG_APBAND_2G,
check_interval=10,
check_iteration=10)
@@ -761,17 +1273,32 @@
True if success.
False if failed.
"""
- ads = self.android_devices
- if not self._test_setup_tethering(RAT_4G):
- self.log.error("Verify 4G Internet access failed.")
- return False
+ num = len(self.android_devices)
+ for idx, ad in enumerate(self.android_devices):
+ self.provider = self.android_devices[idx]
+ self.clients = self.android_devices[:idx] + self.android_devices[
+ idx+1:]
+ if not self._test_setup_tethering(RAT_4G):
+ ad.log.error("Verify 4G Internet access failed.")
+ continue
- return wifi_tethering_setup_teardown(
- self.log,
- self.provider, [self.clients[0]],
- ap_band=WIFI_CONFIG_APBAND_5G,
- check_interval=10,
- check_iteration=10)
+ if not self.provider.droid.carrierConfigIsTetheringModeAllowed(
+ TETHERING_MODE_WIFI, MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK):
+ ad.log.info("Tethering is not entitled")
+ continue
+
+ if wifi_tethering_setup_teardown(self.log, self.provider,
+ [self.clients[0]],
+ ap_band=WIFI_CONFIG_APBAND_5G,
+ check_interval=10,
+ check_iteration=10):
+ self.android_devices = [self.provider] + self.clients
+ return True
+ elif idx == num - 1:
+ self.log.error("Tethering is not working on all devices")
+ return False
+ self.log.error("Faile to enable tethering on all devices")
+ return False
@test_tracker_info(uuid="59be8d68-f05b-4448-8584-de971174fd81")
@TelephonyBaseTest.tel_test_wrap
@@ -787,14 +1314,13 @@
True if success.
False if failed.
"""
- ads = self.android_devices
if not self._test_setup_tethering(RAT_3G):
self.log.error("Verify 3G Internet access failed.")
return False
return wifi_tethering_setup_teardown(
self.log,
- self.provider, [self.clients[0]],
+ self.provider, self.clients,
ap_band=WIFI_CONFIG_APBAND_2G,
check_interval=10,
check_iteration=10)
@@ -813,45 +1339,17 @@
True if success.
False if failed.
"""
- ads = self.android_devices
if not self._test_setup_tethering(RAT_3G):
self.log.error("Verify 3G Internet access failed.")
return False
return wifi_tethering_setup_teardown(
self.log,
- self.provider, [self.clients[0]],
+ self.provider, self.clients,
ap_band=WIFI_CONFIG_APBAND_5G,
check_interval=10,
check_iteration=10)
- @test_tracker_info(uuid="f8c4e3d8-b0e5-40ac-a31e-5ae5705a42c6")
- @TelephonyBaseTest.tel_test_wrap
- def test_tethering_4g_to_2gwifi_2clients(self):
- """WiFi Tethering test: LTE to WiFI 2.4G Tethering, with multiple clients
-
- 1. DUT in 3G mode, idle.
- 2. DUT start 5G WiFi Tethering
- 3. PhoneB and PhoneC disable data, connect to DUT's softAP
- 4. Verify Internet access on DUT and PhoneB PhoneC
-
- Returns:
- True if success.
- False if failed.
- """
- ads = self.android_devices
- if not self._test_setup_tethering(RAT_4G):
- self.log.error("Verify 4G Internet access failed.")
- return False
-
- return wifi_tethering_setup_teardown(
- self.log,
- self.provider,
- self.clients,
- ap_band=WIFI_CONFIG_APBAND_2G,
- check_interval=10,
- check_iteration=10)
-
@test_tracker_info(uuid="89fe6321-4c0d-40c0-89b2-54008ecca68f")
@TelephonyBaseTest.tel_test_wrap
def test_tethering_2g_to_2gwifi(self):
@@ -937,8 +1435,8 @@
self.log.error("WiFi Tethering failed.")
return False
- if (not wait_for_wifi_data_connection(self.log, self.provider, True) or
- not verify_internet_connection(self.log, self.provider)):
+ if (not wait_for_wifi_data_connection(self.log, self.provider, True)
+ or not verify_internet_connection(self.log, self.provider)):
self.log.error("Provider data did not return to Wifi")
return False
return True
@@ -1009,8 +1507,10 @@
self.provider.log.error("Provider WiFi tethering stopped.")
return False
- if not check_is_wifi_connected(self.log, self.clients[0], ssid) or (
- not verify_internet_connection(self.log, self.clients[0])):
+ if not check_is_wifi_connected(
+ self.log, self.clients[0],
+ ssid) or (not verify_internet_connection(
+ self.log, self.clients[0])):
self.clients[0].log.error(
"Client wifi connection check failed!")
return False
@@ -1228,8 +1728,8 @@
Returns:
True if entitlement check returns True.
"""
- if (not wait_for_cell_data_connection(self.log, self.provider, True) or
- not verify_internet_connection(self.log, self.provider)):
+ if (not wait_for_cell_data_connection(self.log, self.provider, True)
+ or not verify_internet_connection(self.log, self.provider)):
self.log.error("Failed cell data call for entitlement check.")
return False
@@ -1257,8 +1757,7 @@
for i in range(1, self.stress_test_number + 1):
- ensure_phones_default_state(self.log,
- [self.provider, self.clients[0]])
+ ensure_phones_default_state(self.log, self.android_devices)
if self.test_tethering_4g_to_2gwifi():
success_count += 1
@@ -1299,7 +1798,7 @@
return wifi_tethering_setup_teardown(
self.log,
- self.provider, [self.clients[0]],
+ self.provider, self.clients,
ap_band=WIFI_CONFIG_APBAND_2G,
check_interval=10,
check_iteration=10,
@@ -1328,7 +1827,7 @@
return wifi_tethering_setup_teardown(
self.log,
- self.provider, [self.clients[0]],
+ self.provider, self.clients,
ap_band=WIFI_CONFIG_APBAND_2G,
check_interval=10,
check_iteration=10,
@@ -1352,6 +1851,7 @@
True if no error happen, otherwise False.
"""
result = True
+ self.number_of_devices = 2
# Turn off active SoftAP if any.
if ad_host.droid.wifiIsApEnabled():
stop_wifi_tethering(self.log, ad_host)
@@ -1366,8 +1866,8 @@
self.log.error("Client connect to WiFi failed.")
result = False
if not wifi_reset(self.log, ad_client):
- self.log.error(
- "Reset client WiFi failed. {}".format(ad_client.serial))
+ self.log.error("Reset client WiFi failed. {}".format(
+ ad_client.serial))
result = False
if not stop_wifi_tethering(self.log, ad_host):
self.log.error("Stop WiFi tethering failed.")
@@ -1442,7 +1942,6 @@
fail_list = {}
for password in password_list:
- result = True
ssid = rand_ascii_str(8)
self.log.info("SSID: <{}>, Password: <{}>".format(ssid, password))
if not self._test_start_wifi_tethering_connect_teardown(
@@ -1458,6 +1957,7 @@
def _test_tethering_wifi_and_voice_call(self, provider_data_rat,
provider_setup_func,
provider_in_call_check_func):
+ self.number_of_devices = 2
if not self._test_setup_tethering(provider_data_rat):
self.log.error("Verify 4G Internet access failed.")
return False
@@ -1635,8 +2135,8 @@
self.provider.log.info("Reboot provider")
self.provider.reboot()
- time.sleep(WAIT_TIME_AFTER_REBOOT +
- WAIT_TIME_TETHERING_AFTER_REBOOT)
+ time.sleep(
+ WAIT_TIME_AFTER_REBOOT + WAIT_TIME_TETHERING_AFTER_REBOOT)
self.log.info("After reboot check if tethering stopped.")
if self.provider.droid.wifiIsApEnabled():
@@ -1674,8 +2174,8 @@
self.log.info("Make sure DUT can connect to live network by WIFI")
if ((not ensure_wifi_connected(self.log, self.provider,
self.wifi_network_ssid,
- self.wifi_network_pass)) or
- (not verify_internet_connection(self.log, self.provider))):
+ self.wifi_network_pass))
+ or (not verify_internet_connection(self.log, self.provider))):
self.log.error("WiFi connect fail.")
return False
@@ -1743,8 +2243,8 @@
self.log.info("Make sure DUT can connect to live network by WIFI")
if ((not ensure_wifi_connected(self.log, self.provider,
self.wifi_network_ssid,
- self.wifi_network_pass)) or
- (not verify_internet_connection(self.log, self.provider))):
+ self.wifi_network_pass))
+ or (not verify_internet_connection(self.log, self.provider))):
self.log.error("WiFi connect fail.")
return False
@@ -1958,9 +2458,8 @@
ad = self.android_devices[0]
if not ensure_network_generation_for_subscription(
- self.log, ad,
- ad.droid.subscriptionGetDefaultDataSubId(), GEN_4G,
- MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+ self.log, ad, ad.droid.subscriptionGetDefaultDataSubId(),
+ GEN_4G, MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
self.log.error("Device {} failed to reselect in {}s.".format(
ad.serial, MAX_WAIT_TIME_NW_SELECTION))
return False
@@ -1988,9 +2487,8 @@
ad = self.android_devices[0]
if not ensure_network_generation_for_subscription(
- self.log, ad,
- ad.droid.subscriptionGetDefaultDataSubId(), GEN_3G,
- MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+ self.log, ad, ad.droid.subscriptionGetDefaultDataSubId(),
+ GEN_3G, MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
self.log.error("Device {} failed to reselect in {}s.".format(
ad.serial, MAX_WAIT_TIME_NW_SELECTION))
return False
@@ -2017,9 +2515,8 @@
"""
ad = self.android_devices[0]
if not ensure_network_generation_for_subscription(
- self.log, ad,
- ad.droid.subscriptionGetDefaultDataSubId(), GEN_2G,
- MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+ self.log, ad, ad.droid.subscriptionGetDefaultDataSubId(),
+ GEN_2G, MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
self.log.error("Device {} failed to reselect in {}s.".format(
ad.serial, MAX_WAIT_TIME_NW_SELECTION))
return False
@@ -2278,8 +2775,8 @@
self.wifi_network_pass):
self.log.error("WiFi connect fail.")
return False
- if (not wait_for_wifi_data_connection(self.log, ad, True) or
- not verify_internet_connection(self.log, ad)):
+ if (not wait_for_wifi_data_connection(self.log, ad, True)
+ or not verify_internet_connection(self.log, ad)):
self.log.error("Data is not on WiFi")
return False
@@ -2310,15 +2807,13 @@
ad = self.android_devices[0]
# Install App and Push config
self.log.info("Pushing embms config and apk to the Android device.")
- embms_path_str = "embms_path"
android_embms_path = "/sdcard/mobitv"
- if embms_path_str not in self.user_params:
- self.log.error("Need vzwdca for embms test in config file")
- return False
- embms_path = self.user_params[embms_path_str]
+ embms_path = self.user_params.get("embms_path", "embms_path")
+ if isinstance(embms_path, list):
+ embms_path = embms_path[0]
ad.adb.shell("mkdir /sdcard/mobitv")
dcafile = os.path.join(embms_path, "dca.config")
- apkfile = os.path.join(embms_path, "VzwDCA-v3035.apk")
+ apkfile = os.path.join(embms_path, "VzwDCA.apk")
ad.adb.push("%s %s" % (dcafile, android_embms_path))
ad.adb.install("%s" % apkfile)
@@ -2487,7 +2982,7 @@
Steps:
1. Download a file random picked.
- 2. Device sleep for sometime and Repeat 1 .
+ 2. Device sleep for sometime and Repeat 1.
Expected Results:
Total download failure rate is less than 10%.
@@ -2497,4 +2992,48 @@
False if failed.
"""
return self.file_download_stress()
+
+ @test_tracker_info(uuid="c9970955-123b-467c-afbb-95ec8f99e9b7")
+ def test_file_download_with_mobile_data_usage_limit_set(self):
+ """ Steps:
+ 1. Set the data usage limit to current data usage + 9MB
+ 2. Download 5MB file from internet.
+ 3. The first file download should succeed
+ 4. The second file download should fail
+ """
+ dut = self.android_devices[0]
+ ensure_phones_default_state(self.log, [dut])
+ subscriber_id = dut.droid.telephonyGetSubscriberId()
+ old_data_usage = get_mobile_data_usage(dut, subscriber_id)
+
+ # set data usage limit to current usage limit + 10MB
+ data_limit = old_data_usage + 9 * 1000 * 1000
+ set_mobile_data_usage_limit(dut, data_limit, subscriber_id)
+
+ # download file - size 5MB twice
+ try:
+ for _ in range(2):
+ if not active_file_download_test(self.log, dut, "5MB"):
+ if get_mobile_data_usage(
+ dut, subscriber_id) + 5 * 1000 * 1000 < data_limit:
+ dut.log.error(
+ "Fail to download file when mobile data usage is"
+ " below data usage limit")
+ return False
+ else:
+ dut.log.info(
+ "Download fails as expected due to data limit reached"
+ )
+ else:
+ if get_mobile_data_usage(dut, subscriber_id) < data_limit:
+ dut.log.info(
+ "Download file succeed when mobile data usage is"
+ " below data usage limit")
+ else:
+ dut.log.error(
+ "Download should fail due to data limit reached")
+ return False
+ return True
+ finally:
+ remove_mobile_data_usage_limit(dut, subscriber_id)
""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveEmergencyTest.py b/acts/tests/google/tel/live/TelLiveEmergencyTest.py
new file mode 100644
index 0000000..28dd15d
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveEmergencyTest.py
@@ -0,0 +1,289 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - Google
+#
+# 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.
+"""
+ Test Script for Telephony Pre Check In Sanity
+"""
+
+import time
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
+from acts.test_utils.tel.tel_test_utils import abort_all_tests
+from acts.test_utils.tel.tel_test_utils import dumpsys_telecom_call_info
+from acts.test_utils.tel.tel_test_utils import get_service_state_by_adb
+from acts.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts.test_utils.tel.tel_test_utils import hangup_call_by_adb
+from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts.test_utils.tel.tel_test_utils import initiate_emergency_dialer_call_by_adb
+from acts.test_utils.tel.tel_test_utils import reset_device_password
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts.test_utils.tel.tel_test_utils import unlock_sim
+from acts.test_utils.tel.tel_test_utils import wait_for_sim_ready_by_adb
+
+
+class TelLiveEmergencyTest(TelephonyBaseTest):
+ def __init__(self, controllers):
+ TelephonyBaseTest.__init__(self, controllers)
+
+ self.dut = self.android_devices[0]
+ self.number_of_devices = 1
+ fake_number = self.user_params.get("fake_emergency_number", "800")
+ self.fake_emergency_number = fake_number.strip("+").replace("-", "")
+ self.wifi_network_ssid = self.user_params.get(
+ "wifi_network_ssid") or self.user_params.get(
+ "wifi_network_ssid_2g")
+ self.wifi_network_pass = self.user_params.get(
+ "wifi_network_pass") or self.user_params.get(
+ "wifi_network_pass_2g")
+
+ def setup_test(self):
+ if not unlock_sim(self.dut):
+ abort_all_tests(self.dut.log, "unable to unlock SIM")
+ self.expected_call_result = True
+
+ def teardown_test(self):
+ self.dut.ensure_screen_on()
+ reset_device_password(self.dut, None)
+
+ def change_emergency_number_list(self):
+ for _ in range(5):
+ existing = self.dut.adb.getprop("ril.ecclist")
+ self.dut.log.info("Existing ril.ecclist is: %s", existing)
+ if self.fake_emergency_number in existing:
+ return True
+ emergency_numbers = "%s,%s" % (existing,
+ self.fake_emergency_number)
+ cmd = "setprop ril.ecclist %s" % emergency_numbers
+ self.dut.log.info(cmd)
+ self.dut.adb.shell(cmd)
+ # After some system events, ril.ecclist might change
+ # wait sometime for it to settle
+ time.sleep(10)
+ if self.fake_emergency_number in existing:
+ return True
+ return False
+
+ def change_qcril_emergency_source_mcc_table(self):
+ # This will add the fake number into emergency number list for a mcc
+ # in qcril. Please note, the fake number will be send as an emergency
+ # number by modem and reach the real 911 by this
+ qcril_database_path = self.dut.adb.shell("find /data -iname qcril.db")
+ if not qcril_database_path: return
+ mcc = self.dut.droid.telephonyGetNetworkOperator()
+ mcc = mcc[:3]
+ self.dut.log.info("Add %s mcc %s in qcril_emergency_source_mcc_table")
+ self.dut.adb.shell(
+ "sqlite3 %s \"INSERT INTO qcril_emergency_source_mcc_table VALUES('%s','%s','','')\""
+ % (qcril_database_path, mcc, self.fake_emergency_number))
+
+ def fake_emergency_call_test(self, by_emergency_dialer=True, attemps=3):
+ self.dut.log.info("ServiceState is in %s",
+ get_service_state_by_adb(self.log, self.dut))
+ if by_emergency_dialer:
+ dialing_func = initiate_emergency_dialer_call_by_adb
+ callee = self.fake_emergency_number
+ else:
+ dialing_func = initiate_call
+ # Initiate_call method has to have "+" in front
+ # otherwise the number will be in dialer without dial out
+ # with sl4a fascade. Need further investigation
+ callee = "+%s" % self.fake_emergency_number
+ for i in range(attemps):
+ result = True
+ if not self.change_emergency_number_list():
+ self.dut.log.error("Unable to add number to ril.ecclist")
+ return False
+ time.sleep(1)
+ call_numbers = len(dumpsys_telecom_call_info(self.dut))
+ dial_result = dialing_func(self.log, self.dut, callee)
+ hangup_call_by_adb(self.dut)
+ self.dut.send_keycode("BACK")
+ self.dut.send_keycode("BACK")
+ calls_info = dumpsys_telecom_call_info(self.dut)
+ if len(calls_info) <= call_numbers:
+ self.dut.log.error("New call is not in sysdump telecom")
+ result = False
+ else:
+ self.dut.log.info("New call info = %s", calls_info[-1])
+
+ if dial_result == self.expected_call_result:
+ self.dut.log.info("Call to %s returns %s as expected", callee,
+ self.expected_call_result)
+ else:
+ self.dut.log.info("Call to %s returns %s", callee,
+ not self.expected_call_result)
+ result = False
+ if result:
+ return True
+ ecclist = self.dut.adb.getprop("ril.ecclist")
+ self.dut.log.info("ril.ecclist = %s", ecclist)
+ if self.fake_emergency_number in ecclist:
+ if i == attemps - 1:
+ self.dut.log.error("%s is in ril-ecclist, but call failed",
+ self.fake_emergency_number)
+ else:
+ self.dut.log.warning(
+ "%s is in ril-ecclist, but call failed, try again",
+ self.fake_emergency_number)
+ else:
+ if i == attemps - 1:
+ self.dut.log.error("Fail to write %s to ril-ecclist",
+ self.fake_emergency_number)
+ else:
+ self.dut.log.info("%s is not in ril-ecclist",
+ self.fake_emergency_number)
+ self.dut.log.info("fake_emergency_call_test result is %s", result)
+ import pdb
+ pdb.set_trace()
+ return result
+
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="fe75ba2c-e4ea-4fc1-881b-97e7a9a7f48e")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_by_emergency_dialer(self):
+ """Test emergency call with emergency dialer in user account.
+
+ Add system emergency number list with storyline number.
+ Use the emergency dialer to call storyline.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ return self.fake_emergency_call_test()
+
+ @test_tracker_info(uuid="8a0978a8-d93e-4f6a-99fe-d0e28bf1be2a")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_by_dialer(self):
+ """Test emergency call with dialer.
+
+ Add system emergency number list with storyline number.
+ Call storyline by dialer.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ return self.fake_emergency_call_test(by_emergency_dialer=False)
+
+ @test_tracker_info(uuid="2e6fcc75-ff9e-47b1-9ae8-ed6f9966d0f5")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_in_apm(self):
+ """Test emergency call with emergency dialer in airplane mode.
+
+ Enable airplane mode.
+ Add system emergency number list with storyline number.
+ Use the emergency dialer to call storyline.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ try:
+ toggle_airplane_mode_by_adb(self.log, self.dut, True)
+ if self.fake_emergency_call_test():
+ return True
+ else:
+ return False
+ finally:
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
+
+ @test_tracker_info(uuid="469bfa60-6e8f-4159-af1f-ab6244073079")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_in_screen_lock(self):
+ """Test emergency call with emergency dialer in screen lock phase.
+
+ Enable device password and then reboot upto password query window.
+ Add system emergency number list with storyline.
+ Use the emergency dialer to call storyline.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
+ reset_device_password(self.dut, DEFAULT_DEVICE_PASSWORD)
+ if not wait_for_sim_ready_by_adb(self.log, self.dut):
+ self.dut.log.error("SIM is not ready")
+ return False
+ self.dut.reboot(stop_at_lock_screen=True)
+ if self.fake_emergency_call_test():
+ return True
+ else:
+ return False
+
+ @test_tracker_info(uuid="17401c57-0dc2-49b5-b954-a94dbb2d5ad0")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_in_screen_lock_apm(self):
+ """Test emergency call with emergency dialer in screen lock phase.
+
+ Enable device password and then reboot upto password query window.
+ Add system emergency number list with storyline.
+ Use the emergency dialer to call storyline.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ try:
+ toggle_airplane_mode_by_adb(self.log, self.dut, True)
+ reset_device_password(self.dut, DEFAULT_DEVICE_PASSWORD)
+ self.dut.reboot(stop_at_lock_screen=True)
+ if not wait_for_sim_ready_by_adb(self.log, self.dut):
+ self.dut.log.error("SIM is not ready")
+ return False
+ if self.fake_emergency_call_test():
+ return True
+ else:
+ return False
+ finally:
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
+
+ @test_tracker_info(uuid="ccea13ae-6951-4790-a5f7-b5b7a2451c6c")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_in_setupwizard(self):
+ """Test emergency call with emergency dialer in setupwizard.
+
+ Wipe the device and then reboot upto setupwizard.
+ Add system emergency number list with storyline number.
+ Use the emergency dialer to call storyline.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ try:
+ if not fastboot_wipe(self.dut, skip_setup_wizard=False):
+ return False
+ if not wait_for_sim_ready_by_adb(self.log, self.dut):
+ self.dut.log.error("SIM is not ready")
+ return False
+ if self.fake_emergency_call_test():
+ return True
+ else:
+ return False
+ finally:
+ self.dut.exit_setup_wizard()
+
+
+""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveLockedSimTest.py b/acts/tests/google/tel/live/TelLiveLockedSimTest.py
new file mode 100644
index 0000000..b44fd95
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveLockedSimTest.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - Google
+#
+# 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.
+"""
+ Test Script for Telephony Locked SIM Emergency Call Test
+"""
+
+import time
+from acts.base_test import BaseTestClass
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
+from acts.test_utils.tel.tel_test_utils import abort_all_tests
+from acts.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts.test_utils.tel.tel_test_utils import is_sim_locked
+from acts.test_utils.tel.tel_test_utils import is_sim_ready_by_adb
+from acts.test_utils.tel.tel_test_utils import reset_device_password
+from acts.test_utils.tel.tel_test_utils import refresh_sl4a_session
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts.test_utils.tel.tel_test_utils import unlocking_device
+from acts.test_utils.tel.tel_test_utils import unlock_sim
+from acts.test_utils.tel.tel_test_utils import STORY_LINE
+from TelLiveEmergencyTest import TelLiveEmergencyTest
+
+EXPECTED_CALL_TEST_RESULT = False
+
+
+class TelLiveLockedSimTest(TelLiveEmergencyTest):
+ def __init__(self, controllers):
+ BaseTestClass.__init__(self, controllers)
+ self.logger_sessions = []
+ fake_number = self.user_params.get("fake_emergency_number", STORY_LINE)
+ self.fake_emergency_number = fake_number.strip("+").replace("-", "")
+ for ad in self.android_devices:
+ if not is_sim_locked(ad):
+ ad.log.info("SIM is not locked")
+ else:
+ ad.log.info("SIM is locked")
+ self.dut = ad
+ return
+ #if there is no locked SIM, reboot the device and check again
+ for ad in self.android_devices:
+ reset_device_password(ad, None)
+ ad.reboot(stop_at_lock_screen=True)
+ for _ in range(10):
+ if is_sim_ready_by_adb(self.log, ad):
+ ad.log.info("SIM is not locked")
+ break
+ elif is_sim_locked(ad):
+ ad.log.info("SIM is locked")
+ self.dut = ad
+ ad.ensure_screen_on()
+ ad.start_services(ad.skip_sl4a)
+ return
+ else:
+ time.sleep(5)
+ self.log.error("There is no locked SIM in this testbed")
+ abort_all_tests(self.log, "There is no locked SIM")
+
+ def setup_class(self):
+ self.android_devices = [self.dut]
+ pass
+
+ def setup_test(self):
+ self.expected_call_result = False
+ unlocking_device(self.dut)
+ refresh_sl4a_session(self.dut)
+ unlock_sim(self.dut)
+
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="fd7fb69c-6fd4-4874-a4ca-769353b9db25")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_by_emergency_dialer(self):
+ """Test emergency call with emergency dialer in user account.
+
+ Enable SIM lock on the SIM. Reboot device to SIM pin request page.
+ Add storyline number to system emergency number list.
+ Use the emergency dialer to call "611".
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ self.expected_call_result = True
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
+ return self.fake_emergency_call_test()
+
+ @test_tracker_info(uuid="669cf1d9-9513-4f90-b0fd-2f0e8f1cc941")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_by_dialer(self):
+ """Test emergency call with dialer.
+
+ Enable SIM lock on the SIM. Reboot device to SIM pin request page.
+ Add system emergency number list with storyline number.
+ Call storyline by dialer.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ self.expected_call_result = True
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
+ return self.fake_emergency_call_test(by_emergency_dialer=True)
+
+ @test_tracker_info(uuid="1990f166-66a7-4092-b448-c179a9194371")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_in_apm(self):
+ """Test emergency call with emergency dialer in airplane mode.
+
+ Enable airplane mode.
+ Enable SIM lock on the SIM. Reboot device to SIM pin request page.
+ Add system emergency number list with storyline number.
+ Use the emergency dialer to call storyline.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ self.expected_call_result = True
+ try:
+ toggle_airplane_mode_by_adb(self.log, self.dut, True)
+ if self.fake_emergency_call_test():
+ return True
+ else:
+ return False
+ finally:
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
+
+ @test_tracker_info(uuid="7ffdad34-b8fb-41b0-b0fd-2def5adc67bc")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_in_screen_lock(self):
+ """Test emergency call with emergency dialer in screen lock phase.
+
+ Enable SIM lock on the SIM.
+ Enable device password and then reboot upto password and pin query stage.
+ Add system emergency number list with storyline number.
+ Use the emergency dialer to call storyline.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ self.dut.log.info("Turn off airplane mode")
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
+ self.dut.log.info("Set screen lock pin")
+ reset_device_password(self.dut, DEFAULT_DEVICE_PASSWORD)
+ self.dut.log.info("Reboot device to screen lock screen")
+ self.dut.reboot(stop_at_lock_screen=True)
+ if self.fake_emergency_call_test():
+ return True
+ else:
+ return False
+
+ @test_tracker_info(uuid="12dc1eb6-50ed-4ad9-b195-5d96c6b6952e")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_in_screen_lock_apm(self):
+ """Test emergency call with emergency dialer in screen lock phase.
+
+ Enable device password and airplane mode
+ Enable SIM lock on the SIM.
+ Reboot upto pin query window.
+ Add system emergency number list with story line.
+ Use the emergency dialer to call story line.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ toggle_airplane_mode_by_adb(self.log, self.dut, True)
+ self.dut.log.info("Set screen lock pin")
+ reset_device_password(self.dut, DEFAULT_DEVICE_PASSWORD)
+ self.dut.log.info("Reboot device to screen lock screen")
+ self.dut.reboot(stop_at_lock_screen=True)
+ if self.fake_emergency_call_test():
+ return True
+ else:
+ return False
+
+ @test_tracker_info(uuid="1e01927a-a077-466d-8bf8-52dca87ab87c")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_fake_emergency_call_in_setupwizard(self):
+ """Test emergency call with emergency dialer in setupwizard.
+
+ Enable SIM lock on the SIM.
+ Wipe the device and then reboot upto setupwizard.
+ Add system emergency number list with story line.
+ Use the emergency dialer to call story line.
+ Verify DUT has in call activity.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ try:
+ if not fastboot_wipe(self.dut, skip_setup_wizard=False):
+ return False
+ if self.fake_emergency_call_test():
+ return True
+ else:
+ return False
+ finally:
+ self.dut.exit_setup_wizard()
+
+
+""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveMobilityStressTest.py b/acts/tests/google/tel/live/TelLiveMobilityStressTest.py
index 16d4d95..2120c04 100644
--- a/acts/tests/google/tel/live/TelLiveMobilityStressTest.py
+++ b/acts/tests/google/tel/live/TelLiveMobilityStressTest.py
@@ -20,6 +20,7 @@
import collections
import random
import time
+from acts.asserts import explicit_pass
from acts.asserts import fail
from acts.test_decorators import test_tracker_info
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
@@ -44,6 +45,7 @@
from acts.test_utils.tel.tel_test_utils import run_multithread_func
from acts.test_utils.tel.tel_test_utils import set_wfc_mode
from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
@@ -100,6 +102,9 @@
return True
+ def on_fail(self, test_name, begin_time):
+ pass
+
def _setup_volte_wfc_wifi_preferred(self):
return self._wfc_phone_setup(
False, WFC_MODE_WIFI_PREFERRED, volte_mode=True)
@@ -136,13 +141,20 @@
0: sms_send_receive_verify,
1: mms_send_receive_verify
}
- self.result_info["Total %s" % message_type_map[selection]] += 1
+ message_type = message_type_map[selection]
+ self.result_info["Total %s" % message_type] += 1
+ begin_time = get_current_epoch_time()
+ start_qxdm_loggers(self.log, self.android_devices)
if not message_func_map[selection](self.log, ads[0], ads[1],
message_content_map[selection]):
- self.log.error("%s of length %s from %s to %s fails",
- message_type_map[selection], length, ads[0].serial,
- ads[1].serial)
- self.result_info["%s failure" % message_type_map[selection]] += 1
+ self.log.error("%s of length %s from %s to %s fails", message_type,
+ length, ads[0].serial, ads[1].serial)
+ self.result_info["%s failure" % message_type] += 1
+ if message_type == "SMS" or self.result_info["%s failure" %
+ message_type] == 1:
+ self._take_bug_report("%s_%s_failure" % (self.test_name,
+ message_type),
+ begin_time)
return False
else:
self.log.info("%s of length %s from %s to %s succeed",
@@ -152,6 +164,8 @@
def _make_phone_call(self, ads):
self.result_info["Total Calls"] += 1
+ begin_time = get_current_epoch_time()
+ start_qxdm_loggers(self.log, self.android_devices)
if not call_setup_teardown(
self.log,
ads[0],
@@ -162,6 +176,8 @@
self.max_phone_call_duration)):
self.log.error("Call setup and teardown failed.")
self.result_info["Call Failure"] += 1
+ self._take_bug_report("%s_call_failure" % self.test_name,
+ begin_time)
return False
self.log.info("Call setup and teardown succeed.")
return True
@@ -171,8 +187,7 @@
while time.time() < self.finishing_time:
self.dut.log.info(dict(self.result_info))
try:
- begin_time = epoch_to_log_line_timestamp(
- get_current_epoch_time())
+ begin_time = get_current_epoch_time()
time.sleep(self.crash_check_interval)
crash_report = self.dut.check_crash_report(
"checking_crash", begin_time, True)
@@ -185,16 +200,14 @@
self.log.error("Exception error %s", str(e))
self.result_info["Exception Errors"] += 1
if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
+ return False
except Exception as e:
- self.finishing_time = time.time()
- raise
+ return False
self.dut.log.info("Crashes found: %s", failure)
if failure:
- return "%s crashes" % failure
+ return False
else:
- return ""
+ return True
def environment_change_4g_wifi(self):
#block cell 3G, WIFI 2G
@@ -274,24 +287,23 @@
self.log, self.helper))
if not self._make_phone_call(ads):
failure += 1
- self._take_bug_report("%s_call_failure" % self.test_name,
- time.strftime("%m-%d-%Y-%H-%M-%S"))
self.dut.droid.goToSleepNow()
time.sleep(random.randrange(0, self.max_sleep_time))
- except IGNORE_EXCEPTION as e:
+ except IGNORE_EXCEPTIONS as e:
self.log.error("Exception error %s", str(e))
self.result_info["Exception Errors"] += 1
if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
+ self.log.error("Too many exception errors %s",
+ IGNORE_EXCEPTIONS)
+ return False
except Exception as e:
- self.finishing_time = time.time()
- raise
+ self.log.error(e)
+ return False
self.dut.log.info("Call test failure: %s/%s", failure, total_count)
if failure:
- return "Call test failure: %s/%s" % (failure, total_count)
+ return False
else:
- return ""
+ return True
def message_test(self):
failure = 0
@@ -303,25 +315,24 @@
total_count += 1
if not self._send_message(ads):
failure += 1
- #self._take_bug_report("%s_messaging_failure" % self.test_name,
- # time.strftime("%m-%d-%Y-%H-%M-%S"))
self.dut.droid.goToSleepNow()
time.sleep(random.randrange(0, self.max_sleep_time))
- except IGNORE_EXCEPTION as e:
+ except IGNORE_EXCEPTIONS as e:
self.log.error("Exception error %s", str(e))
self.result_info["Exception Errors"] += 1
if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
+ self.log.error("Too many exception errors %s",
+ IGNORE_EXCEPTIONS)
+ return False
except Exception as e:
- self.finishing_time = time.time()
- raise
+ self.log.error(e)
+ return False
self.dut.log.info("Messaging test failure: %s/%s", failure,
total_count)
if failure / total_count > 0.1:
- return "Messaging test failure: %s/%s" % (failure, total_count)
+ return False
else:
- return ""
+ return True
def data_test(self):
failure = 0
@@ -331,6 +342,8 @@
file_names = ["5MB", "10MB", "20MB", "50MB", "200MB"]
while time.time() < self.finishing_time:
total_count += 1
+ begin_time = get_current_epoch_time()
+ start_qxdm_loggers(self.log, self.android_devices)
try:
self.dut.log.info(dict(self.result_info))
self.result_info["Total file download"] += 1
@@ -338,28 +351,30 @@
file_name = file_names[selection]
if not active_file_download_test(self.log, self.dut,
file_name):
- self.result_info["%s file download failure" %
- file_name] += 1
- #self._take_bug_report("%s_download_failure" % self.test_name,
- # time.strftime("%m-%d-%Y-%H-%M-%S"))
+ self.result_info["File download failure"] += 1
failure += 1
- self.dut.droid.goToSleepNow()
- time.sleep(random.randrange(0, self.max_sleep_time))
- except IGNORE_EXCEPTION as e:
+ if self.result_info["File download failure"] == 1:
+ self._take_bug_report(
+ "%s_file_download_failure" % self.test_name,
+ begin_time)
+ self.dut.droid.goToSleepNow()
+ time.sleep(random.randrange(0, self.max_sleep_time))
+ except IGNORE_EXCEPTIONS as e:
self.log.error("Exception error %s", str(e))
self.result_info["Exception Errors"] += 1
if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
+ self.log.error("Too many exception error %s",
+ IGNORE_EXCEPTIONS)
+ return False
except Exception as e:
- self.finishing_time = time.time()
- raise
+ self.log.error(e)
+ return False
self.dut.log.info("File download test failure: %s/%s", failure,
total_count)
if failure / total_count > 0.1:
- return "File download test failure: %s/%s" % (failure, total_count)
+ return False
else:
- return ""
+ return True
def parallel_tests(self, change_env_func, setup_func=None):
if setup_func and not setup_func():
@@ -370,12 +385,12 @@
results = run_multithread_func(self.log, [(self.call_test, []), (
self.message_test, []), (self.data_test, []), (
self.crash_check_test, []), (change_env_func, [])])
- self.log.info(dict(self.result_info))
- error_message = " ".join(results).strip()
- if error_message:
- self.log.error(error_message)
- fail(error_message)
- return True
+ result_message = "%s" % dict(self.result_info)
+ self.log.info(result_message)
+ if all(results):
+ explicit_pass(result_message)
+ else:
+ fail(result_message)
""" Tests Begin """
diff --git a/acts/tests/google/tel/live/TelLiveNoSimTest.py b/acts/tests/google/tel/live/TelLiveNoSimTest.py
index f886e61..87b204a 100644
--- a/acts/tests/google/tel/live/TelLiveNoSimTest.py
+++ b/acts/tests/google/tel/live/TelLiveNoSimTest.py
@@ -17,64 +17,29 @@
Test Script for Telephony Pre Check In Sanity
"""
-import time
-import os
from acts.test_decorators import test_tracker_info
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_test_utils import dumpsys_telecom_call_info
-from acts.test_utils.tel.tel_test_utils import hung_up_call_by_adb
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import initiate_emergency_dialer_call_by_adb
+from acts.test_utils.tel.tel_defines import DEFAULT_DEVICE_PASSWORD
+from acts.test_utils.tel.tel_defines import SIM_STATE_ABSENT
+from acts.test_utils.tel.tel_test_utils import fastboot_wipe
+from acts.test_utils.tel.tel_test_utils import reset_device_password
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from acts.test_utils.tel.tel_test_utils import STORY_LINE
+from TelLiveEmergencyTest import TelLiveEmergencyTest
-class TelLiveNoSimTest(TelephonyBaseTest):
+class TelLiveNoSimTest(TelLiveEmergencyTest):
def setup_class(self):
- self.wifi_network_ssid = self.user_params.get(
- "wifi_network_ssid") or self.user_params.get("wifi_network_ssid_2g")
- self.wifi_network_pass = self.user_params.get(
- "wifi_network_pass") or self.user_params.get("wifi_network_pass_2g")
- self.dut = self.android_devices[0]
- self.fake_emergency_number = self.user_params.get(
- "fake_emergency_number", STORY_LINE.strip("+"))
-
- def teardown_class(self):
- super(TelephonyBaseTest, self).teardown_class()
- #reboot to load default emergency number list ril.ecclist
- self.dut.reboot()
+ for ad in self.android_devices:
+ if ad.adb.getprop("gsm.sim.state") == SIM_STATE_ABSENT:
+ ad.log.info("Device has no SIM in it, set as DUT")
+ self.dut = ad
+ return True
+ self.log.error("No device meets no SIM requirement")
+ return False
def setup_test(self):
- if not self.dut.skip_sl4a and not getattr(self.dut, "droid"):
- self.dut.start_services()
-
- def change_emergency_number_list(self):
- existing = self.dut.adb.shell("getprop ril.ecclist")
- if self.fake_emergency_number in existing: return
- emergency_numbers = "%s,%s" % (existing, self.fake_emergency_number)
- self.dut.log.info("Change emergency numbes to %s", emergency_numbers)
- self.dut.adb.shell("setprop ril.ecclist %s" % emergency_numbers)
-
- def fake_emergency_call_test(self, by_emergency_dialer=True):
- self.change_emergency_number_list()
- time.sleep(1)
- call_numbers = len(dumpsys_telecom_call_info(self.dut))
- if by_emergency_dialer:
- dialing_func = initiate_emergency_dialer_call_by_adb
- else:
- dialing_func = initiate_call
- if dialing_func(
- self.log, self.dut, self.fake_emergency_number, timeout=10):
- hung_up_call_by_adb(self.dut)
- self.dut.log.error(
- "calling to the fake emergency number should fail")
- calls_info = dumpsys_telecom_call_info(self.dut)
- if len(calls_info) <= call_numbers:
- self.dut.log.error("New call is not in sysdump telecom")
- return False
- else:
- self.dut.log.info("New call info = %s", calls_info[call_numbers])
- return True
+ self.expected_call_result = False
+ self.android_devices = [self.dut]
""" Tests Begin """
@@ -83,14 +48,15 @@
def test_fake_emergency_call_by_emergency_dialer(self):
"""Test emergency call with emergency dialer in user account.
- Change system emergency number list to "611".
- Use the emergency dialer to call "611".
+ Add storyline number to system emergency number list.
+ Use the emergency dialer to call storyline.
Verify DUT has in call activity.
Returns:
True if success.
False if failed.
"""
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
return self.fake_emergency_call_test()
@test_tracker_info(uuid="cdf7ddad-480f-4757-83bd-a74321b799f7")
@@ -98,14 +64,15 @@
def test_fake_emergency_call_by_dialer(self):
"""Test emergency call with dialer.
- Change system emergency number list to "611".
- Call "611" by dialer.
+ Add storyline number to system emergency number list.
+ Call storyline by dialer.
Verify DUT has in call activity.
Returns:
True if success.
False if failed.
"""
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
return self.fake_emergency_call_test(by_emergency_dialer=False)
@test_tracker_info(uuid="e147960a-4227-41e2-bd06-65001ad5e0cd")
@@ -114,8 +81,8 @@
"""Test emergency call with emergency dialer in airplane mode.
Enable airplane mode.
- Change system emergency number list to "611".
- Use the emergency dialer to call "611".
+ Add storyline number to system emergency number list.
+ Use the emergency dialer to call storyline.
Verify DUT has in call activity.
Returns:
@@ -137,29 +104,21 @@
"""Test emergency call with emergency dialer in screen lock phase.
Enable device password and then reboot upto password query window.
- Change system emergency number list to "611".
- Use the emergency dialer to call "611".
+ Add storyline number to system emergency number list.
+ Use the emergency dialer to call storyline.
Verify DUT has in call activity.
Returns:
True if success.
False if failed.
"""
- try:
- if not self.dut.device_password and getattr(self.dut, "droid"):
- self.dut.droid.setDevicePassword("1111")
- self.dut.reboot(stop_at_lock_screen=True)
- if self.fake_emergency_call_test():
- return True
- else:
- return False
- finally:
- if not self.dut.ensure_screen_on():
- self.dut.log.error("User screen cannot come up")
- return False
- self.dut.start_services(self.dut.skip_sl4a)
- if not self.dut.device_password:
- self.dut.droid.disableDevicePassword()
+ toggle_airplane_mode_by_adb(self.log, self.dut, False)
+ reset_device_password(self.dut, DEFAULT_DEVICE_PASSWORD)
+ self.dut.reboot(stop_at_lock_screen=True)
+ if self.fake_emergency_call_test():
+ return True
+ else:
+ return False
@test_tracker_info(uuid="1ef97f8a-eb3d-45b7-b947-ac409bb70587")
@TelephonyBaseTest.tel_test_wrap
@@ -167,8 +126,8 @@
"""Test emergency call with emergency dialer in screen lock phase.
Enable device password and then reboot upto password query window.
- Change system emergency number list to "611".
- Use the emergency dialer to call "611".
+ Add storyline number to system emergency number list.
+ Use the emergency dialer to call storyline.
Verify DUT has in call activity.
Returns:
@@ -177,8 +136,7 @@
"""
try:
toggle_airplane_mode_by_adb(self.log, self.dut, True)
- if not self.dut.device_password and getattr(self.dut, "droid"):
- self.dut.droid.setDevicePassword("1111")
+ reset_device_password(self.dut, DEFAULT_DEVICE_PASSWORD)
self.dut.reboot(stop_at_lock_screen=True)
if self.fake_emergency_call_test():
return True
@@ -186,12 +144,6 @@
return False
finally:
toggle_airplane_mode_by_adb(self.log, self.dut, False)
- if not self.dut.ensure_screen_on():
- self.dut.log.error("User screen cannot come up")
- return False
- self.dut.start_services(self.dut.skip_sl4a)
- if not self.dut.device_password:
- self.dut.droid.disableDevicePassword()
@test_tracker_info(uuid="50f8b3d9-b126-4419-b5e5-b37b850deb8e")
@TelephonyBaseTest.tel_test_wrap
@@ -199,8 +151,8 @@
"""Test emergency call with emergency dialer in setupwizard.
Wipe the device and then reboot upto setupwizard.
- Change system emergency number list to "611".
- Use the emergency dialer to call "611".
+ Add storyline number to system emergency number list.
+ Use the emergency dialer to call storyline.
Verify DUT has in call activity.
Returns:
@@ -208,16 +160,14 @@
False if failed.
"""
try:
- self.dut.fastboot_wipe()
+ if not fastboot_wipe(self.dut, skip_setup_wizard=False):
+ return False
if self.fake_emergency_call_test():
return True
else:
return False
finally:
- self.dut.ensure_screen_on()
self.dut.exit_setup_wizard()
- if self.dut.device_password:
- self.dut.droid.setDevicePassword(self.dut.device_password)
""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLivePostflightTest.py b/acts/tests/google/tel/live/TelLivePostflightTest.py
index 85f4bcd..34b30ea 100644
--- a/acts/tests/google/tel/live/TelLivePostflightTest.py
+++ b/acts/tests/google/tel/live/TelLivePostflightTest.py
@@ -18,26 +18,48 @@
"""
import os
from acts import utils
+from acts.asserts import fail
+from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.asserts import fail
class TelLivePostflightTest(TelephonyBaseTest):
+ def __init__(self, controllers):
+ BaseTestClass.__init__(self, controllers)
+
+ def setup_class(self):
+ pass
+
+ def teardown_class(self):
+ pass
+
+ def setup_test(self):
+ pass
+
+ def on_pass(self, *arg):
+ pass
+
+ def on_fail(self, *arg):
+ pass
+
@test_tracker_info(uuid="ba6e260e-d2e1-4c01-9d51-ef2df1591039")
@TelephonyBaseTest.tel_test_wrap
def test_check_crash(self):
msg = ""
for ad in self.android_devices:
- post_crash = ad.check_crash_report(self.test_id, None, False)
+ post_crash = ad.check_crash_report(self.test_id)
pre_crash = getattr(ad, "crash_report_preflight", [])
crash_diff = list(set(post_crash).difference(set(pre_crash)))
if crash_diff:
- msg += "%s find new crash reports %s" % (ad.serial, crash_diff)
+ msg += "%s find new crash reports %s " % (ad.serial,
+ crash_diff)
ad.log.error("Find new crash reports %s", crash_diff)
- crash_path = os.path.join(ad.log_path, self.test_id, "Crashes")
+ crash_path = os.path.join(ad.log_path, self.test_name,
+ "Crashes")
utils.create_dir(crash_path)
ad.pull_files(crash_diff, crash_path)
+ self._ad_take_bugreport(ad, self.test_name, self.begin_time)
if msg:
fail(msg)
return True
@@ -50,15 +72,26 @@
tombstones = ad.get_file_names("/data/tombstones/")
if not tombstones: continue
for tombstone in tombstones:
- ts_path = os.path.join("/data/tombstones/", tombstone)
- if ad.adb.shell("cat %s | grep pid | grep dialer" % ts_path):
- message = "%s dialer crash: %s " % (ad.serial, ts_path)
+ if ad.adb.shell("cat %s | grep pid | grep dialer" % tombstone):
+ message = "%s dialer crash: %s " % (ad.serial, tombstone)
ad.log.error(message)
msg += message
- crash_path = os.path.join(ad.log_path, self.test_id,
+ crash_path = os.path.join(ad.log_path, self.test_name,
"Crashes")
utils.create_dir(crash_path)
- ad.pull_files([], crash_path)
+ ad.pull_files([tombstone], crash_path)
+ if msg:
+ fail(msg)
+ return True
+
+ @test_tracker_info(uuid="707d4a33-2e21-40ea-bd27-d15f4e3ff0f0")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_check_data_accounting_failures(self):
+ msg = ""
+ for ad in self.android_devices:
+ ad.log.info("data_accounting_errors: %s", dict(ad.data_accounting))
+ if any(ad.data_accounting.values()):
+ msg += "%s %s" % (ad.serial, dict(ad.data_accounting))
if msg:
fail(msg)
return True
diff --git a/acts/tests/google/tel/live/TelLivePreflightTest.py b/acts/tests/google/tel/live/TelLivePreflightTest.py
index adb1379..916ef9b 100644
--- a/acts/tests/google/tel/live/TelLivePreflightTest.py
+++ b/acts/tests/google/tel/live/TelLivePreflightTest.py
@@ -20,6 +20,10 @@
import time
from queue import Empty
+from acts import signals
+from acts import utils
+from acts.controllers.android_device import get_info
+from acts.libs.ota import ota_updater
from acts.test_decorators import test_tracker_info
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_utils.tel.tel_defines import AOSP_PREFIX
@@ -42,10 +46,13 @@
from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
from acts.test_utils.tel.tel_test_utils import get_operator_name
+from acts.test_utils.tel.tel_test_utils import is_sim_locked
+from acts.test_utils.tel.tel_test_utils import run_multithread_func
from acts.test_utils.tel.tel_test_utils import setup_droid_properties
from acts.test_utils.tel.tel_test_utils import set_phone_screen_on
from acts.test_utils.tel.tel_test_utils import set_phone_silent_mode
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts.test_utils.tel.tel_test_utils import unlock_sim
from acts.test_utils.tel.tel_test_utils import verify_http_connection
from acts.test_utils.tel.tel_test_utils import wait_for_voice_attach_for_subscription
from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
@@ -60,11 +67,19 @@
TelephonyBaseTest.__init__(self, controllers)
self.wifi_network_ssid = self.user_params.get(
- "wifi_network_ssid") or self.user_params.get("wifi_network_ssid_2g")
+ "wifi_network_ssid") or self.user_params.get(
+ "wifi_network_ssid_2g") or self.user_params.get(
+ "wifi_network_ssid_5g")
self.wifi_network_pass = self.user_params.get(
- "wifi_network_pass") or self.user_params.get("wifi_network_pass_2g")
+ "wifi_network_pass") or self.user_params.get(
+ "wifi_network_pass_2g") or self.user_params.get(
+ "wifi_network_ssid_5g")
def setup_class(self):
+ for ad in self.android_devices:
+ toggle_airplane_mode_by_adb(self.log, ad, False)
+
+ def teardown_class(self):
pass
def setup_test(self):
@@ -72,6 +87,48 @@
""" Tests Begin """
+ @test_tracker_info(uuid="cb897221-99e1-4697-927e-02d92d969440")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_ota_upgrade(self):
+ ota_package = self.user_params.get("ota_package")
+ if isinstance(ota_package, list):
+ ota_package = ota_package[0]
+ if ota_package and "dev/null" not in ota_package:
+ self.log.info("Upgrade with ota_package %s", ota_package)
+ self.log.info("Before OTA upgrade: %s",
+ get_info(self.android_devices))
+ else:
+ raise signals.TestSkip("No ota_package is defined")
+ ota_util = self.user_params.get("ota_util")
+ if isinstance(ota_util, list):
+ ota_util = ota_util[0]
+ if ota_util:
+ if "update_engine_client.zip" in ota_util:
+ self.user_params["UpdateDeviceOtaTool"] = ota_util
+ self.user_params["ota_tool"] = "UpdateDeviceOtaTool"
+ else:
+ self.user_params["AdbSideloadOtaTool"] = ota_util
+ self.user_params["ota_tool"] = "AdbSideloadOtaTool"
+ self.log.info("OTA upgrade with %s by %s", ota_package,
+ self.user_params["ota_tool"])
+ ota_updater.initialize(self.user_params, self.android_devices)
+ tasks = [(ota_updater.update, [ad]) for ad in self.android_devices]
+ try:
+ run_multithread_func(self.log, tasks)
+ except Exception as err:
+ abort_all_tests(self.log, "Unable to do ota upgrade: %s" % err)
+ device_info = get_info(self.android_devices)
+ self.log.info("After OTA upgrade: %s", device_info)
+ self.results.add_controller_info("AndroidDevice", device_info)
+ for ad in self.android_devices:
+ if is_sim_locked(ad):
+ ad.log.info("After OTA, SIM keeps the locked state")
+ elif getattr(ad, "is_sim_locked", False):
+ ad.log.error("After OTA, SIM loses the locked state")
+ if not unlock_sim(ad):
+ abort_all_tests(ad.log, "unable to unlock SIM")
+ return True
+
@test_tracker_info(uuid="8390a2eb-a744-4cda-bade-f94a2cc83f02")
@TelephonyBaseTest.tel_test_wrap
def test_check_environment(self):
@@ -104,11 +161,19 @@
@test_tracker_info(uuid="1070b160-902b-43bf-92a0-92cc2d05bb13")
@TelephonyBaseTest.tel_test_wrap
def test_check_crash(self):
+ result = True
+ begin_time = None
for ad in self.android_devices:
+ output = ad.adb.shell("cat /proc/uptime")
+ epoch_up_time = utils.get_current_epoch_time() - 1000 * float(
+ output.split(" ")[0])
ad.crash_report_preflight = ad.check_crash_report(
- self.test_id, None, True)
+ self.test_name,
+ begin_time=epoch_up_time,
+ log_crash_report=True)
if ad.crash_report_preflight:
msg = "Find crash reports %s before test starts" % (
ad.crash_report_preflight)
ad.log.warn(msg)
- return True
+ result = False
+ return result
diff --git a/acts/tests/google/tel/live/TelLiveProjectFiTest.py b/acts/tests/google/tel/live/TelLiveProjectFiTest.py
new file mode 100644
index 0000000..4b4873f
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveProjectFiTest.py
@@ -0,0 +1,579 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - Google
+#
+# 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.
+"""
+ Test Script for Project Fi Setting
+"""
+
+import time
+
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_defines import CARRIER_SPT
+from acts.test_utils.tel.tel_defines import CARRIER_TMO
+from acts.test_utils.tel.tel_defines import CARRIER_USCC
+from acts.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
+from acts.test_utils.tel.tel_test_utils import abort_all_tests
+from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts.test_utils.tel.tel_test_utils import is_sim_ready
+from acts.test_utils.tel.tel_test_utils import log_screen_shot
+from acts.test_utils.tel.tel_test_utils import multithread_func
+from acts.test_utils.tel.tel_test_utils import refresh_droid_config
+from acts.test_utils.tel.tel_test_utils import send_dialer_secret_code
+from acts.test_utils.tel.tel_test_utils import wait_for_state
+
+CARRIER_AUTO = "auto"
+
+_CARRIER_DIALER_CODE_LOOKUP = {
+ CARRIER_AUTO: '342886',
+ CARRIER_SPT: '34777',
+ CARRIER_TMO: '34866',
+ CARRIER_USCC: '34872'
+}
+
+_SWITCHING_PREF_FILE = (
+ '/data/data/com.google.android.apps.tycho/shared_prefs/switching.xml')
+
+_INTENT_FLAGS = int(0x00008000 | 0x10000000 | 0x00080000 | 0x00020000)
+_TYCHO_PKG = 'com.google.android.apps.tycho'
+_MAX_WAIT_TIME = 600
+
+
+class TychoClassId(object):
+ """Tycho Activity/Service Classnames."""
+ # Activities
+ CARRIER_SETUP = 'CarrierSetupEntryPointTrampoline'
+ INIT_ACTIVITY = 'InitActivity'
+ # Services
+ SYNC_SERVICE = 'services.SyncService'
+ ACTIVATE_SUPER_NETWORK_SERVICE = 'services.SuperNetworkConfigurationService'
+
+
+class ActionTypeId(object):
+ """Andorid Action Type to trigger events."""
+ MAIN = 'android.intent.action.MAIN'
+ MASTER_CLEAR_NOTIFICATION = 'android.intent.action.MASTER_CLEAR_NOTIFICATION'
+ TYCHO_ACTIVATE_SUPER_NETWORK = (
+ 'com.google.android.apps.tycho.ActionType.ACTIVATE_SUPER_NETWORK')
+
+
+class TelLiveProjectFiTest(TelephonyBaseTest):
+ def setup_class(self):
+ self.wifi_network_ssid = self.user_params.get(
+ "wifi_network_ssid") or self.user_params.get(
+ "wifi_network_ssid_2g") or self.user_params.get(
+ "wifi_network_ssid_5g")
+ self.wifi_network_pass = self.user_params.get(
+ "wifi_network_pass") or self.user_params.get(
+ "wifi_network_pass_2g") or self.user_params.get(
+ "wifi_network_ssid_5g")
+
+ def _add_google_account(self, ad, retries=3):
+ for _ in range(3):
+ ad.ensure_screen_on()
+ output = ad.adb.shell(
+ 'am instrument -w -e account "%s@gmail.com" -e password '
+ '"%s" -e sync true -e wait-for-checkin false '
+ 'com.google.android.tradefed.account/.AddAccount' %
+ (ad.user_account, ad.user_password))
+ if "result=SUCCESS" in output:
+ ad.log.info("google account is added successfully")
+ return True
+ ad.log.error("Fail to add google account due to %s", output)
+ return False
+
+ def _remove_google_account(self, ad, retries=3):
+ if not ad.is_apk_installed("com.google.android.tradefed.account"
+ ) and self.user_params.get("account_util"):
+ account_util = self.user_params["account_util"]
+ if isinstance(account_util, list):
+ account_util = account_util[0]
+ ad.log.info("Install account_util %s", account_util)
+ ad.ensure_screen_on()
+ ad.adb.install("-r %s" % account_util, timeout=180)
+ if not ad.is_apk_installed("com.google.android.tradefed.account"):
+ ad.log.error(
+ "com.google.android.tradefed.account is not installed")
+ return False
+ for _ in range(3):
+ ad.ensure_screen_on()
+ output = ad.adb.shell(
+ 'am instrument -w '
+ 'com.google.android.tradefed.account/.RemoveAccounts')
+ if "result=SUCCESS" in output:
+ ad.log.info("google account is removed successfully")
+ return True
+ ad.log.error("Fail to remove google account due to %s", output)
+ return False
+
+ def _install_account_util(self, ad):
+ account_util = self.user_params["account_util"]
+ if isinstance(account_util, list):
+ account_util = account_util[0]
+ ad.log.info("Install account_util %s", account_util)
+ ad.ensure_screen_on()
+ ad.adb.install("-r %s" % account_util, timeout=180)
+ time.sleep(3)
+ if not ad.is_apk_installed("com.google.android.tradefed.account"):
+ ad.log.info("com.google.android.tradefed.account is not installed")
+ return False
+ return True
+
+ def _account_registration(self, ad):
+ if hasattr(ad, "user_account"):
+ ad.exit_setup_wizard()
+ if not ad.is_apk_installed("com.google.android.tradefed.account"
+ ) and self.user_params.get(
+ "account_util"):
+ for _ in range(2):
+ if self._install_account_util(ad):
+ break
+ else:
+ ad.log.error(
+ "Fail to install com.google.android.tradefed.account")
+ return False
+ ad.force_stop_apk(_TYCHO_PKG)
+ if not ensure_wifi_connected(self.log, ad, self.wifi_network_ssid,
+ self.wifi_network_pass):
+ ad.log.error("Failed to connect to wifi")
+ return False
+ ad.log.info("Add google account")
+ if not self._add_google_account(ad):
+ ad.log.error("Failed to add google account")
+ return False
+ ad.adb.shell(
+ 'am instrument -w -e account "%s@gmail.com" -e password '
+ '"%s" -e sync true -e wait-for-checkin false '
+ 'com.google.android.tradefed.account/.AddAccount' %
+ (ad.user_account, ad.user_password))
+ ad.log.info("Enable and activate tycho apk")
+ ad.adb.shell('pm enable %s' % _TYCHO_PKG)
+ self.activate_fi_account(ad)
+ if not self.check_project_fi_activated(ad):
+ ad.log.error("Fail to activate Fi account")
+ return False
+ elif "Fi Network" in ad.adb.getprop("gsm.sim.operator.alpha"):
+ ad.log.error("Google account is not provided for Fi Network")
+ return False
+ if not ensure_phone_subscription(self.log, ad):
+ ad.log.error("Unable to find a valid subscription!")
+ return False
+ refresh_droid_config(self.log, ad)
+ return True
+
+ def start_service(self, ad, package, service_id, extras, action_type):
+ """Starts the specified service.
+
+ Args:
+ ad: (android_device.AndroidDevice) device to start activity on
+ package: (str) the package to start the service from
+ service_id: (str) service to start
+ extras: (dict) extras needed to specify with the activity id
+ action_type: The action type id to create the intent
+ """
+ ad.log.info('Starting service %s/.%s.', package, service_id)
+ intent = ad.droid.makeIntent(action_type, None, None, extras, [
+ 'android.intent.category.DEFAULT'
+ ], package, package + '.' + service_id, _INTENT_FLAGS)
+ ad.droid.startServiceIntent(intent)
+
+ def start_activity(self, ad, package, activity_id, extras=None):
+ """Starts the specified activity.
+
+ Args:
+ ad: (android_device.AndroidDevice) device to start activity on
+ package: (str) the package to start
+ activity_id: (str) activity to start
+ extras: (dict) extras needed to specify with the activity id
+ """
+ ad.log.info('Starting activity %s/.%s.', package, activity_id)
+ intent = ad.droid.makeIntent(ActionTypeId.MAIN, None, None, extras, [
+ 'android.intent.category.LAUNCHER'
+ ], package, package + '.' + activity_id, _INTENT_FLAGS)
+ ad.droid.startActivityIntent(intent, False)
+
+ def activate_fi_account(self, ad):
+ """Start Tycho InitActivity.
+
+ For in-app Tycho activition (post-SUW tests), Tycho does not
+ automatically trigger OMADM process. This method is used to start
+ Tycho InitActivity before launching super network activation.
+
+ The device will finally stay on Sprint network if everything goes well.
+
+ Args:
+ ad: Android device need to start Tycho InitActivity.
+ """
+ extra = {'in_setup_wizard': False, 'force_show_account_chooser': False}
+ self.start_activity(ad, _TYCHO_PKG, TychoClassId.INIT_ACTIVITY, extra)
+ for _ in range(30):
+ ad.send_keycode("WAKEUP")
+ time.sleep(1)
+ current_window = ad.get_my_current_focus_window()
+ log_screen_shot(ad, self.test_name)
+ if 'SwitchConfirmDialogActivity' in current_window:
+ ad.log.info("In Switch Confirmation Dialog")
+ if ad.adb.getprop("ro.build.version.release")[0] not in ("8", "O"):
+ ad.send_keycode("TAB")
+ ad.send_keycode("TAB")
+ ad.send_keycode("ENTER")
+ time.sleep(10)
+ elif 'tycho.InitActivity' in current_window:
+ ad.log.info("In Tycho InitActivity")
+ ad.send_keycode("TAB")
+ ad.send_keycode("TAB")
+ ad.send_keycode("ENTER")
+ time.sleep(10)
+
+ elif 'tycho.AccountChooserActivity' in current_window:
+ ad.send_keycode("ENTER")
+ else:
+ ad.log.info("Finished activation process")
+ return
+
+ def check_project_fi_activated(self, ad):
+ for _ in range(20):
+ if is_sim_ready(self.log, ad) and (
+ ad.droid.telephonyGetSimOperatorName() == "Fi Network"):
+ ad.log.info("SIM state is READY, SIM operator is Fi")
+ return True
+ time.sleep(5)
+
+ def start_tycho_activation(self, ad):
+ """Start the Tycho client and register to cellular network.
+
+ Starts Tycho within SUW:
+ - Tycho is expected to follow the in-SUW work flow:
+ - Tycho will perform TychoInit, handshake to server,
+ account configuration, etc
+ - If successful, Tycho will trigger a switch to Sprint Network
+ - If successful, Tycho will start OMA-DM activation sessions
+
+ The device will finally stay on Sprint network if everything goes well.
+
+ Args:
+ ad: Android device need to start Tycho activation.
+ """
+ extra = {'device_setup': True, 'has_account': True}
+ self.start_activity(ad, _TYCHO_PKG, TychoClassId.CARRIER_SETUP, extra)
+
+ def start_super_network_activation(self, ad):
+ """Start the Super-Network activation.
+
+ For in-app Tycho activition (post-SUW tests), this method starts
+ super-network activation after Tycho is initialized.
+
+ The device will finally stay on Sprint network if everything goes well.
+
+ Args:
+ ad: Android device need to start Tycho super network activation.
+ """
+ extra = {'in_setup_wizard': False, 'is_interactive': True}
+ self.start_service(ad, _TYCHO_PKG,
+ TychoClassId.ACTIVATE_SUPER_NETWORK_SERVICE, extra,
+ ActionTypeId.TYCHO_ACTIVATE_SUPER_NETWORK)
+
+ def get_active_carrier(self, ad):
+ """Gets the active carrier profile value from the device.
+
+ Args:
+ ad: An AndroidDevice Object.
+
+ Returns:
+ (string) A key from the CARRIER_TO_MCC_MNC map representing the
+ active carrier.
+
+ Raises:
+ KeyError: when an mcc_mnc code reported by the device is not a
+ recognized Fi partner carrier.
+ """
+ mcc_mnc = ad.droid.telephonyGetSimOperator()
+ if not mcc_mnc:
+ return "UNKNOWN"
+ try:
+ return operator_name_from_plmn_id(mcc_mnc)
+ except KeyError:
+ ad.log.error('Unknown Mobile Country Code/Mobile Network Code %s',
+ mcc_mnc)
+ raise
+
+ def switch_sim(self, ad):
+ """Requests switch between physical sim and esim.
+
+ Args:
+ ad: An AndroidDevice Object.
+ timeout: (optional -- integer) the number of seconds in which a
+ switch should be completed.
+
+ Raises:
+ Error: whenever a device is not set to the desired carrier within
+ the timeout window.
+ """
+ old_sim_operator = ad.droid.telephonyGetSimOperatorName()
+ ad.log.info("Before SIM switch, SIM operator = %s", old_sim_operator)
+ send_dialer_secret_code(ad, "794824746")
+ time.sleep(10)
+ new_sim_operator = ad.droid.telephonyGetSimOperatorName()
+ ad.log.info("After SIM switch, SIM operator = %s", new_sim_operator)
+ refresh_droid_config(self.log, ad)
+ return old_sim_operator != new_sim_operator
+
+ def set_active_carrier(self,
+ ad,
+ carrier,
+ timeout=_MAX_WAIT_TIME,
+ check_interval=10):
+ """Requests an active carrier to be set on the device sim.
+
+ If switching to a different carrier, after the switch is completed
+ auto-switching will be disabled. To re-enable, call enable_auto_switching.
+
+ Args:
+ ad: An AndroidDevice Object.
+ carrier: (carrier_constants.Carrier) Which carrier to switch to.
+ timeout: (optional -- integer) the number of seconds in which a
+ switch should be completed.
+
+ Raises:
+ Error: whenever a device is not set to the desired carrier within
+ the timeout window.
+ """
+ # If there's no need to switch, then don't.
+ max_time = timeout
+ while max_time >= 0:
+ if self.is_ready_to_make_carrier_switch(ad):
+ break
+ time.sleep(check_interval)
+ max_time -= check_interval
+ else:
+ ad.log.error("Device stays in carrier switch lock state")
+ return False
+ if carrier == CARRIER_AUTO:
+ send_dialer_secret_code(ad, _CARRIER_DIALER_CODE_LOOKUP[carrier])
+ return True
+ old_carrier = self.get_active_carrier(ad)
+ if carrier == old_carrier:
+ ad.log.info('Already on %s, so no need to switch', carrier)
+ return True
+
+ # Start switch on device, using events to verify that the switch starts.
+ ad.log.info('Initiating unsolicited switch from %s to %s.',
+ old_carrier, carrier)
+ send_dialer_secret_code(ad, _CARRIER_DIALER_CODE_LOOKUP[carrier])
+ return self.wait_for_carrier_switch_completed(
+ ad, carrier, timeout=timeout, check_interval=check_interval)
+
+ def is_switching_silent(self, ad):
+ """Checks if Tycho switching controller is in silent mode.
+
+ Note that silent mode is a sign of airplane mode, not of a switching lock.
+
+ Args: ad: An AndroidDevice Object.
+
+ Returns:
+ A Boolean True if the preferences file reports True, False otherwise.
+ """
+ return "isInSilentMode\" value=\"true" in ad.adb.shell(
+ "cat %s | grep isInSilentMode" % _SWITCHING_PREF_FILE,
+ ignore_status=True)
+
+ def is_switching_locked(self, ad):
+ """Checks if Tycho switching controller is locked.
+
+ Args: ad: An AndroidDevice Object.
+
+ Returns:
+ A Boolean True if the switching controller is locked for any reason,
+ False otherwise.
+ """
+ return "switchingInProgress\" value=\"true" in ad.adb.shell(
+ "cat %s | grep switchingInProgress" % _SWITCHING_PREF_FILE)
+
+ def is_ready_to_make_carrier_switch(self, ad):
+ """Checks if device is ready to make carrier switch.
+
+ Args:
+ ad: An AndroidDevice Object.
+
+ Returns:
+ A Boolean True if it is ready to make switch, False otherwise.
+ """
+ # Check Tycho switching controller states.
+ if self.is_switching_silent(ad):
+ ad.log.info(
+ "Cannot make carrier switch: SwitchingController is in silent "
+ "mode!")
+ return False
+ if self.is_switching_locked(ad):
+ ad.log.info(
+ "Cannot make carrier switch: SwitchingController is locked!")
+ return False
+ if self.is_carrier_switch_in_progress(ad):
+ ad.log.info("Cannot make carrier switch: Switch in progress!")
+ return False
+ return True
+
+ def is_carrier_switch_in_progress(self, ad):
+ """Checks if Tycho says that a switch is currently in progress.
+
+ Args:
+ ad: An AndroidDevice Object.
+
+ Returns:
+ A Boolean True if the preferences file reports True, False otherwise.
+ """
+ switching_preferences = ad.adb.shell("cat %s" % _SWITCHING_PREF_FILE)
+ return 'InProgress\" value=\"true' in switching_preferences
+
+ def check_network_carrier(self, ad, carrier):
+ current_carrier = self.get_active_carrier(ad)
+ ad.log.info("Current network carrier is %s", current_carrier)
+ is_in_switch = self.is_carrier_switch_in_progress(ad)
+ ad.log.info("Device in carrier switch progress mode")
+ return current_carrier == carrier and is_in_switch
+
+ def wait_for_carrier_switch_completed(self,
+ ad,
+ carrier,
+ timeout=_MAX_WAIT_TIME,
+ check_interval=10):
+ """Wait for carrier switch to complete.
+
+ This function waits for a carrier switch to complete by monitoring the
+ Tycho switching controller preference file.
+
+ Args:
+ ad: An Android device object.
+ carrier: The target carrier network to switch to.
+ timeout: (integer) Time wait for switch to complete.
+
+ Return:
+ True or False for successful/unsuccessful switch.
+ """
+ check_args = [ad, carrier]
+ if wait_for_state(self.check_network_carrier, True, check_interval,
+ timeout, *check_args):
+ ad.log.info("Switched to %s successfully", carrier)
+ ad.send_keycode("ENTER")
+ return True
+ else:
+ ad.log.error("Carrier is %s. Fail to switch to %s",
+ self.get_active_carrier(ad), carrier)
+ return False
+
+ def operator_network_switch(self, ad, carrier):
+ if ad.droid.telephonyGetSimOperatorName() == "Fi Network":
+ for i in range(3):
+ if self.set_active_carrier(ad, carrier):
+ break
+ elif i == 2:
+ ad.log.error("Failed to switch to %s", carrier)
+ return False
+ if not ensure_phone_subscription(self.log, ad):
+ ad.log.error("Unable to find a valid subscription!")
+ return False
+ refresh_droid_config(self.log, ad)
+ return True
+
+ def network_switch_test(self, carrier):
+ tasks = [(self.operator_network_switch, [ad, carrier])
+ for ad in self.android_devices]
+ if not multithread_func(self.log, tasks):
+ abort_all_tests(self.log,
+ "Unable to switch to network %s" % carrier)
+ return True
+
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="4d92318e-4980-471a-882b-3136c5dda384")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_project_fi_account_activation(self):
+ """Test activate Fi account.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ tasks = [(self._account_registration, [ad])
+ for ad in self.android_devices]
+ if not multithread_func(self.log, tasks):
+ abort_all_tests(self.log, "Unable to activate Fi account!")
+ return True
+
+ @test_tracker_info(uuid="6bfbcc1d-e318-4964-bf36-5b82f086860d")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_switch_to_tmobile_network(self):
+ """Test switch to tmobile network.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ return self.network_switch_test(CARRIER_TMO)
+
+ @test_tracker_info(uuid="4f27944d-f3c5-423d-b0c5-5c66dbb98376")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_switch_to_sprint_network(self):
+ """Test switch to tmobile network.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ return self.network_switch_test(CARRIER_SPT)
+
+ @test_tracker_info(uuid="5f30c9bd-b79e-4805-aa46-7855ed9023f0")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_switch_to_uscc_network(self):
+ """Test switch to tmobile network.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ return self.network_switch_test(CARRIER_USCC)
+
+ @test_tracker_info(uuid="0b062751-d59d-420e-941e-3ffa02aea0d5")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_switch_to_auto_network(self):
+ """Test switch to auto network selection.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ return self.network_switch_test(CARRIER_AUTO)
+
+ @test_tracker_info(uuid="13c5f080-69bf-42fd-86ed-c67b1984c347")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_switch_between_sim(self):
+ """Test switch between physical sim and esim.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ for ad in self.android_devices:
+ self.switch_sim(ad)
+
+ @test_tracker_info(uuid="")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_remove_google_account(self):
+ for ad in self.android_devices:
+ self._remove_google_account(ad)
+
+
+""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveRebootStressTest.py b/acts/tests/google/tel/live/TelLiveRebootStressTest.py
index bcdbe66..70c2f77 100644
--- a/acts/tests/google/tel/live/TelLiveRebootStressTest.py
+++ b/acts/tests/google/tel/live/TelLiveRebootStressTest.py
@@ -19,67 +19,59 @@
import collections
import time
+
+from acts import signals
from acts.test_decorators import test_tracker_info
from acts.controllers.sl4a_lib.sl4a_types import Sl4aNetworkInfo
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
-from acts.test_utils.tel.tel_defines import AOSP_PREFIX
from acts.test_utils.tel.tel_defines import CAPABILITY_VOLTE
from acts.test_utils.tel.tel_defines import CAPABILITY_VT
from acts.test_utils.tel.tel_defines import CAPABILITY_WFC
from acts.test_utils.tel.tel_defines import CAPABILITY_OMADM
-from acts.test_utils.tel.tel_defines import DATA_STATE_CONNECTED
from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_PROVISIONING
from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
from acts.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT
from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_CRASH
-from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
-from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
-from acts.test_utils.tel.tel_subscription_utils import \
- get_incoming_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import \
- get_outgoing_voice_sub_id
from acts.test_utils.tel.tel_lookup_tables import device_capabilities
from acts.test_utils.tel.tel_lookup_tables import operator_capabilities
from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
from acts.test_utils.tel.tel_test_utils import get_model_name
from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import multithread_func
+from acts.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
+from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
+from acts.test_utils.tel.tel_test_utils import is_sim_locked
+from acts.test_utils.tel.tel_test_utils import power_off_sim
+from acts.test_utils.tel.tel_test_utils import power_on_sim
+from acts.test_utils.tel.tel_test_utils import reboot_device
from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
from acts.test_utils.tel.tel_test_utils import verify_http_connection
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
from acts.test_utils.tel.tel_test_utils import trigger_modem_crash
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts.test_utils.tel.tel_test_utils import trigger_modem_crash_by_modem
+from acts.test_utils.tel.tel_test_utils import unlock_sim
+from acts.test_utils.tel.tel_test_utils import wait_for_state
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import \
- phone_setup_iwlan_cellular_preferred
from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import phone_idle_3g
-from acts.test_utils.tel.tel_voice_utils import phone_idle_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
from acts.test_utils.tel.tel_video_utils import video_call_setup_teardown
from acts.test_utils.tel.tel_video_utils import phone_setup_video
from acts.test_utils.tel.tel_video_utils import \
is_phone_in_call_video_bidirectional
+from acts.utils import get_current_epoch_time
from acts.utils import rand_ascii_str
@@ -100,38 +92,100 @@
self.android_devices) > 1 else None
self.dut_model = get_model_name(self.dut)
self.dut_operator = get_operator_name(self.log, self.dut)
+ self.dut_capabilities = set(
+ device_capabilities.get(
+ self.dut_model, device_capabilities["default"])) & set(
+ operator_capabilities.get(
+ self.dut_operator, operator_capabilities["default"]))
+ self.user_params["check_crash"] = False
+ self.skip_reset_between_cases = False
- def _check_provisioning(self):
- if (CAPABILITY_OMADM in device_capabilities[self.dut_model] and
- CAPABILITY_OMADM in operator_capabilities[self.dut_operator]):
- self.log.info("Check Provisioning bit")
- if not self.dut.droid.imsIsVolteProvisionedOnDevice():
- self.log.error("{}: VoLTE Not Provisioned on the Platform".
- format(self.dut.serial))
- return False
- return True
+ def setup_class(self):
+ TelephonyBaseTest.setup_class(self)
+ methods = [("check_subscription",
+ self._check_subscription), ("check_data",
+ self._check_data),
+ ("check_call_setup_teardown",
+ self._check_call_setup_teardown), ("check_sms",
+ self._check_sms),
+ ("check_mms", self._check_mms), ("check_lte_data",
+ self._check_lte_data),
+ ("check_volte",
+ self._check_volte), ("check_vt",
+ self._check_vt), ("check_wfc",
+ self._check_wfc),
+ ("check_3g", self._check_3g), ("check_tethering",
+ self._check_tethering)]
+ self.testing_methods = []
+ for name, func in methods:
+ check_result = func()
+ self.dut.log.info("%s is %s before tests start", name,
+ check_result)
+ if check_result:
+ self.testing_methods.append((name, func))
+ self.log.info("Working features: %s", self.testing_methods)
+
+ def feature_validator(self, **kwargs):
+ failed_tests = []
+ for name, func in self.testing_methods:
+ if kwargs.get(name, True):
+ if not func():
+ self.log.error("%s failed", name)
+ failed_tests.append(name)
+ else:
+ self.log.info("%s succeeded", name)
+ if failed_tests:
+ self.log.error("%s failed", failed_tests)
+ return failed_tests
+
+ def _check_subscription(self):
+ if not ensure_phone_subscription(self.log, self.dut):
+ self.dut.log.error("Subscription check failed")
+ return False
+ else:
+ return True
def _check_provision(self):
- elapsed_time = 0
- while (elapsed_time < MAX_WAIT_TIME_PROVISIONING):
- if self._check_provisioning():
- return True
+ if CAPABILITY_OMADM in self.dut_capabilities:
+ if not wait_for_state(self.dut.droid.imsIsVolteProvisionedOnDevice,
+ True):
+ self.log.error("VoLTE provisioning check fails.")
+ return False
else:
- time.sleep(CHECK_INTERVAL)
- elapsed_time += CHECK_INTERVAL
- self.log.error("Provisioning fail.")
+ return True
return False
def _clear_provisioning(self):
- if (CAPABILITY_OMADM in device_capabilities[self.dut_model] and
- CAPABILITY_OMADM in operator_capabilities[self.dut_operator]):
+ if CAPABILITY_OMADM in self.dut_capabilities:
self.log.info("Clear Provisioning bit")
self.dut.droid.imsSetVolteProvisioning(False)
return True
def _check_call_setup_teardown(self):
- if not call_setup_teardown(self.log, self.dut, self.ad_reference):
- self.log.error("Phone Call Failed.")
+ if not call_setup_teardown(self.log, self.dut, self.ad_reference,
+ self.dut):
+ self.log.error("Phone call test failed.")
+ return False
+ return True
+
+ def _check_sms(self):
+ if not sms_send_receive_verify(self.log, self.dut, self.ad_reference,
+ [rand_ascii_str(180)]):
+ self.log.error("SMS test failed")
+ return False
+ return True
+
+ def _check_mms(self):
+ message_array = [("Test Message", rand_ascii_str(180), None)]
+ if not mms_send_receive_verify(self.log, self.dut, self.ad_reference,
+ message_array):
+ self.log.error("MMS test failed")
+ return False
+ return True
+
+ def _check_data(self):
+ if not verify_http_connection(self.log, self.dut):
+ self.log.error("Data connection is not available.")
return False
return True
@@ -153,7 +207,7 @@
return True
def _check_volte(self):
- if (CAPABILITY_VOLTE in operator_capabilities[self.dut_operator]):
+ if CAPABILITY_VOLTE in self.dut_capabilities:
self.log.info("Check VoLTE")
if not phone_setup_volte(self.log, self.dut):
self.log.error("Failed to setup VoLTE.")
@@ -171,10 +225,10 @@
return True
def _check_vt(self):
- if (CAPABILITY_VT in operator_capabilities[self.dut_operator]):
+ if CAPABILITY_VT in self.dut_capabilities:
self.log.info("Check VT")
if not phone_setup_video(self.log, self.dut):
- self.log.error("Failed to setup VT.")
+ self.dut.log.error("Failed to setup VT.")
return False
time.sleep(5)
if not video_call_setup_teardown(
@@ -190,7 +244,7 @@
return True
def _check_wfc(self):
- if (CAPABILITY_WFC in operator_capabilities[self.dut_operator]):
+ if CAPABILITY_WFC in self.dut_capabilities:
self.log.info("Check WFC")
if not phone_setup_iwlan(
self.log, self.dut, True, WFC_MODE_WIFI_PREFERRED,
@@ -228,17 +282,25 @@
def _check_tethering(self):
self.log.info("Check tethering")
- if not self.dut.droid.carrierConfigIsTetheringModeAllowed(
- TETHERING_MODE_WIFI,
- MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK):
- self.log.error("Tethering Entitlement check failed.")
- return False
+ for i in range(3):
+ try:
+ if not self.dut.droid.carrierConfigIsTetheringModeAllowed(
+ TETHERING_MODE_WIFI,
+ MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK):
+ self.log.error("Tethering Entitlement check failed.")
+ if i == 2: return False
+ time.sleep(10)
+ except Exception as e:
+ if i == 2:
+ self.dut.log.error(e)
+ return False
+ time.sleep(10)
if not wifi_tethering_setup_teardown(
self.log,
self.dut, [self.ad_reference],
check_interval=5,
check_iteration=1):
- self.log.error("Tethering Failed.")
+ self.log.error("Tethering check failed.")
return False
return True
@@ -266,7 +328,7 @@
return False
return True
- def _telephony_monitor_test(self):
+ def _telephony_monitor_test(self, negative_test=False):
"""
Steps -
1. Reboot the phone
@@ -284,57 +346,110 @@
Returns:
True is pass, False if fail.
"""
- # Reboot
+ self.number_of_devices = 2
ads = self.android_devices
+ # Ensure apk is running/not running
+ monitor_apk = None
+ for apk in ("com.google.telephonymonitor",
+ "com.google.android.connectivitymonitor"):
+ if ads[0].is_apk_installed(apk):
+ ads[0].log.info("apk %s is installed", apk)
+ monitor_apk = apk
+ break
+ if not monitor_apk:
+ ads[0].log.info(
+ "ConnectivityMonitor|TelephonyMonitor is not installed")
+ return False
+
ads[0].adb.shell(
"am start -n com.android.settings/.DevelopmentSettings",
ignore_status=True)
- ads[0].log.info("reboot!")
- ads[0].reboot()
- ads[0].log.info("wait %d secs for radio up." % WAIT_TIME_AFTER_REBOOT)
- time.sleep(WAIT_TIME_AFTER_REBOOT)
+ cmd = "setprop persist.radio.enable_tel_mon user_enabled"
+ ads[0].log.info(cmd)
+ ads[0].adb.shell(cmd)
- # Ensure apk is running
- if not ads[0].is_apk_running("com.google.telephonymonitor"):
- ads[0].log.info("TelephonyMonitor is not running, start it now")
- ads[0].adb.shell(
- 'am broadcast -a '
- 'com.google.gservices.intent.action.GSERVICES_OVERRIDE -e '
- '"ce.telephony_monitor_enable" "true"')
+ if not ads[0].is_apk_running(monitor_apk):
+ ads[0].log.info("%s is not running", monitor_apk)
+ # Reboot
+ ads = self.android_devices
+ ads[0].log.info("reboot to bring up %s", monitor_apk)
+ reboot_device(ads[0])
+ for i in range(30):
+ if ads[0].is_apk_running(monitor_apk):
+ ads[0].log.info("%s is running after reboot", monitor_apk)
+ break
+ elif i == 19:
+ ads[0].log.error("%s is not running after reboot",
+ monitor_apk)
+ return False
+ else:
+ ads[0].log.info(
+ "%s is not running after reboot. Wait and check again",
+ monitor_apk)
+ time.sleep(30)
- # Setup Phone Call
- caller_number = ads[0].cfg['subscription'][get_outgoing_voice_sub_id(
- ads[0])]['phone_num']
- callee_number = ads[1].cfg['subscription'][get_incoming_voice_sub_id(
- ads[1])]['phone_num']
- tasks = [(phone_setup_voice_general, (self.log, ads[0])),
- (phone_setup_voice_general, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ ads[0].adb.shell(
+ "am start -n com.android.settings/.DevelopmentSettings",
+ ignore_status=True)
+ monitor_setting = ads[0].adb.getprop("persist.radio.enable_tel_mon")
+ ads[0].log.info("radio.enable_tel_mon setting is %s", monitor_setting)
+ expected_monitor_setting = "disabled" if negative_test else "user_enabled"
+ cmd = "setprop persist.radio.enable_tel_mon %s" % (
+ expected_monitor_setting)
+ if monitor_setting != expected_monitor_setting:
+ ads[0].log.info(cmd)
+ ads[0].adb.shell(cmd)
- if not initiate_call(ads[0].log, ads[0], callee_number):
- ads[0].log.error("Phone was unable to initate a call")
- return False
- if not wait_and_answer_call(self.log, ads[1], caller_number):
- ads[0].log.error("wait_and_answer_call failed")
+ if not call_setup_teardown(
+ self.log, ads[0], ads[1], ad_hangup=None,
+ wait_time_in_call=10):
+ self.log.error("Call setup failed")
return False
# Modem SSR
time.sleep(5)
- ads[1].log.info("Triggering ModemSSR")
- ads[1].adb.shell(
- "echo restart > /sys/kernel/debug/msm_subsys/modem",
- ignore_status=True)
- time.sleep(60)
-
- # Parse logcat for UI notification
- if ads[0].search_logcat("Bugreport notification title Call Drop:"):
- ads[0].log.info("User got the Call Drop Notification")
+ ads[0].log.info("Triggering ModemSSR")
+ if (not ads[0].is_apk_installed("com.google.mdstest")
+ ) or ads[0].adb.getprop("ro.build.version.release")[0] in (
+ "8", "O", "7", "N") or self.dut.model in ("angler", "bullhead",
+ "sailfish",
+ "marlin"):
+ trigger_modem_crash(self.dut)
else:
- ads[0].log.error("User didn't get Call Drop Notification in 1 min")
- return False
- return True
+ trigger_modem_crash_by_modem(self.dut)
+
+ try:
+ if ads[0].droid.telecomIsInCall():
+ ads[0].log.info("Still in call after call drop trigger event")
+ return False
+ else:
+ reasons = self.dut.search_logcat(
+ "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause")
+ if reasons:
+ ads[0].log.info(reasons[-1]["log_message"])
+ except Exception as e:
+ ads[0].log.error(e)
+ # Parse logcat for UI notification
+ result = True
+ if not negative_test:
+ if ads[0].search_logcat("Bugreport notification title Call Drop:"):
+ ads[0].log.info(
+ "User got Call Drop Notification with TelephonyMonitor on")
+ else:
+ ads[0].log.error(
+ "User didn't get Call Drop Notify with TelephonyMonitor on"
+ )
+ result = False
+ else:
+ if ads[0].search_logcat("Bugreport notification title Call Drop:"):
+ ads[0].log.error("User got the Call Drop Notification with "
+ "TelephonyMonitor/ConnectivityMonitor off")
+ result = False
+ else:
+ ads[0].log.info("User still get Call Drop Notify with "
+ "TelephonyMonitor/ConnectivityMonitor off")
+ reboot_device(ads[0])
+ return result
def _reboot_stress_test(self, **kwargs):
"""Reboot Reliability Test
@@ -356,62 +471,63 @@
Returns:
True is pass, False if fail.
"""
- CHECK_INTERVAL = 10
-
+ self.number_of_devices = 2
toggle_airplane_mode(self.log, self.dut, False)
phone_setup_voice_general(self.log, self.ad_reference)
fail_count = collections.defaultdict(int)
test_result = True
- test_method_mapping = {
- "check_provision": self._check_provision,
- "check_call_setup_teardown": self._check_call_setup_teardown,
- "check_lte_data": self._check_lte_data,
- "check_volte": self._check_volte,
- "check_wfc": self._check_wfc,
- "check_3g": self._check_3g,
- "check_tethering": self._check_tethering,
- "check_data_roaming": self._check_data_roaming_status,
- "clear_provision": self._clear_provisioning
- }
- for kwarg in kwargs:
- if kwarg not in test_method_mapping:
- self.log.error("method %s is not supported" % method)
-
- required_methods = []
- for method in test_method_mapping.keys():
- if method in kwargs: required_methods.append(method)
for i in range(1, self.stress_test_number + 1):
- self.log.info("Reboot Stress Test {} Iteration: <{}> / <{}>".
- format(self.test_name, i, self.stress_test_number))
-
- self.log.info("{} reboot!".format(self.dut.serial))
- self.dut.reboot()
+ self.log.info("Reboot Stress Test %s Iteration: <%s> / <%s>",
+ self.test_name, i, self.stress_test_number)
+ begin_time = get_current_epoch_time()
+ self.dut.log.info("Reboot")
+ reboot_device(self.dut)
self.log.info("{} wait {}s for radio up.".format(
self.dut.serial, WAIT_TIME_AFTER_REBOOT))
time.sleep(WAIT_TIME_AFTER_REBOOT)
- iteration_result = "pass"
- for check in required_methods:
- if not test_method_mapping[check]():
- fail_count[check] += 1
- iteration_result = "fail"
- self.log.info("Reboot Stress Test {} Iteration: <{}> / <{}> {}".
- format(self.test_name, i, self.stress_test_number,
- iteration_result))
+ failed_tests = self.feature_validator(**kwargs)
+ for test in failed_tests:
+ fail_count[test] += 1
- # TODO: Check if crash happens.
+ crash_report = self.dut.check_crash_report(
+ "%s_%s" % (self.test_name, i),
+ begin_time,
+ log_crash_report=True)
+ if crash_report:
+ fail_count["crashes"] += 1
+ if failed_tests or crash_report:
+ self.log.error(
+ "Reboot Stress Test Iteration <%s> / <%s> FAIL",
+ i,
+ self.stress_test_number,
+ )
+ self._take_bug_report("%s_%s" % (self.test_name, i),
+ begin_time)
+ else:
+ self.log.info(
+ "Reboot Stress Test Iteration <%s> / <%s> PASS",
+ i,
+ self.stress_test_number,
+ )
+ self.log.info("Total failure count: %s", list(fail_count))
for failure, count in fail_count.items():
if count:
- self.log.error("{} {} failures in {} iterations".format(
- count, failure, self.stress_test_number))
+ self.log.error("%s %s failures in %s iterations", count,
+ failure, self.stress_test_number)
test_result = False
return test_result
- def _crash_recovery_test(self, **kwargs):
+ def _crash_recovery_test(self, process="modem", **kwargs):
"""Crash Recovery Test
Arguments:
+ process: the process to be killed. For example:
+ "rild", "netmgrd", "com.android.phone", "imsqmidaemon",
+ "imsdatadaemon", "ims_rtp_daemon",
+ "com.android.ims.rcsservice", "system_server", "cnd",
+ "modem"
check_lte_data: whether to check the LTE data.
check_volte: whether to check Voice over LTE.
check_vt: whether to check VT
@@ -423,73 +539,75 @@
Returns:
True is pass, False if fail.
"""
- CHECK_INTERVAL = 10
+ self.number_of_devices = 2
- toggle_airplane_mode(self.log, self.dut, False)
- phone_setup_voice_general(self.log, self.ad_reference)
- fail_count = collections.defaultdict(int)
- test_result = True
- test_method_mapping = {
- "check_provision": self._check_provision,
- "check_call_setup_teardown": self._check_call_setup_teardown,
- "check_lte_data": self._check_lte_data,
- "check_volte": self._check_volte,
- "check_vt": self._check_vt,
- "check_wfc": self._check_wfc,
- "check_3g": self._check_3g,
- "check_tethering": self._check_tethering,
- "check_data_roaming": self._check_data_roaming_status,
- "clear_provision": self._clear_provisioning
- }
- for kwarg in kwargs:
- if kwarg not in test_method_mapping:
- self.log.error("method %s is not supported" % method)
+ if process == "modem":
+ self.user_params["check_crash"] = False
+ self.dut.log.info("Crash modem from kernal")
+ trigger_modem_crash(self.dut)
+ elif process == "modem-crash":
+ self.user_params["check_crash"] = False
+ self.dut.log.info("Crash modem from modem")
+ trigger_modem_crash_by_modem(self.dut)
+ elif process == "sim":
+ self.user_params["check_crash"] = True
+ sub_id = get_outgoing_voice_sub_id(self.dut)
+ slot_index = get_slot_index_from_subid(self.log, self.dut, sub_id)
+ if not power_off_sim(self.dut, slot_index):
+ self.dut.log.warning("Fail to power off SIM")
+ raise signals.TestSkip("Power cycle SIM not working")
+ if not power_on_sim(self.dut, slot_index):
+ self.dut.log.error("Fail to power on SIM slot")
+ setattr(self.dut, "reboot_to_recover", True)
+ return False
+ else:
+ self.dut.log.info("Crash recover test by killing process <%s>",
+ process)
+ process_pid = self.dut.adb.shell("pidof %s" % process)
+ self.dut.log.info("Pid of %s is %s", process, process_pid)
+ if not process_pid:
+ self.dut.log.error("Process %s not running", process)
+ return False
+ if process in ("netd", "system_server"):
+ self.dut.stop_services()
+ self.dut.adb.shell("kill -9 %s" % process_pid, ignore_status=True)
+ self.dut.log.info("Wait %s sec for process %s come up.",
+ WAIT_TIME_AFTER_CRASH, process)
+ time.sleep(WAIT_TIME_AFTER_CRASH)
+ if process in ("netd", "system_server"):
+ self.dut.ensure_screen_on()
+ try:
+ self.dut.start_services(self.dut.skip_sl4a)
+ except Exception as e:
+ self.dut.log.warning(e)
+ process_pid_new = self.dut.adb.shell("pidof %s" % process)
+ if process_pid == process_pid_new:
+ self.dut.log.error(
+ "Process %s has the same pid: old:%s new:%s", process,
+ process_pid, process_pid_new)
+ try:
+ self.dut.droid.logI("Start testing after restarting %s" % process)
+ except Exception:
+ self.dut.ensure_screen_on()
+ self.dut.start_services(self.dut.skip_sl4a)
+ if is_sim_locked(self.dut):
+ unlock_sim(self.dut)
- required_methods = []
- for method in test_method_mapping.keys():
- if method in kwargs: required_methods.append(method)
-
- process_list = ("rild", "netmgrd", "com.android.phone", "imsqmidaemon",
- "imsdatadaemon", "ims_rtp_daemon", "netd",
- "com.android.ims.rcsservice", "system_server", "cnd",
- "modem")
- for service in process_list:
- iteration_result = "pass"
- self.log.info("Crash Recover Test for Process <%s>" % service)
- self.log.info("%s kill Process %s" % (self.dut.serial, service))
- if service == "modem":
- trigger_modem_crash(self.log, self.dut)
- time.sleep(WAIT_TIME_AFTER_CRASH * 2)
- else:
- process_pid = self.dut.adb.shell("pidof %s" % service)
- self.log.info("%s is the pidof %s" % (process_pid, service))
- if not process_pid:
- self.dut.log.error("Process %s not running" % service)
- iteration_result = "fail"
- if service == "netd" or service == "system_server":
- self.dut.stop_services()
- self.dut.adb.shell(
- "kill -9 %s" % process_pid, ignore_status=True)
- self.log.info("%s wait %d sec for radio up." %
- (self.dut.serial, WAIT_TIME_AFTER_CRASH))
- time.sleep(WAIT_TIME_AFTER_CRASH)
- if service == "netd" or service == "system_server":
- self.dut.start_services()
- process_pid_new = self.dut.adb.shell("pidof %s" % service)
- if process_pid == process_pid_new:
- self.log.error("kill failed old:%s new:%s" %
- (process_pid, process_pid_new))
- for check in required_methods:
- if not test_method_mapping[check]():
- fail_count[check] += 1
- iteration_result = "fail"
- self.log.info("Crash Recover Test for Process <%s> %s" %
- (service, iteration_result))
- for failure, count in fail_count.items():
- if count:
- self.log.error("%d %s failures" % (count, failure))
- test_result = False
- return test_result
+ begin_time = get_current_epoch_time()
+ failed_tests = self.feature_validator(**kwargs)
+ crash_report = self.dut.check_crash_report(
+ self.test_name, begin_time, log_crash_report=True)
+ if failed_tests or crash_report:
+ if failed_tests:
+ self.dut.log.error("%s failed after %s restart", failed_tests,
+ process)
+ setattr(self.dut, "reboot_to_recover", True)
+ if crash_report:
+ self.dut.log.error("Crash %s found after %s restart",
+ crash_report, process)
+ return False
+ else:
+ return True
def _telephony_bootup_time_test(self, **kwargs):
"""Telephony Bootup Perf Test
@@ -505,6 +623,7 @@
Returns:
True is pass, False if fail.
"""
+ self.number_of_devices = 1
ad = self.dut
toggle_airplane_mode(self.log, ad, False)
if not phone_setup_volte(self.log, ad):
@@ -518,7 +637,7 @@
ad.log.info("Telephony Bootup Time Test %s Iteration: %d / %d",
self.test_name, i, self.stress_test_number)
ad.log.info("reboot!")
- ad.reboot()
+ reboot_device(ad)
iteration_result = "pass"
time.sleep(30)
@@ -626,47 +745,9 @@
check_data_roaming=False,
clear_provision=True)
- @test_tracker_info(uuid="39a822e5-0360-44ce-97c7-f75468eba8d7")
- @TelephonyBaseTest.tel_test_wrap
- def test_reboot_stress_without_clear_provisioning(self):
- """Reboot Reliability Test without Clear Provisioning
-
- Steps:
- 1. Reboot DUT.
- 2. Check Provisioning bit (if support provisioning)
- 3. Wait for DUT to camp on LTE, Verify Data.
- 4. Enable VoLTE, check IMS registration. Wait for DUT report VoLTE
- enabled, make VoLTE call. And verify VoLTE SMS.
- (if support VoLTE)
- 5. Connect WiFi, enable WiFi Calling, wait for DUT report WiFi
- Calling enabled and make a WFC call and verify SMS.
- Disconnect WiFi. (if support WFC)
- 6. Wait for DUT to camp on 3G, Verify Data.
- 7. Make CS call and verify SMS.
- 8. Verify Tethering Entitlement Check and Verify WiFi Tethering.
- 9. Check crashes.
- 10. Repeat Step 1~9 for N times.
-
- Expected Results:
- No crash happens in stress test.
-
- Returns:
- True is pass, False if fail.
- """
- return self._reboot_stress_test(
- check_provision=True,
- check_call_setup_teardown=True,
- check_lte_data=True,
- check_volte=True,
- check_wfc=True,
- check_3g=True,
- check_tethering=True,
- check_data_roaming=False,
- clear_provision=False)
-
@test_tracker_info(uuid="8b0e2c06-02bf-40fd-a374-08860e482757")
@TelephonyBaseTest.tel_test_wrap
- def test_reboot_stress_check_phone_call_only(self):
+ def test_reboot_stress(self):
"""Reboot Reliability Test
Steps:
@@ -682,31 +763,10 @@
Returns:
True is pass, False if fail.
"""
- return self._stress_test(
- check_provision=True, check_call_setup_teardown=True)
+ return self._reboot_stress_test()
- @test_tracker_info(uuid="6c243b53-379a-4cda-9848-84fcec4019bd")
- @TelephonyBaseTest.tel_test_wrap
- def test_reboot_stress_data_roaming(self):
- """Reboot Reliability Test
-
- Steps:
- 1. Reboot DUT.
- 8. Check the data connection
- 9. Check crashes.
- 10. Repeat Step 1~9 for N times. (before reboot, clear Provisioning
- bit if provisioning is supported)
-
- Expected Results:
- No crash happens in stress test.
-
- Returns:
- True is pass, False if fail.
- """
- return self._reboot_stress_test(check_data_roaming=True)
-
- @TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="109d59ff-a488-4a68-87fd-2d8d0c035326")
+ @TelephonyBaseTest.tel_test_wrap
def test_bootup_optimized_stress(self):
"""Bootup Optimized Reliability Test
@@ -735,13 +795,13 @@
"""
return self._telephony_bootup_time_test()
- @TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="08752fac-dbdb-4d5b-91f6-4ffc3a3ac6d6")
- def test_crash_recovery_functional(self):
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_modem(self):
"""Crash Recovery Test
Steps:
- 1. Crash multiple daemons/processes
+ 1. Crash modem
2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
Expected Results:
@@ -750,11 +810,203 @@
Returns:
True is pass, False if fail.
"""
- return self._crash_recovery_test(
- check_lte_data=True, check_volte=True, check_vt=True)
+ return self._crash_recovery_test(process="modem")
+ @test_tracker_info(uuid="ce5f4d63-7f3d-48b7-831d-2c1d5db60733")
@TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_crash_modem_from_modem(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash modem
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ if (not self.dut.is_apk_installed("com.google.mdstest")) or (
+ self.dut.adb.getprop("ro.build.version.release")[0] in
+ ("8", "O", "7", "N")) or self.dut.model in ("angler", "bullhead",
+ "sailfish", "marlin"):
+ raise signals.TestSkip(
+ "com.google.mdstest not installed or supported")
+ return self._crash_recovery_test(process="modem-crash")
+
+ @test_tracker_info(uuid="489284e8-77c9-4961-97c8-b6f1a833ff90")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_rild(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash rild
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="rild")
+
+ @test_tracker_info(uuid="e1b34b2c-99e6-4966-a11c-88cedc953b47")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_netmgrd(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash netmgrd
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="netmgrd")
+
+ @test_tracker_info(uuid="fa34f994-bc49-4444-9187-87691c94b4f4")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_phone(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash com.android.phone
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="com.android.phone")
+
+ @test_tracker_info(uuid="6f5a24bb-3cf3-4362-9675-36a6be90282f")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_imsqmidaemon(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash imsqmidaemon
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="imsqmidaemon")
+
+ @test_tracker_info(uuid="7a8dc971-054b-47e7-9e57-3bb7b39937d3")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_imsdatadaemon(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash imsdatadaemon
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="imsdatadaemon")
+
+ @test_tracker_info(uuid="350ca58c-01f2-4a61-baff-530b8b24f1f6")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_ims_rtp_daemon(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash imsdatadaemon
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="ims_rtp_daemon")
+
+ @test_tracker_info(uuid="af78f33a-2b50-4c55-a302-3701b655c557")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_ims_rcsservice(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash imsdatadaemon
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="com.android.ims.rcsservice")
+
+ @test_tracker_info(uuid="8119aeef-84ba-415c-88ea-6eba35bd91fd")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_system_server(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash system_server
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="system_server")
+
+ @test_tracker_info(uuid="c3891aca-9e1a-4e37-9f2f-23f12ef0a86f")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_crash_recovery_cnd(self):
+ """Crash Recovery Test
+
+ Steps:
+ 1. Crash cnd
+ 2. Post crash recovery, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="cnd")
+
+ @test_tracker_info(uuid="c1b661b9-d5cf-4a22-90a9-3fd55ddc2f3f")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_sim_slot_power_cycle(self):
+ """SIM slot power cycle Test
+
+ Steps:
+ 1. Power cycle SIM slot to simulate SIM resit
+ 2. Post power cycle SIM, verify Voice, Data, SMS, VoLTE, VT
+
+ Expected Results:
+ No crash happens in functional test, features work fine.
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._crash_recovery_test(process="sim")
+
@test_tracker_info(uuid="b6d2fccd-5dfd-4637-aa3b-257837bfba54")
+ @TelephonyBaseTest.tel_test_wrap
def test_telephonymonitor_functional(self):
"""Telephony Monitor Functional Test
@@ -771,5 +1023,23 @@
"""
return self._telephony_monitor_test()
+ @test_tracker_info(uuid="f048189b-e4bb-46f7-b150-37acf020af6e")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_telephonymonitor_negative(self):
+ """Telephony Monitor Functional Test
+
+ Steps:
+ 1. Verify Telephony Monitor functionality is working or not
+ 2. Force Trigger a call drop : media timeout and ensure it is
+ not notified by Telephony Monitor
+
+ Expected Results:
+ feature work fine, and does not report to User about Call Drop
+
+ Returns:
+ True is pass, False if fail.
+ """
+ return self._telephony_monitor_test(negative_test=True)
+
""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveSettingsTest.py b/acts/tests/google/tel/live/TelLiveSettingsTest.py
index 59d84c6..4a41f9a 100644
--- a/acts/tests/google/tel/live/TelLiveSettingsTest.py
+++ b/acts/tests/google/tel/live/TelLiveSettingsTest.py
@@ -17,7 +17,15 @@
Test Script for Telephony Settings
"""
+import os
import time
+
+from acts import signals
+from acts.keys import Config
+from acts.utils import create_dir
+from acts.utils import unzip_maintain_permissions
+from acts.utils import get_current_epoch_time
+from acts.utils import exe_cmd
from acts.test_decorators import test_tracker_info
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_WIFI_CONNECTION
@@ -31,12 +39,24 @@
from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
from acts.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts.test_utils.tel.tel_test_utils import flash_radio
+from acts.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
+from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
from acts.test_utils.tel.tel_test_utils import is_droid_in_rat_family
+from acts.test_utils.tel.tel_test_utils import is_sim_locked
from acts.test_utils.tel.tel_test_utils import is_wfc_enabled
+from acts.test_utils.tel.tel_test_utils import multithread_func
+from acts.test_utils.tel.tel_test_utils import power_off_sim
+from acts.test_utils.tel.tel_test_utils import power_on_sim
+from acts.test_utils.tel.tel_test_utils import print_radio_info
+from acts.test_utils.tel.tel_test_utils import set_qxdm_logger_command
from acts.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts.test_utils.tel.tel_test_utils import system_file_push
from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
from acts.test_utils.tel.tel_test_utils import toggle_volte
+from acts.test_utils.tel.tel_test_utils import unlock_sim
from acts.test_utils.tel.tel_test_utils import verify_http_connection
from acts.test_utils.tel.tel_test_utils import wait_for_ims_registered
from acts.test_utils.tel.tel_test_utils import wait_for_network_rat
@@ -47,6 +67,7 @@
from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
from acts.test_utils.tel.tel_test_utils import wifi_reset
from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts.test_utils.tel.tel_test_utils import set_wifi_to_default
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
@@ -54,6 +75,7 @@
from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts.utils import set_mobile_data_always_on
class TelLiveSettingsTest(TelephonyBaseTest):
@@ -71,7 +93,7 @@
self.wifi_network_pass = self.user_params["wifi_network_pass"]
except KeyError:
self.wifi_network_pass = None
-
+ self.number_of_devices = 1
self.stress_test_number = self.get_stress_test_number()
def _wifi_connected_enable_wfc_teardown_wfc(
@@ -207,8 +229,8 @@
if is_wfc_available_after_turn_off_apm and is_wfc_not_available:
self.log.error("WFC is not available.")
return False
- elif (not is_wfc_available_after_turn_off_apm and
- not is_wfc_not_available):
+ elif (not is_wfc_available_after_turn_off_apm
+ and not is_wfc_not_available):
self.log.error("WFC is available.")
return False
return True
@@ -286,7 +308,7 @@
3. DUT WiFi Calling feature bit return True, network rat is iwlan.
4. DUT WiFi Calling feature bit return False, network rat is not iwlan.
"""
-
+ set_wifi_to_default(self.log, self.ad)
if not phone_setup_voice_3g(self.log, self.ad):
self.log.error("Failed to setup 3G")
return False
@@ -389,7 +411,7 @@
3. DUT WiFi Calling feature bit return True, network rat is iwlan.
4. DUT WiFi Calling feature bit return False, network rat is not iwlan.
"""
-
+ set_wifi_to_default(self.log, self.ad)
if not phone_setup_voice_3g(self.log, self.ad):
self.log.error("Failed to setup 3G")
return False
@@ -539,6 +561,7 @@
2. DUT WiFi Calling feature bit return True, network rat is iwlan.
4. DUT WiFI Calling feature bit return False, network rat is not iwlan.
"""
+ set_wifi_to_default(self.log, self.ad)
if not phone_setup_voice_3g(self.log, self.ad):
self.log.error("Failed to setup 3G.")
return False
@@ -640,6 +663,7 @@
2. DUT WiFi Calling feature bit return False, network rat is not iwlan.
4. DUT WiFI Calling feature bit return True, network rat is iwlan.
"""
+ set_wifi_to_default(self.log, self.ad)
if not phone_setup_voice_3g(self.log, self.ad):
self.log.error("Failed to setup 3G.")
return False
@@ -1047,8 +1071,8 @@
result = False
return result
- @TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="135301ea-6d00-4233-98fd-cda706d61eb2")
+ @TelephonyBaseTest.tel_test_wrap
def test_ims_factory_reset_to_volte_on_wfc_off(self):
"""Test VOLTE is enabled WFC is disabled after ims factory reset.
@@ -1079,8 +1103,8 @@
if not self.verify_volte_on_wfc_off(): result = False
return result
- @TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="5318bf7a-4210-4b49-b361-9539d28f3e38")
+ @TelephonyBaseTest.tel_test_wrap
def test_ims_factory_reset_to_volte_off_wfc_off(self):
"""Test VOLTE is enabled WFC is disabled after ims factory reset.
@@ -1111,8 +1135,8 @@
if not self.verify_volte_off_wfc_off(): result = False
return result
- @TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="c6149bd6-7080-453d-af37-1f9bd350a764")
+ @TelephonyBaseTest.tel_test_wrap
def test_telephony_factory_reset(self):
"""Test VOLTE is enabled WFC is disabled after telephony factory reset.
@@ -1129,8 +1153,8 @@
self.ad.droid.telephonyFactoryReset()
return self.verify_default_telephony_setting()
- @TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="ce60740f-4d8e-4013-a7cf-65589e8a0893")
+ @TelephonyBaseTest.tel_test_wrap
def test_factory_reset_by_wipe_to_volte_on_wfc_off(self):
"""Verify the network setting after factory reset by wipe.
@@ -1149,13 +1173,13 @@
set_wfc_mode(self.log, self.ad, WFC_MODE_WIFI_PREFERRED)
self.revert_default_telephony_setting()
self.ad.log.info("Wipe in fastboot")
- self.ad.fastboot_wipe()
+ fastboot_wipe(self.ad)
result = self.verify_volte_on_wfc_off()
if not self.verify_default_telephony_setting(): result = False
return result
- @TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="44e9291e-949b-4db1-a209-c6d41552ec27")
+ @TelephonyBaseTest.tel_test_wrap
def test_factory_reset_by_wipe_to_volte_off_wfc_off(self):
"""Verify the network setting after factory reset by wipe.
@@ -1174,7 +1198,300 @@
set_wfc_mode(self.log, self.ad, WFC_MODE_WIFI_PREFERRED)
self.revert_default_telephony_setting()
self.ad.log.info("Wipe in fastboot")
- self.ad.fastboot_wipe()
+ fastboot_wipe(self.ad)
result = self.verify_volte_off_wfc_off()
if not self.verify_default_telephony_setting(): result = False
return result
+
+ @test_tracker_info(uuid="64deba57-c1c2-422f-b771-639c95edfbc0")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_disable_mobile_data_always_on(self):
+ """Verify mobile_data_always_on can be disabled.
+
+ Steps:
+ 1. Disable mobile_data_always_on by adb.
+ 2. Verify the mobile data_always_on state.
+
+ Expected Results: mobile_data_always_on return 0.
+ """
+ self.ad.log.info("Disable mobile_data_always_on")
+ set_mobile_data_always_on(self.ad, False)
+ time.sleep(1)
+ return self.ad.adb.shell(
+ "settings get global mobile_data_always_on") == "0"
+
+ @test_tracker_info(uuid="56ddcd5a-92b0-46c7-9c2b-d743794efb7c")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_enable_mobile_data_always_on(self):
+ """Verify mobile_data_always_on can be enabled.
+
+ Steps:
+ 1. Enable mobile_data_always_on by adb.
+ 2. Verify the mobile data_always_on state.
+
+ Expected Results: mobile_data_always_on return 1.
+ """
+ self.ad.log.info("Enable mobile_data_always_on")
+ set_mobile_data_always_on(self.ad, True)
+ time.sleep(1)
+ return "1" in self.ad.adb.shell(
+ "settings get global mobile_data_always_on")
+
+ @test_tracker_info(uuid="c2cc5b66-40af-4ba6-81cb-6c44ae34cbbb")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_push_new_radio_or_mbn(self):
+ """Verify new mdn and radio can be push to device.
+
+ Steps:
+ 1. If new radio path is given, flash new radio on the device.
+ 2. Verify the radio version.
+ 3. If new mbn path is given, push new mbn to device.
+ 4. Verify the installed mbn version.
+
+ Expected Results:
+ radio and mbn can be pushed to device and mbn.ver is available.
+ """
+ result = True
+ paths = {}
+ for path_key, dst_name in zip(["radio_image", "mbn_path"],
+ ["radio.img", "mcfg_sw"]):
+ path = self.user_params.get(path_key)
+ if not path:
+ continue
+ elif isinstance(path, list):
+ if not path[0]:
+ continue
+ path = path[0]
+ if "dev/null" in path:
+ continue
+ if not os.path.exists(path):
+ self.log.error("path %s does not exist", path)
+ self.log.info(self.user_params)
+ path = os.path.join(self.user_params[Config.key_config_path],
+ path)
+ if not os.path.exists(path):
+ self.log.error("path %s does not exist", path)
+ continue
+
+ self.log.info("%s path = %s", path_key, path)
+ if "zip" in path:
+ self.log.info("Unzip %s", path)
+ file_path, file_name = os.path.split(path)
+ dest_path = os.path.join(file_path, dst_name)
+ os.system("rm -rf %s" % dest_path)
+ unzip_maintain_permissions(path, file_path)
+ path = dest_path
+ os.system("chmod -R 777 %s" % path)
+ paths[path_key] = path
+ if not paths:
+ self.log.info("No radio_path or mbn_path is provided")
+ raise signals.TestSkip("No radio_path or mbn_path is provided")
+ self.log.info("paths = %s", paths)
+ for ad in self.android_devices:
+ if paths.get("radio_image"):
+ print_radio_info(ad, "Before flash radio, ")
+ flash_radio(ad, paths["radio_image"])
+ print_radio_info(ad, "After flash radio, ")
+ if not paths.get("mbn_path") or "mbn" not in ad.adb.shell(
+ "ls /vendor"):
+ ad.log.info("No need to push mbn files")
+ continue
+ push_result = True
+ try:
+ mbn_ver = ad.adb.shell(
+ "cat /vendor/mbn/mcfg/configs/mcfg_sw/mbn.ver")
+ if mbn_ver:
+ ad.log.info("Before push mbn, mbn.ver = %s", mbn_ver)
+ else:
+ ad.log.info(
+ "There is no mbn.ver before push, unmatching device")
+ continue
+ except:
+ ad.log.info(
+ "There is no mbn.ver before push, unmatching device")
+ continue
+ print_radio_info(ad, "Before push mbn, ")
+ for i in range(2):
+ if not system_file_push(ad, paths["mbn_path"],
+ "/vendor/mbn/mcfg/configs/"):
+ if i == 1:
+ ad.log.error("Failed to push mbn file")
+ push_result = False
+ else:
+ ad.log.info("The mbn file is pushed to device")
+ break
+ if not push_result:
+ result = False
+ continue
+ print_radio_info(ad, "After push mbn, ")
+ try:
+ new_mbn_ver = ad.adb.shell(
+ "cat /vendor/mbn/mcfg/configs/mcfg_sw/mbn.ver")
+ if new_mbn_ver:
+ ad.log.info("new mcfg_sw mbn.ver = %s", new_mbn_ver)
+ if new_mbn_ver == mbn_ver:
+ ad.log.error(
+ "mbn.ver is the same before and after push")
+ result = False
+ else:
+ ad.log.error("Unable to get new mbn.ver")
+ result = False
+ except Exception as e:
+ ad.log.error("cat mbn.ver with error %s", e)
+ result = False
+ return result
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_set_qxdm_log_mask_ims(self):
+ """Set the QXDM Log mask to IMS_DS_CNE_LnX_Golden.cfg"""
+ tasks = [(set_qxdm_logger_command, [ad, "IMS_DS_CNE_LnX_Golden.cfg"])
+ for ad in self.android_devices]
+ return multithread_func(self.log, tasks)
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_set_qxdm_log_mask_qc_default(self):
+ """Set the QXDM Log mask to QC_Default.cfg"""
+ tasks = [(set_qxdm_logger_command, [ad, " QC_Default.cfg"])
+ for ad in self.android_devices]
+ return multithread_func(self.log, tasks)
+
+ @test_tracker_info(uuid="e2734d66-6111-4e76-aa7b-d3b4cbcde4f1")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_check_carrier_id(self):
+ """Verify mobile_data_always_on can be enabled.
+
+ Steps:
+ 1. Enable mobile_data_always_on by adb.
+ 2. Verify the mobile data_always_on state.
+
+ Expected Results: mobile_data_always_on return 1.
+ """
+ result = True
+ if self.ad.adb.getprop("ro.build.version.release")[0] in ("8", "O",
+ "7", "N"):
+ raise signals.TestSkip("Not supported in this build")
+ old_carrier_id = self.ad.droid.telephonyGetSubscriptionCarrierId()
+ old_carrier_name = self.ad.droid.telephonyGetSubscriptionCarrierName()
+ self.result_detail = "carrier_id = %s, carrier_name = %s" % (
+ old_carrier_id, old_carrier_name)
+ self.ad.log.info(self.result_detail)
+ sub_id = get_outgoing_voice_sub_id(self.ad)
+ slot_index = get_slot_index_from_subid(self.log, self.ad, sub_id)
+ if power_off_sim(self.ad, slot_index):
+ for i in range(3):
+ carrier_id = self.ad.droid.telephonyGetSubscriptionCarrierId()
+ carrier_name = self.ad.droid.telephonyGetSubscriptionCarrierName(
+ )
+ msg = "After SIM power down, carrier_id = %s(expecting -1), " \
+ "carrier_name = %s(expecting None)" % (carrier_id, carrier_name)
+ if carrier_id != -1 or carrier_name:
+ if i == 2:
+ self.ad.log.error(msg)
+ self.result_detail = "%s %s" % (self.result_detail,
+ msg)
+ result = False
+ else:
+ time.sleep(5)
+ else:
+ self.ad.log.info(msg)
+ break
+ else:
+ if self.ad.model in ("taimen", "walleye"):
+ msg = "Power off SIM slot is not working"
+ self.ad.log.error(msg)
+ result = False
+ else:
+ msg = "Power off SIM slot is not supported"
+ self.ad.log.warning(msg)
+ self.result_detail = "%s %s" % (self.result_detail, msg)
+
+ if not power_on_sim(self.ad, slot_index):
+ self.ad.log.error("Fail to power up SIM")
+ result = False
+ setattr(self.ad, "reboot_to_recover", True)
+ else:
+ if is_sim_locked(self.ad):
+ self.ad.log.info("Sim is locked")
+ carrier_id = self.ad.droid.telephonyGetSubscriptionCarrierId()
+ carrier_name = self.ad.droid.telephonyGetSubscriptionCarrierName(
+ )
+ msg = "In locked SIM, carrier_id = %s(expecting -1), " \
+ "carrier_name = %s(expecting None)" % (carrier_id, carrier_name)
+ if carrier_id != -1 or carrier_name:
+ self.ad.log.error(msg)
+ self.result_detail = "%s %s" % (self.result_detail, msg)
+ result = False
+ else:
+ self.ad.log.info(msg)
+ unlock_sim(self.ad)
+ elif getattr(self.ad, "is_sim_locked", False):
+ self.ad.log.error(
+ "After SIM slot power cycle, SIM in not in locked state")
+ return False
+
+ if not ensure_phone_subscription(self.log, self.ad):
+ self.ad.log.error("Unable to find a valid subscription!")
+ result = False
+ new_carrier_id = self.ad.droid.telephonyGetSubscriptionCarrierId()
+ new_carrier_name = self.ad.droid.telephonyGetSubscriptionCarrierName(
+ )
+ msg = "After SIM power up, new_carrier_id = %s, " \
+ "new_carrier_name = %s" % (new_carrier_id, new_carrier_name)
+ if old_carrier_id != new_carrier_id or (old_carrier_name !=
+ new_carrier_name):
+ self.ad.log.error(msg)
+ self.result_detail = "%s %s" % (self.result_detail, msg)
+ result = False
+ else:
+ self.ad.log.info(msg)
+ return result
+
+ @TelephonyBaseTest.tel_test_wrap
+ def test_modem_power_anomaly_file_existence(self):
+ """Verify if the power anomaly file exists
+
+ 1. Collect Bugreport
+ 2. unzip bugreport
+ 3. remane the .bin file to .tar
+ 4. unzip dumpstate.tar
+ 5. Verify if the file exists
+
+ """
+ ad = self.android_devices[0]
+ begin_time = get_current_epoch_time()
+ for i in range(3):
+ try:
+ bugreport_path = os.path.join(ad.log_path, self.test_name)
+ create_dir(bugreport_path)
+ ad.take_bug_report(self.test_name, begin_time)
+ break
+ except Exception as e:
+ ad.log.error("bugreport attempt %s error: %s", i + 1, e)
+ ad.log.info("Bugreport Path is %s" % bugreport_path)
+ try:
+ list_of_files = os.listdir(bugreport_path)
+ ad.log.info(list_of_files)
+ for filename in list_of_files:
+ if ".zip" in filename:
+ ad.log.info(filename)
+ file_path = os.path.join(bugreport_path, filename)
+ ad.log.info(file_path)
+ unzip_maintain_permissions(file_path, bugreport_path)
+ dumpstate_path = os.path.join(bugreport_path,
+ "dumpstate_board.bin")
+ if os.path.isfile(dumpstate_path):
+ os.rename(dumpstate_path,
+ bugreport_path + "/dumpstate_board.tar")
+ os.chmod(bugreport_path + "/dumpstate_board.tar", 0o777)
+ current_dir = os.getcwd()
+ os.chdir(bugreport_path)
+ exe_cmd("tar -xvf %s" %
+ (bugreport_path + "/dumpstate_board.tar"))
+ os.chdir(current_dir)
+ if os.path.isfile(bugreport_path + "/power_anomaly_data.txt"):
+ ad.log.info("Modem Power Anomaly File Exists!!")
+ return True
+ return False
+ except Exception as e:
+ ad.log.error(e)
+ return False
diff --git a/acts/tests/google/tel/live/TelLiveSinglePhoneStressTest.py b/acts/tests/google/tel/live/TelLiveSinglePhoneStressTest.py
deleted file mode 100644
index 2541ca6..0000000
--- a/acts/tests/google/tel/live/TelLiveSinglePhoneStressTest.py
+++ /dev/null
@@ -1,504 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - Google
-#
-# 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.
-"""
- Test Script for Telephony Stress Call Test
-"""
-
-import collections
-import random
-import time
-
-from acts.asserts import fail
-from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_WCDMA_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GLOBAL
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_TDSCDMA_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_MODE_CHANGE
-from acts.test_utils.tel.tel_test_utils import active_file_download_test
-from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_phone_idle
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts.test_utils.tel.tel_test_utils import hangup_call
-from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts.test_utils.tel.tel_test_utils import run_multithread_func
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
-from acts.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
-from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
-from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.tel.tel_voice_utils import get_current_voice_rat
-from acts.logger import epoch_to_log_line_timestamp
-from acts.utils import get_current_epoch_time
-from acts.utils import rand_ascii_str
-
-import socket
-from acts.controllers.sl4a_client import Sl4aProtocolError
-
-IGNORE_EXCEPTIONS = (BrokenPipeError, Sl4aProtocolError)
-EXCEPTION_TOLERANCE = 20
-
-
-class TelLiveSinglePhoneStressTest(TelephonyBaseTest):
- def setup_class(self):
- super(TelLiveSinglePhoneStressTest, self).setup_class()
- self.dut = self.android_devices[0]
- self.call_server_number = self.user_params.get("call_server_number",
- "7124325335")
- self.user_params["telephony_auto_rerun"] = False
- self.wifi_network_ssid = self.user_params.get(
- "wifi_network_ssid") or self.user_params.get(
- "wifi_network_ssid_2g")
- self.wifi_network_pass = self.user_params.get(
- "wifi_network_pass") or self.user_params.get(
- "wifi_network_pass_2g")
- self.max_phone_call_duration = int(
- self.user_params.get("max_phone_call_duration", 3600))
- self.max_sleep_time = int(self.user_params.get("max_sleep_time", 1200))
- self.max_run_time = int(self.user_params.get("max_run_time", 18000))
- self.max_sms_length = int(self.user_params.get("max_sms_length", 1000))
- self.max_mms_length = int(self.user_params.get("max_mms_length", 160))
- self.min_sms_length = int(self.user_params.get("min_sms_length", 1))
- self.min_mms_length = int(self.user_params.get("min_mms_length", 1))
- self.min_phone_call_duration = int(
- self.user_params.get("min_phone_call_duration", 10))
- self.crash_check_interval = int(
- self.user_params.get("crash_check_interval", 300))
-
- return True
-
- def _setup_wfc(self):
- if not ensure_wifi_connected(
- self.log,
- self.dut,
- self.wifi_network_ssid,
- self.wifi_network_pass,
- retry=3):
- self.dut.log.error("Phone Wifi connection fails.")
- return False
- self.dut.log.info("Phone WIFI is connected successfully.")
- if not set_wfc_mode(self.log, self.dut, WFC_MODE_WIFI_PREFERRED):
- self.dut.log.error("Phone failed to enable Wifi-Calling.")
- return False
- self.dut.log.info("Phone is set in Wifi-Calling successfully.")
- if not phone_idle_iwlan(self.log, self.dut):
- self.dut.log.error("Phone is not in WFC enabled state.")
- return False
- self.dut.log.info("Phone is in WFC enabled state.")
- return True
-
- def _setup_lte_volte_enabled(self):
- if not phone_setup_volte(self.log, self.dut):
- self.dut.log.error("Phone failed to enable VoLTE.")
- return False
- self.dut.log.info("Phone VOLTE is enabled successfully.")
- return True
-
- def _setup_lte_volte_disabled(self):
- if not phone_setup_csfb(self.log, self.dut):
- self.dut.log.error("Phone failed to setup CSFB.")
- return False
- self.dut.log.info("Phone VOLTE is disabled successfully.")
- return True
-
- def _setup_3g(self):
- if not phone_setup_voice_3g(self.log, self.dut):
- self.dut.log.error("Phone failed to setup 3g.")
- return False
- self.dut.log.info("Phone RAT 3G is enabled successfully.")
- return True
-
- def _setup_2g(self):
- if not phone_setup_voice_2g(self.log, self.dut):
- self.dut.log.error("Phone failed to setup 2g.")
- return False
- self.dut.log.info("RAT 2G is enabled successfully.")
- return True
-
- def crash_check_test(self):
- failure = 0
- while time.time() < self.finishing_time:
- self.dut.log.info(dict(self.result_info))
- try:
- begin_time = epoch_to_log_line_timestamp(
- get_current_epoch_time())
- time.sleep(self.crash_check_interval)
- crash_report = self.dut.check_crash_report("checking_crash",
- begin_time, True)
- if crash_report:
- self.dut.log.error("Find new crash reports %s",
- crash_report)
- failure += 1
- self.result_info["Crashes"] += 1
- except IGNORE_EXCEPTIONS as e:
- self.log.error("Exception error %s", str(e))
- self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
- except Exception as e:
- self.finishing_time = time.time()
- raise
- self.dut.log.info("Crashes found: %s", failure)
- if failure:
- return "%s crashes" % failure
- else:
- return ""
-
- def call_test(self):
- failure = 0
- total_count = 0
- while time.time() < self.finishing_time:
- total_count += 1
- try:
- self.dut.log.info(dict(self.result_info))
- self.result_info["Total Calls"] += 1
- duration = random.randrange(self.min_phone_call_duration,
- self.max_phone_call_duration)
- # Current Voice RAT
- self.dut.log.info("Current Voice RAT is %s",
- get_current_voice_rat(self.log, self.dut))
- self.dut.log.info("Make call to %s with call duration %s",
- self.call_server_number, duration)
- if not initiate_call(self.log, self.dut,
- self.call_server_number):
- self.dut.log.error("Initiate phone call to %s failed.",
- self.call_server_number)
- self.result_info["Call initiation failure"] += 1
- failure += 1
- self._take_bug_report("%s_call_initiation_failure" %
- self.test_name,
- time.strftime("%m-%d-%Y-%H-%M-%S"))
- continue
- elapse_time = 0
- interval = min(60, duration)
- while elapse_time < duration:
- interval = min(duration - elapse_time, interval)
- time.sleep(interval)
- elapse_time += interval
- if not is_phone_in_call(self.log, self.dut):
- self.dut.log.error("Call droped.")
- self.result_info["Call drop"] += 1
- failure += 1
- self._take_bug_report(
- "%s_call_drop" % self.test_name,
- time.strftime("%m-%d-%Y-%H-%M-%S"))
- break
- else:
- self.dut.log.info("DUT is in call")
- else:
- hangup_call(self.log, self.dut)
- self.dut.log.info("Call test succeed.")
- ensure_phone_idle(self.log, self.dut)
- self.dut.droid.goToSleepNow()
- time.sleep(random.randrange(0, self.max_sleep_time))
- except IGNORE_EXCEPTIONS as e:
- self.log.error("Exception error %s", str(e))
- self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
- except Exception as e:
- self.finishing_time = time.time()
- raise
- self.dut.log.info("Call test failure: %s/%s", failure, total_count)
- if failure:
- return "Call test failure: %s/%s" % (failure, total_count)
- else:
- return ""
-
- def volte_modechange_volte_test(self):
- failure = 0
- total_count = 0
- sub_id = self.dut.droid.subscriptionGetDefaultSubId()
- while time.time() < self.finishing_time:
- total_count += 1
- try:
- self.dut.log.info(dict(self.result_info))
- self.result_info["Total Calls"] += 1
- duration = random.randrange(self.min_phone_call_duration,
- self.max_phone_call_duration)
- # Current Voice RAT
- self.dut.log.info("Current Voice RAT is %s",
- get_current_voice_rat(self.log, self.dut))
- self.dut.log.info("Make call to %s with call duration %s",
- self.call_server_number, duration)
- if not initiate_call(self.log, self.dut,
- self.call_server_number):
- self.dut.log.error("Initiate phone call to %s failed.",
- self.call_server_number)
- self.result_info["Call initiation failure"] += 1
- failure += 1
- self._take_bug_report("%s_call_initiation_failure" %
- self.test_name,
- time.strftime("%m-%d-%Y-%H-%M-%S"))
- continue
- elapse_time = 0
- interval = min(5, duration)
- while elapse_time < duration:
- interval = min(duration - elapse_time, interval)
- time.sleep(interval)
- elapse_time += interval
- if not is_phone_in_call_volte(self.log, self.dut):
- self.dut.log.error("Call not VoLTE")
- self.result_info["Call not VoLTE"] += 1
- failure += 1
- self._take_bug_report(
- "%s_not_in_volte" % self.test_name,
- time.strftime("%m-%d-%Y-%H-%M-%S"))
- break
- else:
- self.dut.log.info("DUT is in VoLTE call")
- else:
- hangup_call(self.log, self.dut)
- self.dut.log.info("VoLTE test succeed.")
-
- # ModePref change to non-LTE
- network_preference_list = [
- NETWORK_MODE_TDSCDMA_GSM_WCDMA,
- NETWORK_MODE_WCDMA_ONLY, NETWORK_MODE_GLOBAL,
- NETWORK_MODE_CDMA, NETWORK_MODE_GSM_ONLY
- ]
- network_preference = random.choice(network_preference_list)
- set_preferred_network_mode_pref(self.dut.log, self.dut,
- sub_id, network_preference)
- time.sleep(WAIT_TIME_AFTER_MODE_CHANGE)
- self.dut.log.info(
- "Current Voice RAT is %s",
- get_current_voice_rat(self.log, self.dut))
-
- # ModePref change back to with LTE
- set_preferred_network_mode_pref(
- self.dut.log, self.dut, sub_id,
- NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)
- time.sleep(WAIT_TIME_AFTER_MODE_CHANGE)
-
- except IGNORE_EXCEPTIONS as e:
- self.log.error("Exception error %s", str(e))
- self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
- except Exception as e:
- self.finishing_time = time.time()
- raise
- self.dut.log.info("VoLTE test failure: %s/%s", failure,
- total_count)
- if failure:
- return "VoLTE test failure: %s/%s" % (failure, total_count)
- else:
- return ""
-
- def message_test(self):
- failure = 0
- total_count = 0
- message_type_map = {0: "SMS", 1: "MMS"}
- max_length_map = {0: self.max_sms_length, 1: self.max_mms_length}
- min_length_map = {0: self.min_sms_length, 1: self.min_mms_length}
- message_func_map = {
- 0: sms_send_receive_verify,
- 1: mms_send_receive_verify
- }
- while time.time() < self.finishing_time:
- try:
- self.dut.log.info(dict(self.result_info))
- total_count += 1
- selection = random.randrange(0, 2)
- message_type = message_type_map[selection]
- self.result_info["Total %s" % message_type] += 1
- length = random.randrange(min_length_map[selection],
- max_length_map[selection] + 1)
- text = rand_ascii_str(length)
- message_content_map = {
- 0: [text],
- 1: [("Mms Message", text, None)]
- }
- if not message_func_map[selection](
- self.log, self.dut, self.dut,
- message_content_map[selection]):
- self.log.error("%s of length %s from self to self fails",
- message_type, length)
- self.result_info["%s failure" % message_type] += 1
- #self._take_bug_report("%s_messaging_failure" % self.test_name,
- # time.strftime("%m-%d-%Y-%H-%M-%S"))
- failure += 1
- else:
- self.dut.log.info(
- "%s of length %s from self to self succeed",
- message_type, length)
- self.dut.droid.goToSleepNow()
- time.sleep(random.randrange(0, self.max_sleep_time))
- except IGNORE_EXCEPTIONS as e:
- self.log.error("Exception error %s", str(e))
- self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
- except Exception as e:
- self.finishing_time = time.time()
- raise
- self.dut.log.info("Messaging test failure: %s/%s", failure,
- total_count)
- if failure / total_count > 0.1:
- return "Messaging test failure: %s/%s" % (failure, total_count)
- else:
- return ""
-
- def data_test(self):
- failure = 0
- total_count = 0
- tcpdump_pid = None
- #file_names = ["5MB", "10MB", "20MB", "50MB", "200MB", "512MB", "1GB"]
- file_names = ["5MB", "10MB", "20MB", "50MB", "200MB", "512MB"]
- while time.time() < self.finishing_time:
- total_count += 1
- pull_tcpdump = False
- try:
- self.dut.log.info(dict(self.result_info))
- self.result_info["Total file download"] += 1
- selection = random.randrange(0, len(file_names))
- file_name = file_names[selection]
- (tcpdump_pid, tcpdump_file) = \
- start_adb_tcpdump(self.dut, self.test_name, mask="all")
- if not active_file_download_test(self.log, self.dut,
- file_name):
- self.result_info["%s file download failure" %
- file_name] += 1
- failure += 1
- pull_tcpdump = True
- self._take_bug_report("%s_download_failure" %
- self.test_name,
- time.strftime("%m-%d-%Y-%H-%M-%S"))
- self.dut.droid.goToSleepNow()
- time.sleep(random.randrange(0, self.max_sleep_time))
- except IGNORE_EXCEPTIONS as e:
- self.log.error("Exception error %s", str(e))
- self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
- except Exception as e:
- self.finishing_time = time.time()
- raise
- finally:
- if tcpdump_pid is not None:
- stop_adb_tcpdump(self.dut, tcpdump_pid, tcpdump_file,
- pull_tcpdump)
- self.dut.log.info("File download test failure: %s/%s", failure,
- total_count)
- if failure / total_count > 0.1:
- return "File download test failure: %s/%s" % (failure, total_count)
- else:
- return ""
-
- def parallel_tests(self, setup_func=None):
- if setup_func and not setup_func():
- self.log.error("Test setup %s failed", setup_func.__name__)
- return False
- self.result_info = collections.defaultdict(int)
- self.finishing_time = time.time() + self.max_run_time
- results = run_multithread_func(self.log, [(self.call_test, []), (
- self.message_test, []), (self.data_test, []),
- (self.crash_check_test, [])])
- self.log.info(dict(self.result_info))
- error_message = " ".join(results).strip()
- if error_message:
- self.log.error(error_message)
- fail(error_message)
- return True
-
- def parallel_volte_tests(self, setup_func=None):
- if setup_func and not setup_func():
- self.log.error("Test setup %s failed", setup_func.__name__)
- return False
- self.result_info = collections.defaultdict(int)
- self.finishing_time = time.time() + self.max_run_time
- results = run_multithread_func(
- self.log, [(self.volte_modechange_volte_test, []),
- (self.message_test, []), (self.crash_check_test, [])])
- self.log.info(dict(self.result_info))
- error_message = " ".join(results).strip()
- if error_message:
- self.log.error(error_message)
- fail(error_message)
- return True
-
- """ Tests Begin """
-
- @test_tracker_info(uuid="d035e5b9-476a-4e3d-b4e9-6fd86c51a68d")
- @TelephonyBaseTest.tel_test_wrap
- def test_default_parallel_stress(self):
- """ Default state stress test"""
- return self.parallel_tests()
-
- @test_tracker_info(uuid="c21e1f17-3282-4f0b-b527-19f048798098")
- @TelephonyBaseTest.tel_test_wrap
- def test_lte_volte_parallel_stress(self):
- """ VoLTE on stress test"""
- return self.parallel_tests(setup_func=self._setup_lte_volte_enabled)
-
- @test_tracker_info(uuid="a317c23a-41e0-4ef8-af67-661451cfefcf")
- @TelephonyBaseTest.tel_test_wrap
- def test_csfb_parallel_stress(self):
- """ LTE non-VoLTE stress test"""
- return self.parallel_tests(setup_func=self._setup_lte_volte_disabled)
-
- @test_tracker_info(uuid="fdb791bf-c414-4333-9fa3-cc18c9b3b234")
- @TelephonyBaseTest.tel_test_wrap
- def test_wfc_parallel_stress(self):
- """ Wifi calling on stress test"""
- return self.parallel_tests(setup_func=self._setup_wfc)
-
- @test_tracker_info(uuid="4566eef6-55de-4ac8-87ee-58f2ef41a3e8")
- @TelephonyBaseTest.tel_test_wrap
- def test_3g_parallel_stress(self):
- """ 3G stress test"""
- return self.parallel_tests(setup_func=self._setup_3g)
-
- @test_tracker_info(uuid="f34f1a31-3948-4675-8698-372a83b8088d")
- @TelephonyBaseTest.tel_test_wrap
- def test_call_2g_parallel_stress(self):
- """ 2G call stress test"""
- return self.parallel_tests(setup_func=self._setup_2g)
-
- @test_tracker_info(uuid="af580fca-fea6-4ca5-b981-b8c710302d37")
- @TelephonyBaseTest.tel_test_wrap
- def test_volte_modeprefchange_parallel_stress(self):
- """ VoLTE Mode Pref call stress test"""
- return self.parallel_volte_tests(
- setup_func=self._setup_lte_volte_enabled)
-
- """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveSmsTest.py b/acts/tests/google/tel/live/TelLiveSmsTest.py
index 9b4e874..c100379 100644
--- a/acts/tests/google/tel/live/TelLiveSmsTest.py
+++ b/acts/tests/google/tel/live/TelLiveSmsTest.py
@@ -35,10 +35,13 @@
from acts.test_utils.tel.tel_test_utils import ensure_network_generation
from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts.test_utils.tel.tel_test_utils import get_mobile_data_usage
+from acts.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit
from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
from acts.test_utils.tel.tel_test_utils import mms_receive_verify_after_call_hangup
from acts.test_utils.tel.tel_test_utils import multithread_func
from acts.test_utils.tel.tel_test_utils import set_call_state_listen_level
+from acts.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
from acts.test_utils.tel.tel_test_utils import setup_sim
from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
from acts.test_utils.tel.tel_video_utils import phone_setup_video
@@ -77,10 +80,8 @@
# use the second one as sms/mms help device, use the third one
# as the active call help device.
self.caller = self.android_devices[0]
- if len(self.android_devices) > 2:
- self.callee = self.android_devices[2]
- else:
- self.callee = self.android_devices[1]
+ self.callee = self.android_devices[1]
+ self.number_of_devices = 2
self.message_lengths = (50, 160, 180)
def setup_class(self):
@@ -469,6 +470,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mo(ads)
@@ -492,7 +494,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mt(ads)
@test_tracker_info(uuid="bb8e1a06-a4b5-4f9b-9ab2-408ace9a1deb")
@@ -515,6 +517,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mo(ads)
@@ -538,7 +541,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mt(ads)
@test_tracker_info(uuid="2c229a4b-c954-4ba3-94ba-178dc7784d03")
@@ -561,6 +564,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mo(ads)
@@ -584,6 +588,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mt(ads)
@@ -607,6 +612,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mo(ads)
@@ -630,6 +636,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mt(ads)
@@ -654,6 +661,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -680,6 +688,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -706,7 +715,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mo(ads)
@test_tracker_info(uuid="2186e152-bf83-4d6e-93eb-b4bf9ae2d76e")
@@ -730,6 +739,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mt(ads)
@@ -754,6 +764,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mo(ads)
@@ -778,6 +789,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mt(ads)
@@ -805,6 +817,7 @@
return False
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mo(ads)
@@ -832,6 +845,7 @@
return False
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mt(ads)
@@ -850,13 +864,14 @@
"""
ads = self.android_devices
- if (not phone_setup_data_general(self.log, ads[1]) and
- not phone_setup_voice_general(self.log, ads[1])):
+ if (not phone_setup_data_general(self.log, ads[1])
+ and not phone_setup_voice_general(self.log, ads[1])):
self.log.error("Failed to setup PhoneB.")
return False
if not ensure_network_generation(self.log, ads[0], GEN_4G):
self.log.error("DUT Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mo(ads)
@@ -876,13 +891,14 @@
ads = self.android_devices
- if (not phone_setup_data_general(self.log, ads[1]) and
- not phone_setup_voice_general(self.log, ads[1])):
+ if (not phone_setup_data_general(self.log, ads[1])
+ and not phone_setup_voice_general(self.log, ads[1])):
self.log.error("Failed to setup PhoneB.")
return False
if not ensure_network_generation(self.log, ads[0], GEN_4G):
self.log.error("DUT Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mt(ads)
@@ -907,6 +923,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mo(ads)
@@ -931,6 +948,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mt(ads)
@@ -956,6 +974,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
return self._mms_test_mo(ads)
@@ -982,6 +1001,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -1006,7 +1026,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call SMS Test.")
if not call_setup_teardown(
self.log,
@@ -1042,7 +1062,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call SMS Test.")
if not call_setup_teardown(
self.log,
@@ -1078,7 +1098,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call SMS Test.")
if not call_setup_teardown(
self.log,
@@ -1114,7 +1134,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call MMS Test.")
if not call_setup_teardown(
self.log,
@@ -1151,7 +1171,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
self.log.info("Begin In Call SMS Test.")
@@ -1190,7 +1210,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
self.log.info("Begin In Call MMS Test.")
@@ -1232,6 +1252,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mo_sms_in_3g_call(ads)
@@ -1258,6 +1279,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mt_sms_in_3g_call(ads)
@@ -1284,6 +1306,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mo_mms_in_3g_call(ads)
@@ -1310,6 +1333,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mt_mms_in_3g_call(ads)
@@ -1337,6 +1361,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -1366,7 +1391,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
-
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
return self._mt_mms_in_3g_call(ads, wifi=True)
@@ -1394,6 +1419,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mo_sms_in_csfb_call(ads)
@@ -1420,6 +1446,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mt_sms_in_csfb_call(ads)
@@ -1446,6 +1473,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mo_mms_in_csfb_call(ads)
@@ -1472,6 +1500,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mt_mms_in_csfb_call(ads)
@@ -1499,6 +1528,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -1528,6 +1558,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -1556,6 +1587,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mo_sms_in_1x_call(ads)
@@ -1582,6 +1614,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mt_sms_in_1x_call(ads)
@@ -1609,6 +1642,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mo_mms_in_1x_call(ads)
@@ -1635,6 +1669,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mt_mms_in_1x_call(ads)
@@ -1662,6 +1697,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -1690,6 +1726,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -1718,6 +1755,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mo_sms_in_1x_call(ads)
@@ -1744,6 +1782,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mt_sms_in_1x_call(ads)
@@ -1770,6 +1809,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mo_mms_in_1x_call(ads)
@@ -1796,6 +1836,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mt_mms_in_1x_call(ads)
@@ -1822,6 +1863,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -1850,6 +1892,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
@@ -1878,6 +1921,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mo(ads)
@@ -1904,6 +1948,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mt(ads)
@@ -1930,6 +1975,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mo(ads)
@@ -1956,6 +2002,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mt(ads)
@@ -1974,12 +2021,14 @@
ads = self.android_devices
phone_setup_voice_general(self.log, ads[0])
- tasks = [(ensure_wifi_connected, (
- self.log, ads[0], self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_general, (self.log, ads[1]))]
+ tasks = [(ensure_wifi_connected,
+ (self.log, ads[0], self.wifi_network_ssid,
+ self.wifi_network_pass)), (phone_setup_voice_general,
+ (self.log, ads[1]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mo(ads)
@@ -1998,12 +2047,14 @@
ads = self.android_devices
phone_setup_voice_general(self.log, ads[0])
- tasks = [(ensure_wifi_connected, (
- self.log, ads[0], self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_general, (self.log, ads[1]))]
+ tasks = [(ensure_wifi_connected,
+ (self.log, ads[0], self.wifi_network_ssid,
+ self.wifi_network_pass)), (phone_setup_voice_general,
+ (self.log, ads[1]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._sms_test_mt(ads)
@@ -2022,12 +2073,14 @@
ads = self.android_devices
phone_setup_voice_general(self.log, ads[0])
- tasks = [(ensure_wifi_connected, (
- self.log, ads[0], self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_general, (self.log, ads[1]))]
+ tasks = [(ensure_wifi_connected,
+ (self.log, ads[0], self.wifi_network_ssid,
+ self.wifi_network_pass)), (phone_setup_voice_general,
+ (self.log, ads[1]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mo(ads)
@@ -2046,12 +2099,14 @@
ads = self.android_devices
phone_setup_voice_general(self.log, ads[0])
- tasks = [(ensure_wifi_connected, (
- self.log, ads[0], self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_general, (self.log, ads[1]))]
+ tasks = [(ensure_wifi_connected,
+ (self.log, ads[0], self.wifi_network_ssid,
+ self.wifi_network_pass)), (phone_setup_voice_general,
+ (self.log, ads[1]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mms_test_mt(ads)
@@ -2079,6 +2134,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call SMS Test.")
if not call_setup_teardown(
@@ -2116,6 +2172,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call SMS Test.")
if not call_setup_teardown(
@@ -2153,6 +2210,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call MMS Test.")
if not call_setup_teardown(
@@ -2190,6 +2248,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call MMS Test.")
if not call_setup_teardown(
@@ -2222,6 +2281,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
if not video_call_setup_teardown(
self.log,
@@ -2255,6 +2315,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
if not video_call_setup_teardown(
self.log,
@@ -2288,6 +2349,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
if not video_call_setup_teardown(
self.log,
@@ -2321,6 +2383,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
if not video_call_setup_teardown(
self.log,
@@ -2358,6 +2421,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call SMS Test.")
if not call_setup_teardown(
@@ -2398,6 +2462,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
self.log.info("Begin In Call SMS Test.")
if not call_setup_teardown(
@@ -2438,6 +2503,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mo_mms_in_2g_call(ads)
@@ -2464,6 +2530,7 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
return self._mt_mms_in_2g_call(ads)
@@ -2490,10 +2557,11 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
- return self._mo_mms_in_2g_call(ads, wifi=True)
+ return self._mo_mms_in_2g_call(ads)
@test_tracker_info(uuid="060def89-01bd-4b44-a49b-a4536fe39165")
@TelephonyBaseTest.tel_test_wrap
@@ -2518,7 +2586,128 @@
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
self.wifi_network_pass)
- return self._mt_mms_in_2g_call(ads, wifi=True)
+ return self._mt_mms_in_2g_call(ads)
+
+ @test_tracker_info(uuid="7de95a56-8055-4c0c-9438-f249403c6078")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_sms_mo_general_after_mobile_data_usage_limit_reached(self):
+ """Test SMS send after mobile data usage limit is reached.
+
+ Airplane mode is off.
+ Set the data limit to the current usage
+ Send SMS from PhoneA to PhoneB.
+ Verify received message on PhoneB is correct.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ ads = self.android_devices
+ try:
+ subscriber_id = ads[0].droid.telephonyGetSubscriberId()
+ data_usage = get_mobile_data_usage(ads[0], subscriber_id)
+ set_mobile_data_usage_limit(ads[0], data_usage, subscriber_id)
+
+ tasks = [(phone_setup_voice_general, (self.log, ads[0])),
+ (phone_setup_voice_general, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ return self._sms_test_mo(ads)
+ finally:
+ remove_mobile_data_usage_limit(ads[0], subscriber_id)
+
+ @test_tracker_info(uuid="df56687f-0932-4b13-952c-ae0ce30b1d7a")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_sms_mt_general_after_mobile_data_usage_limit_reached(self):
+ """Test SMS receive after mobile data usage limit is reached.
+
+ Airplane mode is off.
+ Set the data limit to the current usage
+ Send SMS from PhoneB to PhoneA.
+ Verify received message on PhoneA is correct.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ ads = self.android_devices
+ try:
+ subscriber_id = ads[0].droid.telephonyGetSubscriberId()
+ data_usage = get_mobile_data_usage(ads[0], subscriber_id)
+ set_mobile_data_usage_limit(ads[0], data_usage, subscriber_id)
+
+ tasks = [(phone_setup_voice_general, (self.log, ads[0])),
+ (phone_setup_voice_general, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ return self._sms_test_mt(ads)
+ finally:
+ remove_mobile_data_usage_limit(ads[0], subscriber_id)
+
+ @test_tracker_info(uuid="131f98c6-3b56-44df-b5e7-66f33e2cf117")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_mms_mo_general_after_mobile_data_usage_limit_reached(self):
+ """Test MMS send after mobile data usage limit is reached.
+
+ Airplane mode is off.
+ Set the data limit to the current usage
+ Send MMS from PhoneA to PhoneB.
+ Verify MMS cannot be send.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ ads = self.android_devices
+ try:
+ subscriber_id = ads[0].droid.telephonyGetSubscriberId()
+ data_usage = get_mobile_data_usage(ads[0], subscriber_id)
+ set_mobile_data_usage_limit(ads[0], data_usage, subscriber_id)
+
+ tasks = [(phone_setup_voice_general, (self.log, ads[0])),
+ (phone_setup_voice_general, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ return not self._mms_test_mo(ads)
+ finally:
+ remove_mobile_data_usage_limit(ads[0], subscriber_id)
+
+ @test_tracker_info(uuid="051e259f-0cb9-417d-9a68-8e8a4266fca1")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_mms_mt_general_after_mobile_data_usage_limit_reached(self):
+ """Test MMS receive after mobile data usage limit is reached.
+
+ Airplane mode is off.
+ Set the data limit to the current usage
+ Send MMS from PhoneB to PhoneA.
+ Verify MMS cannot be received.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ ads = self.android_devices
+ try:
+ subscriber_id = ads[0].droid.telephonyGetSubscriberId()
+ data_usage = get_mobile_data_usage(ads[0], subscriber_id)
+ set_mobile_data_usage_limit(ads[0], data_usage, subscriber_id)
+
+ tasks = [(phone_setup_voice_general, (self.log, ads[0])),
+ (phone_setup_voice_general, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ return False
+ time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+ return not self._mms_test_mt(ads)
+ finally:
+ remove_mobile_data_usage_limit(ads[0], subscriber_id)
diff --git a/acts/tests/google/tel/live/TelLiveStressCallTest.py b/acts/tests/google/tel/live/TelLiveStressCallTest.py
index 42f85a9..ebbea5f 100644
--- a/acts/tests/google/tel/live/TelLiveStressCallTest.py
+++ b/acts/tests/google/tel/live/TelLiveStressCallTest.py
@@ -24,15 +24,16 @@
from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
from acts.test_utils.tel.tel_test_utils import hangup_call
from acts.test_utils.tel.tel_test_utils import set_wfc_mode
from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.test_utils.tel.tel_test_utils import verify_incall_state
from acts.test_utils.tel.tel_test_utils import multithread_func
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
@@ -48,6 +49,8 @@
from acts.test_utils.tel.tel_video_utils import video_call_setup
from acts.test_utils.tel.tel_video_utils import \
is_phone_in_call_video_bidirectional
+from acts.logger import epoch_to_log_line_timestamp
+from acts.utils import get_current_epoch_time
from acts.utils import rand_ascii_str
@@ -56,13 +59,12 @@
super(TelLiveStressCallTest, self).setup_class()
self.caller = self.android_devices[0]
self.callee = self.android_devices[1]
+ self.number_of_devices = 2
self.user_params["telephony_auto_rerun"] = False
self.wifi_network_ssid = self.user_params.get(
- "wifi_network_ssid") or self.user_params.get(
- "wifi_network_ssid_2g")
+ "wifi_network_ssid") or self.user_params.get("wifi_network_ssid_2g")
self.wifi_network_pass = self.user_params.get(
- "wifi_network_pass") or self.user_params.get(
- "wifi_network_pass_2g")
+ "wifi_network_pass") or self.user_params.get("wifi_network_pass_2g")
self.phone_call_iteration = int(
self.user_params.get("phone_call_iteration", 500))
self.phone_call_duration = int(
@@ -72,6 +74,9 @@
return True
+ def on_fail(self, test_name, begin_time):
+ pass
+
def _setup_wfc(self):
for ad in self.android_devices:
if not ensure_wifi_connected(
@@ -93,6 +98,28 @@
ad.log.info("Phone is in WFC enabled state.")
return True
+ def _setup_wfc_apm(self):
+ for ad in self.android_devices:
+ toggle_airplane_mode(ad.log, ad, True)
+ if not ensure_wifi_connected(
+ ad.log,
+ ad,
+ self.wifi_network_ssid,
+ self.wifi_network_pass,
+ retries=3):
+ ad.log.error("Phone Wifi connection fails.")
+ return False
+ ad.log.info("Phone WIFI is connected successfully.")
+ if not set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED):
+ ad.log.error("Phone failed to enable Wifi-Calling.")
+ return False
+ ad.log.info("Phone is set in Wifi-Calling successfully.")
+ if not phone_idle_iwlan(self.log, ad):
+ ad.log.error("Phone is not in WFC enabled state.")
+ return False
+ ad.log.info("Phone is in WFC enabled state.")
+ return True
+
def _setup_vt(self):
ads = self.android_devices
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
@@ -170,6 +197,7 @@
for i in range(1, self.phone_call_iteration + 1):
msg = "Stress Call Test %s Iteration: <%s> / <%s>" % (
self.test_name, i, self.phone_call_iteration)
+ begin_time = get_current_epoch_time()
self.log.info(msg)
iteration_result = True
ensure_phones_idle(self.log, self.android_devices)
@@ -179,16 +207,26 @@
iteration_result = False
self.log.error("%s call dialing failure.", msg)
else:
- if network_check_func and not network_check_func(self.log,
- self.caller):
+ if network_check_func and not network_check_func(
+ self.log, self.caller):
fail_count["caller_network_check"] += 1
+ reasons = self.caller.search_logcat(
+ "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause",
+ begin_time)
+ if reasons:
+ self.caller.log.info(reasons[-1]["log_message"])
iteration_result = False
self.log.error("%s network check %s failure.", msg,
network_check_func.__name__)
- if network_check_func and not network_check_func(self.log,
- self.callee):
+ if network_check_func and not network_check_func(
+ self.log, self.callee):
fail_count["callee_network_check"] += 1
+ reasons = self.callee.search_logcat(
+ "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause",
+ begin_time)
+ if reasons:
+ self.callee.log.info(reasons[-1]["log_message"])
iteration_result = False
self.log.error("%s network check failure.", msg)
@@ -208,8 +246,9 @@
self.log.info("%s %s", msg, iteration_result)
if not iteration_result:
- self._take_bug_report("%s_%s" % (self.test_name, i),
- self.begin_time)
+ self._take_bug_report("%s_CallNo_%s" % (self.test_name, i),
+ begin_time)
+ start_qxdm_loggers(self.log, self.android_devices)
if self.sleep_time_between_test_iterations:
self.caller.droid.goToSleepNow()
@@ -317,7 +356,7 @@
""" Wifi calling call stress test
Steps:
- 1. Make Sure PhoneA and PhoneB in Wifi Calling mode.
+ 1. Make Sure PhoneA and PhoneB in WFC On + Wifi Connected
2. Call from PhoneA to PhoneB, hang up on PhoneA.
3, Repeat 2 around N times based on the config setup
@@ -333,6 +372,28 @@
setup_func=self._setup_wfc,
network_check_func=is_phone_in_call_iwlan)
+ @test_tracker_info(uuid="be45c620-b45b-4a06-8424-b17d744d0735")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_wifi_calling_stress_apm(self):
+ """ Wifi calling in AirPlaneMode call stress test
+
+ Steps:
+ 1. Make Sure PhoneA and PhoneB in WFC On + APM ON + Wifi Connected
+ 2. Call from PhoneA to PhoneB, hang up on PhoneA.
+ 3, Repeat 2 around N times based on the config setup
+
+ Expected Results:
+ 1, Verify phone is at IDLE state
+ 2, Verify the phone is at ACTIVE, if it is in dialing, then we retry
+ 3, Verify the phone is IDLE after hung up
+
+ Returns:
+ True if pass; False if fail.
+ """
+ return self.stress_test(
+ setup_func=self._setup_wfc_apm,
+ network_check_func=is_phone_in_call_iwlan)
+
@test_tracker_info(uuid="8af0454b-b4db-46d8-b5cc-e13ec5bc59ab")
@TelephonyBaseTest.tel_test_wrap
def test_call_3g_stress(self):
diff --git a/acts/tests/google/tel/live/TelLiveStressTest.py b/acts/tests/google/tel/live/TelLiveStressTest.py
index 6cb0bca..6f5a203 100644
--- a/acts/tests/google/tel/live/TelLiveStressTest.py
+++ b/acts/tests/google/tel/live/TelLiveStressTest.py
@@ -18,13 +18,16 @@
"""
import collections
+import json
+import os
import random
import time
+
+from acts import utils
from acts.asserts import fail
from acts.test_decorators import test_tracker_info
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_ONLY
+from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
from acts.test_utils.tel.tel_defines import NETWORK_MODE_WCDMA_ONLY
from acts.test_utils.tel.tel_defines import NETWORK_MODE_GLOBAL
from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
@@ -32,46 +35,62 @@
from acts.test_utils.tel.tel_defines import NETWORK_MODE_TDSCDMA_GSM_WCDMA
from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_MODE_CHANGE
+from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts.test_utils.tel.tel_test_utils import STORY_LINE
from acts.test_utils.tel.tel_test_utils import active_file_download_test
+from acts.test_utils.tel.tel_test_utils import is_phone_in_call
from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
-from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import hangup_call_by_adb
+from acts.test_utils.tel.tel_test_utils import initiate_call
from acts.test_utils.tel.tel_test_utils import run_multithread_func
from acts.test_utils.tel.tel_test_utils import set_wfc_mode
from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
+from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
-from acts.test_utils.tel.tel_test_utils import verify_incall_state
from acts.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
+from acts.test_utils.tel.tel_test_utils import wait_for_in_call_active
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
-from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
from acts.test_utils.tel.tel_voice_utils import get_current_voice_rat
-from acts.logger import epoch_to_log_line_timestamp
from acts.utils import get_current_epoch_time
from acts.utils import rand_ascii_str
-from acts.controllers.sl4a_lib.rpc_client import Sl4aProtocolError
-
-IGNORE_EXCEPTIONS = (BrokenPipeError, Sl4aProtocolError)
-EXCEPTION_TOLERANCE = 20
+EXCEPTION_TOLERANCE = 5
class TelLiveStressTest(TelephonyBaseTest):
def setup_class(self):
super(TelLiveStressTest, self).setup_class()
self.dut = self.android_devices[0]
- self.helper = self.android_devices[1]
+ self.single_phone_test = self.user_params.get("single_phone_test",
+ False)
+ # supported file download methods: chrome, sl4a, curl
+ self.file_download_method = self.user_params.get(
+ "file_download_method", "sl4a")
+ if len(self.android_devices) == 1:
+ self.single_phone_test = True
+ if self.single_phone_test:
+ self.android_devices = self.android_devices[:1]
+ self.call_server_number = self.user_params.get(
+ "call_server_number", STORY_LINE)
+ if self.file_download_method == "sl4a":
+ # with single device, do not use sl4a file download
+ # due to stability issue
+ self.file_download_method = "chrome"
+ else:
+ self.android_devices = self.android_devices[:2]
self.user_params["telephony_auto_rerun"] = False
self.wifi_network_ssid = self.user_params.get(
"wifi_network_ssid") or self.user_params.get(
@@ -83,8 +102,9 @@
self.user_params.get("phone_call_iteration", 500))
self.max_phone_call_duration = int(
self.user_params.get("max_phone_call_duration", 600))
+ self.min_sleep_time = int(self.user_params.get("min_sleep_time", 10))
self.max_sleep_time = int(self.user_params.get("max_sleep_time", 120))
- self.max_run_time = int(self.user_params.get("max_run_time", 18000))
+ self.max_run_time = int(self.user_params.get("max_run_time", 14400))
self.max_sms_length = int(self.user_params.get("max_sms_length", 1000))
self.max_mms_length = int(self.user_params.get("max_mms_length", 160))
self.min_sms_length = int(self.user_params.get("min_sms_length", 1))
@@ -96,6 +116,14 @@
return True
+ def setup_test(self):
+ super(TelLiveStressTest, self).setup_test()
+ self.result_info = collections.defaultdict(int)
+ self._init_perf_json()
+
+ def on_fail(self, test_name, begin_time):
+ pass
+
def _setup_wfc(self):
for ad in self.android_devices:
if not ensure_wifi_connected(
@@ -149,275 +177,401 @@
ad.log.info("RAT 2G is enabled successfully.")
return True
- def _send_message(self, ads):
+ def _send_message(self, max_wait_time=2 * MAX_WAIT_TIME_SMS_RECEIVE):
+ if self.single_phone_test:
+ ads = [self.dut, self.dut]
+ else:
+ ads = self.android_devices[:]
+ random.shuffle(ads)
selection = random.randrange(0, 2)
message_type_map = {0: "SMS", 1: "MMS"}
max_length_map = {0: self.max_sms_length, 1: self.max_mms_length}
min_length_map = {0: self.min_sms_length, 1: self.min_mms_length}
length = random.randrange(min_length_map[selection],
max_length_map[selection] + 1)
- text = rand_ascii_str(length)
- message_content_map = {0: [text], 1: [("Mms Message", text, None)]}
message_func_map = {
0: sms_send_receive_verify,
1: mms_send_receive_verify
}
- self.result_info["Total %s" % message_type_map[selection]] += 1
+ message_type = message_type_map[selection]
+ the_number = self.result_info["%s Total" % message_type] + 1
+ begin_time = get_current_epoch_time()
+ start_qxdm_loggers(self.log, self.android_devices)
+ log_msg = "The %s-th %s test: of length %s from %s to %s" % (
+ the_number, message_type, length, ads[0].serial, ads[1].serial)
+ self.log.info(log_msg)
+ for ad in self.android_devices:
+ for session in ad._sl4a_manager.sessions.values():
+ try:
+ session.rpc_client.logI(log_msg)
+ break
+ except Exception as e:
+ ad.log.warning(e)
+ text = "%s: " % log_msg
+ text_length = len(text)
+ if length < text_length:
+ text = text[:length]
+ else:
+ text += rand_ascii_str(length - text_length)
+ message_content_map = {0: [text], 1: [(log_msg, text, None)]}
+ incall_non_ims = False
+ for ad in self.android_devices:
+ if ad.droid.telecomIsInCall() and (
+ not ad.droid.telephonyIsImsRegistered()):
+ incall_non_ims = True
+ break
+
if not message_func_map[selection](self.log, ads[0], ads[1],
- message_content_map[selection]):
- self.log.error("%s of length %s from %s to %s fails",
- message_type_map[selection], length, ads[0].serial,
- ads[1].serial)
- self.result_info["%s failure" % message_type_map[selection]] += 1
+ message_content_map[selection],
+ max_wait_time):
+ self.result_info["%s Total" % message_type] += 1
+ if message_type == "SMS":
+ self.log.error("%s fails", log_msg)
+ self.result_info["%s Failure" % message_type] += 1
+ self._take_bug_report("%s_%s_No_%s_failure" %
+ (self.test_name, message_type,
+ the_number), begin_time)
+ else:
+ if incall_non_ims:
+ self.log.info(
+ "Device not in IMS, MMS in call is not support")
+ self.result_info["Expected In-call MMS failure"] += 1
+ return True
+ else:
+ self.log.error("%s fails", log_msg)
+ self.result_info["MMS Failure"] += 1
+ if self.result_info["MMS Failure"] == 1:
+ self._take_bug_report("%s_%s_No_%s_failure" %
+ (self.test_name, message_type,
+ the_number), begin_time)
return False
else:
- self.log.info("%s of length %s from %s to %s succeed",
- message_type_map[selection], length, ads[0].serial,
- ads[1].serial)
+ self.result_info["%s Total" % message_type] += 1
+ self.log.info("%s succeed", log_msg)
+ self.result_info["%s Success" % message_type] += 1
return True
- def _make_phone_call(self, ads):
- self.result_info["Total Calls"] += 1
- if not call_setup_teardown(
+ def _make_phone_call(self, call_verification_func=None):
+ ads = self.android_devices[:]
+ if not self.single_phone_test:
+ random.shuffle(ads)
+ for ad in ads:
+ hangup_call_by_adb(ad)
+ the_number = self.result_info["Call Total"] + 1
+ duration = random.randrange(self.min_phone_call_duration,
+ self.max_phone_call_duration)
+ result = True
+ if self.single_phone_test:
+ log_msg = "The %s-th phone call test for %ssec duration" % (
+ the_number, duration)
+ else:
+ log_msg = "The %s-th phone call test from %s to %s for %ssec" % (
+ the_number, ads[0].serial, ads[1].serial, duration)
+ self.log.info(log_msg)
+ for ad in ads:
+ try:
+ ad.droid.logI(log_msg)
+ except Exception as e:
+ ad.log.warning(e)
+ begin_time = get_current_epoch_time()
+ start_qxdm_loggers(self.log, self.android_devices, begin_time)
+ if self.single_phone_test:
+ call_setup_result = initiate_call(
self.log,
- ads[0],
- ads[1],
- ad_hangup=ads[random.randrange(0, 2)],
- wait_time_in_call=random.randrange(
- self.min_phone_call_duration,
- self.max_phone_call_duration)):
- self.log.error("Call setup and teardown failed.")
- self.result_info["Call Failure"] += 1
- return False
- self.log.info("Call setup and teardown succeed.")
- return True
+ self.dut,
+ self.call_server_number,
+ wait_time_betwn_call_initcheck=5) and wait_for_in_call_active(
+ self.dut, 60, 3)
+ else:
+ call_setup_result = call_setup_teardown(
+ self.log, ads[0], ads[1], ad_hangup=None, wait_time_in_call=0)
+ if not call_setup_result:
+ self.log.error("%s: Setup Call failed.", log_msg)
+ self.result_info["Call Setup Failure"] += 1
+ failure_reason = "setup"
+ result = False
+ else:
+ elapsed_time = 0
+ check_interval = 5
+ while (elapsed_time < duration):
+ check_interval = min(check_interval, duration - elapsed_time)
+ time.sleep(check_interval)
+ elapsed_time += check_interval
+ time_message = "at <%s>/<%s> second." % (elapsed_time,
+ duration)
+ for ad in ads:
+ if not call_verification_func(self.log, ad):
+ ad.log.error("Call is NOT in correct %s state at %s",
+ call_verification_func.__name__,
+ time_message)
+ self.result_info["Call Maintenance Failure"] += 1
+ failure_reason = "maintenance"
+ reasons = ad.search_logcat(
+ "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause",
+ begin_time)
+ if reasons:
+ ad.log.info(reasons[-1]["log_message"])
+ hangup_call(self.log, ads[0])
+ result = False
+ else:
+ ad.log.info("Call is in correct %s state at %s",
+ call_verification_func.__name__,
+ time_message)
+ if not result:
+ break
+ if not hangup_call(self.log, ads[0]):
+ time.sleep(10)
+ for ad in ads:
+ if ad.droid.telecomIsInCall():
+ ad.log.error("Still in call after hungup")
+ self.result_info["Call Teardown Failure"] += 1
+ failure_reason = "teardown"
+ result = False
+ self.result_info["Call Total"] += 1
+ if not result:
+ self.log.info("%s test failed", log_msg)
+ test_name = "%s_call_No_%s_%s_failure" % (self.test_name,
+ the_number,
+ failure_reason)
+ for ad in ads:
+ log_path = os.path.join(self.log_path, test_name,
+ "%s_binder" % ad.serial)
+ utils.create_dir(log_path)
+ ad.adb.pull("/sys/kernel/debug/binder %s" % log_path)
+ self._take_bug_report(test_name, begin_time)
+ else:
+ self.log.info("%s test succeed", log_msg)
+ self.result_info["Call Success"] += 1
+ if self.result_info["Call Total"] % 50 == 0:
+ test_name = "%s_call_No_%s_success_binder_logs" % (
+ self.test_name, the_number)
+ for ad in ads:
+ log_path = os.path.join(self.log_path, test_name,
+ "%s_binder" % ad.serial)
+ utils.create_dir(log_path)
+ ad.adb.pull("/sys/kernel/debug/binder %s" % log_path)
+ return result
- def _make_volte_call(self, ads):
- self.result_info["Total Calls"] += 1
- if not call_setup_teardown(
- self.log,
- ads[0],
- ads[1],
- ad_hangup=ads[0],
- verify_caller_func=is_phone_in_call_volte,
- verify_callee_func=None,
- wait_time_in_call=random.randrange(
- self.min_phone_call_duration,
- self.max_phone_call_duration)):
- self.log.error("Call setup and teardown failed.")
- self.result_info["Call Failure"] += 1
+ def _prefnetwork_mode_change(self, sub_id):
+ # ModePref change to non-LTE
+ begin_time = get_current_epoch_time()
+ start_qxdm_loggers(self.log, self.android_devices)
+ network_preference_list = [
+ NETWORK_MODE_TDSCDMA_GSM_WCDMA, NETWORK_MODE_WCDMA_ONLY,
+ NETWORK_MODE_GLOBAL, NETWORK_MODE_CDMA, NETWORK_MODE_GSM_ONLY
+ ]
+ network_preference = random.choice(network_preference_list)
+ set_preferred_network_mode_pref(self.log, self.dut, sub_id,
+ network_preference)
+ time.sleep(WAIT_TIME_AFTER_MODE_CHANGE)
+ self.dut.log.info("Current Voice RAT is %s",
+ get_current_voice_rat(self.log, self.dut))
+
+ # ModePref change back to with LTE
+ set_preferred_network_mode_pref(self.log, self.dut, sub_id,
+ NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)
+ time.sleep(WAIT_TIME_AFTER_MODE_CHANGE)
+ rat = get_current_voice_rat(self.log, self.dut)
+ self.dut.log.info("Current Voice RAT is %s", rat)
+ self.result_info["RAT Change Total"] += 1
+ if rat != "LTE":
+ self.result_info["RAT Change Failure"] += 1
+ self._take_bug_report("%s_rat_change_failure" % self.test_name,
+ begin_time)
return False
- self.log.info("Call setup and teardown succeed.")
- return True
+ else:
+ self.result_info["RAT Change Success"] += 1
+ return True
+
+ def _get_result_message(self):
+ msg_list = [
+ "%s: %s" % (count, self.result_info[count])
+ for count in sorted(self.result_info.keys())
+ ]
+ return ", ".join(msg_list)
+
+ def _write_perf_json(self):
+ json_str = json.dumps(self.perf_data, indent=4, sort_keys=True)
+ with open(self.perf_file, 'w') as f:
+ f.write(json_str)
+
+ def _init_perf_json(self):
+ self.perf_file = os.path.join(self.log_path, "%s_perf_data_%s.json" %
+ (self.test_name, self.begin_time))
+ self.perf_data = self.android_devices[0].build_info.copy()
+ self.perf_data["model"] = self.android_devices[0].model
+ self._write_perf_json()
+
+ def _update_perf_json(self):
+ for result_key, result_value in self.result_info.items():
+ self.perf_data[result_key] = result_value
+ self._write_perf_json()
def crash_check_test(self):
failure = 0
while time.time() < self.finishing_time:
- self.dut.log.info(dict(self.result_info))
try:
- begin_time = epoch_to_log_line_timestamp(
- get_current_epoch_time())
+ self.log.info(dict(self.result_info))
+ self._update_perf_json()
+ begin_time = get_current_epoch_time()
time.sleep(self.crash_check_interval)
- crash_report = self.dut.check_crash_report("checking_crash",
- begin_time, True)
- if crash_report:
- self.dut.log.error("Find new crash reports %s",
- crash_report)
- failure += 1
- self.result_info["Crashes"] += 1
- except IGNORE_EXCEPTIONS as e:
+ for ad in self.android_devices:
+ crash_report = ad.check_crash_report(
+ "checking_crash", begin_time, log_crash_report=True)
+ if crash_report:
+ ad.log.error("Find new crash reports %s", crash_report)
+ failure += 1
+ self.result_info["Crashes"] += 1
+ for crash in crash_report:
+ if "ramdump_modem" in crash:
+ self.result_info["Crashes-Modem"] += 1
+ except Exception as e:
self.log.error("Exception error %s", str(e))
self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
- except Exception as e:
- self.finishing_time = time.time()
- raise
- self.dut.log.info("Crashes found: %s", failure)
+ self.log.info("Crashes found: %s", failure)
+ if self.result_info["Exception Errors"] >= EXCEPTION_TOLERANCE:
+ self.log.error("Too many exception errors, quit test")
+ return False
if failure:
- return "%s crashes" % failure
+ return False
else:
- return ""
+ return True
- def call_test(self):
- failure = 0
- total_count = 0
+ def call_test(self, call_verification_func=None):
while time.time() < self.finishing_time:
try:
- ads = [self.dut, self.helper]
- random.shuffle(ads)
- total_count += 1
- if not self._make_phone_call(ads):
- failure += 1
- self._take_bug_report("%s_call_failure" % self.test_name,
- time.strftime("%m-%d-%Y-%H-%M-%S"))
- self.dut.droid.goToSleepNow()
- time.sleep(random.randrange(0, self.max_sleep_time))
- except IGNORE_EXCEPTIONS as e:
+ self._make_phone_call(call_verification_func)
+ except Exception as e:
self.log.error("Exception error %s", str(e))
self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
- except Exception as e:
- self.finishing_time = time.time()
- raise
- self.dut.log.info("Call test failure: %s/%s", failure, total_count)
- if failure:
- return "Call test failure: %s/%s" % (failure, total_count)
+ if self.result_info["Exception Errors"] >= EXCEPTION_TOLERANCE:
+ self.log.error("Too many exception errors, quit test")
+ return False
+ self.log.info("%s", dict(self.result_info))
+ time.sleep(
+ random.randrange(self.min_sleep_time, self.max_sleep_time))
+ if any([
+ self.result_info["Call Setup Failure"],
+ self.result_info["Call Maintenance Failure"],
+ self.result_info["Call Teardown Failure"]
+ ]):
+ return False
else:
- return ""
+ return True
+
+ def message_test(self, max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
+ while time.time() < self.finishing_time:
+ try:
+ self._send_message(max_wait_time=max_wait_time)
+ except Exception as e:
+ self.log.error("Exception error %s", str(e))
+ self.result_info["Exception Errors"] += 1
+ self.log.info(dict(self.result_info))
+ if self.result_info["Exception Errors"] >= EXCEPTION_TOLERANCE:
+ self.log.error("Too many exception errors, quit test")
+ return False
+ time.sleep(
+ random.randrange(self.min_sleep_time, self.max_sleep_time))
+ if self.result_info["SMS Failure"] or (
+ self.result_info["MMS Failure"] / self.result_info["MMS Total"]
+ > 0.3):
+ return False
+ else:
+ return True
+
+ def _data_download(self):
+ #file_names = ["5MB", "10MB", "20MB", "50MB", "200MB", "512MB", "1GB"]
+ file_names = ["5MB", "10MB", "20MB", "50MB", "200MB"]
+ begin_time = get_current_epoch_time()
+ start_qxdm_loggers(self.log, self.android_devices)
+ self.dut.log.info(dict(self.result_info))
+ selection = random.randrange(0, len(file_names))
+ file_name = file_names[selection]
+ self.result_info["File Download Total"] += 1
+ if not active_file_download_test(
+ self.log, self.dut, file_name,
+ method=self.file_download_method):
+ self.result_info["File Download Failure"] += 1
+ if self.result_info["File Download Failure"] == 1:
+ self._take_bug_report(
+ "%s_file_download_failure" % self.test_name, begin_time)
+ return False
+ else:
+ self.result_info["File Download Success"] += 1
+ return True
+
+ def data_test(self):
+ while time.time() < self.finishing_time:
+ try:
+ self._data_download()
+ except Exception as e:
+ self.log.error("Exception error %s", str(e))
+ self.result_info["Exception Errors"] += 1
+ self.log.info("%s", dict(self.result_info))
+ if self.result_info["Exception Errors"] >= EXCEPTION_TOLERANCE:
+ self.log.error("Too many exception errors, quit test")
+ return False
+ time.sleep(
+ random.randrange(self.min_sleep_time, self.max_sleep_time))
+ if self.result_info["File Download Failure"] / self.result_info["File Download Total"] > 0.1:
+ return False
+ else:
+ return True
+
+ def parallel_tests(self, setup_func=None, call_verification_func=None):
+ self.log.info(self._get_result_message())
+ if setup_func and not setup_func():
+ msg = "Test setup %s failed" % setup_func.__name__
+ self.log.error(msg)
+ fail(msg)
+ if not call_verification_func:
+ call_verification_func = is_phone_in_call
+ self.finishing_time = time.time() + self.max_run_time
+ results = run_multithread_func(
+ self.log, [(self.call_test, [call_verification_func]),
+ (self.message_test, []), (self.data_test, []),
+ (self.crash_check_test, [])])
+ result_message = self._get_result_message()
+ self.log.info(result_message)
+ self._update_perf_json()
+ self.result_detail = result_message
+ return all(results)
def volte_modechange_volte_test(self):
- failure = 0
- total_count = 0
sub_id = self.dut.droid.subscriptionGetDefaultSubId()
while time.time() < self.finishing_time:
try:
- ads = [self.dut, self.helper]
- total_count += 1
- if not self._make_volte_call(ads):
- failure += 1
- self._take_bug_report("%s_call_failure" % self.test_name,
- time.strftime("%m-%d-%Y-%H-%M-%S"))
-
- # ModePref change to non-LTE
- network_preference_list = [
- NETWORK_MODE_TDSCDMA_GSM_WCDMA, NETWORK_MODE_WCDMA_ONLY,
- NETWORK_MODE_GLOBAL, NETWORK_MODE_CDMA,
- NETWORK_MODE_GSM_ONLY
- ]
- network_preference = random.choice(network_preference_list)
- set_preferred_network_mode_pref(ads[0].log, ads[0], sub_id,
- network_preference)
- time.sleep(WAIT_TIME_AFTER_MODE_CHANGE)
- self.dut.log.info("Current Voice RAT is %s",
- get_current_voice_rat(self.log, self.dut))
-
- # ModePref change back to with LTE
- set_preferred_network_mode_pref(
- ads[0].log, ads[0], sub_id,
- NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)
- time.sleep(WAIT_TIME_AFTER_MODE_CHANGE)
- self.dut.log.info("Current Voice RAT is %s",
- get_current_voice_rat(self.log, self.dut))
-
- except IGNORE_EXCEPTIONS as e:
+ run_multithread_func(
+ self.log,
+ [(self._data_download, []),
+ (self._make_phone_call, [is_phone_in_call_volte]),
+ (self._send_message, [])])
+ self._prefnetwork_mode_change(sub_id)
+ except Exception as e:
self.log.error("Exception error %s", str(e))
self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
- except Exception as e:
- self.finishing_time = time.time()
- raise
- self.dut.log.info("VoLTE test failure: %s/%s", failure,
- total_count)
- if failure:
- return "VoLTE test failure: %s/%s" % (failure, total_count)
+ self.log.info(dict(self.result_info))
+ if self.result_info["Exception Errors"] >= EXCEPTION_TOLERANCE:
+ self.log.error("Too many exception errors, quit test")
+ return False
+ if self.result_info["Call Failure"] or self.result_info["RAT Change Failure"] or self.result_info["SMS Failure"]:
+ return False
else:
- return ""
+ return True
- def message_test(self):
- failure = 0
- total_count = 0
- while time.time() < self.finishing_time:
- try:
- ads = [self.dut, self.helper]
- random.shuffle(ads)
- total_count += 1
- if not self._send_message(ads):
- failure += 1
- #self._take_bug_report("%s_messaging_failure" % self.test_name,
- # time.strftime("%m-%d-%Y-%H-%M-%S"))
- self.dut.droid.goToSleepNow()
- time.sleep(random.randrange(0, self.max_sleep_time))
- except IGNORE_EXCEPTIONS as e:
- self.log.error("Exception error %s", str(e))
- self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise
- except Exception as e:
- self.finishing_time = time.time()
- raise
- self.dut.log.info("Messaging test failure: %s/%s", failure,
- total_count)
- if failure / total_count > 0.1:
- return "Messaging test failure: %s/%s" % (failure, total_count)
- else:
- return ""
-
- def data_test(self):
- failure = 0
- total_count = 0
- #file_names = ["5MB", "10MB", "20MB", "50MB", "200MB", "512MB", "1GB"]
- file_names = ["5MB", "10MB", "20MB", "50MB", "200MB", "512MB"]
- while time.time() < self.finishing_time:
- try:
- self.dut.log.info(dict(self.result_info))
- self.result_info["Total file download"] += 1
- selection = random.randrange(0, len(file_names))
- file_name = file_names[selection]
- total_count += 1
- if not active_file_download_test(self.log, self.dut,
- file_name):
- self.result_info["%s file download failure" %
- file_name] += 1
- failure += 1
- #self._take_bug_report("%s_download_failure" % self.test_name,
- # time.strftime("%m-%d-%Y-%H-%M-%S"))
- self.dut.droid.goToSleepNow()
- time.sleep(random.randrange(0, self.max_sleep_time))
- except IGNORE_EXCEPTIONS as e:
- self.log.error("Exception error %s", str(e))
- self.result_info["Exception Errors"] += 1
- if self.result_info["Exception Errors"] > EXCEPTION_TOLERANCE:
- self.finishing_time = time.time()
- raise "Too many %s errors" % IGNORE_EXCEPTIONS
- except Exception as e:
- self.log.error(e)
- self.finishing_time = time.time()
- raise
- self.dut.log.info("File download test failure: %s/%s", failure,
- total_count)
- if failure / total_count > 0.1:
- return "File download test failure: %s/%s" % (failure, total_count)
- else:
- return ""
-
- def parallel_tests(self, setup_func=None):
+ def parallel_with_network_change_tests(self, setup_func=None):
if setup_func and not setup_func():
self.log.error("Test setup %s failed", setup_func.__name__)
return False
- self.result_info = collections.defaultdict(int)
self.finishing_time = time.time() + self.max_run_time
- results = run_multithread_func(self.log, [(self.call_test, []), (
- self.message_test, []), (self.data_test, []),
- (self.crash_check_test, [])])
- self.log.info(dict(self.result_info))
- error_message = " ".join(results).strip()
- if error_message:
- self.log.error(error_message)
- fail(error_message)
- return True
-
- def parallel_volte_tests(self, setup_func=None):
- if setup_func and not setup_func():
- self.log.error("Test setup %s failed", setup_func.__name__)
- return False
- self.result_info = collections.defaultdict(int)
- self.finishing_time = time.time() + self.max_run_time
- results = run_multithread_func(
- self.log, [(self.volte_modechange_volte_test, []),
- (self.message_test, []), (self.crash_check_test, [])])
- self.log.info(dict(self.result_info))
- error_message = " ".join(results).strip()
- if error_message:
- self.log.error(error_message)
- fail(error_message)
- return True
+ results = run_multithread_func(self.log,
+ [(self.volte_modechange_volte_test, []),
+ (self.crash_check_test, [])])
+ result_message = self._get_result_message()
+ self.log.info(result_message)
+ self._update_perf_json()
+ self.result_detail = result_message
+ return all(results)
""" Tests Begin """
@@ -431,37 +585,47 @@
@TelephonyBaseTest.tel_test_wrap
def test_lte_volte_parallel_stress(self):
""" VoLTE on stress test"""
- return self.parallel_tests(setup_func=self._setup_lte_volte_enabled)
+ return self.parallel_tests(
+ setup_func=self._setup_lte_volte_enabled,
+ call_verification_func=is_phone_in_call_volte)
@test_tracker_info(uuid="a317c23a-41e0-4ef8-af67-661451cfefcf")
@TelephonyBaseTest.tel_test_wrap
def test_csfb_parallel_stress(self):
""" LTE non-VoLTE stress test"""
- return self.parallel_tests(setup_func=self._setup_lte_volte_disabled)
+ return self.parallel_tests(
+ setup_func=self._setup_lte_volte_disabled,
+ call_verification_func=is_phone_in_call_csfb)
@test_tracker_info(uuid="fdb791bf-c414-4333-9fa3-cc18c9b3b234")
@TelephonyBaseTest.tel_test_wrap
def test_wfc_parallel_stress(self):
""" Wifi calling on stress test"""
- return self.parallel_tests(setup_func=self._setup_wfc)
+ return self.parallel_tests(
+ setup_func=self._setup_wfc,
+ call_verification_func=is_phone_in_call_iwlan)
@test_tracker_info(uuid="4566eef6-55de-4ac8-87ee-58f2ef41a3e8")
@TelephonyBaseTest.tel_test_wrap
def test_3g_parallel_stress(self):
""" 3G stress test"""
- return self.parallel_tests(setup_func=self._setup_3g)
+ return self.parallel_tests(
+ setup_func=self._setup_3g,
+ call_verification_func=is_phone_in_call_3g)
@test_tracker_info(uuid="f34f1a31-3948-4675-8698-372a83b8088d")
@TelephonyBaseTest.tel_test_wrap
def test_call_2g_parallel_stress(self):
""" 2G call stress test"""
- return self.parallel_tests(setup_func=self._setup_2g)
+ return self.parallel_tests(
+ setup_func=self._setup_2g,
+ call_verification_func=is_phone_in_call_2g)
@test_tracker_info(uuid="af580fca-fea6-4ca5-b981-b8c710302d37")
@TelephonyBaseTest.tel_test_wrap
def test_volte_modeprefchange_parallel_stress(self):
""" VoLTE Mode Pref call stress test"""
- return self.parallel_volte_tests(
+ return self.parallel_with_network_change_tests(
setup_func=self._setup_lte_volte_enabled)
""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveVideoDataTest.py b/acts/tests/google/tel/live/TelLiveVideoDataTest.py
index 095215c..f28c7ed 100644
--- a/acts/tests/google/tel/live/TelLiveVideoDataTest.py
+++ b/acts/tests/google/tel/live/TelLiveVideoDataTest.py
@@ -33,6 +33,7 @@
self.stress_test_number = self.get_stress_test_number()
self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+ self.number_of_devices = 2
try:
self.wifi_network_pass = self.user_params["wifi_network_pass"]
diff --git a/acts/tests/google/tel/live/TelLiveVideoTest.py b/acts/tests/google/tel/live/TelLiveVideoTest.py
index 367c484..53b626e 100644
--- a/acts/tests/google/tel/live/TelLiveVideoTest.py
+++ b/acts/tests/google/tel/live/TelLiveVideoTest.py
@@ -98,6 +98,7 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -117,7 +118,7 @@
return True
- @test_tracker_info(uuid="345e6ae9-4e9f-45e6-86a1-b661a84b6293")
+ @test_tracker_info(uuid="8abebda7-6646-4180-a37d-2f0acca63b64")
@TelephonyBaseTest.tel_test_wrap
def test_call_video_to_video_long(self):
""" Test VT<->VT call functionality.
@@ -133,6 +134,7 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -167,6 +169,7 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -202,6 +205,7 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -247,6 +251,7 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -294,6 +299,7 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -351,6 +357,7 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -451,6 +458,7 @@
"""
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -484,6 +492,7 @@
"""
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -608,8 +617,8 @@
call_id_requester, EVENT_VIDEO_SESSION_EVENT)
ad_responder.droid.telecomCallVideoStartListeningForEvent(
call_id_responder, EVENT_VIDEO_SESSION_EVENT)
- self.log.info(
- "Put In-Call UI on {} to background.".format(ad_requester.serial))
+ self.log.info("Put In-Call UI on {} to background.".format(
+ ad_requester.serial))
ad_requester.droid.showHomeScreen()
try:
event_on_responder = ad_responder.ed.pop_event(
@@ -647,8 +656,8 @@
VT_STATE_BIDIRECTIONAL_PAUSED, CALL_STATE_ACTIVE):
return False
- self.log.info(
- "Put In-Call UI on {} to foreground.".format(ad_requester.serial))
+ self.log.info("Put In-Call UI on {} to foreground.".format(
+ ad_requester.serial))
ad_requester.droid.telecomCallVideoStartListeningForEvent(
call_id_requester, EVENT_VIDEO_SESSION_EVENT)
ad_responder.droid.telecomCallVideoStartListeningForEvent(
@@ -696,6 +705,7 @@
@TelephonyBaseTest.tel_test_wrap
def test_call_video_to_video_mo_to_backgroundpause_foregroundresume(self):
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -722,6 +732,7 @@
@TelephonyBaseTest.tel_test_wrap
def test_call_video_to_video_mt_to_backgroundpause_foregroundresume(self):
ads = self.android_devices
+ self.number_of_devices = 2
tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
(self.log, ads[1]))]
if not multithread_func(self.log, tasks):
@@ -752,6 +763,7 @@
Hangup on PhoneC.
Verify all phones not in call.
"""
+ self.number_of_devices = 3
if not hangup_call(self.log, ads[1]):
return False
time.sleep(WAIT_TIME_IN_CALL)
@@ -774,6 +786,7 @@
Accept the call on Phone_C
Verify both calls remain active.
"""
+ self.number_of_devices = 3
# This test case is not supported by VZW.
ads = self.android_devices
tasks = [(phone_setup_video, (self.log, ads[0])),
@@ -845,6 +858,7 @@
Accept the call on Phone_A
Verify both calls remain active.
"""
+ self.number_of_devices = 3
ads = self.android_devices
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_video, (self.log, ads[1])), (phone_setup_volte,
@@ -918,6 +932,7 @@
"""
# This test case is not supported by VZW.
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_volte, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -993,6 +1008,7 @@
# TODO (b/21437650):
# Test will fail. After established 2nd call ~15s, Phone C will drop call.
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_volte, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -1070,6 +1086,7 @@
End Voice call on PhoneA.
"""
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_video, (self.log, ads[1])), (phone_setup_volte,
(self.log, ads[2]))]
@@ -1181,6 +1198,7 @@
"""
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_video, (self.log, ads[1])), (phone_setup_volte,
(self.log, ads[2]))]
@@ -1269,8 +1287,8 @@
# Audio will goto earpiece in here
for ad in [ads[0], ads[1]]:
if get_audio_route(self.log, ad) != AUDIO_ROUTE_EARPIECE:
- self.log.error(
- "{} Audio is not on EARPIECE.".format(ad.serial))
+ self.log.error("{} Audio is not on EARPIECE.".format(
+ ad.serial))
# TODO: b/26337892 Define expected audio route behavior.
time.sleep(WAIT_TIME_IN_CALL)
@@ -1298,8 +1316,8 @@
# Audio will goto earpiece in here
for ad in [ads[0], ads[1]]:
if get_audio_route(self.log, ad) != AUDIO_ROUTE_EARPIECE:
- self.log.error(
- "{} Audio is not on EARPIECE.".format(ad.serial))
+ self.log.error("{} Audio is not on EARPIECE.".format(
+ ad.serial))
# TODO: b/26337892 Define expected audio route behavior.
time.sleep(WAIT_TIME_IN_CALL)
@@ -1327,6 +1345,7 @@
"""
# This test case is not supported by VZW.
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_video, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -1401,6 +1420,7 @@
# TODO: b/21437650 Test will fail. After established 2nd call ~15s,
# Phone C will drop call.
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_video, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -1487,6 +1507,7 @@
# TODO: b/21437650 Test will fail. After established 2nd call ~15s,
# Phone C will drop call.
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_video, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -1570,6 +1591,7 @@
"""
# This test case is not supported by VZW.
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_video, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -1647,6 +1669,7 @@
True if succeed;
False if failed.
"""
+ self.number_of_devices = 3
self.log.info(
"Merge - Step1: Merge to Conf Call and verify Conf Call.")
ads[0].droid.telecomCallJoinCallsInConf(call_ab_id, call_ac_id)
@@ -1670,9 +1693,10 @@
# Check if Conf Call is currently active
if ads[0].droid.telecomCallGetCallState(
call_conf_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_conf_id, ads[
- 0].droid.telecomCallGetCallState(call_conf_id)))
+ self.log.error(
+ "Call_id:{}, state:{}, expected: STATE_ACTIVE".format(
+ call_conf_id,
+ ads[0].droid.telecomCallGetCallState(call_conf_id)))
return False
self.log.info(
@@ -1688,8 +1712,8 @@
if not verify_incall_state(self.log, [ads[1]], False):
return False
- if not (hangup_call(self.log, ads[2]) and
- hangup_call(self.log, ads[0])):
+ if not (hangup_call(self.log, ads[2])
+ and hangup_call(self.log, ads[0])):
self.log.error("Failed to clean up remaining calls")
return False
return True
@@ -1709,6 +1733,7 @@
call_id for conference
"""
+ self.number_of_devices = 2
self.log.info("Step4: Merge to Conf Call and verify Conf Call.")
ads[0].droid.telecomCallJoinCallsInConf(call_ab_id, call_ac_id)
time.sleep(WAIT_TIME_IN_CALL)
@@ -1731,14 +1756,14 @@
if (CALL_PROPERTY_CONFERENCE not in ads[0]
.droid.telecomCallGetProperties(call_conf_id)):
- self.log.error("Conf call id properties wrong: {}".format(ads[
- 0].droid.telecomCallGetProperties(call_conf_id)))
+ self.log.error("Conf call id properties wrong: {}".format(
+ ads[0].droid.telecomCallGetProperties(call_conf_id)))
return False
if (CALL_CAPABILITY_MANAGE_CONFERENCE not in ads[0]
.droid.telecomCallGetCapabilities(call_conf_id)):
- self.log.error("Conf call id capabilities wrong: {}".format(ads[
- 0].droid.telecomCallGetCapabilities(call_conf_id)))
+ self.log.error("Conf call id capabilities wrong: {}".format(
+ ads[0].droid.telecomCallGetCapabilities(call_conf_id)))
return False
if (call_ab_id in calls) or (call_ac_id in calls):
@@ -1752,9 +1777,10 @@
# Check if Conf Call is currently active
if ads[0].droid.telecomCallGetCallState(
call_conf_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_conf_id, ads[
- 0].droid.telecomCallGetCallState(call_conf_id)))
+ self.log.error(
+ "Call_id:{}, state:{}, expected: STATE_ACTIVE".format(
+ call_conf_id,
+ ads[0].droid.telecomCallGetCallState(call_conf_id)))
return False
if not hangup_call(self.log, ads[1]):
@@ -1768,8 +1794,8 @@
if not verify_incall_state(self.log, [ads[1]], False):
return False
- if not (hangup_call(self.log, ads[2]) and
- hangup_call(self.log, ads[0])):
+ if not (hangup_call(self.log, ads[2])
+ and hangup_call(self.log, ads[0])):
self.log.error("Failed to clean up remaining calls")
return False
@@ -1815,6 +1841,7 @@
self, use_cep=False):
# This test case is not supported by VZW.
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_volte, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -1911,6 +1938,7 @@
def _test_call_volte_add_mt_video_accept_as_voice_merge_drop(
self, use_cep=False):
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_volte, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -2010,6 +2038,7 @@
def _test_call_video_add_mo_voice_swap_downgrade_merge_drop(self, use_cep):
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_video, (self.log, ads[1])), (phone_setup_volte,
(self.log, ads[2]))]
@@ -2089,10 +2118,10 @@
return False
self.log.info("Step5: Disable camera on PhoneA and PhoneB.")
- if not video_call_downgrade(
- self.log, ads[0], call_id_video_ab, ads[1],
- get_call_id_in_video_state(self.log, ads[1],
- VT_STATE_BIDIRECTIONAL)):
+ if not video_call_downgrade(self.log, ads[0], call_id_video_ab, ads[1],
+ get_call_id_in_video_state(
+ self.log, ads[1],
+ VT_STATE_BIDIRECTIONAL)):
self.log.error("Failed to disable video on PhoneA.")
return False
if not video_call_downgrade(self.log, ads[1],
@@ -2159,9 +2188,10 @@
return self._test_call_video_add_mt_voice_swap_downgrade_merge_drop(
True)
- def _test_call_video_add_mt_voice_swap_downgrade_merge_drop(self,
- use_cep=False):
+ def _test_call_video_add_mt_voice_swap_downgrade_merge_drop(
+ self, use_cep=False):
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_video, (self.log, ads[1])), (phone_setup_volte,
(self.log, ads[2]))]
@@ -2242,10 +2272,10 @@
return False
self.log.info("Step5: Disable camera on PhoneA and PhoneB.")
- if not video_call_downgrade(
- self.log, ads[0], call_id_video_ab, ads[1],
- get_call_id_in_video_state(self.log, ads[1],
- VT_STATE_BIDIRECTIONAL)):
+ if not video_call_downgrade(self.log, ads[0], call_id_video_ab, ads[1],
+ get_call_id_in_video_state(
+ self.log, ads[1],
+ VT_STATE_BIDIRECTIONAL)):
self.log.error("Failed to disable video on PhoneA.")
return False
if not video_call_downgrade(self.log, ads[1],
@@ -2310,6 +2340,7 @@
def _test_call_volte_add_mo_video_downgrade_merge_drop(self, use_cep):
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_volte, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -2371,10 +2402,10 @@
return False
self.log.info("Step4: Disable camera on PhoneA and PhoneC.")
- if not video_call_downgrade(
- self.log, ads[0], call_id_video_ac, ads[2],
- get_call_id_in_video_state(self.log, ads[2],
- VT_STATE_BIDIRECTIONAL)):
+ if not video_call_downgrade(self.log, ads[0], call_id_video_ac, ads[2],
+ get_call_id_in_video_state(
+ self.log, ads[2],
+ VT_STATE_BIDIRECTIONAL)):
self.log.error("Failed to disable video on PhoneA.")
return False
if not video_call_downgrade(self.log, ads[2],
@@ -2441,6 +2472,7 @@
# TODO: b/21437650 Test will fail. After established 2nd call ~15s,
# Phone C will drop call.
ads = self.android_devices
+ self.number_of_devices = 3
tasks = [(phone_setup_video, (self.log, ads[0])),
(phone_setup_volte, (self.log, ads[1])), (phone_setup_video,
(self.log, ads[2]))]
@@ -2502,10 +2534,10 @@
return False
self.log.info("Step4: Disable camera on PhoneA and PhoneC.")
- if not video_call_downgrade(
- self.log, ads[0], call_id_video_ac, ads[2],
- get_call_id_in_video_state(self.log, ads[2],
- VT_STATE_BIDIRECTIONAL)):
+ if not video_call_downgrade(self.log, ads[0], call_id_video_ac, ads[2],
+ get_call_id_in_video_state(
+ self.log, ads[2],
+ VT_STATE_BIDIRECTIONAL)):
self.log.error("Failed to disable video on PhoneA.")
return False
if not video_call_downgrade(self.log, ads[2],
@@ -2564,8 +2596,8 @@
if wait_for_video_enabled(self.log, ads[0],
MAX_WAIT_TIME_VOLTE_ENABLED):
self.log.error(
- "{} failed to <report vt enabled false> for {}s."
- .format(ads[0].serial, MAX_WAIT_TIME_VOLTE_ENABLED))
+ "{} failed to <report vt enabled false> for {}s.".format(
+ ads[0].serial, MAX_WAIT_TIME_VOLTE_ENABLED))
return False
self.log.info(
"Step4 Attempt to make VT call, verify call is AUDIO_ONLY.")
diff --git a/acts/tests/google/tel/live/TelLiveVoiceConfTest.py b/acts/tests/google/tel/live/TelLiveVoiceConfTest.py
index fabc646..f0e335b 100644
--- a/acts/tests/google/tel/live/TelLiveVoiceConfTest.py
+++ b/acts/tests/google/tel/live/TelLiveVoiceConfTest.py
@@ -94,8 +94,7 @@
def _hangup_call(self, ad, device_description='Device'):
if not hangup_call(self.log, ad):
- self.log.error("Failed to hang up on {}: {}".format(
- device_description, ad.serial))
+ ad.log.error("Failed to hang up on %s", device_description)
return False
return True
@@ -133,8 +132,7 @@
for ad in ads:
ad.droid.telecomCallClearCallList()
if num_active_calls(self.log, ad) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ad.serial))
+ ad.log.error("Phone Call List is not empty.")
raise _CallException("Clear call list failed.")
self.log.info("Step1: Call From PhoneA to PhoneB.")
@@ -148,7 +146,7 @@
raise _CallException("PhoneA call PhoneB failed.")
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 1:
raise _CallException("Call list verify failed.")
call_ab_id = calls[0]
@@ -205,8 +203,7 @@
for ad in ads:
ad.droid.telecomCallClearCallList()
if num_active_calls(self.log, ad) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ad.serial))
+ ad.log.error("Phone Call List is not empty.")
raise _CallException("Clear call list failed.")
self.log.info("Step1: Call From PhoneA to PhoneB.")
@@ -220,7 +217,7 @@
raise _CallException("PhoneA call PhoneB failed.")
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 1:
raise _CallException("Call list verify failed.")
call_ab_id = calls[0]
@@ -322,8 +319,7 @@
for ad in ads:
ad.droid.telecomCallClearCallList()
if num_active_calls(self.log, ad) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ad.serial))
+ ad.log.error("Phone Call List is not empty.")
raise _CallException("Clear call list failed.")
self.log.info("Step1: Call From PhoneB to PhoneA.")
@@ -337,7 +333,7 @@
raise _CallException("PhoneB call PhoneA failed.")
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 1:
raise _CallException("Call list verify failed.")
call_ab_id = calls[0]
@@ -375,8 +371,7 @@
# make sure PhoneA is CDMA phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
- self.log.error(
- "{} not CDMA phone, abort this 1x test.".format(ads[0].serial))
+ ads[0].log.error("not CDMA phone, abort this 1x test.")
return None, None, None
call_ab_id = self._three_phone_call_mo_add_mo(
@@ -389,7 +384,7 @@
return None, None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 3:
return None, None, None
for call_id in calls:
@@ -417,8 +412,7 @@
# make sure PhoneA is CDMA phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
- self.log.error(
- "{} not CDMA phone, abort this 1x test.".format(ads[0].serial))
+ ads[0].log.error("not CDMA phone, abort this 1x test.")
return None, None, None
call_ab_id = self._three_phone_call_mo_add_mt(
@@ -432,7 +426,7 @@
call_conf_id = None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 3:
return None, None, None
for call_id in calls:
@@ -443,7 +437,7 @@
call_ac_id = call_id
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(
self.log,
ads,
@@ -472,8 +466,7 @@
# make sure PhoneA is CDMA phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
- self.log.error(
- "{} not CDMA phone, abort this 1x test.".format(ads[0].serial))
+ ads[0].log.error("not CDMA phone, abort this 1x test.")
return None, None, None
call_ab_id = self._three_phone_call_mt_add_mt(
@@ -487,7 +480,7 @@
call_conf_id = None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 3:
return None, None, None
for call_id in calls:
@@ -498,7 +491,7 @@
call_ac_id = call_id
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(
self.log,
ads,
@@ -535,7 +528,7 @@
return False
time.sleep(WAIT_TIME_IN_CALL)
calls = host.droid.telecomCallGetCallIds()
- self.log.info("Calls in Host{}".format(calls))
+ host.log.info("Calls list: %s", calls)
if num_active_calls(self.log, host) != 3:
return False
if not verify_incall_state(self.log, [host, second_drop_ad], True):
@@ -590,9 +583,9 @@
if not self._hangup_call(host, "Host"):
return False
time.sleep(WAIT_TIME_IN_CALL)
- if not verify_incall_state(self.log, [
- host, held_participant_ad, active_participant_ad
- ], False):
+ if not verify_incall_state(
+ self.log, [host, held_participant_ad, active_participant_ad],
+ False):
return False
return True
@@ -639,7 +632,7 @@
host.droid.telecomCallMergeToConf(call_conf_id)
time.sleep(WAIT_TIME_IN_CALL)
calls = host.droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ host.log.info("Calls in Phone %s", calls)
if num_active_calls(self.log, host) != 3:
return False
if not verify_incall_state(self.log, [host], True):
@@ -681,7 +674,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -690,7 +683,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -727,7 +720,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -736,7 +729,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -773,7 +766,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -782,7 +775,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -811,8 +804,7 @@
# make sure PhoneB and PhoneC are GSM phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".
- format(ad.serial))
+ ad.log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mo_add_mo(
@@ -826,7 +818,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -835,7 +827,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -864,8 +856,7 @@
# make sure PhoneB and PhoneC are GSM phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".
- format(ad.serial))
+ ad.log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mo_add_mt(
@@ -879,7 +870,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -888,7 +879,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -917,8 +908,7 @@
# make sure PhoneB and PhoneC are GSM phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".
- format(ad.serial))
+ ad.log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mt_add_mt(
@@ -932,7 +922,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -941,7 +931,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -970,8 +960,7 @@
# make sure PhoneB and PhoneC are CDMA phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
- self.log.error(
- "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
+ ad.log.error("not CDMA phone, abort 1x swap test.")
return None, None
call_ab_id = self._three_phone_call_mo_add_mo(
@@ -983,7 +972,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -992,7 +981,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1021,8 +1010,7 @@
# make sure PhoneB and PhoneC are CDMA phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
- self.log.error(
- "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
+ ad.log.error("not CDMA phone, abort 1x swap test.")
return None, None
call_ab_id = self._three_phone_call_mo_add_mt(
@@ -1034,7 +1022,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1043,7 +1031,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1072,8 +1060,7 @@
# make sure PhoneB and PhoneC are CDMA phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
- self.log.error(
- "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
+ self.log.error("not CDMA phone, abort 1x swap test.")
return None, None
call_ab_id = self._three_phone_call_mt_add_mt(
@@ -1085,7 +1072,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1094,7 +1081,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1122,8 +1109,7 @@
# make sure PhoneA is GSM phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".format(
- ads[0].serial))
+ ad.log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mo_add_mo(
@@ -1136,7 +1122,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1145,7 +1131,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1173,8 +1159,7 @@
# make sure PhoneA is GSM phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".format(
- ads[0].serial))
+ ads[0].log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mt_add_mt(
@@ -1187,7 +1172,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1196,7 +1181,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1224,8 +1209,7 @@
# make sure PhoneA is GSM phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".format(
- ads[0].serial))
+ ads[0].log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mo_add_mt(
@@ -1238,7 +1222,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1247,7 +1231,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1275,8 +1259,7 @@
# make sure PhoneA is GSM phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".format(
- ads[0].serial))
+ ads[0].log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mo_add_mo(
@@ -1289,7 +1272,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1298,7 +1281,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1326,8 +1309,7 @@
# make sure PhoneA is GSM phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".format(
- ads[0].serial))
+ ads[0].log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mo_add_mt(
@@ -1340,7 +1322,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1349,7 +1331,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1357,8 +1339,8 @@
return call_ab_id, call_ac_id
- def _test_ims_conference_merge_drop_second_call_no_cep(self, call_ab_id,
- call_ac_id):
+ def _test_ims_conference_merge_drop_second_call_no_cep(
+ self, call_ab_id, call_ac_id):
"""Test conference merge and drop in VoLTE call.
PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneB.
@@ -1381,10 +1363,9 @@
ads[0].droid.telecomCallJoinCallsInConf(call_ab_id, call_ac_id)
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 1:
- self.log.error("Total number of call ids in {} is not 1.".format(
- ads[0].serial))
+ ads[0].log.error("Total number of call lists is not 1.")
if get_cep_conference_call_id(ads[0]) is not None:
self.log.error("CEP enabled.")
else:
@@ -1403,9 +1384,9 @@
# Check if Conf Call is currently active
if ads[0].droid.telecomCallGetCallState(
call_conf_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_conf_id, ads[
- 0].droid.telecomCallGetCallState(call_conf_id)))
+ ads[0].log.error(
+ "Call_id:%s, state:%s, expected: STATE_ACTIVE", call_conf_id,
+ ads[0].droid.telecomCallGetCallState(call_conf_id))
return False
self.log.info("Step5: End call on PhoneC and verify call continues.")
@@ -1413,7 +1394,7 @@
return False
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if not verify_incall_state(self.log, [ads[0], ads[1]], True):
return False
if not verify_incall_state(self.log, [ads[2]], False):
@@ -1451,7 +1432,7 @@
ads[0].droid.telecomCallJoinCallsInConf(call_ab_id, call_ac_id)
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
call_conf_id = get_cep_conference_call_id(ads[0])
if call_conf_id is None:
@@ -1462,21 +1443,23 @@
calls.remove(call_conf_id)
if (set(ads[0].droid.telecomCallGetCallChildren(call_conf_id)) !=
set(calls)):
- self.log.error(
- "Children list<{}> for conference call is not correct.".format(
- ads[0].droid.telecomCallGetCallChildren(call_conf_id)))
+ ads[0].log.error(
+ "Children list %s for conference call is not correct.",
+ ads[0].droid.telecomCallGetCallChildren(call_conf_id))
return None
if (CALL_PROPERTY_CONFERENCE not in ads[0]
.droid.telecomCallGetProperties(call_conf_id)):
- self.log.error("Conf call id properties wrong: {}".format(ads[
- 0].droid.telecomCallGetProperties(call_conf_id)))
+ ads[0].log.error(
+ "Conf call id % properties wrong: %s", call_conf_id,
+ ads[0].droid.telecomCallGetProperties(call_conf_id))
return None
if (CALL_CAPABILITY_MANAGE_CONFERENCE not in ads[0]
.droid.telecomCallGetCapabilities(call_conf_id)):
- self.log.error("Conf call id capabilities wrong: {}".format(ads[
- 0].droid.telecomCallGetCapabilities(call_conf_id)))
+ ads[0].log.error(
+ "Conf call id %s capabilities wrong: %s", call_conf_id,
+ ads[0].droid.telecomCallGetCapabilities(call_conf_id))
return None
if (call_ab_id in calls) or (call_ac_id in calls):
@@ -1490,9 +1473,9 @@
# Check if Conf Call is currently active
if ads[0].droid.telecomCallGetCallState(
call_conf_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_conf_id, ads[
- 0].droid.telecomCallGetCallState(call_conf_id)))
+ ads[0].log.error(
+ "Call_ID: %s, state: %s, expected: STATE_ACTIVE", call_conf_id,
+ ads[0].droid.telecomCallGetCallState(call_conf_id))
return None
return call_conf_id
@@ -1527,7 +1510,7 @@
return False
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if not verify_incall_state(self.log, [ads[0], ads[1]], True):
return False
if not verify_incall_state(self.log, [ads[2]], False):
@@ -1740,10 +1723,9 @@
ads[0].droid.telecomCallJoinCallsInConf(call_ab_id, call_ac_id)
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 3:
- self.log.error("Total number of call ids in {} is not 3.".format(
- ads[0].serial))
+ ads[0].log.error("Total number of call ids is not 3.")
return False
call_conf_id = None
for call_id in calls:
@@ -1758,9 +1740,9 @@
# Check if Conf Call is currently active
if ads[0].droid.telecomCallGetCallState(
call_conf_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_conf_id, ads[
- 0].droid.telecomCallGetCallState(call_conf_id)))
+ ads[0].log.error(
+ "Call_id: %s, state: %s, expected: STATE_ACTIVE", call_conf_id,
+ ads[0].droid.telecomCallGetCallState(call_conf_id))
return False
self.log.info("Step5: End call on PhoneC and verify call continues.")
@@ -1768,7 +1750,7 @@
return False
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 1:
return False
if not verify_incall_state(self.log, [ads[0], ads[1]], True):
@@ -1806,17 +1788,16 @@
"""
- self.log.info(
- "Hangup at {}, verify call continues.".format(ad_hangup.serial))
+ ad_hangup.log.info("Hangup, verify call continues.")
if not self._hangup_call(ad_hangup):
ad_hangup.log.error("Phone fails to hang up")
return False
time.sleep(WAIT_TIME_IN_CALL)
if ad_verify.droid.telecomCallGetCallState(call_id) != call_state:
- self.log.error("Call_id:{}, state:{}, expected: {}".format(
- call_id,
- ad_verify.droid.telecomCallGetCallState(call_id), call_state))
+ ad_verify.log.error(
+ "Call_id: %s, state: %s, expected: %s", call_id,
+ ad_verify.droid.telecomCallGetCallState(call_id), call_state)
return False
ad_verify.log.info("Call in expected %s state", call_state)
# TODO: b/26296375 add voice check.
@@ -1860,7 +1841,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1869,7 +1850,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1907,7 +1888,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1916,7 +1897,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -1954,7 +1935,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -1963,7 +1944,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2001,7 +1982,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA: %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -2010,7 +1991,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2048,7 +2029,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA: %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -2057,7 +2038,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2095,7 +2076,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -2104,7 +2085,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2133,8 +2114,7 @@
# make sure PhoneB and PhoneC are GSM phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".
- format(ad.serial))
+ ad.log.error("not GSM phone, abort wcdma swap test.")
return None, None
# To make thing simple, for epdg, setup should be called before calling
@@ -2149,7 +2129,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -2158,7 +2138,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2187,8 +2167,7 @@
# make sure PhoneB and PhoneC are GSM phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".
- format(ad.serial))
+ ad.log.error("not GSM phone, abort wcdma swap test.")
return None, None
# To make thing simple, for epdg, setup should be called before calling
@@ -2203,7 +2182,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -2212,7 +2191,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2241,8 +2220,7 @@
# make sure PhoneB and PhoneC are GSM phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".
- format(ad.serial))
+ ad.log.error("not GSM phone, abort wcdma swap test.")
return None, None
# To make thing simple, for epdg, setup should be called before calling
@@ -2257,7 +2235,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -2266,7 +2244,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2295,8 +2273,7 @@
# make sure PhoneB and PhoneC are CDMA phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
- self.log.error(
- "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
+ ad.log.error("not CDMA phone, abort 1x swap test.")
return None, None
# To make thing simple, for epdg, setup should be called before calling
@@ -2309,7 +2286,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -2318,7 +2295,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2347,8 +2324,7 @@
# make sure PhoneB and PhoneC are CDMA phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
- self.log.error(
- "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
+ ad.log.error("not CDMA phone, abort 1x swap test.")
return None, None
# To make thing simple, for epdg, setup should be called before calling
@@ -2361,7 +2337,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -2370,7 +2346,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2399,8 +2375,7 @@
# make sure PhoneB and PhoneC are CDMA phone before proceed.
for ad in [ads[1], ads[2]]:
if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
- self.log.error(
- "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
+ ad.log.error("not CDMA phone, abort 1x swap test.")
return None, None
# To make thing simple, for epdg, setup should be called before calling
@@ -2413,7 +2388,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -2422,7 +2397,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -2454,10 +2429,9 @@
ads[0].droid.telecomCallJoinCallsInConf(call_ab_id, call_ac_id)
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 1:
- self.log.error("Total number of call ids in {} is not 1.".format(
- ads[0].serial))
+ ads[0].log.error("Total number of call ids is not 1.")
return False
call_conf_id = None
for call_id in calls:
@@ -2472,9 +2446,9 @@
# Check if Conf Call is currently active
if ads[0].droid.telecomCallGetCallState(
call_conf_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_conf_id, ads[
- 0].droid.telecomCallGetCallState(call_conf_id)))
+ ads[0].log.error(
+ "Call_id: %s, state: %s, expected: STATE_ACTIVE", call_conf_id,
+ ads[0].droid.telecomCallGetCallState(call_conf_id))
return False
self.log.info("Step5: End call on PhoneC and verify call continues.")
@@ -2482,7 +2456,7 @@
return False
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if not verify_incall_state(self.log, [ads[0], ads[1]], True):
return False
if not verify_incall_state(self.log, [ads[2]], False):
@@ -2563,8 +2537,8 @@
ads = self.android_devices
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mo_add()
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2574,8 +2548,8 @@
self.log.error("1x Conference merge failed.")
self.log.info("End call on PhoneC, and end call on PhoneB.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[2],
- ads[1])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[2], ads[1])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="a36b02a6-480e-4cb6-9201-bd8bfa5ae8a4")
@@ -2600,8 +2574,8 @@
ads = self.android_devices
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mo_add()
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2636,8 +2610,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(
0)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2647,8 +2621,8 @@
return False
self.log.info("End call on PhoneC, and end call on PhoneB.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[2],
- ads[1])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[2], ads[1])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="9dc16b45-3470-44c8-abf8-19cd5944a53c")
@@ -2677,8 +2651,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(
2)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2688,8 +2662,8 @@
return False
self.log.info("End call on PhoneC, and end call on PhoneB.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[2],
- ads[1])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[2], ads[1])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="dc7a3187-142e-4754-a914-d0241397a2b3")
@@ -2716,8 +2690,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(
1)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2727,8 +2701,8 @@
return False
self.log.info("End call on PhoneB, and end call on PhoneC.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[1],
- ads[2])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[1], ads[2])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="24cd0ef0-1a69-4603-89c2-0f2b96715348")
@@ -2753,8 +2727,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(
0)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2764,8 +2738,8 @@
return False
self.log.info("End call on PhoneB, and end call on PhoneC.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[1],
- ads[2])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[1], ads[2])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="1c5c1780-84c2-4547-9e57-eeadac6569d7")
@@ -2794,8 +2768,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(
2)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2805,8 +2779,8 @@
return False
self.log.info("End call on PhoneB, and end call on PhoneC.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[1],
- ads[2])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[1], ads[2])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="928a2b21-c4ca-4553-9acc-8d3db61ed6eb")
@@ -2833,8 +2807,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(
1)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2844,8 +2818,8 @@
return False
self.log.info("End call on PhoneC, and end call on PhoneB.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[2],
- ads[1])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[2], ads[1])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="deb57627-a717-41f0-b8f4-f3ccf9ce2e15")
@@ -2870,8 +2844,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(
0)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2910,8 +2884,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(
2)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2948,8 +2922,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(
1)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2984,8 +2958,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(
0)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -2995,8 +2969,8 @@
return False
self.log.info("End call on PhoneC, and end call on PhoneB.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[2],
- ads[1])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[2], ads[1])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="736aa74e-1d0b-4f85-b0f7-11840543cf54")
@@ -3025,8 +2999,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(
2)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -3036,8 +3010,8 @@
return False
self.log.info("End call on PhoneC, and end call on PhoneB.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[2],
- ads[1])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[2], ads[1])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="3eee6b6e-e1b1-43ec-82d5-d298b514fc07")
@@ -3064,8 +3038,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(
1)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -3075,8 +3049,8 @@
return False
self.log.info("End call on PhoneB, and end call on PhoneC.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[1],
- ads[2])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[1], ads[2])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="432549a9-e4bb-44d3-bd44-befffc1af02d")
@@ -3101,8 +3075,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(
0)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -3112,8 +3086,8 @@
return False
self.log.info("End call on PhoneB, and end call on PhoneC.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[1],
- ads[2])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[1], ads[2])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="c8f30fc1-8586-4eb0-854e-264989fd69b8")
@@ -3142,8 +3116,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(
2)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -3153,8 +3127,8 @@
return False
self.log.info("End call on PhoneB, and end call on PhoneC.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[1],
- ads[2])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[1], ads[2])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="065ba51e-9843-4018-8009-7fdc6590011d")
@@ -3181,8 +3155,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(
1)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -3192,8 +3166,8 @@
return False
self.log.info("End call on PhoneC, and end call on PhoneB.")
- return self._test_1x_multi_call_drop_from_participant(ads[0], ads[2],
- ads[1])
+ return self._test_1x_multi_call_drop_from_participant(
+ ads[0], ads[2], ads[1])
@TelephonyBaseTest.tel_test_wrap
@test_tracker_info(uuid="69c69449-d430-4f00-ae19-c51242561ac9")
@@ -3218,8 +3192,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(
0)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -3258,8 +3232,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(
2)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -3296,8 +3270,8 @@
call_ab_id, call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(
1)
- if ((call_ab_id is None) or (call_ac_id is None) or
- (call_conf_id is None)):
+ if ((call_ab_id is None) or (call_ac_id is None)
+ or (call_conf_id is None)):
self.log.error("Failed to setup 3 way call.")
return False
@@ -7205,10 +7179,9 @@
ads[0].droid.telecomCallJoinCallsInConf(call_ab_id, call_ac_id)
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 3:
- self.log.error("Total number of call ids in {} is not 3.".format(
- ads[0].serial))
+ ads[0].log.error("Total number of call ids is not 3.")
return False
call_conf_id = None
for call_id in calls:
@@ -7223,9 +7196,9 @@
# Check if Conf Call currently active
if ads[0].droid.telecomCallGetCallState(
call_conf_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_conf_id, ads[
- 0].droid.telecomCallGetCallState(call_conf_id)))
+ ads[0].log.error(
+ "Call_id: %s, state: %s, expected: STATE_ACTIVE", call_conf_id,
+ ads[0].droid.telecomCallGetCallState(call_conf_id))
return False
# Unmerge
@@ -7233,12 +7206,11 @@
ads[0].droid.telecomCallSplitFromConf(call_ab_id)
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
# Are there 2 calls?
if num_active_calls(self.log, ads[0]) != 2:
- self.log.error("Total number of call ids in {} is not 2".format(
- ads[0].serial))
+ ads[0].log.error("Total number of call ids is not 2")
return False
# Unmerged calls not dropped?
@@ -7249,9 +7221,9 @@
# Unmerged call in call state ACTIVE?
if ads[0].droid.telecomCallGetCallState(
call_ab_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_ab_id, ads[0]
- .droid.telecomCallGetCallState(call_ab_id)))
+ ads[0].log.error("Call_id: %s, state:%s, expected: STATE_ACTIVE",
+ call_ab_id,
+ ads[0].droid.telecomCallGetCallState(call_ab_id))
return False
# Swap call
@@ -7264,9 +7236,9 @@
# Other call in call state ACTIVE?
if ads[0].droid.telecomCallGetCallState(
call_ac_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_ac_id, ads[0]
- .droid.telecomCallGetCallState(call_ac_id)))
+ ads[0].log.error("Call_id: %s, state: %s, expected: STATE_ACTIVE",
+ call_ac_id,
+ ads[0].droid.telecomCallGetCallState(call_ac_id))
return False
# All calls still CONNECTED?
@@ -7785,8 +7757,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -7816,8 +7788,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -7847,8 +7819,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8403,8 +8375,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8434,8 +8406,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8465,8 +8437,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8497,8 +8469,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8527,8 +8499,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8558,8 +8530,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8589,8 +8561,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8621,8 +8593,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8651,8 +8623,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8681,8 +8653,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8712,8 +8684,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8743,8 +8715,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8773,8 +8745,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8803,8 +8775,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8834,8 +8806,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -8865,8 +8837,8 @@
tasks = [(phone_setup_iwlan,
(self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1])), (
- phone_setup_voice_3g, (self.log, ads[2]))]
+ (phone_setup_voice_3g, (self.log, ads[1])),
+ (phone_setup_voice_3g, (self.log, ads[2]))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
return False
@@ -9612,8 +9584,7 @@
# make sure PhoneA is GSM phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".format(
- ads[0].serial))
+ ads[0].log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mo_add_mo(
@@ -9626,7 +9597,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -9635,7 +9606,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -9663,8 +9634,7 @@
# make sure PhoneA is GSM phone before proceed.
if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_GSM):
- self.log.error("{} not GSM phone, abort wcdma swap test.".format(
- ads[0].serial))
+ ads[0].log.error("not GSM phone, abort wcdma swap test.")
return None, None
call_ab_id = self._three_phone_call_mt_add_mt(
@@ -9677,7 +9647,7 @@
return None, None
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 2:
return None, None
if calls[0] == call_ab_id:
@@ -9686,7 +9656,7 @@
call_ac_id = calls[0]
if num_swaps > 0:
- self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+ self.log.info("Step3: Begin Swap x%s test.", num_swaps)
if not swap_calls(self.log, ads, call_ab_id, call_ac_id,
num_swaps):
self.log.error("Swap test failed.")
@@ -9717,10 +9687,9 @@
ads[0].droid.telecomCallJoinCallsInConf(call_ab_id, call_ac_id)
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 3:
- self.log.error("Total number of call ids in {} is not 3.".format(
- ads[0].serial))
+ ads[0].log.error("Total number of call ids is not 3.")
return False
call_conf_id = None
for call_id in calls:
@@ -9735,9 +9704,9 @@
# Check if Conf Call is currently active
if ads[0].droid.telecomCallGetCallState(
call_conf_id) != CALL_STATE_ACTIVE:
- self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
- format(call_conf_id, ads[
- 0].droid.telecomCallGetCallState(call_conf_id)))
+ ads[0].log.error(
+ "Call_id: %s, state: %s, expected: STATE_ACTIVE", call_conf_id,
+ ads[0].droid.telecomCallGetCallState(call_conf_id))
return False
self.log.info("Step5: End call on PhoneC and verify call continues.")
@@ -9745,7 +9714,7 @@
return False
time.sleep(WAIT_TIME_IN_CALL)
calls = ads[0].droid.telecomCallGetCallIds()
- self.log.info("Calls in PhoneA{}".format(calls))
+ ads[0].log.info("Calls in PhoneA %s", calls)
if num_active_calls(self.log, ads[0]) != 1:
return False
if not verify_incall_state(self.log, [ads[0], ads[1]], True):
diff --git a/acts/tests/google/tel/live/TelLiveVoiceTest.py b/acts/tests/google/tel/live/TelLiveVoiceTest.py
index 4740a47..1d04c6a 100644
--- a/acts/tests/google/tel/live/TelLiveVoiceTest.py
+++ b/acts/tests/google/tel/live/TelLiveVoiceTest.py
@@ -18,20 +18,13 @@
"""
import time
-import os
+
+from acts import signals
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel.tel_subscription_utils import \
- get_subid_from_slot_index
-from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
-from acts.test_utils.tel.tel_subscription_utils import \
- set_subid_for_message
-from acts.test_utils.tel.tel_subscription_utils import \
- set_subid_for_outgoing_call
from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
from acts.test_utils.tel.tel_defines import GEN_2G
-from acts.test_utils.tel.tel_defines import GEN_3G
from acts.test_utils.tel.tel_defines import GEN_4G
from acts.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
from acts.test_utils.tel.tel_defines import CALL_STATE_HOLDING
@@ -40,14 +33,9 @@
from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
from acts.test_utils.tel.tel_defines import PHONE_TYPE_CDMA
from acts.test_utils.tel.tel_defines import PHONE_TYPE_GSM
-from acts.test_utils.tel.tel_defines import RAT_3G
-from acts.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
-from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
-from acts.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID
from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
-from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
from acts.test_utils.tel.tel_subscription_utils import \
@@ -57,31 +45,25 @@
from acts.test_utils.tel.tel_test_utils import call_setup_teardown
from acts.test_utils.tel.tel_test_utils import \
call_voicemail_erase_all_pending_voicemail
-from acts.test_utils.tel.tel_test_utils import \
- ensure_network_generation_for_subscription
from acts.test_utils.tel.tel_test_utils import active_file_download_task
from acts.utils import adb_shell_ping
-from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
from acts.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts.test_utils.tel.tel_test_utils import get_mobile_data_usage
from acts.test_utils.tel.tel_test_utils import get_phone_number
from acts.test_utils.tel.tel_test_utils import hangup_call
from acts.test_utils.tel.tel_test_utils import initiate_call
-from acts.test_utils.tel.tel_test_utils import is_droid_in_rat_family
from acts.test_utils.tel.tel_test_utils import multithread_func
from acts.test_utils.tel.tel_test_utils import num_active_calls
from acts.test_utils.tel.tel_test_utils import phone_number_formatter
+from acts.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit
from acts.test_utils.tel.tel_test_utils import run_multithread_func
-from acts.test_utils.tel.tel_test_utils import set_call_state_listen_level
+from acts.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
from acts.test_utils.tel.tel_test_utils import set_phone_number
-from acts.test_utils.tel.tel_test_utils import set_wfc_mode
-from acts.test_utils.tel.tel_test_utils import setup_sim
-from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
from acts.test_utils.tel.tel_test_utils import verify_http_connection
from acts.test_utils.tel.tel_test_utils import verify_incall_state
from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
from acts.test_utils.tel.tel_test_utils import wait_for_ringing_call
-from acts.test_utils.tel.tel_test_utils import wait_for_not_network_rat
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts.test_utils.tel.tel_test_utils import wait_for_state
from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
from acts.test_utils.tel.tel_test_utils import set_wifi_to_default
@@ -124,11 +106,13 @@
self.long_duration_call_total_duration = self.user_params.get(
"long_duration_call_total_duration",
DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION)
+ self.tcpdump_proc = [None, None]
+ self.number_of_devices = 2
""" Tests Begin """
@TelephonyBaseTest.tel_test_wrap
- @test_tracker_info(uuid="8036004e-e42e-441f-b32d-96069be71ec2")
+ @test_tracker_info(uuid="fca3f9e1-447a-416f-9a9c-50b7161981bf")
def test_call_mo_voice_general(self):
""" General voice to voice call.
@@ -152,7 +136,7 @@
None, None)
@TelephonyBaseTest.tel_test_wrap
- @test_tracker_info(uuid="448e1597-c28f-4e1d-88fd-3158e6b7c630")
+ @test_tracker_info(uuid="69faeb84-3830-47c0-ad80-dc657381a83b")
def test_call_mt_voice_general(self):
""" General voice to voice call.
@@ -213,6 +197,9 @@
Returns:
True if pass; False if fail.
"""
+ if self.android_devices[0].droid.telephonyGetSimCountryIso() == "ca":
+ raise signals.TestSkip("7 digit dialing not supported")
+
ads = self.android_devices
tasks = [(phone_setup_volte, (self.log, ads[0])), (phone_setup_volte,
@@ -246,6 +233,9 @@
Returns:
True if pass; False if fail.
"""
+ if self.android_devices[0].droid.telephonyGetSimCountryIso() == "ca":
+ raise signals.TestSkip("10 digit dialing not supported")
+
ads = self.android_devices
tasks = [(phone_setup_volte, (self.log, ads[0])), (phone_setup_volte,
@@ -583,41 +573,49 @@
Returns:
True if pass; False if fail.
"""
+ result = True
try:
- (tcpdump_pid, tcpdump_file) = \
- start_adb_tcpdump(ads[0], self.test_name)
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ self.tcpdump_proc[1] = start_adb_tcpdump(ads[1], self.test_name)
tasks = [(phone_setup_iwlan, (self.log, ads[0], apm_mode, wfc_mode,
wifi_ssid, wifi_pwd)),
(phone_setup_iwlan, (self.log, ads[1], apm_mode, wfc_mode,
wifi_ssid, wifi_pwd))]
if not multithread_func(self.log, tasks):
self.log.error("Phone Failed to Set Up Properly.")
+ result = False
return False
ad_ping = ads[0]
- call_task = (two_phone_call_short_seq, (
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
- ads[1], phone_idle_iwlan, is_phone_in_call_iwlan, None,
- WAIT_TIME_IN_CALL_FOR_IMS))
+ call_task = (two_phone_call_short_seq,
+ (self.log, ads[0], phone_idle_iwlan,
+ is_phone_in_call_iwlan, ads[1], phone_idle_iwlan,
+ is_phone_in_call_iwlan, None,
+ WAIT_TIME_IN_CALL_FOR_IMS))
ping_task = (adb_shell_ping, (ad_ping, DEFAULT_PING_DURATION))
results = run_multithread_func(self.log, [ping_task, call_task])
if not results[1]:
self.log.error("Call setup failed in active ICMP transfer.")
- return False
if results[0]:
self.log.info(
"ICMP transfer succeeded with parallel phone call.")
- return True
else:
self.log.error(
"ICMP transfer failed with parallel phone call.")
- return False
+ result = all(results)
+ return result
finally:
- if tcpdump_pid is not None:
- stop_adb_tcpdump(ads[0], tcpdump_pid, tcpdump_file)
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
+ if self.tcpdump_proc[1] is not None:
+ stop_adb_tcpdump(ads[1], self.tcpdump_proc[1], not result,
+ self.test_name)
+ self.tcpdump_proc[1] = None
@test_tracker_info(uuid="a4a043c0-f4ba-4405-9262-42c752cc4487")
@TelephonyBaseTest.tel_test_wrap
@@ -667,18 +665,35 @@
True if pass; False if fail.
"""
ads = [self.android_devices[0], self.android_devices[1]]
- tasks = [(phone_setup_iwlan_cellular_preferred, (
- self.log, ads[0], self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_iwlan_cellular_preferred,
- (self.log, ads[1], self.wifi_network_ssid,
- self.wifi_network_pass))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ self.tcpdump_proc[1] = start_adb_tcpdump(ads[1], self.test_name)
+ tasks = [(phone_setup_iwlan_cellular_preferred,
+ (self.log, ads[0], self.wifi_network_ssid,
+ self.wifi_network_pass)),
+ (phone_setup_iwlan_cellular_preferred,
+ (self.log, ads[1], self.wifi_network_ssid,
+ self.wifi_network_pass))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return False
- return two_phone_call_short_seq(
- self.log, ads[0], None, is_phone_in_call_not_iwlan, ads[1], None,
- is_phone_in_call_not_iwlan, None, WAIT_TIME_IN_CALL_FOR_IMS)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], None, is_phone_in_call_not_iwlan, ads[1],
+ None, is_phone_in_call_not_iwlan, None,
+ WAIT_TIME_IN_CALL_FOR_IMS)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
+ if self.tcpdump_proc[1] is not None:
+ stop_adb_tcpdump(ads[1], self.tcpdump_proc[1], not result,
+ self.test_name)
+ self.tcpdump_proc[1] = None
@test_tracker_info(uuid="0d63c250-d9e7-490c-8c48-0a6afbad5f88")
@TelephonyBaseTest.tel_test_wrap
@@ -745,19 +760,28 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_volte, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_volte, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
-
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_volte, is_phone_in_call_volte, None,
- WAIT_TIME_IN_CALL_FOR_IMS)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_volte, is_phone_in_call_volte, None,
+ WAIT_TIME_IN_CALL_FOR_IMS)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="6e0630a9-63b2-4ea1-8ec9-6560f001905c")
@TelephonyBaseTest.tel_test_wrap
@@ -773,19 +797,28 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_volte, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_volte, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
-
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_volte, is_phone_in_call_volte, None,
- WAIT_TIME_IN_CALL_FOR_IMS)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_volte, is_phone_in_call_volte, None,
+ WAIT_TIME_IN_CALL_FOR_IMS)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="51077985-2229-491f-9a54-1ff53871758c")
@TelephonyBaseTest.tel_test_wrap
@@ -801,19 +834,28 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], True, WFC_MODE_WIFI_ONLY,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_volte, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], True, WFC_MODE_WIFI_ONLY,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_volte, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
-
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_volte, is_phone_in_call_volte, None,
- WAIT_TIME_IN_CALL_FOR_IMS)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_volte, is_phone_in_call_volte, None,
+ WAIT_TIME_IN_CALL_FOR_IMS)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="fff9edcd-1ace-4f2d-a09b-06f3eea56cca")
@TelephonyBaseTest.tel_test_wrap
@@ -829,19 +871,28 @@
True if pass; False if fail.
"""
ads = self.android_devices
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], True, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_volte, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], True, WFC_MODE_WIFI_PREFERRED,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_volte, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
-
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_volte, is_phone_in_call_volte, None,
- WAIT_TIME_IN_CALL_FOR_IMS)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_volte, is_phone_in_call_volte, None,
+ WAIT_TIME_IN_CALL_FOR_IMS)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="8591554e-4e38-406c-97bf-8921d5329c47")
@TelephonyBaseTest.tel_test_wrap
@@ -857,19 +908,29 @@
True if pass; False if fail.
"""
ads = self.android_devices
- # Turn OFF WiFi for Phone B
- set_wifi_to_default(self.log, ads[1])
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_csfb, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ # Turn OFF WiFi for Phone B
+ set_wifi_to_default(self.log, ads[1])
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_csfb, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_csfb, is_phone_in_call_csfb, None)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_csfb, is_phone_in_call_csfb, None)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="9711888d-5b1e-4d05-86e9-98f94f46098b")
@TelephonyBaseTest.tel_test_wrap
@@ -885,19 +946,29 @@
True if pass; False if fail.
"""
ads = self.android_devices
- # Turn OFF WiFi for Phone B
- set_wifi_to_default(self.log, ads[1])
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_csfb, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ # Turn OFF WiFi for Phone B
+ set_wifi_to_default(self.log, ads[1])
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_csfb, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_csfb, is_phone_in_call_csfb, None)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_csfb, is_phone_in_call_csfb, None)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="902c96a4-858f-43ff-bd56-6d7d27004320")
@TelephonyBaseTest.tel_test_wrap
@@ -913,19 +984,29 @@
True if pass; False if fail.
"""
ads = self.android_devices
- # Turn OFF WiFi for Phone B
- set_wifi_to_default(self.log, ads[1])
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], True, WFC_MODE_WIFI_ONLY,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_csfb, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ # Turn OFF WiFi for Phone B
+ set_wifi_to_default(self.log, ads[1])
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], True, WFC_MODE_WIFI_ONLY,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_csfb, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_csfb, is_phone_in_call_csfb, None)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_csfb, is_phone_in_call_csfb, None)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="362a5396-ebda-4706-a73a-d805e5028fd7")
@TelephonyBaseTest.tel_test_wrap
@@ -941,19 +1022,29 @@
True if pass; False if fail.
"""
ads = self.android_devices
- # Turn OFF WiFi for Phone B
- set_wifi_to_default(self.log, ads[1])
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], True, WFC_MODE_WIFI_PREFERRED,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_csfb, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ # Turn OFF WiFi for Phone B
+ set_wifi_to_default(self.log, ads[1])
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], True, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_csfb, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_csfb, is_phone_in_call_csfb, None)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_csfb, is_phone_in_call_csfb, None)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="647bb859-46bc-4e3e-b6ab-7944d3bbcc26")
@TelephonyBaseTest.tel_test_wrap
@@ -969,19 +1060,29 @@
True if pass; False if fail.
"""
ads = self.android_devices
- # Turn OFF WiFi for Phone B
- set_wifi_to_default(self.log, ads[1])
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ # Turn OFF WiFi for Phone B
+ set_wifi_to_default(self.log, ads[1])
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_voice_3g, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_3g, is_phone_in_call_3g, None)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_3g, is_phone_in_call_3g, None)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="3688ea1f-a52d-4a35-9df4-d5ed0985e49b")
@TelephonyBaseTest.tel_test_wrap
@@ -997,19 +1098,29 @@
True if pass; False if fail.
"""
ads = self.android_devices
- # Turn OFF WiFi for Phone B
- set_wifi_to_default(self.log, ads[1])
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ # Turn OFF WiFi for Phone B
+ set_wifi_to_default(self.log, ads[1])
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_voice_3g, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return result
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_3g, is_phone_in_call_3g, None)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_3g, is_phone_in_call_3g, None)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="f4efc821-fbaf-4ec2-b89b-5a47354344f0")
@TelephonyBaseTest.tel_test_wrap
@@ -1025,19 +1136,29 @@
True if pass; False if fail.
"""
ads = self.android_devices
- # Turn OFF WiFi for Phone B
- set_wifi_to_default(self.log, ads[1])
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], True, WFC_MODE_WIFI_ONLY,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ # Turn OFF WiFi for Phone B
+ set_wifi_to_default(self.log, ads[1])
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], True, WFC_MODE_WIFI_ONLY,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_voice_3g, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return False
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_3g, is_phone_in_call_3g, None)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_3g, is_phone_in_call_3g, None)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="2b1345b7-3b62-44bd-91ad-9c5a4925b0e1")
@TelephonyBaseTest.tel_test_wrap
@@ -1053,19 +1174,29 @@
True if pass; False if fail.
"""
ads = self.android_devices
- # Turn OFF WiFi for Phone B
- set_wifi_to_default(self.log, ads[1])
- tasks = [(phone_setup_iwlan,
- (self.log, ads[0], True, WFC_MODE_WIFI_PREFERRED,
- self.wifi_network_ssid, self.wifi_network_pass)),
- (phone_setup_voice_3g, (self.log, ads[1]))]
- if not multithread_func(self.log, tasks):
- self.log.error("Phone Failed to Set Up Properly.")
- return False
+ result = True
+ try:
+ self.tcpdump_proc[0] = start_adb_tcpdump(ads[0], self.test_name)
+ # Turn OFF WiFi for Phone B
+ set_wifi_to_default(self.log, ads[1])
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], True, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_voice_3g, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ result = False
+ return False
- return two_phone_call_short_seq(
- self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
- phone_idle_3g, is_phone_in_call_3g, None)
+ result = two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_3g, is_phone_in_call_3g, None)
+ return result
+ finally:
+ if self.tcpdump_proc[0] is not None:
+ stop_adb_tcpdump(ads[0], self.tcpdump_proc[0], not result,
+ self.test_name)
+ self.tcpdump_proc[0] = None
@test_tracker_info(uuid="7b3fea22-114a-442e-aa12-dde3b6001681")
@TelephonyBaseTest.tel_test_wrap
@@ -1179,7 +1310,7 @@
phone_idle_iwlan, is_phone_in_call_iwlan, None,
WAIT_TIME_IN_CALL_FOR_IMS)
- @test_tracker_info(uuid="7049de19-3abf-48df-868f-18d0af829393")
+ @test_tracker_info(uuid="a7293d6c-0fdb-4842-984a-e4c6395fd41d")
@TelephonyBaseTest.tel_test_wrap
def test_call_epdg_to_epdg_long_wfc_wifi_preferred(self):
""" WiFi Preferred, WiFi calling to WiFi Calling test
@@ -1243,7 +1374,7 @@
phone_idle_iwlan, is_phone_in_call_iwlan, None,
WAIT_TIME_IN_CALL_FOR_IMS)
- @test_tracker_info(uuid="2b926e4a-f493-41fa-98af-20d25ec132bb")
+ @test_tracker_info(uuid="3c751d79-7159-4407-a63c-96f835dd6cb0")
@TelephonyBaseTest.tel_test_wrap
def test_call_epdg_to_epdg_long_apm_wfc_wifi_preferred(self):
""" Airplane + WiFi Preferred, WiFi calling to WiFi Calling test
@@ -1275,7 +1406,7 @@
phone_idle_iwlan, is_phone_in_call_iwlan, None,
WAIT_TIME_IN_CALL_FOR_IMS)
- @test_tracker_info(uuid="30d5d573-043f-4d8b-98e0-e7f7bc9b8d6f")
+ @test_tracker_info(uuid="9deab765-e2da-4826-bae8-ba8755551a1b")
@TelephonyBaseTest.tel_test_wrap
def test_call_csfb_3g_to_csfb_3g_long(self):
""" CSFB 3G to CSFB 3G call test
@@ -1380,8 +1511,8 @@
self.log.info("Final Count - Success: {}, Failure: {} - {}%".format(
success_count, fail_count,
str(100 * success_count / (success_count + fail_count))))
- if success_count / (success_count + fail_count
- ) >= MINIMUM_SUCCESS_RATE:
+ if success_count / (
+ success_count + fail_count) >= MINIMUM_SUCCESS_RATE:
return True
else:
return False
@@ -1439,8 +1570,8 @@
self.log.info("Final Count - Success: {}, Failure: {} - {}%".format(
success_count, fail_count,
str(100 * success_count / (success_count + fail_count))))
- if success_count / (success_count + fail_count
- ) >= MINIMUM_SUCCESS_RATE:
+ if success_count / (
+ success_count + fail_count) >= MINIMUM_SUCCESS_RATE:
return True
else:
return False
@@ -1498,8 +1629,8 @@
self.log.info("Final Count - Success: {}, Failure: {} - {}%".format(
success_count, fail_count,
str(100 * success_count / (success_count + fail_count))))
- if success_count / (success_count + fail_count
- ) >= MINIMUM_SUCCESS_RATE:
+ if success_count / (
+ success_count + fail_count) >= MINIMUM_SUCCESS_RATE:
return True
else:
return False
@@ -1557,8 +1688,8 @@
self.log.info("Final Count - Success: {}, Failure: {} - {}%".format(
success_count, fail_count,
str(100 * success_count / (success_count + fail_count))))
- if success_count / (success_count + fail_count
- ) >= MINIMUM_SUCCESS_RATE:
+ if success_count / (
+ success_count + fail_count) >= MINIMUM_SUCCESS_RATE:
return True
else:
return False
@@ -1616,8 +1747,8 @@
self.log.info("Final Count - Success: {}, Failure: {} - {}%".format(
success_count, fail_count,
str(100 * success_count / (success_count + fail_count))))
- if success_count / (success_count + fail_count
- ) >= MINIMUM_SUCCESS_RATE:
+ if success_count / (
+ success_count + fail_count) >= MINIMUM_SUCCESS_RATE:
return True
else:
return False
@@ -1669,8 +1800,8 @@
self.log.info("Final Count - Success: {}, Failure: {}".format(
success_count, fail_count))
- if success_count / (success_count + fail_count
- ) >= MINIMUM_SUCCESS_RATE:
+ if success_count / (
+ success_count + fail_count) >= MINIMUM_SUCCESS_RATE:
return True
else:
return False
@@ -1722,8 +1853,8 @@
self.log.info("Final Count - Success: {}, Failure: {}".format(
success_count, fail_count))
- if success_count / (success_count + fail_count
- ) >= MINIMUM_SUCCESS_RATE:
+ if success_count / (
+ success_count + fail_count) >= MINIMUM_SUCCESS_RATE:
return True
else:
return False
@@ -1810,8 +1941,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MO Call Hold/Unhold Test.")
@@ -1855,8 +1986,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MO Call Hold/Unhold Test.")
@@ -1900,8 +2031,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MO Call Hold/Unhold Test.")
@@ -1945,8 +2076,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MO Call Hold/Unhold Test.")
@@ -1990,8 +2121,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MT Call Hold/Unhold Test.")
@@ -2035,8 +2166,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MT Call Hold/Unhold Test.")
@@ -2080,8 +2211,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MT Call Hold/Unhold Test.")
@@ -2125,8 +2256,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MT Call Hold/Unhold Test.")
@@ -2168,8 +2299,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MO Call Hold/Unhold Test.")
@@ -2211,8 +2342,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MT Call Hold/Unhold Test.")
@@ -2258,8 +2389,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MO Call Hold/Unhold Test.")
@@ -2305,8 +2436,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MT Call Hold/Unhold Test.")
@@ -2352,8 +2483,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MO Call Hold/Unhold Test.")
@@ -2399,8 +2530,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MT Call Hold/Unhold Test.")
@@ -2645,7 +2776,7 @@
self.log, ads[0], phone_idle_2g, is_phone_in_call_2g, ads[1],
phone_idle_2g, is_phone_in_call_2g, None)
- @test_tracker_info(uuid="6e24e64f-aa0e-4101-89ed-4cc30c738c7e")
+ @test_tracker_info(uuid="947f3178-735b-4ac2-877c-a06a94972457")
@TelephonyBaseTest.tel_test_wrap
def test_call_2g_to_2g_long(self):
""" Test 2g<->2g call functionality.
@@ -2698,8 +2829,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MO Call Hold/Unhold Test.")
@@ -2744,8 +2875,8 @@
ads[0].droid.telecomCallClearCallList()
if num_active_calls(self.log, ads[0]) != 0:
- self.log.error("Phone {} Call List is not empty."
- .format(ads[0].serial))
+ self.log.error("Phone {} Call List is not empty.".format(
+ ads[0].serial))
return False
self.log.info("Begin MT Call Hold/Unhold Test.")
@@ -2886,13 +3017,13 @@
ad_caller.droid.telecomCallClearCallList()
if num_active_calls(self.log, ad_caller) != 0:
- self.log.error(
- "Phone {} has ongoing calls.".format(ad_caller.serial))
+ self.log.error("Phone {} has ongoing calls.".format(
+ ad_caller.serial))
return False
if not initiate_call(self.log, ad_caller, callee_number):
- self.log.error(
- "Phone was {} unable to initate a call".format(ads[0].serial))
+ self.log.error("Phone was {} unable to initate a call".format(
+ ads[0].serial))
return False
if not wait_for_ringing_call(self.log, ad_callee, caller_number):
@@ -2962,7 +3093,7 @@
caller_verifier, callee_verifier,
wait_time_in_call):
#wait time for active data transfer
- time.sleep(10)
+ time.sleep(5)
return call_setup_teardown(log, ad_caller, ad_callee, ad_hangup,
caller_verifier, callee_verifier,
wait_time_in_call)
@@ -2975,9 +3106,6 @@
MAX_WAIT_TIME_NW_SELECTION)
return False
- #toggle_airplane_mode(self.log, self.android_devices[0], False)
- #wifi_toggle_state(self.log, self.android_devices[0], False)
-
self.android_devices[0].droid.telephonyToggleDataConnection(True)
if not wait_for_cell_data_connection(
self.log, self.android_devices[0], True):
@@ -2996,10 +3124,23 @@
ad_callee = self.android_devices[0]
ad_download = self.android_devices[0]
+ ad_download.ensure_screen_on()
+ ad_download.adb.shell('am start -a android.intent.action.VIEW -d '
+ '"https://www.youtube.com/watch?v=VHF-XK0Vg1s"')
+ if wait_for_state(ad_download.droid.audioIsMusicActive, True, 15, 1):
+ ad_download.log.info("Before call, audio is in MUSIC_state")
+ else:
+ ad_download.log.warning("Before call, audio is not in MUSIC state")
call_task = (_call_setup_teardown, (self.log, ad_caller, ad_callee,
- ad_caller, None, None, 60))
+ ad_caller, None, None, 30))
download_task = active_file_download_task(self.log, ad_download)
results = run_multithread_func(self.log, [download_task, call_task])
+ if wait_for_state(ad_download.droid.audioIsMusicActive, True, 15, 1):
+ ad_download.log.info("After call hangup, audio is back to music")
+ else:
+ ad_download.log.warning(
+ "After call hang up, audio is not back to music")
+ ad_download.force_stop_apk("com.google.android.youtube")
if not results[1]:
self.log.error("Call setup failed in active data transfer.")
return False
@@ -3009,8 +3150,9 @@
elif not allow_data_transfer_interruption:
self.log.error("Data transfer failed with parallel phone call.")
return False
- ad_download.log.info("Retry data transfer after call hung up")
- return download_task[0](*download_task[1])
+ else:
+ ad_download.log.info("Retry data transfer after call hung up")
+ return download_task[0](*download_task[1])
@test_tracker_info(uuid="aa40e7e1-e64a-480b-86e4-db2242449555")
@TelephonyBaseTest.tel_test_wrap
@@ -3354,5 +3496,441 @@
return self._test_call_setup_in_active_data_transfer(
None, DIRECTION_MOBILE_TERMINATED)
+ def _test_call_setup_in_active_youtube_video(
+ self,
+ nw_gen=None,
+ call_direction=DIRECTION_MOBILE_ORIGINATED,
+ allow_data_transfer_interruption=False):
+ """Test call can be established during active data connection.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting playing youtube video.
+ Initiate a voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if nw_gen:
+ if not ensure_network_generation(
+ self.log, self.android_devices[0], nw_gen,
+ MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+ self.log.error("Device failed to reselect in %s.",
+ MAX_WAIT_TIME_NW_SELECTION)
+ return False
+ else:
+ ensure_phones_default_state(self.log, self.android_devices)
+ self.android_devices[0].droid.telephonyToggleDataConnection(True)
+ if not wait_for_cell_data_connection(self.log, self.android_devices[0],
+ True):
+ self.log.error("Data connection is not on cell")
+ return False
+
+ if not verify_http_connection(self.log, self.android_devices[0]):
+ self.log.error("HTTP connection is not available")
+ return False
+
+ if call_direction == DIRECTION_MOBILE_ORIGINATED:
+ ad_caller = self.android_devices[0]
+ ad_callee = self.android_devices[1]
+ else:
+ ad_caller = self.android_devices[1]
+ ad_callee = self.android_devices[0]
+ ad_download = self.android_devices[0]
+
+ ad_download.log.info("Open an youtube video")
+ ad_download.ensure_screen_on()
+ ad_download.adb.shell('am start -a android.intent.action.VIEW -d '
+ '"https://www.youtube.com/watch?v=VHF-XK0Vg1s"')
+ if wait_for_state(ad_download.droid.audioIsMusicActive, True, 15, 1):
+ ad_download.log.info("Before call, audio is in MUSIC_state")
+ else:
+ ad_download.log.warning("Before call, audio is not in MUSIC state")
+
+ if not call_setup_teardown(self.log, ad_caller, ad_callee, ad_caller,
+ None, None, 30):
+ self.log.error("Call setup failed in active youtube video")
+ result = False
+ else:
+ self.log.info("Call setup succeed in active youtube video")
+ result = True
+
+ if wait_for_state(ad_download.droid.audioIsMusicActive, True, 15, 1):
+ ad_download.log.info("After call hangup, audio is back to music")
+ else:
+ ad_download.log.warning(
+ "After call hang up, audio is not back to music")
+ ad_download.force_stop_apk("com.google.android.youtube")
+ return result
+
+ @test_tracker_info(uuid="1dc9f03f-1b6c-4c17-993b-3acafdc26ea3")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mo_voice_general_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting an youtube video.
+ Initiate a MO voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ return self._test_call_setup_in_active_youtube_video(
+ None, DIRECTION_MOBILE_ORIGINATED)
+
+ @test_tracker_info(uuid="32bc8fab-a0b9-4d47-8afb-940d1fdcde02")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mt_voice_general_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting an youtube video.
+ Initiate a MT voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ return self._test_call_setup_in_active_youtube_video(
+ None, DIRECTION_MOBILE_TERMINATED)
+
+ @test_tracker_info(uuid="72204212-e0c8-4447-be3f-ae23b2a63a1c")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mo_voice_volte_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting an youtube video.
+ Initiate a MO voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_volte(self.log, self.android_devices[0]):
+ self.android_devices[0].log.error("Failed to setup VoLTE")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ GEN_4G, DIRECTION_MOBILE_ORIGINATED)
+
+ @test_tracker_info(uuid="84cd3ab9-a2b2-4ef9-b531-ee6201bec128")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mt_voice_volte_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting an youtube video.
+ Initiate a MT voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_volte(self.log, self.android_devices[0]):
+ self.android_devices[0].log.error("Failed to setup VoLTE")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ GEN_4G, DIRECTION_MOBILE_TERMINATED)
+
+ @test_tracker_info(uuid="a8dca8d3-c44c-40a6-be56-931b4be5499b")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mo_voice_csfb_in_active_youtube_video(self):
+ """Test call can be established during active youbube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting an youtube video.
+ Initiate a MO voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_csfb(self.log, self.android_devices[0]):
+ self.android_devices[0].log.error("Failed to setup VoLTE")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ GEN_4G,
+ DIRECTION_MOBILE_ORIGINATED,
+ allow_data_transfer_interruption=True)
+
+ @test_tracker_info(uuid="d11f7263-f51d-4ea3-916a-0df4f52023ce")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mt_voice_csfb_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting an youtube video.
+ Initiate a MT voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_csfb(self.log, self.android_devices[0]):
+ self.android_devices[0].log.error("Failed to setup VoLTE")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ GEN_4G,
+ DIRECTION_MOBILE_TERMINATED,
+ allow_data_transfer_interruption=True)
+
+ @test_tracker_info(uuid="676378b4-94b7-4ad7-8242-7ccd2bf1efba")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mo_voice_3g_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting an youtube video.
+ Initiate a MO voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_voice_3g(self.log, self.android_devices[0]):
+ self.android_devices[0].log.error("Failed to setup 3G")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ GEN_3G,
+ DIRECTION_MOBILE_ORIGINATED,
+ allow_data_transfer_interruption=True)
+
+ @test_tracker_info(uuid="6216fc6d-2aa2-4eb9-90e2-5791cb31c12e")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mt_voice_3g_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting youtube video.
+ Initiate a MT voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_voice_3g(self.log, self.android_devices[0]):
+ self.android_devices[0].log.error("Failed to setup 3G")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ GEN_3G,
+ DIRECTION_MOBILE_TERMINATED,
+ allow_data_transfer_interruption=True)
+
+ @test_tracker_info(uuid="58ec9783-6f8e-49f6-8dae-9dd33108b6f9")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mo_voice_2g_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting youtube video.
+ Initiate a MO voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_voice_2g(self.log, self.android_devices[0]):
+ self.android_devices[0].log.error("Failed to setup voice in 2G")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ GEN_2G,
+ DIRECTION_MOBILE_ORIGINATED,
+ allow_data_transfer_interruption=True)
+
+ @test_tracker_info(uuid="e8ba7c0c-48a3-4fc6-aa34-a2e1c570521a")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mt_voice_2g_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, disable WiFi, enable Cellular Data.
+ Make sure phone in <nw_gen>.
+ Starting an youtube video.
+ Initiate a MT voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_voice_2g(self.log, self.android_devices[0]):
+ self.android_devices[0].log.error("Failed to setup voice in 2G")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ GEN_2G,
+ DIRECTION_MOBILE_TERMINATED,
+ allow_data_transfer_interruption=True)
+
+ @test_tracker_info(uuid="eb8971c1-b34a-430f-98df-0d4554c7ab12")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mo_voice_wifi_wfc_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn off airplane mode, turn on wfc and wifi.
+ Starting youtube video.
+ Initiate a MO voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_iwlan(self.log, self.android_devices[0], False,
+ WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid,
+ self.wifi_network_pass):
+ self.android_devices[0].log.error(
+ "Failed to setup IWLAN with NON-APM WIFI WFC on")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ None, DIRECTION_MOBILE_ORIGINATED)
+
+ @test_tracker_info(uuid="275a93d6-1f39-40c8-893f-ff77afd09e54")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mt_voice_wifi_wfc_in_active_youtube_video(self):
+ """Test call can be established during active youtube_video.
+
+ Turn off airplane mode, turn on wfc and wifi.
+ Starting an youtube video.
+ Initiate a MT voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_iwlan(self.log, self.android_devices[0], False,
+ WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid,
+ self.wifi_network_pass):
+ self.android_devices[0].log.error(
+ "Failed to setup iwlan with APM off and WIFI and WFC on")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ None, DIRECTION_MOBILE_TERMINATED)
+
+ @test_tracker_info(uuid="ea087709-d4df-4223-b80c-1b33bacbd5a2")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mo_voice_apm_wifi_wfc_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn on wifi-calling, airplane mode and wifi.
+ Starting an youtube video.
+ Initiate a MO voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_iwlan(self.log, self.android_devices[0], True,
+ WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid,
+ self.wifi_network_pass):
+ self.android_devices[0].log.error(
+ "Failed to setup iwlan with APM, WIFI and WFC on")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ None, DIRECTION_MOBILE_ORIGINATED)
+
+ @test_tracker_info(uuid="44cc14e0-60c7-4fdb-ad26-31fdc4e52aaf")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_mt_voice_apm_wifi_wfc_in_active_youtube_video(self):
+ """Test call can be established during active youtube video.
+
+ Turn on wifi-calling, airplane mode and wifi.
+ Starting youtube video.
+ Initiate a MT voice call. Verify call can be established.
+
+ Returns:
+ True if success.
+ False if failed.
+ """
+ if not phone_setup_iwlan(self.log, self.android_devices[0], True,
+ WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid,
+ self.wifi_network_pass):
+ self.android_devices[0].log.error(
+ "Failed to setup iwlan with APM, WIFI and WFC on")
+ return False
+ return self._test_call_setup_in_active_youtube_video(
+ None, DIRECTION_MOBILE_TERMINATED)
+
+ @test_tracker_info(uuid="f367de12-1fd8-488d-816f-091deaacb791")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_wfc_wifi_preferred_after_mobile_data_usage_limit_reached(
+ self):
+ """ WiFi Preferred, WiFi calling test after data limit reached
+
+ 1. Set the data limit to the current usage
+ 2. Setup PhoneA WFC mode: WIFI_PREFERRED.
+ 3. Make Sure PhoneB is in 3G mode.
+ 4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+ 5. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+ Returns:
+ True if pass; False if fail.
+ """
+ ads = self.android_devices
+ try:
+ subscriber_id = ads[0].droid.telephonyGetSubscriberId()
+ data_usage = get_mobile_data_usage(ads[0], subscriber_id)
+ set_mobile_data_usage_limit(ads[0], data_usage, subscriber_id)
+
+ # Turn OFF WiFi for Phone B
+ set_wifi_to_default(self.log, ads[1])
+ tasks = [(phone_setup_iwlan,
+ (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_voice_3g, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ return False
+
+ return two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan,
+ ads[1], phone_idle_3g, is_phone_in_call_3g, None)
+ finally:
+ remove_mobile_data_usage_limit(ads[0], subscriber_id)
+
+ @test_tracker_info(uuid="af943c7f-2b42-408f-b8a3-2d360a7483f7")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_volte_after_mobile_data_usage_limit_reached(self):
+ """ VoLTE to VoLTE call test after mobile data usage limit reached
+
+ 1. Set the data limit to the current usage
+ 2. Make Sure PhoneA is in LTE mode (with VoLTE).
+ 3. Make Sure PhoneB is in LTE mode (with VoLTE).
+ 4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+ 5. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+ Returns:
+ True if pass; False if fail.
+ """
+ ads = self.android_devices
+ try:
+ subscriber_id = ads[0].droid.telephonyGetSubscriberId()
+ data_usage = get_mobile_data_usage(ads[0], subscriber_id)
+ set_mobile_data_usage_limit(ads[0], data_usage, subscriber_id)
+
+ tasks = [(phone_setup_volte, (self.log, ads[0])),
+ (phone_setup_volte, (self.log, ads[1]))]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ return False
+
+ return two_phone_call_short_seq(
+ self.log, ads[0], phone_idle_volte, is_phone_in_call_volte,
+ ads[1], phone_idle_volte, is_phone_in_call_volte, None,
+ WAIT_TIME_IN_CALL_FOR_IMS)
+ finally:
+ remove_mobile_data_usage_limit(ads[0], subscriber_id)
+
""" Tests End """
diff --git a/acts/tests/google/tel/live/TelWifiDataTest.py b/acts/tests/google/tel/live/TelWifiDataTest.py
index 4ee1f2e..c30afb0 100644
--- a/acts/tests/google/tel/live/TelWifiDataTest.py
+++ b/acts/tests/google/tel/live/TelWifiDataTest.py
@@ -30,6 +30,8 @@
from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
from acts.test_utils.tel.tel_test_utils import run_multithread_func
from acts.test_utils.tel.tel_test_utils import active_file_download_test
+from acts.test_utils.tel.tel_test_utils import get_telephony_signal_strength
+from acts.test_utils.tel.tel_test_utils import get_wifi_signal_strength
from acts.utils import adb_shell_ping
# Attenuator name
@@ -79,15 +81,16 @@
Make sure DUT get Cell Data coverage (LTE)
Make sure DUT WiFi is connected
"""
- toggle_airplane_mode(self.log, self.android_devices[0], False)
- if not ensure_network_generation(self.log, self.android_devices[0],
+ ad = self.android_devices[0]
+ toggle_airplane_mode(self.log, ad, False)
+ if not ensure_network_generation(self.log, ad,
GEN_4G, NETWORK_SERVICE_DATA):
return False
- if not ensure_wifi_connected(self.log, self.android_devices[0],
+ if not ensure_wifi_connected(self.log, ad,
self.live_network_ssid,
self.live_network_pwd):
- ad.log.error("%s connect WiFI failed")
+ ad.log.error("connect WiFi failed")
return False
return True
@@ -152,6 +155,8 @@
irat_wait_time) or
not verify_http_connection(self.log, ad)):
ad.log.error("Data not on WiFi")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
return False
ad.log.info("Triggering WiFi to Cellular IRAT")
@@ -160,6 +165,8 @@
irat_wait_time) or
not verify_http_connection(self.log, ad)):
ad.log.error("Data not on Cell")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
return False
return True
@@ -256,17 +263,29 @@
self._atten_setup_wifi_cell()
if (not wait_for_wifi_data_connection(self.log, ad, True)):
ad.log.error("Data not on WiFi")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
break
+
+ ad.on_mobile_data = False
if not active_file_download_test(self.log, ad):
ad.log.error("HTTP file download failed on WiFi")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
break
self._atten_setup_cell_only()
if (not wait_for_cell_data_connection(self.log, ad, True)):
ad.log.error("Data not on Cell")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
break
+
+ ad.on_mobile_data = True
if not active_file_download_test(self.log, ad):
ad.log.error("HTTP file download failed on cell")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
break
self.log.info(">----Iteration : %d/%d succeed.----<",
@@ -319,13 +338,18 @@
if (not wait_for_wifi_data_connection(self.log, ad, True) or
not verify_http_connection(self.log, ad)):
ad.log.error("Data not on WiFi")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
break
self._atten_setup_cell_only()
if (not wait_for_cell_data_connection(self.log, ad, True) or
not verify_http_connection(self.log, ad)):
ad.log.error("Data not on Cell")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
break
+
self.log.info(">----Iteration : %d/%d succeed.----<",
current_iteration, total_iteration)
current_iteration += 1
@@ -360,9 +384,14 @@
if (not wait_for_wifi_data_connection(self.log, ad, True) or
not verify_http_connection(self.log, ad)):
ad.log.error("Data not on WiFi")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
return False
+ ad.on_mobile_data = False
if not active_file_download_test(self.log, ad, "10MB"):
ad.log.error("HTTP file download failed on WiFi")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
return False
return True
@@ -390,9 +419,14 @@
if (not wait_for_cell_data_connection(self.log, ad, True) or
not verify_http_connection(self.log, ad)):
ad.log.error("Data not on LTE")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
return False
- if not active_file_download_test(self.log, ad, "1GB"):
+ ad.on_mobile_data = True
+ if not active_file_download_test(self.log, ad, "512MB"):
ad.log.error("HTTP file download failed on LTE")
+ get_telephony_signal_strength(ad)
+ get_wifi_signal_strength(ad)
return False
return True
diff --git a/acts/tests/google/tel/live/TelWifiVideoTest.py b/acts/tests/google/tel/live/TelWifiVideoTest.py
new file mode 100644
index 0000000..a2293da
--- /dev/null
+++ b/acts/tests/google/tel/live/TelWifiVideoTest.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2018 - Google
+#
+# 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.
+"""
+ Test Script for ViWiFi live call test
+"""
+
+import time
+from queue import Empty
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
+from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
+from acts.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
+from acts.test_utils.tel.tel_defines import CALL_STATE_HOLDING
+from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MANAGE_CONFERENCE
+from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_MERGE_CONFERENCE
+from acts.test_utils.tel.tel_defines import CALL_CAPABILITY_SWAP_CONFERENCE
+from acts.test_utils.tel.tel_defines import CALL_PROPERTY_CONFERENCE
+from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_VIDEO_SESSION_EVENT
+from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
+from acts.test_utils.tel.tel_defines import VT_STATE_AUDIO_ONLY
+from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
+from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL_PAUSED
+from acts.test_utils.tel.tel_defines import VT_VIDEO_QUALITY_DEFAULT
+from acts.test_utils.tel.tel_defines import VT_STATE_RX_ENABLED
+from acts.test_utils.tel.tel_defines import VT_STATE_TX_ENABLED
+from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts.test_utils.tel.tel_defines import EVENT_VIDEO_SESSION_EVENT
+from acts.test_utils.tel.tel_defines import EventTelecomVideoCallSessionEvent
+from acts.test_utils.tel.tel_defines import SESSION_EVENT_RX_PAUSE
+from acts.test_utils.tel.tel_defines import SESSION_EVENT_RX_RESUME
+from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts.test_utils.tel.tel_test_utils import disconnect_call_by_id
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import multithread_func
+from acts.test_utils.tel.tel_test_utils import num_active_calls
+from acts.test_utils.tel.tel_test_utils import verify_http_connection
+from acts.test_utils.tel.tel_test_utils import verify_incall_state
+from acts.test_utils.tel.tel_test_utils import wait_for_video_enabled
+from acts.test_utils.tel.tel_video_utils import get_call_id_in_video_state
+from acts.test_utils.tel.tel_video_utils import \
+ is_phone_in_call_video_bidirectional
+from acts.test_utils.tel.tel_video_utils import \
+ is_phone_in_call_viwifi_bidirectional
+from acts.test_utils.tel.tel_video_utils import is_phone_in_call_voice_hd
+from acts.test_utils.tel.tel_video_utils import phone_setup_video
+from acts.test_utils.tel.tel_video_utils import \
+ verify_video_call_in_expected_state
+from acts.test_utils.tel.tel_video_utils import video_call_downgrade
+from acts.test_utils.tel.tel_video_utils import video_call_modify_video
+from acts.test_utils.tel.tel_video_utils import video_call_setup_teardown
+from acts.test_utils.tel.tel_voice_utils import get_audio_route
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts.test_utils.tel.tel_voice_utils import set_audio_route
+from acts.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
+from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+
+DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION = 1 * 60 * 60 # default 1 hour
+
+
+class TelWifiVideoTest(TelephonyBaseTest):
+ def __init__(self, controllers):
+ TelephonyBaseTest.__init__(self, controllers)
+
+ self.stress_test_number = self.get_stress_test_number()
+ self.wifi_network_ssid = self.user_params.get("wifi_network_ssid")
+ self.wifi_network_pass = self.user_params.get("wifi_network_pass")
+
+ self.long_duration_call_total_duration = self.user_params.get(
+ "long_duration_call_total_duration",
+ DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION)
+
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="375e9b88-8d8e-45fe-8502-e4da4147682d")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_video_to_video_wifi_preferred(self):
+ """ Test ViWifi<->ViWifi call functionality.
+
+ Make Sure PhoneA is in iWLAN mode (with Video Calling).
+ Make Sure PhoneB is in iWLAN mode (with Video Calling).
+ Connect to Wifi
+ Call from PhoneA to PhoneB as Bi-Directional Video,
+ Accept on PhoneB as video call, hang up on PhoneA.
+
+ Returns:
+ True if pass; False if fail.
+ """
+ ads = self.android_devices
+ tasks = [
+ (phone_setup_iwlan,
+ (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_iwlan,
+ (self.log, ads[1], False, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ ]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ return False
+
+ if not video_call_setup_teardown(
+ self.log,
+ ads[0],
+ ads[1],
+ ads[0],
+ video_state=VT_STATE_BIDIRECTIONAL,
+ verify_caller_func=is_phone_in_call_viwifi_bidirectional,
+ verify_callee_func=is_phone_in_call_viwifi_bidirectional):
+ self.log.error("Failed to setup+teardown a call")
+ return False
+
+ return True
+
+ @test_tracker_info(uuid="0c6782b4-fa81-4c18-a7bf-9f0f5cc05d6d")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_call_video_to_video_wifi_preferred_apm(self):
+ """ Test ViWifi<->ViWifi call functionality in APM Mode.
+
+ Make Sure PhoneA is in iWLAN mode (with Video Calling).
+ Make Sure PhoneB is in iWLAN mode (with Video Calling).
+ Turn on APM Mode
+ Connect to Wifi
+ Call from PhoneA to PhoneB as Bi-Directional Video,
+ Accept on PhoneB as video call, hang up on PhoneA.
+
+ Returns:
+ True if pass; False if fail.
+ """
+ ads = self.android_devices
+ tasks = [
+ (phone_setup_iwlan,
+ (self.log, ads[0], True, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ (phone_setup_iwlan,
+ (self.log, ads[1], True, WFC_MODE_WIFI_PREFERRED,
+ self.wifi_network_ssid, self.wifi_network_pass)),
+ ]
+ if not multithread_func(self.log, tasks):
+ self.log.error("Phone Failed to Set Up Properly.")
+ return False
+
+ if not video_call_setup_teardown(
+ self.log,
+ ads[0],
+ ads[1],
+ ads[0],
+ video_state=VT_STATE_BIDIRECTIONAL,
+ verify_caller_func=is_phone_in_call_viwifi_bidirectional,
+ verify_callee_func=is_phone_in_call_viwifi_bidirectional):
+ self.log.error("Failed to setup+teardown a call")
+ return False
+
+ return True
+
+
+""" Tests End """
diff --git a/acts/tests/google/tel/live/TelWifiVoiceTest.py b/acts/tests/google/tel/live/TelWifiVoiceTest.py
index 4b8af08..21c37f3 100755
--- a/acts/tests/google/tel/live/TelWifiVoiceTest.py
+++ b/acts/tests/google/tel/live/TelWifiVoiceTest.py
@@ -75,6 +75,8 @@
from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
from acts.test_utils.tel.tel_test_utils import verify_http_connection
+from acts.test_utils.tel.tel_test_utils import get_telephony_signal_strength
+from acts.test_utils.tel.tel_test_utils import get_wifi_signal_strength
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
@@ -346,11 +348,15 @@
self.log.info(
"Expected exception happened: <{}>, return True.".format(
e))
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return True
else:
self.log.info(
"Unexpected exception happened: <{}>, return False.".
format(e))
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
finally:
ensure_phones_default_state(self.log, [ads[0], ads[1]])
@@ -455,7 +461,7 @@
def _wfc_phone_setup_cellular_absent(self, wfc_mode):
is_exception_happened = False
- time.sleep(60)
+ time.sleep(90)
try:
if not toggle_airplane_mode(self.log, self.android_devices[0],
False):
@@ -2559,6 +2565,7 @@
# ensure cellular rat, wfc mode, wifi associated
toggle_airplane_mode(self.log, self.android_devices[0], False)
toggle_volte(self.log, self.android_devices[0], True)
+
if not ensure_network_generation(
self.log,
self.android_devices[0],
@@ -2566,25 +2573,35 @@
voice_or_data=NETWORK_SERVICE_DATA):
self.log.error("_rove_out_test: {} failed to be in rat: {}".format(
self.android_devices[0].serial, cellular_rat))
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
+
if not set_wfc_mode(self.log, self.android_devices[0], wfc_mode):
self.log.error("{} set WFC mode failed.".format(
self.android_devices[0].serial))
return False
+
if not ensure_wifi_connected(self.log, self.android_devices[0],
self.live_network_ssid,
self.live_network_pwd):
self.log.error("{} connect WiFI failed, expected succeed".format(
self.android_devices[0].serial))
return False
+
if (not wait_for_wifi_data_connection(self.log,
self.android_devices[0], True) or
not verify_http_connection(self.log, self.android_devices[0])):
self.log.error("No Data on Wifi")
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
+
if not self._phone_idle_iwlan():
self.log.error("Phone failed to report iwlan in {}dBm.".format(
WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE))
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
# set up wifi to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT in 10 seconds
@@ -2598,10 +2615,15 @@
self.android_devices[0], True) or
not verify_http_connection(self.log, self.android_devices[0])):
self.log.error("No Data on Wifi")
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
+
if self._phone_wait_for_not_wfc() or not self._phone_idle_iwlan():
self.log.error("Phone should not rove-out in {}dBm.".format(
WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT))
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
# set up wifi to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10 seconds
@@ -2615,12 +2637,17 @@
self.android_devices[0], True) or
not verify_http_connection(self.log, self.android_devices[0])):
self.log.error("No Data on Wifi")
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
if not self._phone_wait_for_not_wfc() or self._phone_idle_iwlan():
self.log.error("Phone should rove-out in {}dBm.".format(
WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT))
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
+
# make a call.
if wfc_mode == WFC_MODE_WIFI_ONLY:
return self._wfc_call_sequence(
@@ -3126,8 +3153,13 @@
self.wifi_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE, 2, 1)
# Make sure phone hand-out, not drop call
if not self._phone_wait_for_not_wfc():
- self.log.error("Phone should hand out.")
+ self.log.error("Phone should hand out to LTE.")
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
+ self.log.info("iWLAN to LTE switch happened at below Signal Strengths")
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
if not self._is_phone_in_call_volte():
self.log.error("Phone should be in volte call.")
return False
@@ -3402,7 +3434,12 @@
# Make sure phone hand-out to iWLAN, not drop call
if not self._phone_wait_for_wfc():
self.log.error("Phone should hand out to iWLAN.")
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
return False
+ self.log.info("LTE to iWLAN switch happened at below Signal Strengths")
+ get_telephony_signal_strength(self.android_devices[0])
+ get_wifi_signal_strength(self.android_devices[0])
time.sleep(30)
if not self._is_phone_in_call_iwlan():
self.log.error("Phone should be in iWLAN call.")
diff --git a/acts/tests/google/tel/live/TelephonyConnectivitySanityTest.py b/acts/tests/google/tel/live/TelephonyConnectivitySanityTest.py
deleted file mode 100644
index 762117c..0000000
--- a/acts/tests/google/tel/live/TelephonyConnectivitySanityTest.py
+++ /dev/null
@@ -1,546 +0,0 @@
-#/usr/bin/env python3.4
-#
-# Copyright 2016 - 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.
-"""
-Sanity tests for connectivity tests in telephony
-"""
-
-import time
-
-from acts.controllers.anritsu_lib.md8475a import BtsServiceState
-from acts.controllers.anritsu_lib.md8475a import BtsTechnology
-from acts.controllers.anritsu_lib.md8475a import MD8475A
-from acts.controllers.anritsu_lib.md8475a import ProcessingStatus
-from acts.controllers.anritsu_lib.md8475a import TriggerMessageIDs
-from acts.controllers.anritsu_lib.md8475a import TriggerMessageReply
-from acts.test_utils.tel import tel_test_utils
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-
-
-class TelephonyConnectivitySanityTest(TelephonyBaseTest):
- def __init__(self, controllers):
- TelephonyBaseTest.__init__(self, controllers)
- self.anritsu = MD8475A(tel_test_utils.MD8475A_IP_ADDRESS)
-
- def setup_test(self):
- self.lte_bts, self.wcdma_bts = tel_test_utils.set_system_model(
- self.anritsu, "LTE_WCDMA")
- tel_test_utils.init_phone(self.droid, self.ed)
- self.droid.telephonyStartTrackingServiceStateChange()
- self.droid.telephonyStartTrackingDataConnectionStateChange()
- self.log.info("Starting Simulation")
- self.anritsu.start_simulation()
- return True
-
- def teardown_test(self):
- self.droid.telephonyStopTrackingServiceStateChange()
- self.droid.telephonyStopTrackingDataConnectionStateChange()
- self.log.info("Stopping Simulation")
- self.anritsu.stop_simulation()
- # turn off modem
- tel_test_utils.turn_off_modem(self.droid)
-
- def teardown_class(self):
- self.anritsu.disconnect()
-
- """ Tests Begin """
-
- @TelephonyBaseTest.tel_test_wrap
- def test_network_registration(self):
- '''
- Test ID: TEL-CO-01
- Checks the Network service state after bootup. Verifies the
- network registration
-
- Steps
- -----
- 1. The device is in airplane mode
- 2. Turn off the airplane mode (This simulates the Boot up for modem)
- 3. check for the service state. expecting IN SERVICE
- '''
- test_status = "failed"
- # turn on modem to start registration
- tel_test_utils.turn_on_modem(self.droid)
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
-
- if test_status == "passed":
- self.log.info(
- "TEL-CO-01:Network registration verification: Passed")
- return True
- else:
- self.log.info(
- "TEL-CO-01:Network registration verification: Failed")
- return False
-
- @TelephonyBaseTest.tel_test_wrap
- def test_network_params_verification(self):
- '''
- Test ID: TEL-CO-02
- verifies the network registration parameters
-
- Steps
- -----
- 1. The device is in airplane mode
- 2. Turn off the airplane mode (This simulates the Boot up for modem)
- 3. check for the service state. expecting IN SERVICE
- 4. verifies the values for different parameters
- '''
- test_status = "failed"
- # turn on modem to start registration
- tel_test_utils.turn_on_modem(self.droid)
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
-
- if test_status == "passed":
- self.log.info("Verifying the NW Service Parameters")
- expected_voice_nwtype = None
- operator_name = None
- mcc = None
- mnc = None
-
- bts_number, rat_info = self.anritsu.get_camping_cell()
- if rat_info == BtsTechnology.WCDMA.value:
- expected_voice_nwtype = "UMTS"
- operator_name = tel_test_utils.WCDMA_NW_NAME
- mcc = tel_test_utils.NW_MCC
- mnc = tel_test_utils.NW_MNC
- elif rat_info == BtsTechnology.LTE.value:
- expected_voice_nwtype = "LTE"
- operator_name = tel_test_utils.LTE_NW_NAME
- mcc = tel_test_utils.NW_MCC
- mnc = tel_test_utils.NW_MNC
-
- self.log.info("VoiceNwState :{}".format(event['data'][
- 'VoiceRegState']))
- self.log.info("VoiceNetworkType :{}".format(event['data'][
- 'VoiceNetworkType']))
- self.log.info("DataRegState :{}".format(event['data'][
- 'DataRegState']))
- self.log.info("DataNetworkType :{}".format(event['data'][
- 'DataNetworkType']))
- self.log.info("OperatorName :{}".format(event['data'][
- 'OperatorName']))
- self.log.info("OperatorId :{}".format(event['data']['OperatorId']))
- self.log.info("Roaming :{}".format(event['data']['Roaming']))
-
- if event['data']['VoiceNetworkType'] != expected_voice_nwtype:
- test_status = "failed"
- self.log.info("Error:Expected NW Type is not received")
- if event['data']['OperatorId'][:3] != mcc:
- test_status = "failed"
- self.log.info("Error:Expected MNC is not received")
- if event['data']['OperatorId'][3:] != mnc:
- test_status = "failed"
- self.log.info("Error:Expected MCC is not received")
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- self.log.info("Waiting for data state: DATA_CONNECTED")
- test_status, event = tel_test_utils.wait_for_data_state(
- self.ed, self.log, "DATA_CONNECTED", 120)
-
- if test_status == "passed":
- self.log.info("TEL-CO-02: Network registration parameters"
- " verification: Passed")
- return True
- else:
- self.log.info("TEL-CO-02: Network registration parameters"
- " verification: Failed")
- return False
-
- @TelephonyBaseTest.tel_test_wrap
- def test_network_deregistration(self):
- '''
- Test ID: TEL-CO-03
- verifies the network de registration
-
- Steps
- -----
- 1. The device is in airplane mode
- 2. Turn off the airplane mode (This simulates the Boot up for modem)
- 3. check for the service state. expecting IN SERVICE
- 4. Turn on Airplane mode (This simulates network de registration)
- 5. check for the service state. expecting POWER_OFF
- '''
- test_status = "failed"
- # turn on modem to start registration
- tel_test_utils.turn_on_modem(self.droid)
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- test_status = "failed"
- self.ed.clear_all_events()
- self.log.info("Making device to detach from network")
- self.droid.connectivityToggleAirplaneMode(True)
- self.log.info("Waiting for service state: POWER_OFF")
- test_status, event = tel_test_utils.wait_for_network_state(
- self.ed, self.log, "POWER_OFF", 60)
-
- if test_status == "passed":
- self.log.info("TEL-CO-03: Network de-registration"
- " verification: Passed")
- return True
- else:
- self.log.info("TEL-CO-03: Network de-registration"
- " verification: Failed")
- return False
-
- @TelephonyBaseTest.tel_test_wrap
- def test_network_out_of_service(self):
- '''
- Test ID: TEL-CO-04
- verifies the network out of state
-
- Steps
- -----
- 1. The device is in airplane mode
- 2. Turn off the airplane mode (This simulates the Boot up for modem)
- 3. check for the service state. expecting IN SERVICE
- 4. Make network out of service
- 5. check for the service state. expecting OUT_OF_SERVICE
- '''
-
- test_status = "failed"
- # turn on modem to start registration
- tel_test_utils.turn_on_modem(self.droid)
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- test_status = "failed"
- self.ed.clear_all_events()
- # This sleep is required.Sometimes Anritsu box doesn't behave as
- # expected in executing the commands send to it without this delay.
- # May be it is in state transition.so the test doesn't proceed.
- # hence introduced this delay.
- time.sleep(5)
- bts_number, rat_info = self.anritsu.get_camping_cell()
- self.log.info("Making the attached NW as OUT_OF_STATE")
- if rat_info == BtsTechnology.LTE.value:
- self.lte_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
- else:
- self.wcdma_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
- self.log.info("Waiting for service state: OUT_OF_SERVICE")
- test_status, event = tel_test_utils.wait_for_network_state(
- self.ed, self.log, "OUT_OF_SERVICE", 90)
-
- if test_status == "passed":
- self.log.info("TEL-CO-04: Network out-of-service"
- " verification: Passed")
- return True
- else:
- self.log.info("TEL-CO-04: Network out-of-service"
- " verification: Failed")
- return False
-
- @TelephonyBaseTest.tel_test_wrap
- def test_network_return_inservice(self):
- '''
- Test ID: TEL-CO-06
- verifies the network returns to IN_SERVICE from OUT_OF_SERVICE
-
- Steps
- -----
- 1. The device is in airplane mode
- 2. Turn off the airplane mode (This simulates the Boot up for modem)
- 3. check for the service state. expecting IN SERVICE
- 4. Make the network out of service
- 5. check for the service state. expecting OUT_OF_SERVICE
- 6. Bring back the device to IN_SERVICE
- 7. check for the service state. expecting IN_SERVICE
- '''
- test_status = "failed"
- # turn on modem to start registration
- tel_test_utils.turn_on_modem(self.droid)
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
- self.log.info("Waiting for data state: DATA_CONNECTED")
- test_status, event = tel_test_utils.wait_for_data_state(
- self.ed, self.log, "DATA_CONNECTED", 120)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- test_status = "failed"
- self.ed.clear_all_events()
- # This sleep is required.Sometimes Anritsu box doesn't behave as
- # expected in executing the commands send to it without this delay.
- # May be it is in state transition.so the test doesn't proceed.
- # hence introduced this delay.
- time.sleep(5)
- bts_number, rat_info = self.anritsu.get_camping_cell()
- self.log.info("Making the attached NW as OUT_OF_STATE")
- if rat_info == BtsTechnology.LTE.value:
- self.lte_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
- else:
- self.wcdma_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
- self.log.info("Waiting for service state: OUT_OF_SERVICE")
- test_status, event = tel_test_utils.wait_for_network_state(
- self.ed, self.log, "OUT_OF_SERVICE", 120)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- test_status = "failed"
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
- self.log.info("Waiting for data state: DATA_CONNECTED")
- test_status, event = tel_test_utils.wait_for_data_state(
- self.ed, self.log, "DATA_CONNECTED", 120)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- test_status = "failed"
- self.ed.clear_all_events()
- # This sleep is required.Sometimes Anritsu box doesn't behave as
- # expected in executing the commands send to it without this delay.
- # May be it is in state transition.so the test doesn't proceed.
- # hence introduced this delay.
- time.sleep(5)
- bts_number, rat_info = self.anritsu.get_camping_cell()
- self.log.info("Making the attached NW as OUT_OF_STATE")
- if rat_info == BtsTechnology.LTE.value:
- self.lte_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
- else:
- self.wcdma_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
- self.log.info("Waiting for service state: OUT_OF_SERVICE")
- test_status, event = tel_test_utils.wait_for_network_state(
- self.ed, self.log, "OUT_OF_SERVICE", 120)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- test_status = "failed"
- self.ed.clear_all_events()
- # This sleep is required.Sometimes Anritsu box doesn't behave as
- # expected in executing the commands send to it without this delay.
- # May be it is in state transition.so the test doesn't proceed.
- # hence introduced this delay.
- time.sleep(5)
- self.log.info("Making the NW service IN_SERVICE")
- self.lte_bts.service_state = BtsServiceState.SERVICE_STATE_IN
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
- self.log.info("Waiting for data state: DATA_CONNECTED")
- test_status, event = tel_test_utils.wait_for_data_state(
- self.ed, self.log, "DATA_CONNECTED", 120)
-
- if test_status == "passed":
- self.log.info("TEL-CO-06: Network returning to IN_SERVICE"
- " verification: Passed")
- return True
- else:
- self.log.info("TEL-CO-06: Network returning to IN_SERVICE"
- " verification: Failed")
- return False
-
- @TelephonyBaseTest.tel_test_wrap
- def test_set_preferred_network(self):
- '''
- Test ID: TEL-CO-07
- verifies the network is registered on Preferred network
-
- Steps
- -----
- 1. The device is in airplane mode
- 2. Turn off the airplane mode (This simulates the Boot up for modem)
- 3. check for the service state. expecting IN SERVICE
- 4. Set the preferred network type
- 5. check for the service state and registered network
- '''
- test_status = "failed"
- # turn on modem to start registration
- tel_test_utils.turn_on_modem(self.droid)
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- test_status = "failed"
- pref_nwtype = 0
- expected_nwtype = ""
- bts_number, rat_info = self.anritsu.get_camping_cell()
- if rat_info == BtsTechnology.WCDMA.value:
- pref_nwtype = tel_test_utils.NETWORK_MODE_LTE_ONLY
- expected_nwtype = "LTE"
- elif rat_info == BtsTechnology.LTE.value:
- pref_nwtype = tel_test_utils.NETWORK_MODE_WCDMA_ONLY
- expected_nwtype = "UMTS"
- else:
- raise ValueError("Incorrect value of RAT returned by MD8475A")
- self.log.info("Setting preferred Network to " + expected_nwtype)
- self.droid.telephonySetPreferredNetwork(pref_nwtype)
- self.log.info("Waiting for service state: IN_SERVICE in " +
- expected_nwtype)
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log, expected_nwtype)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- test_status = "failed"
- pref_nwtype = 0
- expected_nwtype = ""
- bts_number, rat_info = self.anritsu.get_camping_cell()
- if rat_info == BtsTechnology.WCDMA.value:
- pref_nwtype = tel_test_utils.NETWORK_MODE_LTE_ONLY
- expected_nwtype = "LTE"
- elif rat_info == BtsTechnology.LTE.value:
- pref_nwtype = tel_test_utils.NETWORK_MODE_WCDMA_ONLY
- expected_nwtype = "UMTS"
- else:
- raise ValueError("Incorrect value of RAT returned by MD8475A")
- self.log.info("Setting preferred Network to " + expected_nwtype)
- self.droid.telephonySetPreferredNetwork(pref_nwtype)
- self.log.info("Waiting for service state: IN_SERVICE in " +
- expected_nwtype)
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log, expected_nwtype)
- # setting the preferred network type to default
- self.droid.telephonySetPreferredNetwork(
- tel_test_utils.NETWORK_MODE_LTE_GSM_WCDMA)
-
- if test_status == "passed":
- self.log.info("TEL-CO-07: Setting preferred Network"
- "verification: Passed")
- return True
- else:
- self.log.info("TEL-CO-07: Setting preferred Network"
- "verification: Failed")
- return False
-
- @TelephonyBaseTest.tel_test_wrap
- def test_network_emergency(self):
- '''
- Test ID: TEL-CO-05
- verifies the network state - emergency
-
- Steps
- -----
- 1. The device is in airplane mode
- 2. Turn off the airplane mode (This simulates the Boot up for modem)
- 3. check for the service state. expecting IN SERVICE
- 4. Make the device emergency only
- 5. check for the service state. expecting EMERGENCY_ONLY
- '''
- test_status = "failed"
- CAUSE_LA_NOTALLOWED = 12
- CAUSE_EPS_NOTALLOWED = 7
- triggermessage = self.anritsu.get_TriggerMessage()
- triggermessage.set_reply_type(TriggerMessageIDs.ATTACH_REQ,
- TriggerMessageReply.REJECT)
- triggermessage.set_reject_cause(TriggerMessageIDs.ATTACH_REQ,
- CAUSE_EPS_NOTALLOWED)
- # This sleep is required.Sometimes Anritsu box doesn't behave as
- # expected in executing the commands send to it without this delay.
- # May be it is in state transition.so the test doesn't proceed.
- # hence introduced this delay.
- time.sleep(5)
- triggermessage.set_reply_type(TriggerMessageIDs.MM_LOC_UPDATE_REQ,
- TriggerMessageReply.REJECT)
- triggermessage.set_reject_cause(TriggerMessageIDs.MM_LOC_UPDATE_REQ,
- CAUSE_LA_NOTALLOWED)
-
- # turn on modem to start registration
- tel_test_utils.turn_on_modem(self.droid)
- self.log.info("Waiting for service state: emergency")
- test_status, event = tel_test_utils.wait_for_network_state(
- self.ed, self.log, "EMERGENCY_ONLY", 300)
-
- if test_status == "passed":
- self.droid.connectivityToggleAirplaneMode(True)
- time_to_wait = 60
- sleep_interval = 1
- # Waiting for POWER OFF state in Anritsu
- start_time = time.time()
- end_time = start_time + time_to_wait
- while True:
- ue_status = self.anritsu.get_ue_status()
- if ue_status == ProcessingStatus.PROCESS_STATUS_POWEROFF:
- break
-
- if time.time() <= end_time:
- time.sleep(sleep_interval)
- time_to_wait = end_time - time.time()
- else:
- self.log.info("MD8475A has not come to POWEROFF state")
- break
-
- # This sleep is required.Sometimes Anritsu box doesn't behave as
- # expected in executing the commands send to it without this delay.
- # May be it is in state transition.so the test doesn't proceed.
- # hence introduced this delay.
- time.sleep(10)
- triggermessage.set_reply_type(TriggerMessageIDs.ATTACH_REQ,
- TriggerMessageReply.ACCEPT)
- triggermessage.set_reply_type(TriggerMessageIDs.MM_LOC_UPDATE_REQ,
- TriggerMessageReply.ACCEPT)
- self.droid.connectivityToggleAirplaneMode(False)
- tel_test_utils.wait_for_network_registration(self.ed, self.anritsu,
- self.log)
-
- if test_status == "passed":
- self.log.info("TEL-CO-05: Network emergency state"
- " verification: Passed")
- return True
- else:
- self.log.info("TEL-CO-05: Network emergency state"
- " verification: Failed")
-
- return False
-
- @TelephonyBaseTest.tel_test_wrap
- def test_manual_operator_selection(self):
- '''
- verifies the Manual Operator Selection
-
- Steps
- -----
- 1. The device is in airplane mode
- 2. Turn off the airplane mode (This simulates the Boot up for modem)
- 3. check for the service state. expecting IN SERVICE
- 4. search for NW operators and manually select a non-subscribed operator
- 5. search for NW operators and manually select the subscribed operator
- 6. verify the device is camped on subscribed operator
- '''
- # Android public APIs not available for this operation
- pass
-
- @TelephonyBaseTest.tel_test_wrap
- def test_auto_operator_selection(self):
- '''
- verifies the Automatic Operator Selection
-
- Steps
- -----
- 1. The device is in airplane mode
- 2. Turn off the airplane mode (This simulates the Boot up for modem)
- 3. check for the service state. expecting IN SERVICE
- 4. search for NW operators and manually select a non-subscribed operator
- 5. select the the subscribed operator automatically
- 6. verify the device is camped on subscribed operator
- '''
- # Android public APIs not available for this operation
- pass
-
- """ Tests End """
diff --git a/acts/tests/google/tel/live/TelephonyDataSanityTest.py b/acts/tests/google/tel/live/TelephonyDataSanityTest.py
deleted file mode 100644
index 4d6ae38..0000000
--- a/acts/tests/google/tel/live/TelephonyDataSanityTest.py
+++ /dev/null
@@ -1,150 +0,0 @@
-#/usr/bin/env python3.4
-#
-# Copyright 2016 - 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.
-"""
-Sanity tests for connectivity tests in telephony
-"""
-
-import time
-from queue import Empty
-
-from acts.controllers.anritsu_lib.md8475a import BtsNumber
-from acts.controllers.anritsu_lib.md8475a import BtsTechnology
-from acts.controllers.anritsu_lib.md8475a import MD8475A
-from acts.test_utils.tel import tel_test_utils
-from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-
-
-class TelephonyDataSanityTest(TelephonyBaseTest):
- def __init__(self, controllers):
- TelephonyBaseTest.__init__(self, controllers)
- self.anritsu = MD8475A(tel_test_utils.MD8475A_IP_ADDRESS)
-
- def setup_test(self):
- self.lte_bts, self.wcdma_bts = tel_test_utils.set_system_model(
- self.anritsu, "LTE_WCDMA")
- tel_test_utils.init_phone(self.droid, self.ed)
- self.droid.telephonyStartTrackingServiceStateChange()
- self.droid.telephonyStartTrackingDataConnectionStateChange()
- self.log.info("Starting Simulation")
- self.anritsu.start_simulation()
- return True
-
- def teardown_test(self):
- self.droid.telephonyStopTrackingServiceStateChange()
- self.droid.telephonyStopTrackingDataConnectionStateChange()
- self.log.info("Stopping Simulation")
- self.anritsu.stop_simulation()
- # turn off modem
- tel_test_utils.turn_off_modem(self.droid)
-
- def teardown_class(self):
- self.anritsu.disconnect()
-
- def _wait_for_bts_state(self, btsnumber, state, timeout=30):
- ''' Wait till BTS state changes. state value are "IN" and "OUT" '''
- sleep_interval = 2
- status = "failed"
-
- wait_time = timeout
- while (wait_time > 0):
- if state == btsnumber.service_state:
- print(btsnumber.service_state)
- status = "passed"
- break
- else:
- time.sleep(sleep_interval)
- waiting_time = waiting_time - sleep_interval
-
- if status == "failed":
- self.log.info("Timeout: Expected state is not received.")
-
- """ Tests Begin """
-
- @TelephonyBaseTest.tel_test_wrap
- def test_data_conn_state_when_access_enabled(self):
- '''
- Check data conenction state after boot up
-
- Steps
- -----
- 1. Get the device is IN_SERVICE state
- 2. check the data conenction status
- '''
- test_status = "failed"
- # turn on modem to start registration
- tel_test_utils.turn_on_modem(self.droid)
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- self.log.info("Waiting for data state: DATA_CONNECTED")
- test_status, event = tel_test_utils.wait_for_data_state(
- self.ed, self.log, "DATA_CONNECTED", 120)
-
- if test_status == "passed":
- self.log.info("Data connection state(access enabled) "
- "verification: Passed")
- return True
- else:
- self.log.info("Data connection state(access enabled) "
- "verification: Failed")
- return False
-
- @TelephonyBaseTest.tel_test_wrap
- def test_data_conn_state_when_access_disabled(self):
- '''
- Check data conenction state after disabling data access
-
- Steps
- -----
- 1. Get the device is IN_SERVICE state
- 2. check the data conenction status ( data access enabled)
- 3. disable the data access
- 4. check the data conenction status ( data access enabled)
- '''
- test_status = "failed"
- # turn on modem to start registration
- tel_test_utils.turn_on_modem(self.droid)
- self.log.info("Waiting for Network registration")
- test_status, event = tel_test_utils.wait_for_network_registration(
- self.ed, self.anritsu, self.log)
-
- # proceed with next step only if previous step is success
- if test_status == "passed":
- self.log.info("Waiting for data state: DATA_CONNECTED")
- test_status, event = tel_test_utils.wait_for_data_state(
- self.ed, self.log, "DATA_CONNECTED", 120)
-
- if test_status == "passed":
- time.sleep(20)
- self.log.info("Disabling data access")
- self.droid.telephonyToggleDataConnection(False)
- self.log.info("Waiting for data state: DATA_DISCONNECTED")
- test_status, event = tel_test_utils.wait_for_data_state(
- self.ed, self.log, "DATA_DISCONNECTED", 120)
-
- if test_status == "passed":
- self.log.info("Data connection state(access disabled) "
- "verification: Passed")
- return True
- else:
- self.log.info("Data connection state(access disabled) "
- "verification: Failed")
- return False
-
- """ Tests End """
diff --git a/acts/tests/google/wifi/WifiAutoUpdateTest.py b/acts/tests/google/wifi/WifiAutoUpdateTest.py
index f91c18d..4a8ab7b 100755
--- a/acts/tests/google/wifi/WifiAutoUpdateTest.py
+++ b/acts/tests/google/wifi/WifiAutoUpdateTest.py
@@ -20,7 +20,7 @@
import time
import acts.base_test
-import acts.signals
+import acts.signals as signals
import acts.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
diff --git a/acts/tests/google/wifi/WifiConnectedMacRandomizationTest.py b/acts/tests/google/wifi/WifiConnectedMacRandomizationTest.py
new file mode 100644
index 0000000..0f77be8
--- /dev/null
+++ b/acts/tests/google/wifi/WifiConnectedMacRandomizationTest.py
@@ -0,0 +1,259 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import itertools
+import pprint
+import queue
+import time
+
+import acts.base_test
+import acts.signals as signals
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils as utils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+# Default timeout used for reboot, toggle WiFi and Airplane mode,
+# for the system to settle down after the operation.
+DEFAULT_TIMEOUT = 10
+GET_MAC_ADDRESS= ("ip addr show wlan0"
+ "| grep 'link/ether'"
+ "| cut -d ' ' -f6")
+MAC_SETTING = "wifi_connected_mac_randomization_enabled"
+GET_MAC_RANDOMIZATION_STATUS = "settings get global {}".format(MAC_SETTING)
+TURN_ON_MAC_RANDOMIZATION = "settings put global {} 1".format(MAC_SETTING)
+TURN_OFF_MAC_RANDOMIZATION = "settings put global {} 0".format(MAC_SETTING)
+LOG_CLEAR = "logcat -c"
+LOG_GREP = "logcat -d | grep {}"
+
+class WifiConnectedMacRandomizationTest(WifiBaseTest):
+ """Tests for Connected MAC Randomization.
+
+ Test Bed Requirement:
+ * Two Android devices with the first one supporting MAC randomization.
+ * At least two Wi-Fi networks to connect to.
+ """
+
+ def __init__(self, controllers):
+ WifiBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ self.dut_softap = self.android_devices[1]
+ wutils.wifi_test_device_init(self.dut)
+ wutils.wifi_test_device_init(self.dut_softap)
+
+ self.reset_mac_address_to_factory_mac()
+ self.dut.adb.shell(TURN_ON_MAC_RANDOMIZATION)
+ asserts.assert_equal(
+ self.dut.adb.shell(GET_MAC_RANDOMIZATION_STATUS), "1",
+ "Failed to enable Connected MAC Randomization on dut.")
+
+ req_params = ["reference_networks"]
+ opt_param = []
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start()
+
+ asserts.assert_true(
+ self.reference_networks[0]["2g"],
+ "Need at least 1 2.4Ghz reference network with psk.")
+ asserts.assert_true(
+ self.reference_networks[0]["5g"],
+ "Need at least 1 5Ghz reference network with psk.")
+ self.wpapsk_2g = self.reference_networks[0]["2g"]
+ self.wpapsk_5g = self.reference_networks[0]["5g"]
+
+ def setup_test(self):
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.wifi_toggle_state(self.dut_softap, False)
+
+ def teardown_test(self):
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+ wutils.reset_wifi(self.dut_softap)
+
+ def on_fail(self, test_name, begin_time):
+ self.dut.take_bug_report(test_name, begin_time)
+ self.dut.cat_adb_log(test_name, begin_time)
+
+ def teardown_class(self):
+ wutils.stop_wifi_tethering(self.dut_softap)
+ self.reset_mac_address_to_factory_mac()
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """Helper Functions"""
+ def get_current_mac_address(self, ad):
+ """Get the device's wlan0 MAC address.
+
+ Args:
+ ad: AndroidDevice to get MAC address of.
+
+ Returns:
+ A MAC address string in the format of "12:34:56:78:90:12".
+ """
+ return ad.adb.shell(GET_MAC_ADDRESS)
+
+ def is_valid_randomized_mac_address(self, mac):
+ """Check if the given MAC address is a valid randomized MAC address.
+
+ Args:
+ mac: MAC address to check in the format of "12:34:56:78:90:12".
+ """
+ asserts.assert_true(
+ mac != self.dut_factory_mac,
+ "Randomized MAC address is same as factory MAC address.")
+ first_byte = int(mac[:2], 16)
+ asserts.assert_equal(first_byte & 1, 0, "MAC address is not unicast.")
+ asserts.assert_equal(first_byte & 2, 2, "MAC address is not local.")
+
+ def reset_mac_address_to_factory_mac(self):
+ """Reset dut to and store factory MAC address by turning off
+ Connected MAC Randomization and rebooting dut.
+ """
+ self.dut.adb.shell(TURN_OFF_MAC_RANDOMIZATION)
+ asserts.assert_equal(
+ self.dut.adb.shell(GET_MAC_RANDOMIZATION_STATUS), "0",
+ "Failed to disable Connected MAC Randomization on dut.")
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ self.dut_factory_mac = self.get_current_mac_address(self.dut)
+
+ def get_connection_data(self, ad, network):
+ """Connect and get network id and ssid info from connection data.
+
+ Args:
+ ad: AndroidDevice to use for connection
+ network: network info of the network to connect to
+
+ Returns:
+ A convenience dict with the connected network's ID and SSID.
+ """
+ wutils.connect_to_wifi_network(ad, network)
+ connect_data = ad.droid.wifiGetConnectionInfo()
+ ssid_id_dict = dict()
+ ssid_id_dict[WifiEnums.NETID_KEY] = connect_data[WifiEnums.NETID_KEY]
+ ssid_id_dict[WifiEnums.SSID_KEY] = connect_data[WifiEnums.SSID_KEY]
+ return ssid_id_dict
+
+ """Tests"""
+ @test_tracker_info(uuid="")
+ def test_wifi_connection_2G_with_mac_randomization(self):
+ """Tests connection to 2G network with Connected MAC Randomization.
+ """
+ wutils.connect_to_wifi_network(self.dut, self.wpapsk_2g)
+ mac = self.get_current_mac_address(self.dut)
+ self.is_valid_randomized_mac_address(mac)
+
+ @test_tracker_info(uuid="")
+ def test_wifi_connection_5G_with_mac_randomization(self):
+ """Tests connection to 5G network with Connected MAC Randomization.
+ """
+ wutils.connect_to_wifi_network(self.dut, self.wpapsk_5g)
+ mac = self.get_current_mac_address(self.dut)
+ self.is_valid_randomized_mac_address(mac)
+
+ @test_tracker_info(uuid="")
+ def test_randomized_mac_persistent_between_connections(self):
+ """Tests that randomized MAC address assigned to each network is
+ persistent between connections.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Reconnect to the 2GHz network using its network id.
+ 4. Verify that MAC addresses in Steps 1 and 3 are equal.
+ 5. Reconnect to the 5GHz network using its network id.
+ 6. Verify that MAC addresses in Steps 2 and 5 are equal.
+ """
+ connect_data_2g = self.get_connection_data(self.dut, self.wpapsk_2g)
+ old_mac_2g = self.get_current_mac_address(self.dut)
+ self.is_valid_randomized_mac_address(old_mac_2g)
+
+ connect_data_5g = self.get_connection_data(self.dut, self.wpapsk_5g)
+ old_mac_5g = self.get_current_mac_address(self.dut)
+ self.is_valid_randomized_mac_address(old_mac_5g)
+
+ asserts.assert_true(
+ old_mac_2g != old_mac_5g,
+ "Randomized MAC addresses for 2G and 5G networks are equal.")
+
+ reconnect_2g = wutils.connect_to_wifi_network_with_id(
+ self.dut,
+ connect_data_2g[WifiEnums.NETID_KEY],
+ connect_data_2g[WifiEnums.SSID_KEY])
+ if not reconnect_2g:
+ raise signals.TestFailure("Device did not connect to the correct"
+ " 2GHz network.")
+ new_mac_2g = self.get_current_mac_address(self.dut)
+ asserts.assert_equal(
+ old_mac_2g,
+ new_mac_2g,
+ "Randomized MAC for 2G is not persistent between connections.")
+
+ reconnect_5g = wutils.connect_to_wifi_network_with_id(
+ self.dut,
+ connect_data_5g[WifiEnums.NETID_KEY],
+ connect_data_5g[WifiEnums.SSID_KEY])
+ if not reconnect_5g:
+ raise signals.TestFailure("Device did not connect to the correct"
+ " 5GHz network.")
+ new_mac_5g = self.get_current_mac_address(self.dut)
+ asserts.assert_equal(
+ old_mac_5g,
+ new_mac_5g,
+ "Randomized MAC for 5G is not persistent between connections.")
+
+ @test_tracker_info(uuid="")
+ def test_randomized_mac_used_during_connection(self):
+ """Verify that the randomized MAC address and not the factory
+ MAC address is used during connection by checking the softap logs.
+
+ Steps:
+ 1. Set up softAP on dut_softap.
+ 2. Have dut connect to the softAp.
+ 3. Verify that only randomized MAC appears in softAp logs.
+ """
+ self.dut_softap.adb.shell(LOG_CLEAR)
+ config = wutils.create_softap_config()
+ wutils.start_wifi_tethering(self.dut_softap,
+ config[wutils.WifiEnums.SSID_KEY],
+ config[wutils.WifiEnums.PWD_KEY],
+ WIFI_CONFIG_APBAND_2G)
+
+ # Internet validation fails when dut_softap does not have a valid sim
+ # supporting softap. Since this test is not checking for internet
+ # validation, we suppress failure signals.
+ wutils.connect_to_wifi_network(self.dut, config, assert_on_fail=False)
+ mac = self.get_current_mac_address(self.dut)
+ wutils.stop_wifi_tethering(self.dut_softap)
+
+ self.is_valid_randomized_mac_address(mac)
+ log = self.dut_softap.adb.shell(LOG_GREP.format(mac))
+ asserts.assert_true(len(log) > 0, "Randomized MAC not in log.")
+ log = self.dut_softap.adb.shell(LOG_GREP.format(self.dut_factory_mac))
+ asserts.assert_true(len(log) == 0, "Factory MAC is in log.")
diff --git a/acts/tests/google/wifi/WifiCrashTest.py b/acts/tests/google/wifi/WifiCrashTest.py
new file mode 100755
index 0000000..e85c50e
--- /dev/null
+++ b/acts/tests/google/wifi/WifiCrashTest.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import itertools
+import pprint
+import queue
+import time
+
+import acts.base_test
+import acts.signals as signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+# Default timeout used for reboot, toggle WiFi and Airplane mode,
+# for the system to settle down after the operation.
+DEFAULT_TIMEOUT = 10
+WIFICOND_KILL_SHELL_COMMAND = "killall wificond"
+WIFI_VENDOR_HAL_KILL_SHELL_COMMAND = "killall android.hardware.wifi@1.0-service"
+SUPPLICANT_KILL_SHELL_COMMAND = "killall wpa_supplicant"
+
+class WifiCrashTest(WifiBaseTest):
+ """Crash Tests for wifi stack.
+
+ Test Bed Requirement:
+ * One Android device
+ * One Wi-Fi network visible to the device.
+ """
+
+ def __init__(self, controllers):
+ WifiBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = []
+ opt_param = ["reference_networks"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start()
+
+ asserts.assert_true(
+ len(self.reference_networks) > 0,
+ "Need at least one reference network with psk.")
+ self.network = self.reference_networks[0]["2g"]
+
+ def setup_test(self):
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut, True)
+
+ def teardown_test(self):
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def on_fail(self, test_name, begin_time):
+ self.dut.take_bug_report(test_name, begin_time)
+ self.dut.cat_adb_log(test_name, begin_time)
+
+ def teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+
+ """Helper Functions"""
+
+ """Tests"""
+ @test_tracker_info(uuid="b87fd23f-9bfc-406b-a5b2-17ce6be6c780")
+ def test_wifi_framework_crash_reconnect(self):
+ """Connect to a network, crash framework, then ensure
+ we connect back to the previously connected network.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Restart framework.
+ 3. Reconnect to the previous network.
+
+ """
+ wutils.wifi_connect(self.dut, self.network, num_of_tries=3)
+ # Restart framework
+ self.log.info("Crashing framework")
+ self.dut.restart_runtime()
+ # We won't get the disconnect broadcast because framework crashed.
+ # wutils.wait_for_disconnect(self.dut)
+ time.sleep(DEFAULT_TIMEOUT)
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ if wifi_info[WifiEnums.SSID_KEY] != self.network[WifiEnums.SSID_KEY]:
+ raise signals.TestFailure("Device did not connect to the"
+ " network after crashing framework.")
+
+ @test_tracker_info(uuid="33f9e4f6-29b8-4116-8f9b-5b13d93b4bcb")
+ def test_wifi_cond_crash_reconnect(self):
+ """Connect to a network, crash wificond, then ensure
+ we connect back to the previously connected network.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Crash wificond.
+ 3. Ensure we get a disconnect.
+ 4. Ensure we reconnect to the previous network.
+
+ """
+ wutils.wifi_connect(self.dut, self.network, num_of_tries=3)
+ # Restart wificond
+ self.log.info("Crashing wificond")
+ self.dut.adb.shell(WIFICOND_KILL_SHELL_COMMAND)
+ wutils.wait_for_disconnect(self.dut)
+ time.sleep(DEFAULT_TIMEOUT)
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ if wifi_info[WifiEnums.SSID_KEY] != self.network[WifiEnums.SSID_KEY]:
+ raise signals.TestFailure("Device did not connect to the"
+ " network after crashing wificond.")
+
+ @test_tracker_info(uuid="463e3d7b-b0b7-4843-b83b-5613a71ae2ac")
+ def test_wifi_vendorhal_crash_reconnect(self):
+ """Connect to a network, crash wifi HAL, then ensure
+ we connect back to the previously connected network.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Crash wifi HAL.
+ 3. Ensure we get a disconnect.
+ 4. Ensure we reconnect to the previous network.
+
+ """
+ wutils.wifi_connect(self.dut, self.network, num_of_tries=3)
+ # Restart wificond
+ self.log.info("Crashing wifi HAL")
+ self.dut.adb.shell(WIFI_VENDOR_HAL_KILL_SHELL_COMMAND)
+ wutils.wait_for_disconnect(self.dut)
+ time.sleep(DEFAULT_TIMEOUT)
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ if wifi_info[WifiEnums.SSID_KEY] != self.network[WifiEnums.SSID_KEY]:
+ raise signals.TestFailure("Device did not connect to the"
+ " network after crashing wifi HAL.")
+
+ @test_tracker_info(uuid="7c5cd1fc-8f8d-494c-beaf-4eb61b48917b")
+ def test_wpa_supplicant_crash_reconnect(self):
+ """Connect to a network, crash wpa_supplicant, then ensure
+ we connect back to the previously connected network.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Crash wpa_supplicant.
+ 3. Ensure we get a disconnect.
+ 4. Ensure we reconnect to the previous network.
+
+ """
+ wutils.wifi_connect(self.dut, self.network, num_of_tries=3)
+ # Restart wificond
+ self.log.info("Crashing wpa_supplicant")
+ self.dut.adb.shell(SUPPLICANT_KILL_SHELL_COMMAND)
+ wutils.wait_for_disconnect(self.dut)
+ time.sleep(DEFAULT_TIMEOUT)
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ if wifi_info[WifiEnums.SSID_KEY] != self.network[WifiEnums.SSID_KEY]:
+ raise signals.TestFailure("Device did not connect to the"
+ " network after crashing wpa_supplicant.")
diff --git a/acts/tests/google/wifi/WifiDiagnosticsTest.py b/acts/tests/google/wifi/WifiDiagnosticsTest.py
new file mode 100644
index 0000000..79fb082
--- /dev/null
+++ b/acts/tests/google/wifi/WifiDiagnosticsTest.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import itertools
+import pprint
+import queue
+import time
+
+import acts.base_test
+import acts.signals as signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+DEFAULT_WAIT_TIME = 2
+
+
+class WifiDiagnosticsTest(WifiBaseTest):
+ """
+ Test Bed Requirement:
+ * One Android device
+ * An open Wi-Fi network.
+ * Verbose logging is on.
+ """
+
+ def __init__(self, controllers):
+ WifiBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = []
+ opt_param = ["open_network"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start()
+ wutils.wifi_toggle_state(self.dut, True)
+ asserts.assert_true(
+ len(self.open_network) > 0,
+ "Need at least one open network.")
+ self.open_network = self.open_network[0]["2g"]
+
+ def setup_test(self):
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+
+ def on_fail(self, test_name, begin_time):
+ self.dut.take_bug_report(test_name, begin_time)
+ self.dut.cat_adb_log(test_name, begin_time)
+
+ def teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["open_network"]
+
+ """Tests"""
+
+ @test_tracker_info(uuid="d6f1661b-6732-4939-8c28-f20917774ec0")
+ def test_ringbuffers_are_dumped_during_lsdebug(self):
+ """Steps:
+ 1. Connect to a open network.
+ 2. Delete old files under data/vendor/tombstones/wifi
+ 3. Call lshal debug on wifi hal component
+ 4. Verify that files are created under data/vender/tombstones/wifi
+ """
+ wutils.connect_to_wifi_network(self.dut, self.open_network)
+ time.sleep(DEFAULT_WAIT_TIME)
+ self.dut.adb.shell("rm data/vendor/tombstones/wifi/*")
+ try:
+ self.dut.adb.shell("lshal debug android.hardware.wifi@1.2::IWifi")
+ except UnicodeDecodeError:
+ """ Gets this error because adb.shell trys to parse the output to a string
+ but ringbuffer dumps should already be generated """
+ self.log.info("Unicode decode error occurred, but this is ok")
+ file_count_plus_one = self.dut.adb.shell("ls -l data/vendor/tombstones/wifi | wc -l")
+ if int(file_count_plus_one) <= 1:
+ raise signals.TestFailure("Failed to create ringbuffer debug files.")
\ No newline at end of file
diff --git a/acts/tests/google/wifi/WifiEnterpriseTest.py b/acts/tests/google/wifi/WifiEnterpriseTest.py
index 58ae4db..41a2512 100755
--- a/acts/tests/google/wifi/WifiEnterpriseTest.py
+++ b/acts/tests/google/wifi/WifiEnterpriseTest.py
@@ -130,7 +130,6 @@
# Set screen lock password so ConfigStore is unlocked.
self.dut.droid.setDevicePassword(self.device_password)
self.tcpdump_pid = None
- self.tcpdump_file = None
def teardown_class(self):
wutils.reset_wifi(self.dut)
@@ -143,14 +142,12 @@
self.dut.droid.wakeUpNow()
wutils.reset_wifi(self.dut)
self.dut.ed.clear_all_events()
- (self.tcpdump_pid, self.tcpdump_file) = start_adb_tcpdump(
- self.dut, self.test_name, mask='all')
+ self.tcpdump_pid = start_adb_tcpdump(self.dut, self.test_name, mask='all')
def teardown_test(self):
if self.tcpdump_pid:
stop_adb_tcpdump(self.dut,
self.tcpdump_pid,
- self.tcpdump_file,
pull_tcpdump=True)
self.tcpdump_pid = None
self.dut.droid.wakeLockRelease()
diff --git a/acts/tests/google/wifi/WifiHiddenSSIDTest.py b/acts/tests/google/wifi/WifiHiddenSSIDTest.py
new file mode 100755
index 0000000..f794a36
--- /dev/null
+++ b/acts/tests/google/wifi/WifiHiddenSSIDTest.py
@@ -0,0 +1,166 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import itertools
+import pprint
+import queue
+import time
+
+import acts.base_test
+import acts.signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+
+class WifiHiddenSSIDTest(WifiBaseTest):
+ """Tests for APIs in Android's WifiManager class.
+
+ Test Bed Requirement:
+ * One Android device
+ * Several Wi-Fi networks visible to the device, including an open Wi-Fi
+ network.
+ """
+
+ def __init__(self, controllers):
+ WifiBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = []
+ opt_param = [
+ "open_network", "reference_networks"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start(hidden=True)
+
+ asserts.assert_true(
+ len(self.reference_networks) > 0,
+ "Need at least one reference network with psk.")
+ self.open_hidden_2g = self.open_network[0]["2g"]
+ self.open_hidden_5g = self.open_network[0]["5g"]
+ self.wpa_hidden_2g = self.reference_networks[0]["2g"]
+ self.wpa_hidden_5g = self.reference_networks[0]["5g"]
+
+ def setup_test(self):
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def on_fail(self, test_name, begin_time):
+ self.dut.take_bug_report(test_name, begin_time)
+ self.dut.cat_adb_log(test_name, begin_time)
+
+ def teardown_class(self):
+ wutils.reset_wifi(self.dut)
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """Helper Functions"""
+
+ def check_hiddenSSID_in_scan(self, ap_ssid, max_tries=2):
+ """Check if the ap started by wifi tethering is seen in scan results.
+
+ Args:
+ ap_ssid: SSID of the ap we are looking for.
+ max_tries: Number of scans to try.
+ Returns:
+ True: if ap_ssid is found in scan results.
+ False: if ap_ssid is not found in scan results.
+ """
+ for num_tries in range(max_tries):
+ wutils.start_wifi_connection_scan(self.dut)
+ scan_results = self.dut.droid.wifiGetScanResults()
+ match_results = wutils.match_networks(
+ {wutils.WifiEnums.SSID_KEY: ap_ssid}, scan_results)
+ if len(match_results) > 0:
+ return True
+ return False
+
+ def add_hiddenSSID_and_connect(self, hidden_network):
+ """Add the hidden network and connect to it.
+
+ Args:
+ hidden_network: The hidden network config to connect to.
+
+ """
+ ret = self.dut.droid.wifiAddNetwork(hidden_network)
+ asserts.assert_true(ret != -1, "Add network %r failed" % hidden_network)
+ self.dut.droid.wifiEnableNetwork(ret, 0)
+ wutils.connect_to_wifi_network(self.dut, hidden_network)
+ if not wutils.validate_connection(self.dut):
+ raise signals.TestFailure("Fail to connect to internet on %s" %
+ hidden_network)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="d0871f98-6049-4937-a288-ec4a2746c771")
+ def test_connect_to_wpa_hidden_2g(self):
+ """Connect to a WPA, 2G network.
+
+ Steps:
+ 1. Add a WPA, 2G hidden network.
+ 2. Ensure the network is visible in scan.
+ 3. Connect and run ping.
+
+ """
+ self.add_hiddenSSID_and_connect(self.wpa_hidden_2g)
+
+ @test_tracker_info(uuid="c558b31a-549a-4012-9052-275623992187")
+ def test_connect_to_wpa_hidden_5g(self):
+ """Connect to a WPA, 5G hidden network.
+
+ Steps:
+ 1. Add a WPA, 5G hidden network.
+ 2. Ensure the network is visible in scan.
+ 3. Connect and run ping.
+
+ """
+ self.add_hiddenSSID_and_connect(self.wpa_hidden_5g)
+
+ @test_tracker_info(uuid="cdfce76f-6374-439d-aa1d-e920508269d2")
+ def test_connect_to_open_hidden_2g(self):
+ """Connect to a Open, 2G hidden network.
+
+ Steps:
+ 1. Add a Open, 2G hidden network.
+ 2. Ensure the network is visible in scan.
+ 3. Connect and run ping.
+
+ """
+ self.add_hiddenSSID_and_connect(self.open_hidden_2g)
+
+ @test_tracker_info(uuid="29ccbae4-4382-4df8-8fc5-00e3104230d0")
+ def test_connect_to_open_hidden_5g(self):
+ """Connect to a Open, 5G hidden network.
+
+ Steps:
+ 1. Add a Open, 5G hidden network.
+ 2. Ensure the network is visible in scan.
+ 3. Connect and run ping.
+
+ """
+ self.add_hiddenSSID_and_connect(self.open_hidden_5g)
diff --git a/acts/tests/google/wifi/WifiIOTTest.py b/acts/tests/google/wifi/WifiIOTTest.py
index 7a69229..7544951 100755
--- a/acts/tests/google/wifi/WifiIOTTest.py
+++ b/acts/tests/google/wifi/WifiIOTTest.py
@@ -37,24 +37,28 @@
"""
def __init__(self, controllers):
- self.attenuators = None
WifiBaseTest.__init__(self, controllers)
def setup_class(self):
self.dut = self.android_devices[0]
wutils.wifi_test_device_init(self.dut)
- req_params = [ "iot_networks", "open_network", "iperf_server_address" ]
- self.unpack_userparams(req_param_names=req_params)
+ req_params = [ "iot_networks", ]
+ opt_params = [ "open_network", "iperf_server_address" ]
+ self.unpack_userparams(req_param_names=req_params,
+ opt_param_names=opt_params)
asserts.assert_true(
len(self.iot_networks) > 0,
"Need at least one iot network with psk.")
- self.iot_networks.append(self.open_network)
+
+ if self.open_network:
+ self.iot_networks.append(self.open_network)
wutils.wifi_toggle_state(self.dut, True)
if "iperf_server_address" in self.user_params:
self.iperf_server = self.iperf_servers[0]
+ self.iperf_server.start()
# create hashmap for testcase name and SSIDs
self.iot_test_prefix = "test_iot_connection_to_"
@@ -62,7 +66,6 @@
for network in self.iot_networks:
SSID = network['SSID'].replace('-','_')
self.ssid_map[SSID] = network
- self.iperf_server.start()
def setup_test(self):
self.dut.droid.wakeLockAcquireBright()
@@ -74,7 +77,8 @@
wutils.reset_wifi(self.dut)
def teardown_class(self):
- self.iperf_server.stop()
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server.stop()
def on_fail(self, test_name, begin_time):
self.dut.take_bug_report(test_name, begin_time)
@@ -128,32 +132,32 @@
"""Tests"""
@test_tracker_info(uuid="a57cc861-b6c2-47e4-9db6-7a3ab32c6e20")
- def iot_connection_to_ubiquity_ap1_2g(self):
+ def test_iot_connection_to_ubiquity_ap1_2g(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="2065c2f7-2b89-4da7-a15d-e5dc17b88d52")
- def iot_connection_to_ubiquity_ap1_5g(self):
+ def test_iot_connection_to_ubiquity_ap1_5g(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="6870e35b-f7a7-45bf-b021-fea049ae53de")
- def iot_connection_to_AirportExpress_2G(self):
+ def test_iot_connection_to_AirportExpress_2G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="95f4b405-79d7-4873-a152-4384acc88f41")
- def iot_connection_to_AirportExpress_5G(self):
+ def test_iot_connection_to_AirportExpress_5G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="02a8cc75-6781-4153-8d90-bed7568a1e78")
- def iot_connection_to_AirportExtreme_2G(self):
+ def test_iot_connection_to_AirportExtreme_2G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="83a42c97-1358-4ba7-bdb2-238fdb1c945e")
- def iot_connection_to_AirportExtreme_5G(self):
+ def test_iot_connection_to_AirportExtreme_5G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@@ -168,12 +172,12 @@
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="2503d9ed-35df-4be0-b838-590324cecaee")
- def test_iot_connection_to_Dlink_AC1200_2G(self):
+ def iot_connection_to_Dlink_AC1200_2G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="0a44e148-a4bf-43f4-88eb-e4c1ffa850ce")
- def test_iot_connection_to_Dlink_AC1200_5G(self):
+ def iot_connection_to_Dlink_AC1200_5G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@@ -238,17 +242,17 @@
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="054d2ffc-97fd-4613-bf47-acedd0fa4701")
- def iot_connection_to_NETGEAR_AC3200_2G(self):
+ def test_iot_connection_to_NETGEAR_AC3200_2G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="d15a789a-def5-4c6a-b59e-1a75f73cc6a9")
- def iot_connection_to_NETGEAR_AC3200_5G_1(self):
+ def test_iot_connection_to_NETGEAR_AC3200_5G_1(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="1de6369e-97da-479f-b17c-9144bb814f51")
- def iot_connection_to_NETGEAR_AC3200_5G_2(self):
+ def test_iot_connection_to_NETGEAR_AC3200_5G_2(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@@ -278,12 +282,12 @@
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="e5517b82-c225-449d-83ac-055a561a764f")
- def iot_connection_to_TP_LINK_AC1700_2G(self):
+ def test_iot_connection_to_TP_LINK_AC1700_2G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="9531d3cc-129d-4501-a5e3-d7502120cd8b")
- def iot_connection_to_TP_LINK_AC1700_5G(self):
+ def test_iot_connection_to_TP_LINK_AC1700_5G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@@ -313,17 +317,17 @@
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="7c12f943-d9e2-45b1-aa84-fcb43efbbb04")
- def iot_connection_to_TP_LINK_5504_2G(self):
+ def test_iot_connection_to_TP_LINK_5504_2G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="52be6b76-5e43-4289-83e1-4cd0d995d39b")
- def iot_connection_to_TP_LINK_5504_5G_1(self):
+ def test_iot_connection_to_TP_LINK_5504_5G_1(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="0b43d1da-e207-443d-b16c-c4ee3e924036")
- def iot_connection_to_TP_LINK_5504_5G_2(self):
+ def test_iot_connection_to_TP_LINK_5504_5G_2(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@@ -348,12 +352,12 @@
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="e639f6db-ad8e-4b4f-91f3-10acdf93142a")
- def iot_connection_to_AmpedAthena_2G(self):
+ def test_iot_connection_to_AmpedAthena_2G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
@test_tracker_info(uuid="3dd90d80-952f-4f17-a48a-fe42e7d6e1ff")
- def iot_connection_to_AmpedAthena_5G(self):
+ def test_iot_connection_to_AmpedAthena_5G(self):
ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
diff --git a/acts/tests/google/wifi/WifiIOTtpeTest.py b/acts/tests/google/wifi/WifiIOTtpeTest.py
new file mode 100644
index 0000000..979a434
--- /dev/null
+++ b/acts/tests/google/wifi/WifiIOTtpeTest.py
@@ -0,0 +1,218 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - 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.
+
+import itertools
+import pprint
+import time
+
+import acts.signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+
+class WifiIOTtpeTest(WifiBaseTest):
+ """ Tests for wifi IOT
+
+ Test Bed Requirement:
+ * One Android device
+ * Wi-Fi IOT networks visible to the device
+ """
+
+ def __init__(self, controllers):
+ self.attenuators = None
+ WifiBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+
+ req_params = [ "iot_networks", ]
+ opt_params = [ "open_network", "iperf_server_address" ]
+ self.unpack_userparams(req_param_names=req_params,
+ opt_param_names=opt_params)
+
+ asserts.assert_true(
+ len(self.iot_networks) > 0,
+ "Need at least one iot network with psk.")
+
+ if getattr(self, 'open_network', False):
+ self.iot_networks.append(self.open_network)
+
+ wutils.wifi_toggle_state(self.dut, True)
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server = self.iperf_servers[0]
+ self.iperf_server.start()
+
+ # create hashmap for testcase name and SSIDs
+ self.iot_test_prefix = "test_iot_connection_to_"
+ self.ssid_map = {}
+ for network in self.iot_networks:
+ SSID = network['SSID'].replace('-','_')
+ self.ssid_map[SSID] = network
+
+ def setup_test(self):
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def teardown_class(self):
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server.stop()
+
+ def on_fail(self, test_name, begin_time):
+ self.dut.take_bug_report(test_name, begin_time)
+ self.dut.cat_adb_log(test_name, begin_time)
+
+ """Helper Functions"""
+
+ def connect_to_wifi_network(self, network):
+ """Connection logic for open and psk wifi networks.
+
+ Args:
+ params: Dictionary with network info.
+ """
+ SSID = network[WifiEnums.SSID_KEY]
+ self.dut.ed.clear_all_events()
+ wutils.start_wifi_connection_scan(self.dut)
+ scan_results = self.dut.droid.wifiGetScanResults()
+ wutils.assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
+ wutils.wifi_connect(self.dut, network, num_of_tries=3)
+
+ def run_iperf_client(self, network):
+ """Run iperf traffic after connection.
+
+ Args:
+ params: Dictionary with network info.
+ """
+ if "iperf_server_address" in self.user_params:
+ wait_time = 5
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic through {}".format(SSID))
+ time.sleep(wait_time)
+ port_arg = "-p {}".format(self.iperf_server.port)
+ success, data = self.dut.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+
+ def connect_to_wifi_network_and_run_iperf(self, network):
+ """Connection logic for open and psk wifi networks.
+
+ Logic steps are
+ 1. Connect to the network.
+ 2. Run iperf traffic.
+
+ Args:
+ params: A dictionary with network info.
+ """
+ self.connect_to_wifi_network(network)
+ self.run_iperf_client(network)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="0e4ad6ed-595c-4629-a4c9-c6be9c3c58e0")
+ def test_iot_connection_to_ASUS_RT_AC68U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="a76d8acc-808e-4a5d-a52b-5ba07d07b810")
+ def test_iot_connection_to_ASUS_RT_AC68U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="659a3e5e-07eb-4905-9cda-92e959c7b674")
+ def test_iot_connection_to_D_Link_DIR_868L_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="6bcfd736-30fc-48a8-b4fb-723d1d113f3c")
+ def test_iot_connection_to_D_Link_DIR_868L_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="c9da945a-2c4a-44e1-881d-adf307b39b21")
+ def test_iot_connection_to_TP_LINK_WR940N_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="db0d224d-df81-401f-bf35-08ad02e41a71")
+ def test_iot_connection_to_ASUS_RT_N66U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="845ff1d6-618d-40f3-81c3-6ed3a0751fde")
+ def test_iot_connection_to_ASUS_RT_N66U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="6908039b-ccc9-4777-a0f1-3494ce642014")
+ def test_iot_connection_to_ASUS_RT_AC54U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="2647c15f-2aad-47d7-8dee-b2ee1ac4cef6")
+ def test_iot_connection_to_ASUS_RT_AC54U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="99678f66-ddf1-454d-87e4-e55177ec380d")
+ def test_iot_connection_to_ASUS_RT_N56U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="4dd75e81-9a8e-44fd-9449-09f5ab8a63c3")
+ def test_iot_connection_to_ASUS_RT_N56U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="315397ce-50d5-4abf-a11c-1abcaef832d3")
+ def test_iot_connection_to_BELKIN_F9K1002v1_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="05ba464a-b1ef-4ac1-a32f-c919ec4aa1dd")
+ def test_iot_connection_to_CISCO_E1200_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="04912868-4a47-40ce-877e-4e4c89849557")
+ def test_iot_connection_to_TP_LINK_C2_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="53517a21-3802-4185-b8bb-6eaace063a42")
+ def test_iot_connection_to_TP_LINK_C2_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="71c08c1c-415d-4da4-a151-feef43fb6ad8")
+ def test_iot_connection_to_ASUS_RT_AC66U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="2322c155-07d1-47c9-bd21-2e358e3df6ee")
+ def test_iot_connection_to_ASUS_RT_AC66U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
diff --git a/acts/tests/google/wifi/WifiManagerTest.py b/acts/tests/google/wifi/WifiManagerTest.py
index b2a7b53..5a5dfeb 100755
--- a/acts/tests/google/wifi/WifiManagerTest.py
+++ b/acts/tests/google/wifi/WifiManagerTest.py
@@ -20,7 +20,7 @@
import time
import acts.base_test
-import acts.signals
+import acts.signals as signals
import acts.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
@@ -51,11 +51,6 @@
def setup_class(self):
self.dut = self.android_devices[0]
wutils.wifi_test_device_init(self.dut)
- # If running in a setup with attenuators, set attenuation on all
- # channels to zero.
- if getattr(self, "attenuators", []):
- for a in self.attenuators:
- a.set_atten(0)
req_params = []
opt_param = [
"open_network", "reference_networks", "iperf_server_address"
@@ -75,19 +70,23 @@
self.wpapsk_2g = self.reference_networks[0]["2g"]
self.wpapsk_5g = self.reference_networks[0]["5g"]
self.open_network = self.open_network[0]["2g"]
- self.iperf_server.start()
+ if hasattr(self, 'iperf_server'):
+ self.iperf_server.start()
def setup_test(self):
self.dut.droid.wakeLockAcquireBright()
self.dut.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut, True)
def teardown_test(self):
self.dut.droid.wakeLockRelease()
self.dut.droid.goToSleepNow()
+ self.turn_location_off_and_scan_toggle_off()
wutils.reset_wifi(self.dut)
def teardown_class(self):
- self.iperf_server.stop()
+ if hasattr(self, 'iperf_server'):
+ self.iperf_server.stop()
def on_fail(self, test_name, begin_time):
self.dut.take_bug_report(test_name, begin_time)
@@ -110,10 +109,8 @@
droid = ad.droid
ed = ad.ed
SSID = network[WifiEnums.SSID_KEY]
- ed.clear_all_events()
- wutils.start_wifi_connection_scan(ad)
- scan_results = droid.wifiGetScanResults()
- wutils.assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ ad, SSID);
wutils.wifi_connect(ad, network, num_of_tries=3)
def get_connection_data(self, dut, network):
@@ -218,12 +215,8 @@
False otherwise.
"""
- self.dut.ed.clear_all_events()
- wutils.start_wifi_connection_scan(self.dut)
- scan_results = self.dut.droid.wifiGetScanResults()
- wutils.assert_network_in_list({
- WifiEnums.SSID_KEY: network_ssid
- }, scan_results)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, network_ssid);
wutils.wifi_connect_by_id(self.dut, network_id)
connect_data = self.dut.droid.wifiGetConnectionInfo()
connect_ssid = connect_data[WifiEnums.SSID_KEY]
@@ -328,15 +321,188 @@
idle_time = new_idle_time
wutils.start_wifi_connection_scan(self.dut)
+ def turn_location_on_and_scan_toggle_on(self):
+ """ Turns on wifi location scans.
+ """
+ acts.utils.set_location_service(self.dut, True)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(True)
+ msg = "Failed to turn on location service's scan."
+ asserts.assert_true(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def turn_location_off_and_scan_toggle_off(self):
+ """ Turns off wifi location scans.
+ """
+ acts.utils.set_location_service(self.dut, False)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
+ msg = "Failed to turn off location service's scan."
+ asserts.assert_true(not self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def turn_location_on_and_scan_toggle_off(self):
+ """ Turns off wifi location scans, but keeps location on.
+ """
+ acts.utils.set_location_service(self.dut, True)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
+ msg = "Failed to turn off location service's scan."
+ asserts.assert_true(not self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def helper_reconnect_toggle_wifi(self):
+ """Connect to multiple networks, turn off/on wifi, then reconnect to
+ a previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Turn WiFi OFF/ON.
+ 4. Reconnect to the non-current network.
+
+ """
+ connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
+ connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
+ wutils.toggle_wifi_off_and_on(self.dut)
+ reconnect_to = self.get_enabled_network(connect_2g_data,
+ connect_5g_data)
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ raise signals.TestFailure("Device did not connect to the correct"
+ " network after toggling WiFi.")
+
+ def helper_reconnect_toggle_airplane(self):
+ """Connect to multiple networks, turn on/off Airplane moce, then
+ reconnect a previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Turn ON/OFF Airplane mode.
+ 4. Reconnect to the non-current network.
+
+ """
+ connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
+ connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
+ wutils.toggle_airplane_mode_on_and_off(self.dut)
+ reconnect_to = self.get_enabled_network(connect_2g_data,
+ connect_5g_data)
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ raise signals.TestFailure("Device did not connect to the correct"
+ " network after toggling Airplane mode.")
+
+ def helper_reboot_configstore_reconnect(self):
+ """Connect to multiple networks, reboot then reconnect to previously
+ connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Reboot device.
+ 4. Verify all networks are persistent after reboot.
+ 5. Reconnect to the non-current network.
+
+ """
+ network_list = self.connect_multiple_networks(self.dut)
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ self.check_configstore_networks(network_list)
+
+ reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
+ network_list[BAND_5GHZ])
+
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ raise signals.TestFailure(
+ "Device failed to reconnect to the correct"
+ " network after reboot.")
+
+ def helper_toggle_wifi_reboot_configstore_reconnect(self):
+ """Connect to multiple networks, disable WiFi, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Turn WiFi OFF.
+ 4. Reboot device.
+ 5. Turn WiFi ON.
+ 4. Verify all networks are persistent after reboot.
+ 5. Reconnect to the non-current network.
+
+ """
+ network_list = self.connect_multiple_networks(self.dut)
+ self.log.debug("Toggling wifi OFF")
+ wutils.wifi_toggle_state(self.dut, False)
+ time.sleep(DEFAULT_TIMEOUT)
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ self.log.debug("Toggling wifi ON")
+ wutils.wifi_toggle_state(self.dut, True)
+ time.sleep(DEFAULT_TIMEOUT)
+ self.check_configstore_networks(network_list)
+ reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
+ network_list[BAND_5GHZ])
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ msg = ("Device failed to reconnect to the correct network after"
+ " toggling WiFi and rebooting.")
+ raise signals.TestFailure(msg)
+
+ def helper_toggle_airplane_reboot_configstore_reconnect(self):
+ """Connect to multiple networks, enable Airplane mode, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Toggle Airplane mode ON.
+ 4. Reboot device.
+ 5. Toggle Airplane mode OFF.
+ 4. Verify all networks are persistent after reboot.
+ 5. Reconnect to the non-current network.
+
+ """
+ network_list = self.connect_multiple_networks(self.dut)
+ self.log.debug("Toggling Airplane mode ON")
+ asserts.assert_true(
+ acts.utils.force_airplane_mode(self.dut, True),
+ "Can not turn on airplane mode on: %s" % self.dut.serial)
+ time.sleep(DEFAULT_TIMEOUT)
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ self.log.debug("Toggling Airplane mode OFF")
+ asserts.assert_true(
+ acts.utils.force_airplane_mode(self.dut, False),
+ "Can not turn on airplane mode on: %s" % self.dut.serial)
+ time.sleep(DEFAULT_TIMEOUT)
+ self.check_configstore_networks(network_list)
+ reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
+ network_list[BAND_5GHZ])
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ msg = ("Device failed to reconnect to the correct network after"
+ " toggling Airplane mode and rebooting.")
+ raise signals.TestFailure(msg)
+
"""Tests"""
@test_tracker_info(uuid="525fc5e3-afba-4bfd-9a02-5834119e3c66")
- def test_toggle_state(self):
+ def test_toggle_wifi_state_and_get_startupTime(self):
"""Test toggling wifi"""
self.log.debug("Going from on to off.")
wutils.wifi_toggle_state(self.dut, False)
self.log.debug("Going from off to on.")
+ startTime = time.time()
wutils.wifi_toggle_state(self.dut, True)
+ startup_time = time.time() - startTime
+ self.log.debug("WiFi was enabled on the device in %s s." % startup_time)
@test_tracker_info(uuid="e9d11563-2bbe-4c96-87eb-ec919b51435b")
def test_toggle_with_screen(self):
@@ -360,49 +526,28 @@
@test_tracker_info(uuid="71556e06-7fb1-4e2b-9338-b01f1f8e286e")
def test_scan(self):
"""Test wifi connection scan can start and find expected networks."""
- wutils.wifi_toggle_state(self.dut, True)
- self.log.debug("Start regular wifi scan.")
- wutils.start_wifi_connection_scan(self.dut)
- wifi_results = self.dut.droid.wifiGetScanResults()
- self.log.debug("Scan results: %s", wifi_results)
ssid = self.open_network[WifiEnums.SSID_KEY]
- wutils.assert_network_in_list({WifiEnums.SSID_KEY: ssid}, wifi_results)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, ssid);
@test_tracker_info(uuid="3ea09efb-6921-429e-afb1-705ef5a09afa")
def test_scan_with_wifi_off_and_location_scan_on(self):
"""Put wifi in scan only mode"""
- acts.utils.set_location_service(self.dut, True)
- self.dut.droid.wifiScannerToggleAlwaysAvailable(True)
- msg = "Failed to turn on location service's scan."
- asserts.assert_true(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+ self.turn_location_on_and_scan_toggle_on()
wutils.wifi_toggle_state(self.dut, False)
"""Test wifi connection scan can start and find expected networks."""
- self.log.debug("Start regular wifi scan.")
- wutils.start_wifi_connection_scan(self.dut)
- wifi_results = self.dut.droid.wifiGetScanResults()
- self.log.debug("Scan results: %s", wifi_results)
ssid = self.open_network[WifiEnums.SSID_KEY]
- wutils.assert_network_in_list({WifiEnums.SSID_KEY: ssid}, wifi_results)
-
- """Turn off location scan and wifi on at the end of the test"""
- wutils.wifi_toggle_state(self.dut, True)
- self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
- msg = "Failed to turn off location service's scan."
- asserts.assert_true(not self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
- acts.utils.set_location_service(self.dut, False)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, ssid);
@test_tracker_info(uuid="770caebe-bcb1-43ac-95b6-5dd52dd90e80")
def test_scan_with_wifi_off_and_location_scan_off(self):
"""Turn off wifi and location scan"""
- acts.utils.set_location_service(self.dut, True)
- self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
- msg = "Failed to turn off location service's scan."
- asserts.assert_true(not self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+ self.turn_location_on_and_scan_toggle_off()
wutils.wifi_toggle_state(self.dut, False)
"""Test wifi connection scan should fail."""
- self.log.debug("Start regular wifi scan.")
self.dut.droid.wifiStartScan()
try:
self.dut.ed.pop_event("WifiManagerScanResultsAvailable", 60)
@@ -411,10 +556,6 @@
else:
asserts.fail("Wi-Fi scan results received")
- """Turn wifi on at the end of the test"""
- wutils.wifi_toggle_state(self.dut, True)
- acts.utils.set_location_service(self.dut, False)
-
@test_tracker_info(uuid="a4ad9930-a8fa-4868-81ed-a79c7483e502")
def test_add_network(self):
"""Test wifi connection scan."""
@@ -510,17 +651,23 @@
4. Reconnect to the non-current network.
"""
- connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
- connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
- wutils.toggle_wifi_off_and_on(self.dut)
- reconnect_to = self.get_enabled_network(connect_2g_data,
- connect_5g_data)
- reconnect = self.connect_to_wifi_network_with_id(
- reconnect_to[WifiEnums.NETID_KEY],
- reconnect_to[WifiEnums.SSID_KEY])
- if not reconnect:
- raise signals.TestFailure("Device did not connect to the correct"
- " network after toggling WiFi.")
+ self.helper_reconnect_toggle_wifi()
+
+ @test_tracker_info(uuid="bd2cec9e-7f17-44ef-8a0c-4da92a9b55ae")
+ def test_reconnect_toggle_wifi_with_location_scan_on(self):
+ """Connect to multiple networks, turn off/on wifi, then reconnect to
+ a previously connected network.
+
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Turn WiFi OFF/ON.
+ 5. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_reconnect_toggle_wifi()
@test_tracker_info(uuid="8e6e6c21-fefb-4fe8-9fb1-f09b1182b76d")
def test_reconnect_toggle_airplane(self):
@@ -534,17 +681,23 @@
4. Reconnect to the non-current network.
"""
- connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
- connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
- wutils.toggle_airplane_mode_on_and_off(self.dut)
- reconnect_to = self.get_enabled_network(connect_2g_data,
- connect_5g_data)
- reconnect = self.connect_to_wifi_network_with_id(
- reconnect_to[WifiEnums.NETID_KEY],
- reconnect_to[WifiEnums.SSID_KEY])
- if not reconnect:
- raise signals.TestFailure("Device did not connect to the correct"
- " network after toggling Airplane mode.")
+ self.helper_reconnect_toggle_airplane()
+
+ @test_tracker_info(uuid="28562f13-8a0a-492e-932c-e587560db5f2")
+ def test_reconnect_toggle_airplane_with_location_scan_on(self):
+ """Connect to multiple networks, turn on/off Airplane moce, then
+ reconnect a previously connected network.
+
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Turn ON/OFF Airplane mode.
+ 5. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_reconnect_toggle_airplane()
@test_tracker_info(uuid="3d041c12-05e2-46a7-ab9b-e3f60cc735db")
def test_reboot_configstore_reconnect(self):
@@ -559,21 +712,24 @@
5. Reconnect to the non-current network.
"""
- network_list = self.connect_multiple_networks(self.dut)
- self.dut.reboot()
- time.sleep(DEFAULT_TIMEOUT)
- self.check_configstore_networks(network_list)
+ self.helper_reboot_configstore_reconnect()
- reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
- network_list[BAND_5GHZ])
+ @test_tracker_info(uuid="a70d5853-67b5-4d48-bdf7-08ee51fafd21")
+ def test_reboot_configstore_reconnect_with_location_scan_on(self):
+ """Connect to multiple networks, reboot then reconnect to previously
+ connected network.
- reconnect = self.connect_to_wifi_network_with_id(
- reconnect_to[WifiEnums.NETID_KEY],
- reconnect_to[WifiEnums.SSID_KEY])
- if not reconnect:
- raise signals.TestFailure(
- "Device failed to reconnect to the correct"
- " network after reboot.")
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Reboot device.
+ 5. Verify all networks are persistent after reboot.
+ 6. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_reboot_configstore_reconnect()
@test_tracker_info(uuid="26d94dfa-1349-4c8b-aea0-475eb73bb521")
def test_toggle_wifi_reboot_configstore_reconnect(self):
@@ -590,25 +746,26 @@
5. Reconnect to the non-current network.
"""
- network_list = self.connect_multiple_networks(self.dut)
- self.log.debug("Toggling wifi OFF")
- wutils.wifi_toggle_state(self.dut, False)
- time.sleep(DEFAULT_TIMEOUT)
- self.dut.reboot()
- time.sleep(DEFAULT_TIMEOUT)
- self.log.debug("Toggling wifi ON")
- wutils.wifi_toggle_state(self.dut, True)
- time.sleep(DEFAULT_TIMEOUT)
- self.check_configstore_networks(network_list)
- reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
- network_list[BAND_5GHZ])
- reconnect = self.connect_to_wifi_network_with_id(
- reconnect_to[WifiEnums.NETID_KEY],
- reconnect_to[WifiEnums.SSID_KEY])
- if not reconnect:
- msg = ("Device failed to reconnect to the correct network after"
- " toggling WiFi and rebooting.")
- raise signals.TestFailure(msg)
+ self.helper_toggle_wifi_reboot_configstore_reconnect()
+
+ @test_tracker_info(uuid="7c004a3b-c1c6-4371-9124-0f34650be915")
+ def test_toggle_wifi_reboot_configstore_reconnect_with_location_scan_on(self):
+ """Connect to multiple networks, disable WiFi, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Turn WiFi OFF.
+ 5. Reboot device.
+ 6. Turn WiFi ON.
+ 7. Verify all networks are persistent after reboot.
+ 8. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_toggle_wifi_reboot_configstore_reconnect()
@test_tracker_info(uuid="4fce017b-b443-40dc-a598-51d59d3bb38f")
def test_toggle_airplane_reboot_configstore_reconnect(self):
@@ -625,29 +782,26 @@
5. Reconnect to the non-current network.
"""
- network_list = self.connect_multiple_networks(self.dut)
- self.log.debug("Toggling Airplane mode ON")
- asserts.assert_true(
- acts.utils.force_airplane_mode(self.dut, True),
- "Can not turn on airplane mode on: %s" % self.dut.serial)
- time.sleep(DEFAULT_TIMEOUT)
- self.dut.reboot()
- time.sleep(DEFAULT_TIMEOUT)
- self.log.debug("Toggling Airplane mode OFF")
- asserts.assert_true(
- acts.utils.force_airplane_mode(self.dut, False),
- "Can not turn on airplane mode on: %s" % self.dut.serial)
- time.sleep(DEFAULT_TIMEOUT)
- self.check_configstore_networks(network_list)
- reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
- network_list[BAND_5GHZ])
- reconnect = self.connect_to_wifi_network_with_id(
- reconnect_to[WifiEnums.NETID_KEY],
- reconnect_to[WifiEnums.SSID_KEY])
- if not reconnect:
- msg = ("Device failed to reconnect to the correct network after"
- " toggling Airplane mode and rebooting.")
- raise signals.TestFailure(msg)
+ self.helper_toggle_airplane_reboot_configstore_reconnect()
+
+ @test_tracker_info(uuid="7f0810f9-2338-4158-95f5-057f5a1905b6")
+ def test_toggle_airplane_reboot_configstore_reconnect_with_location_scan_on(self):
+ """Connect to multiple networks, enable Airplane mode, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Toggle Airplane mode ON.
+ 5. Reboot device.
+ 6. Toggle Airplane mode OFF.
+ 7. Verify all networks are persistent after reboot.
+ 8. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_toggle_airplane_reboot_configstore_reconnect()
@test_tracker_info(uuid="81eb7527-4c92-4422-897a-6b5f6445e84a")
def test_config_store_with_wpapsk_2g(self):
diff --git a/acts/tests/google/wifi/WifiNetworkSelectorTest.py b/acts/tests/google/wifi/WifiNetworkSelectorTest.py
index b52946d..948f961 100644
--- a/acts/tests/google/wifi/WifiNetworkSelectorTest.py
+++ b/acts/tests/google/wifi/WifiNetworkSelectorTest.py
@@ -17,6 +17,8 @@
import logging
import time
+import acts.signals as signals
+
from acts import asserts
from acts import base_test
from acts.controllers import android_device
@@ -119,9 +121,13 @@
time.sleep(20)
#verify connection
actual_network = self.dut.droid.wifiGetConnectionInfo()
- logging.debug("Actual network: %s", actual_network)
- asserts.assert_equal(expected_bssid,
- actual_network[WifiEnums.BSSID_KEY])
+ logging.info("Actual network: %s", actual_network)
+ try:
+ asserts.assert_equal(expected_bssid,
+ actual_network[WifiEnums.BSSID_KEY])
+ except:
+ msg = "Device did not connect to any network."
+ raise signals.TestFailure(msg)
""" Tests Begin """
diff --git a/acts/tests/google/wifi/WifiRoamingTest.py b/acts/tests/google/wifi/WifiRoamingTest.py
index 5ced3ae..8c3173c 100644
--- a/acts/tests/google/wifi/WifiRoamingTest.py
+++ b/acts/tests/google/wifi/WifiRoamingTest.py
@@ -25,7 +25,6 @@
from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
-ROAMING_TIMEOUT = 30
class WifiRoamingTest(WifiBaseTest):
@@ -81,54 +80,6 @@
self.dut.cat_adb_log(test_name, begin_time)
self.dut.take_bug_report(test_name, begin_time)
- def set_attns(self, attn_val_name):
- """Sets attenuation values on attenuators used in this test.
-
- Args:
- attn_val_name: Name of the attenuation value pair to use.
- """
- self.log.info("Set attenuation values to %s",
- self.roaming_attn[attn_val_name])
- try:
- self.attenuators[0].set_atten(self.roaming_attn[attn_val_name][0])
- self.attenuators[1].set_atten(self.roaming_attn[attn_val_name][1])
- self.attenuators[2].set_atten(self.roaming_attn[attn_val_name][2])
- self.attenuators[3].set_atten(self.roaming_attn[attn_val_name][3])
- except:
- self.log.exception("Failed to set attenuation values %s.",
- attn_val_name)
- raise
-
- def trigger_roaming_and_validate(self, attn_val_name, expected_con):
- """Sets attenuators to trigger roaming and validate the DUT connected
- to the BSSID expected.
-
- Args:
- attn_val_name: Name of the attenuation value pair to use.
- expected_con: The network information of the expected network.
- """
- expected_con = {
- WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY],
- WifiEnums.BSSID_KEY: expected_con["bssid"],
- }
- self.set_attns(attn_val_name)
- self.log.info("Wait %ss for roaming to finish.", ROAMING_TIMEOUT)
- time.sleep(ROAMING_TIMEOUT)
- try:
- # Wakeup device and verify connection.
- self.dut.droid.wakeLockAcquireBright()
- self.dut.droid.wakeUpNow()
- cur_con = self.dut.droid.wifiGetConnectionInfo()
- wutils.verify_wifi_connection_info(self.dut, expected_con)
- expected_bssid = expected_con[WifiEnums.BSSID_KEY]
- self.log.info("Roamed to %s successfully", expected_bssid)
- if not wutils.validate_connection(self.dut):
- raise signals.TestFailure("Fail to connect to internet on %s" %
- expected_ssid)
- finally:
- self.dut.droid.wifiLockRelease()
- self.dut.droid.goToSleepNow()
-
def roaming_from_AP1_and_AP2(self, AP1_network, AP2_network):
"""Test roaming between two APs.
@@ -143,10 +94,11 @@
4. Expect DUT to roam to AP2.
5. Validate connection information and ping.
"""
- self.set_attns("AP1_on_AP2_off")
+ wutils.set_attns(self.attenuators, "AP1_on_AP2_off")
wutils.wifi_connect(self.dut, AP1_network)
self.log.info("Roaming from %s to %s", AP1_network, AP2_network)
- self.trigger_roaming_and_validate("AP1_off_AP2_on", AP2_network)
+ wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
+ "AP1_off_AP2_on", AP2_network)
""" Tests Begin.
diff --git a/acts/tests/google/wifi/WifiRssiTest.py b/acts/tests/google/wifi/WifiRssiTest.py
index 73449d6..4247344 100644
--- a/acts/tests/google/wifi/WifiRssiTest.py
+++ b/acts/tests/google/wifi/WifiRssiTest.py
@@ -45,9 +45,10 @@
def setup_class(self):
self.dut = self.android_devices[0]
- req_params = ["test_params", "main_network"]
+ req_params = ["rssi_test_params", "testbed_params", "main_network"]
opt_params = ["RetailAccessPoints"]
self.unpack_userparams(req_params, opt_params)
+ self.test_params = self.rssi_test_params
self.num_atten = self.attenuators[0].instrument.num_atten
self.iperf_server = self.iperf_servers[0]
self.access_points = retail_ap.create(self.RetailAccessPoints)
@@ -61,7 +62,7 @@
def teardown_test(self):
self.iperf_server.stop()
- def pass_fail_check_rssi_stability(self, rssi_result):
+ def pass_fail_check_rssi_stability(self, postprocessed_results):
"""Check the test result and decide if it passed or failed.
Checks the RSSI test result and fails the test if the standard
@@ -69,86 +70,31 @@
config file.
Args:
- rssi_result: dict containing attenuation, rssi, and other meta
- data. This dict is the output of self.post_process_results
+ postprocessed_results: compiled arrays of RSSI measurements
"""
- # Save output as text file
- test_name = self.current_test_name
- results_file_path = "{}/{}.json".format(self.log_path,
- self.current_test_name)
- with open(results_file_path, 'w') as results_file:
- json.dump(rssi_result, results_file, indent=4)
-
- x_data = []
- y_data = []
- legends = []
- std_deviations = {
- "signal_poll_rssi": [],
- "signal_poll_avg_rssi": [],
- "chain_0_rssi": [],
- "chain_1_rssi": [],
- }
- for data_point in rssi_result["rssi_result"]:
- for key, val in data_point["connected_rssi"].items():
- if type(val) == list:
- x_data.append([
- x * self.test_params["polling_frequency"]
- for x in range(len(val))
- ])
- y_data.append(val)
- legends.append(key)
- std_deviations[key].append(
- data_point["connected_rssi"]["stdev_" + key])
- data_sets = [x_data, y_data]
- x_label = 'Time (s)'
- y_label = 'RSSI (dBm)'
- fig_property = {
- "title": test_name,
- "x_label": x_label,
- "y_label": y_label,
- "linewidth": 3,
- "markersize": 0
- }
- output_file_path = "{}/{}.html".format(self.log_path, test_name)
- wputils.bokeh_plot(
- data_sets,
- legends,
- fig_property,
- shaded_region=None,
- output_file_path=output_file_path)
-
test_failed = any([
stdev > self.test_params["stdev_tolerance"]
- for stdev in std_deviations["signal_poll_rssi"]
+ for stdev in postprocessed_results["signal_poll_rssi"]["stdev"]
])
- if test_failed:
- asserts.fail(
- "RSSI stability failed. Standard deviations were {0} dB "
- "(limit {1}), per chain standard deviation [{2}, {3}] dB".
- format([
+ test_message = (
+ "RSSI stability {0}. Standard deviation was {1} dB "
+ "(limit {2}), per chain standard deviation [{3}, {4}] dB".format(
+ "failed" * test_failed + "passed" * (not test_failed), [
float("{:.2f}".format(x))
- for x in std_deviations["signal_poll_rssi"]
+ for x in postprocessed_results["signal_poll_rssi"]["stdev"]
], self.test_params["stdev_tolerance"], [
float("{:.2f}".format(x))
- for x in std_deviations["chain_0_rssi"]
+ for x in postprocessed_results["chain_0_rssi"]["stdev"]
], [
float("{:.2f}".format(x))
- for x in std_deviations["chain_1_rssi"]
+ for x in postprocessed_results["chain_1_rssi"]["stdev"]
]))
- asserts.explicit_pass(
- "RSSI stability passed. Standard deviations were {0} dB "
- "(limit {1}), per chain standard deviation [{2}, {3}] dB".format([
- float("{:.2f}".format(x))
- for x in std_deviations["signal_poll_rssi"]
- ], self.test_params["stdev_tolerance"], [
- float("{:.2f}".format(x))
- for x in std_deviations["chain_0_rssi"]
- ], [
- float("{:.2f}".format(x))
- for x in std_deviations["chain_1_rssi"]
- ]))
+ if test_failed:
+ asserts.fail(test_message)
+ asserts.explicit_pass(test_message)
- def pass_fail_check_rssi_vs_attenuation(self, postprocessed_results):
+ def pass_fail_check_rssi_accuracy(self, postprocessed_results,
+ rssi_under_test, absolute_accuracy):
"""Check the test result and decide if it passed or failed.
Checks the RSSI test result and compares and compute its deviation from
@@ -158,153 +104,246 @@
configuration file.
Args:
- result: dict containing attenuation, rssi, and other meta
- data. This dict is the output of self.post_process_results
+ postprocessed_results: compiled arrays of RSSI measurements
+ rssi_under_test: list of RSSIs under test, i.e., can cause test to
+ fail
+ absolute_accuracy: boolean indicating whether to look at absolute
+ RSSI accuracy, or centered RSSI accuracy. Centered accuracy is
+ computed after systematic RSSI shifts are removed.
"""
-
- error_data = {
- "signal_poll_rssi": [
- postprocessed_results["mean_signal_poll_rssi"][idx] -
- postprocessed_results["predicted_rssi"][idx]
- for idx in range(len(postprocessed_results["predicted_rssi"]))
- ],
- "signal_poll_avg_rssi": [
- postprocessed_results["mean_signal_poll_avg_rssi"][idx] -
- postprocessed_results["predicted_rssi"][idx]
- for idx in range(len(postprocessed_results["predicted_rssi"]))
- ],
- "scan_rssi": [
- postprocessed_results["mean_scan_rssi"][idx] -
- postprocessed_results["predicted_rssi"][idx]
- for idx in range(len(postprocessed_results["predicted_rssi"]))
- ],
- "chain_0_rssi": [
- postprocessed_results["mean_chain_0_rssi"][idx] + CONST_3dB -
- postprocessed_results["predicted_rssi"][idx]
- for idx in range(len(postprocessed_results["predicted_rssi"]))
- ],
- "chain_1_rssi": [
- postprocessed_results["mean_chain_1_rssi"][idx] + CONST_3dB -
- postprocessed_results["predicted_rssi"][idx]
- for idx in range(len(postprocessed_results["predicted_rssi"]))
- ]
- }
-
test_failed = False
test_message = ""
- for key, val in error_data.items():
+ if absolute_accuracy:
+ error_type = "absolute"
+ else:
+ error_type = "centered"
+
+ for key, val in postprocessed_results.items():
# Compute the error metrics ignoring invalid RSSI readings
# If all readings invalid, set error to RSSI_ERROR_VAL
- filtered_errors = [x for x in val if not math.isnan(x)]
- if filtered_errors:
- avg_error = sum([abs(x) for x in filtered_errors
- ]) / len(filtered_errors)
- avg_shift = sum(filtered_errors) / len(filtered_errors)
- else:
- avg_error = RSSI_ERROR_VAL
- rssi_failure = (avg_error > self.test_params["abs_tolerance"]
- ) or math.isnan(avg_error)
- if rssi_failure and key in self.test_params["rssi_under_test"]:
- test_message = test_message + (
- "{} failed. Average error is {:.2f} dB. "
- "Average shift is {:.2f} dB.\n").format(
- key, avg_error, avg_shift)
- test_failed = True
- elif rssi_failure:
- test_message = test_message + (
- "{} failed (ignored). Average error is {:.2f} dB. "
- "Average shift is {:.2f} dB.\n").format(
- key, avg_error, avg_shift)
- else:
- test_message = test_message + (
- "{} passed. Average error is {:.2f} dB. "
- "Average shift is {:.2f} dB.\n").format(
- key, avg_error, avg_shift)
+ if "rssi" in key and "predicted" not in key:
+ filtered_error = [x for x in val["error"] if not math.isnan(x)]
+ if filtered_error:
+ avg_shift = statistics.mean(filtered_error)
+ if absolute_accuracy:
+ avg_error = statistics.mean(
+ [abs(x) for x in filtered_error])
+ else:
+ avg_error = statistics.mean(
+ [abs(x - avg_shift) for x in filtered_error])
+ else:
+ avg_error = RSSI_ERROR_VAL
+ avg_shift = RSSI_ERROR_VAL
+ rssi_failure = (avg_error > self.test_params["abs_tolerance"]
+ ) or math.isnan(avg_error)
+ if rssi_failure and key in rssi_under_test:
+ test_message = test_message + (
+ "{} failed. Average {} error is {:.2f} dB. "
+ "Average shift is {:.2f} dB.\n").format(
+ key, error_type, avg_error, avg_shift)
+ test_failed = True
+ elif rssi_failure:
+ test_message = test_message + (
+ "{} failed (ignored). Average {} error is {:.2f} dB. "
+ "Average shift is {:.2f} dB.\n").format(
+ key, error_type, avg_error, avg_shift)
+ else:
+ test_message = test_message + (
+ "{} passed. Average {} error is {:.2f} dB. "
+ "Average shift is {:.2f} dB.\n").format(
+ key, error_type, avg_error, avg_shift)
if test_failed:
asserts.fail(test_message)
asserts.explicit_pass(test_message)
- def post_process_rssi_vs_attenuation(self, rssi_result):
- """Saves plots and JSON formatted results.
+ def post_process_rssi_sweep(self, rssi_result):
+ """Postprocesses and saves JSON formatted results.
Args:
rssi_result: dict containing attenuation, rssi and other meta
data
Returns:
- postprocessed_results: compiled arrays of RSSI measurements used in
+ postprocessed_results: compiled arrays of RSSI data used in
pass/fail check
"""
# Save output as text file
- test_name = self.current_test_name
results_file_path = "{}/{}.json".format(self.log_path,
self.current_test_name)
with open(results_file_path, 'w') as results_file:
json.dump(rssi_result, results_file, indent=4)
- # Plot and save
- total_attenuation = [
- att + rssi_result["fixed_attenuation"]
- for att in rssi_result["attenuation"]
- ]
# Compile results into arrays of RSSIs suitable for plotting
postprocessed_results = {
- "total_attenuation":
- total_attenuation,
- "mean_signal_poll_rssi": [
- x["connected_rssi"]["mean_signal_poll_rssi"]
- for x in rssi_result["rssi_result"]
- ],
- "mean_signal_poll_avg_rssi": [
- x["connected_rssi"]["mean_signal_poll_avg_rssi"]
- for x in rssi_result["rssi_result"]
- ],
- "mean_scan_rssi": [
- x["scan_rssi"][rssi_result["connected_bssid"]]["avg_rssi"]
- for x in rssi_result["rssi_result"]
- ],
- "mean_chain_0_rssi": [
- x["connected_rssi"]["mean_chain_0_rssi"]
- for x in rssi_result["rssi_result"]
- ],
- "mean_chain_1_rssi": [
- x["connected_rssi"]["mean_chain_1_rssi"]
- for x in rssi_result["rssi_result"]
- ],
- "predicted_rssi":
- [rssi_result["ap_tx_power"] - att for att in total_attenuation]
+ "signal_poll_rssi": {},
+ "signal_poll_avg_rssi": {},
+ "scan_rssi": {},
+ "chain_0_rssi": {},
+ "chain_1_rssi": {},
+ "total_attenuation": [],
+ "predicted_rssi": []
}
+ for key, val in postprocessed_results.items():
+ if "scan_rssi" in key:
+ postprocessed_results[key]["data"] = [
+ x for data_point in rssi_result["rssi_result"] for x in
+ data_point[key][rssi_result["connected_bssid"]]["data"]
+ ]
+ postprocessed_results[key]["mean"] = [
+ x[key][rssi_result["connected_bssid"]]["mean"]
+ for x in rssi_result["rssi_result"]
+ ]
+ postprocessed_results[key]["stdev"] = [
+ x[key][rssi_result["connected_bssid"]]["stdev"]
+ for x in rssi_result["rssi_result"]
+ ]
+ elif "predicted_rssi" in key:
+ postprocessed_results["total_attenuation"] = [
+ att + rssi_result["fixed_attenuation"] +
+ rssi_result["dut_front_end_loss"]
+ for att in rssi_result["attenuation"]
+ ]
+ postprocessed_results["predicted_rssi"] = [
+ rssi_result["ap_tx_power"] - att
+ for att in postprocessed_results["total_attenuation"]
+ ]
+ elif "rssi" in key:
+ postprocessed_results[key]["data"] = [
+ x for data_point in rssi_result["rssi_result"]
+ for x in data_point[key]["data"]
+ ]
+ postprocessed_results[key]["mean"] = [
+ x[key]["mean"] for x in rssi_result["rssi_result"]
+ ]
+ postprocessed_results[key]["stdev"] = [
+ x[key]["stdev"] for x in rssi_result["rssi_result"]
+ ]
+ # Compute RSSI errors
+ for key, val in postprocessed_results.items():
+ if "chain" in key:
+ postprocessed_results[key]["error"] = [
+ postprocessed_results[key]["mean"][idx] + CONST_3dB -
+ postprocessed_results["predicted_rssi"][idx]
+ for idx in range(
+ len(postprocessed_results["predicted_rssi"]))
+ ]
+ elif "rssi" in key and "predicted" not in key:
+ postprocessed_results[key]["error"] = [
+ postprocessed_results[key]["mean"][idx] -
+ postprocessed_results["predicted_rssi"][idx]
+ for idx in range(
+ len(postprocessed_results["predicted_rssi"]))
+ ]
+ return postprocessed_results
+
+ def plot_rssi_vs_attenuation(self, postprocessed_results):
+ """Function to plot RSSI vs attenuation sweeps
+
+ Args:
+ postprocessed_results: compiled arrays of RSSI data.
+ """
data_sets = [[
- total_attenuation, total_attenuation, total_attenuation,
- total_attenuation, total_attenuation, total_attenuation
+ postprocessed_results["total_attenuation"],
+ postprocessed_results["total_attenuation"],
+ postprocessed_results["total_attenuation"],
+ postprocessed_results["total_attenuation"],
+ postprocessed_results["total_attenuation"],
+ postprocessed_results["total_attenuation"]
], [
- postprocessed_results["mean_signal_poll_rssi"],
- postprocessed_results["mean_signal_poll_avg_rssi"],
- postprocessed_results["mean_scan_rssi"],
- postprocessed_results["mean_chain_0_rssi"],
- postprocessed_results["mean_chain_1_rssi"],
+ postprocessed_results["signal_poll_rssi"]["mean"],
+ postprocessed_results["signal_poll_avg_rssi"]["mean"],
+ postprocessed_results["scan_rssi"]["mean"],
+ postprocessed_results["chain_0_rssi"]["mean"],
+ postprocessed_results["chain_1_rssi"]["mean"],
postprocessed_results["predicted_rssi"]
]]
legends = [
"Signal Poll RSSI", "Signal Poll AVG_RSSI", "Scan RSSI",
"Chain 0 RSSI", "Chain 1 RSSI", "Predicted RSSI"
]
- x_label = 'Attenuation (dB)'
- y_label = 'RSSI (dBm)'
fig_property = {
- "title": test_name,
- "x_label": x_label,
- "y_label": y_label,
+ "title": self.current_test_name,
+ "x_label": 'Attenuation (dB)',
+ "y_label": 'RSSI (dBm)',
"linewidth": 3,
"markersize": 10
}
- output_file_path = "{}/{}.html".format(self.log_path, test_name)
+ output_file_path = "{}/{}.html".format(self.log_path,
+ self.current_test_name)
wputils.bokeh_plot(
data_sets,
legends,
fig_property,
shaded_region=None,
output_file_path=output_file_path)
- return postprocessed_results
+
+ def plot_rssi_vs_time(self, rssi_result, postprocessed_results,
+ center_curves):
+ """Function to plot RSSI vs time.
+
+ Args:
+ rssi_result: dict containing raw RSSI data
+ postprocessed_results: compiled arrays of RSSI data
+ center_curvers: boolean indicating whether to shift curves to align
+ them with predicted RSSIs
+ """
+ x_data = []
+ y_data = []
+ legends = []
+ rssi_time_series = {
+ "signal_poll_rssi": [],
+ "signal_poll_avg_rssi": [],
+ "scan_rssi": [],
+ "chain_0_rssi": [],
+ "chain_1_rssi": [],
+ "predicted_rssi": []
+ }
+ for key, val in rssi_time_series.items():
+ if "predicted_rssi" in key:
+ rssi_time_series[key] = [
+ x for x in postprocessed_results[key] for copies in range(
+ len(rssi_result["rssi_result"][0]["signal_poll_rssi"][
+ "data"]))
+ ]
+ elif "rssi" in key:
+ if center_curves:
+ filtered_error = [
+ x for x in postprocessed_results[key]["error"]
+ if not math.isnan(x)
+ ]
+ if filtered_error:
+ avg_shift = statistics.mean(filtered_error)
+ else:
+ avg_shift = 0
+ rssi_time_series[key] = [
+ x - avg_shift
+ for x in postprocessed_results[key]["data"]
+ ]
+ else:
+ rssi_time_series[key] = postprocessed_results[key]["data"]
+ time = [
+ self.test_params["polling_frequency"] * x
+ for x in range(len(rssi_time_series[key]))
+ ]
+ if len(rssi_time_series[key]) > 0:
+ x_data.append(time)
+ y_data.append(rssi_time_series[key])
+ legends.append(key)
+ data_sets = [x_data, y_data]
+ fig_property = {
+ "title": self.current_test_name,
+ "x_label": 'Time (s)',
+ "y_label": center_curves * 'Centered' + 'RSSI (dBm)',
+ "linewidth": 3,
+ "markersize": 0
+ }
+ output_file_path = "{}/{}.html".format(self.log_path,
+ self.current_test_name)
+ wputils.bokeh_plot(
+ data_sets,
+ legends,
+ fig_property,
+ shaded_region=None,
+ output_file_path=output_file_path)
def get_scan_rssi(self, tracked_bssids, num_measurements=1):
"""Gets scan RSSI for specified BSSIDs.
@@ -318,7 +357,7 @@
"""
scan_rssi = {}
for bssid in tracked_bssids:
- scan_rssi[bssid] = {"rssi": [], "avg_rssi": None}
+ scan_rssi[bssid] = {"data": [], "mean": None, "stdev": None}
for idx in range(num_measurements):
scan_output = self.dut.adb.shell(SCAN)
time.sleep(MED_SLEEP)
@@ -328,25 +367,24 @@
bssid + ".*", scan_output, flags=re.IGNORECASE)
if bssid_result:
bssid_result = bssid_result.group(0).split("\t")
- scan_rssi[bssid]["rssi"].append(int(bssid_result[2]))
+ scan_rssi[bssid]["data"].append(int(bssid_result[2]))
else:
- scan_rssi[bssid]["rssi"].append(RSSI_ERROR_VAL)
+ scan_rssi[bssid]["data"].append(RSSI_ERROR_VAL)
# Compute mean RSSIs. Only average valid readings.
# Output RSSI_ERROR_VAL if no readings found.
for key, val in scan_rssi.items():
filtered_rssi_values = [
- x for x in val["rssi"] if not math.isnan(x)
+ x for x in val["data"] if not math.isnan(x)
]
if filtered_rssi_values:
- scan_rssi[key]["avg_rssi"] = sum(filtered_rssi_values) / len(
- filtered_rssi_values)
+ scan_rssi[key]["mean"] = statistics.mean(filtered_rssi_values)
if len(filtered_rssi_values) > 1:
scan_rssi[key]["stdev"] = statistics.stdev(
filtered_rssi_values)
else:
scan_rssi[key]["stdev"] = 0
else:
- scan_rssi[key]["avg_rssi"] = RSSI_ERROR_VAL
+ scan_rssi[key]["mean"] = RSSI_ERROR_VAL
scan_rssi[key]["stdev"] = RSSI_ERROR_VAL
return scan_rssi
@@ -364,29 +402,50 @@
statistics
"""
connected_rssi = {
- "signal_poll_rssi": [],
- "signal_poll_avg_rssi": [],
- "chain_0_rssi": [],
- "chain_1_rssi": []
+ "signal_poll_rssi": {
+ "data": [],
+ "mean": None,
+ "stdev": None
+ },
+ "signal_poll_avg_rssi": {
+ "data": [],
+ "mean": None,
+ "stdev": None
+ },
+ "chain_0_rssi": {
+ "data": [],
+ "mean": None,
+ "stdev": None
+ },
+ "chain_1_rssi": {
+ "data": [],
+ "mean": None,
+ "stdev": None
+ }
}
for idx in range(num_measurements):
+ measurement_start_time = time.time()
# Get signal poll RSSI
signal_poll_output = self.dut.adb.shell(SIGNAL_POLL)
match = re.search("RSSI=.*", signal_poll_output)
if match:
temp_rssi = int(match.group(0).split("=")[1])
if temp_rssi == -9999:
- connected_rssi["signal_poll_rssi"].append(RSSI_ERROR_VAL)
+ connected_rssi["signal_poll_rssi"]["data"].append(
+ RSSI_ERROR_VAL)
else:
- connected_rssi["signal_poll_rssi"].append(temp_rssi)
+ connected_rssi["signal_poll_rssi"]["data"].append(
+ temp_rssi)
else:
- connected_rssi["signal_poll_rssi"].append(RSSI_ERROR_VAL)
+ connected_rssi["signal_poll_rssi"]["data"].append(
+ RSSI_ERROR_VAL)
match = re.search("AVG_RSSI=.*", signal_poll_output)
if match:
- connected_rssi["signal_poll_avg_rssi"].append(
+ connected_rssi["signal_poll_avg_rssi"]["data"].append(
int(match.group(0).split("=")[1]))
else:
- connected_rssi["signal_poll_avg_rssi"].append(RSSI_ERROR_VAL)
+ connected_rssi["signal_poll_avg_rssi"]["data"].append(
+ RSSI_ERROR_VAL)
# Get per chain RSSI
per_chain_rssi = self.dut.adb.shell(STATION_DUMP)
match = re.search(".*signal avg:.*", per_chain_rssi)
@@ -394,31 +453,38 @@
per_chain_rssi = per_chain_rssi[per_chain_rssi.find("[") + 1:
per_chain_rssi.find("]")]
per_chain_rssi = per_chain_rssi.split(", ")
- connected_rssi["chain_0_rssi"].append(int(per_chain_rssi[0]))
- connected_rssi["chain_1_rssi"].append(int(per_chain_rssi[1]))
+ connected_rssi["chain_0_rssi"]["data"].append(
+ int(per_chain_rssi[0]))
+ connected_rssi["chain_1_rssi"]["data"].append(
+ int(per_chain_rssi[1]))
else:
- connected_rssi["chain_0_rssi"].append(RSSI_ERROR_VAL)
- connected_rssi["chain_1_rssi"].append(RSSI_ERROR_VAL)
- time.sleep(polling_frequency)
+ connected_rssi["chain_0_rssi"]["data"].append(RSSI_ERROR_VAL)
+ connected_rssi["chain_1_rssi"]["data"].append(RSSI_ERROR_VAL)
+ measurement_elapsed_time = time.time() - measurement_start_time
+ time.sleep(max(0, polling_frequency - measurement_elapsed_time))
+
# Compute mean RSSIs. Only average valid readings.
# Output RSSI_ERROR_VAL if no valid connected readings found.
for key, val in connected_rssi.copy().items():
- filtered_rssi_values = [x for x in val if not math.isnan(x)]
+ filtered_rssi_values = [
+ x for x in val["data"] if not math.isnan(x)
+ ]
if filtered_rssi_values:
- connected_rssi["mean_{}".format(key)] = sum(
- filtered_rssi_values) / len(filtered_rssi_values)
+ connected_rssi[key]["mean"] = statistics.mean(
+ filtered_rssi_values)
if len(filtered_rssi_values) > 1:
- connected_rssi["stdev_{}".format(key)] = statistics.stdev(
+ connected_rssi[key]["stdev"] = statistics.stdev(
filtered_rssi_values)
else:
- connected_rssi["stdev_{}".format(key)] = 0
+ connected_rssi[key]["stdev"] = 0
else:
- connected_rssi["mean_{}".format(key)] = RSSI_ERROR_VAL
- connected_rssi["stdev_{}".format(key)] = RSSI_ERROR_VAL
+ connected_rssi[key]["mean"] = RSSI_ERROR_VAL
+ connected_rssi[key]["stdev"] = RSSI_ERROR_VAL
return connected_rssi
def rssi_test(self, iperf_traffic, connected_measurements,
- scan_measurements, bssids, polling_frequency):
+ scan_measurements, bssids, polling_frequency,
+ first_measurement_delay):
"""Test function to run RSSI tests.
The function runs an RSSI test in the current device/AP configuration.
@@ -443,7 +509,7 @@
if self.iperf_traffic:
self.iperf_server.start(tag=0)
self.dut.run_iperf_client_nb(
- self.test_params["iperf_server_address"],
+ self.testbed_params["iperf_server_address"],
self.iperf_args,
timeout=3600)
for atten in self.rssi_atten_range:
@@ -453,16 +519,15 @@
self.attenuators[i].set_atten(atten)
for i in range(self.num_atten)
]
- time.sleep(MED_SLEEP)
+ time.sleep(first_measurement_delay)
current_rssi = {}
- current_rssi["connected_rssi"] = self.get_connected_rssi(
- connected_measurements, polling_frequency)
+ current_rssi = self.get_connected_rssi(connected_measurements,
+ polling_frequency)
current_rssi["scan_rssi"] = self.get_scan_rssi(
bssids, scan_measurements)
rssi_result.append(current_rssi)
self.log.info("Connected RSSI at {0:.2f} dB is {1:.2f} dB".format(
- atten, current_rssi["connected_rssi"][
- "mean_signal_poll_rssi"]))
+ atten, current_rssi["signal_poll_rssi"]["mean"]))
# Stop iperf traffic if needed
if self.iperf_traffic:
self.iperf_server.stop()
@@ -471,7 +536,8 @@
return rssi_result
def rssi_test_func(self, iperf_traffic, connected_measurements,
- scan_measurements, bssids, polling_frequency):
+ scan_measurements, bssids, polling_frequency,
+ first_measurement_delay):
"""Main function to test RSSI.
The function sets up the AP in the correct channel and mode
@@ -485,6 +551,14 @@
rssi_result = {}
# Configure AP
band = self.access_point.band_lookup_by_channel(self.channel)
+ if "2G" in band:
+ frequency = wutils.WifiEnums.channel_2G_to_freq[self.channel]
+ else:
+ frequency = wutils.WifiEnums.channel_5G_to_freq[self.channel]
+ if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
+ self.access_point.set_region(self.testbed_params["DFS_region"])
+ else:
+ self.access_point.set_region(self.testbed_params["default_region"])
self.access_point.set_channel(band, self.channel)
self.access_point.set_bandwidth(band, self.mode)
self.log.info("Access Point Configuration: {}".format(
@@ -499,27 +573,34 @@
wutils.reset_wifi(self.dut)
self.main_network[band]["channel"] = self.channel
wutils.wifi_connect(self.dut, self.main_network[band], num_of_tries=5)
- time.sleep(5)
+ time.sleep(MED_SLEEP)
# Run RvR and log result
rssi_result["test_name"] = self.current_test_name
rssi_result["ap_settings"] = self.access_point.ap_settings.copy()
rssi_result["attenuation"] = list(self.rssi_atten_range)
rssi_result["connected_bssid"] = self.main_network[band]["BSSID"]
- rssi_result["ap_tx_power"] = self.test_params["ap_tx_power"][str(
- self.channel)]
- rssi_result["fixed_attenuation"] = self.test_params[
+ if "{}_{}".format(str(self.channel),
+ self.mode) in self.testbed_params["ap_tx_power"]:
+ rssi_result["ap_tx_power"] = self.testbed_params["ap_tx_power"][
+ "{}_{}".format(str(self.channel), self.mode)]
+ else:
+ rssi_result["ap_tx_power"] = self.testbed_params["ap_tx_power"][
+ str(self.channel)]
+ rssi_result["fixed_attenuation"] = self.testbed_params[
"fixed_attenuation"][str(self.channel)]
+ rssi_result["dut_front_end_loss"] = self.testbed_params[
+ "dut_front_end_loss"][str(self.channel)]
rssi_result["rssi_result"] = self.rssi_test(
iperf_traffic, connected_measurements, scan_measurements, bssids,
- polling_frequency)
+ polling_frequency, first_measurement_delay)
self.testclass_results.append(rssi_result)
return rssi_result
def _test_rssi_vs_atten(self):
""" Function that gets called for each test case of rssi_vs_atten
- The function gets called in each rvr test case. The function customizes
- the test based on the test name of the test that called it
+ The function gets called in each rssi test case. The function
+ customizes the test based on the test name of the test that called it
"""
test_params = self.current_test_name.split("_")
self.channel = int(test_params[4][2:])
@@ -527,22 +608,25 @@
self.iperf_traffic = "ActiveTraffic" in test_params[6]
self.iperf_args = '-i 1 -t 3600 -J -R'
band = self.access_point.band_lookup_by_channel(self.channel)
- num_atten_steps = int((self.test_params["rssi_atten_stop"] -
- self.test_params["rssi_atten_start"]) /
- self.test_params["rssi_atten_step"])
+ num_atten_steps = int((self.test_params["rssi_vs_atten_stop"] -
+ self.test_params["rssi_vs_atten_start"]) /
+ self.test_params["rssi_vs_atten_step"])
self.rssi_atten_range = [
- self.test_params["rssi_atten_start"] +
- x * self.test_params["rssi_atten_step"]
+ self.test_params["rssi_vs_atten_start"] +
+ x * self.test_params["rssi_vs_atten_step"]
for x in range(0, num_atten_steps)
]
rssi_result = self.rssi_test_func(
- self.iperf_traffic, self.test_params["connected_measurements"],
- self.test_params["scan_measurements"], [
- self.main_network[band]["BSSID"]
- ], self.test_params["polling_frequency"])
- postprocessed_results = self.post_process_rssi_vs_attenuation(
- rssi_result)
- self.pass_fail_check_rssi_vs_attenuation(postprocessed_results)
+ self.iperf_traffic,
+ self.test_params["rssi_vs_atten_connected_measurements"],
+ self.test_params["rssi_vs_atten_scan_measurements"],
+ [self.main_network[band]["BSSID"]],
+ self.test_params["polling_frequency"], MED_SLEEP)
+ postprocessed_results = self.post_process_rssi_sweep(rssi_result)
+ self.plot_rssi_vs_attenuation(postprocessed_results)
+ self.pass_fail_check_rssi_accuracy(
+ postprocessed_results, self.test_params["rssi_vs_atten_metrics"],
+ 1)
def _test_rssi_stability(self):
""" Function that gets called for each test case of rssi_stability
@@ -556,15 +640,60 @@
self.iperf_traffic = "ActiveTraffic" in test_params[5]
self.iperf_args = '-i 1 -t 3600 -J -R'
band = self.access_point.band_lookup_by_channel(self.channel)
- self.rssi_atten_range = self.test_params["stability_test_atten"]
+ self.rssi_atten_range = self.test_params["rssi_stability_atten"]
connected_measurements = int(
- self.test_params["stability_test_duration"] /
+ self.test_params["rssi_stability_duration"] /
self.test_params["polling_frequency"])
rssi_result = self.rssi_test_func(
- self.iperf_traffic, connected_measurements, 0, [
- self.main_network[band]["BSSID"]
- ], self.test_params["polling_frequency"])
- self.pass_fail_check_rssi_stability(rssi_result)
+ self.iperf_traffic, connected_measurements, 0,
+ [self.main_network[band]["BSSID"]],
+ self.test_params["polling_frequency"], MED_SLEEP)
+ postprocessed_results = self.post_process_rssi_sweep(rssi_result)
+ self.plot_rssi_vs_time(rssi_result, postprocessed_results, 1)
+ self.pass_fail_check_rssi_stability(postprocessed_results)
+
+ def _test_rssi_tracking(self):
+ """ Function that gets called for each test case of rssi_tracking
+
+ The function gets called in each rssi test case. The function
+ customizes the test based on the test name of the test that called it
+ """
+ test_params = self.current_test_name.split("_")
+ self.channel = int(test_params[3][2:])
+ self.mode = test_params[4]
+ self.iperf_traffic = "ActiveTraffic" in test_params[5]
+ self.iperf_args = '-i 1 -t 3600 -J -R'
+ band = self.access_point.band_lookup_by_channel(self.channel)
+ self.rssi_atten_range = []
+ for waveform in self.test_params["rssi_tracking_waveforms"]:
+ waveform_vector = []
+ for section in range(len(waveform["atten_levels"]) - 1):
+ section_limits = waveform["atten_levels"][section:section + 2]
+ if section_limits[0] < section_limits[1]:
+ waveform_vector = waveform_vector + sorted(
+ list(
+ range(section_limits[0], section_limits[1],
+ waveform["step_size"])) *
+ waveform["step_duration"])
+ else:
+ waveform_vector = waveform_vector + list(
+ reversed(
+ sorted(
+ list(
+ range(section_limits[1], section_limits[0],
+ waveform["step_size"])) *
+ waveform["step_duration"])))
+ waveform_vector = waveform_vector * waveform["repetitions"]
+ self.rssi_atten_range = self.rssi_atten_range + waveform_vector
+ connected_measurements = int(1 / self.test_params["polling_frequency"])
+ rssi_result = self.rssi_test_func(
+ self.iperf_traffic, connected_measurements, 0,
+ [self.main_network[band]["BSSID"]],
+ self.test_params["polling_frequency"], 0)
+ postprocessed_results = self.post_process_rssi_sweep(rssi_result)
+ self.plot_rssi_vs_time(rssi_result, postprocessed_results, 1)
+ self.pass_fail_check_rssi_accuracy(postprocessed_results,
+ ["signal_poll_rssi"], 0)
@test_tracker_info(uuid='519689b8-0a3c-4fd9-9227-fd7962d0f1a0')
def test_rssi_stability_ch1_VHT20_ActiveTraffic(self):
@@ -664,7 +793,7 @@
@test_tracker_info(uuid='4b74dd46-4190-4556-8ad8-c55808e9e847')
def test_rssi_stability_ch161_VHT20_ActiveTraffic(self):
- self._test_rssi_vs_atten()
+ self._test_rssi_stability()
@test_tracker_info(uuid='ae54b7cc-d76d-4460-8dcc-2c439265c7c9')
def test_rssi_vs_atten_ch1_VHT20_ActiveTraffic(self):
@@ -766,6 +895,14 @@
def test_rssi_vs_atten_ch161_VHT20_ActiveTraffic(self):
self._test_rssi_vs_atten()
+ @test_tracker_info(uuid='')
+ def test_rssi_tracking_ch161_VHT20_ActiveTraffic(self):
+ self._test_rssi_tracking()
+
+ @test_tracker_info(uuid='')
+ def test_rssi_tracking_ch161_VHT20_NoTraffic(self):
+ self._test_rssi_tracking()
+
class WifiRssi_2GHz_ActiveTraffic_Test(WifiRssiTest):
def __init__(self, controllers):
diff --git a/acts/tests/google/wifi/WifiRvrTest.py b/acts/tests/google/wifi/WifiRvrTest.py
index ba3fb7c..9c45525 100644
--- a/acts/tests/google/wifi/WifiRvrTest.py
+++ b/acts/tests/google/wifi/WifiRvrTest.py
@@ -28,35 +28,43 @@
from acts.test_utils.wifi import wifi_retail_ap as retail_ap
from acts.test_utils.wifi import wifi_test_utils as wutils
-TEST_TIMEOUT = 10
-EPSILON = 1e-6
-
class WifiRvrTest(base_test.BaseTestClass):
+ TEST_TIMEOUT = 10
+ SHORT_SLEEP = 1
+ MED_SLEEP = 5
+
def __init__(self, controllers):
base_test.BaseTestClass.__init__(self, controllers)
def setup_class(self):
- self.dut = self.android_devices[0]
- req_params = ["test_params", "main_network"]
- opt_params = ["RetailAccessPoints", "golden_files_list"]
+ self.client_dut = self.android_devices[-1]
+ req_params = ["rvr_test_params", "testbed_params"]
+ opt_params = [
+ "main_network", "RetailAccessPoints", "golden_files_list"
+ ]
self.unpack_userparams(req_params, opt_params)
+ self.test_params = self.rvr_test_params
self.num_atten = self.attenuators[0].instrument.num_atten
self.iperf_server = self.iperf_servers[0]
- self.access_points = retail_ap.create(self.RetailAccessPoints)
- self.access_point = self.access_points[0]
+ if hasattr(self, "RetailAccessPoints"):
+ self.access_points = retail_ap.create(self.RetailAccessPoints)
+ self.access_point = self.access_points[0]
+ self.log.info("Access Point Configuration: {}".format(
+ self.access_point.ap_settings))
self.log_path = os.path.join(logging.log_path, "rvr_results")
utils.create_dir(self.log_path)
- self.log.info("Access Point Configuration: {}".format(
- self.access_point.ap_settings))
if not hasattr(self, "golden_files_list"):
self.golden_files_list = [
- os.path.join(self.test_params["golden_results_path"], file)
- for file in os.listdir(self.test_params["golden_results_path"])
+ os.path.join(self.testbed_params["golden_results_path"],
+ file) for file in os.listdir(
+ self.testbed_params["golden_results_path"])
]
self.testclass_results = []
+
# Turn WiFi ON
- wutils.wifi_toggle_state(self.dut, True)
+ for dev in self.android_devices:
+ wutils.wifi_toggle_state(dev, True)
def teardown_test(self):
self.iperf_server.stop()
@@ -65,7 +73,8 @@
"""Saves plot with all test results to enable comparison.
"""
# Turn WiFi OFF
- wutils.wifi_toggle_state(self.dut, False)
+ for dev in self.android_devices:
+ wutils.wifi_toggle_state(dev, False)
# Plot and save all results
x_data = []
y_data = []
@@ -112,9 +121,12 @@
file_name for file_name in self.golden_files_list
if test_name in file_name
]
- golden_path = golden_path[0]
- throughput_limits = self.compute_throughput_limits(
- golden_path, rvr_result)
+ try:
+ golden_path = golden_path[0]
+ throughput_limits = self.compute_throughput_limits(
+ golden_path, rvr_result)
+ except:
+ asserts.fail("Test failed: Golden file not found")
failure_count = 0
for idx, current_throughput in enumerate(
@@ -131,9 +143,8 @@
throughput_limits["lower_limit"][idx],
throughput_limits["upper_limit"][idx]))
if failure_count >= self.test_params["failure_count_tolerance"]:
- asserts.fail(
- "Test failed. Found {} points outside throughput limits.".
- format(failure_count))
+ asserts.fail("Test failed. Found {} points outside limits.".format(
+ failure_count))
asserts.explicit_pass(
"Test passed. Found {} points outside throughput limits.".format(
failure_count))
@@ -273,10 +284,11 @@
self.iperf_server.start(tag=str(atten))
try:
client_output = ""
- client_status, client_output = self.dut.run_iperf_client(
- self.test_params["iperf_server_address"],
+ client_status, client_output = self.client_dut.run_iperf_client(
+ self.testbed_params["iperf_server_address"],
self.iperf_args,
- timeout=self.test_params["iperf_duration"] + TEST_TIMEOUT)
+ timeout=self.test_params["iperf_duration"] +
+ self.TEST_TIMEOUT)
except:
self.log.warning("TimeoutError: Iperf measurement timed out.")
client_output_path = os.path.join(
@@ -295,7 +307,7 @@
curr_throughput = (math.fsum(iperf_result.instantaneous_rates[
self.test_params["iperf_ignored_interval"]:-1]) / len(
iperf_result.instantaneous_rates[self.test_params[
- "iperf_ignored_interval"]:-1])) * 8
+ "iperf_ignored_interval"]:-1])) * 8 * (1.024**2)
except:
self.log.warning(
"ValueError: Cannot get iperf result. Setting to 0")
@@ -331,6 +343,14 @@
rvr_result = {}
# Configure AP
band = self.access_point.band_lookup_by_channel(channel)
+ if "2G" in band:
+ frequency = wutils.WifiEnums.channel_2G_to_freq[channel]
+ else:
+ frequency = wutils.WifiEnums.channel_5G_to_freq[channel]
+ if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
+ self.access_point.set_region(self.testbed_params["DFS_region"])
+ else:
+ self.access_point.set_region(self.testbed_params["default_region"])
self.access_point.set_channel(band, channel)
self.access_point.set_bandwidth(band, mode)
self.log.info("Access Point Configuration: {}".format(
@@ -338,15 +358,16 @@
# Set attenuator to 0 dB
[self.attenuators[i].set_atten(0) for i in range(self.num_atten)]
# Connect DUT to Network
- wutils.reset_wifi(self.dut)
+ wutils.reset_wifi(self.client_dut)
self.main_network[band]["channel"] = channel
- wutils.wifi_connect(self.dut, self.main_network[band], num_of_tries=5)
- time.sleep(5)
+ wutils.wifi_connect(
+ self.client_dut, self.main_network[band], num_of_tries=5)
+ time.sleep(self.MED_SLEEP)
# Run RvR and log result
rvr_result["test_name"] = self.current_test_name
rvr_result["ap_settings"] = self.access_point.ap_settings.copy()
rvr_result["attenuation"] = list(self.rvr_atten_range)
- rvr_result["fixed_attenuation"] = self.test_params[
+ rvr_result["fixed_attenuation"] = self.testbed_params[
"fixed_attenuation"][str(channel)]
rvr_result["throughput_receive"] = self.rvr_test()
self.testclass_results.append(rvr_result)
@@ -456,6 +477,118 @@
def test_rvr_TCP_UL_ch48_VHT20(self):
self._test_rvr()
+ @test_tracker_info(uuid='c2e199ce-d23f-4a24-b146-74e762085620')
+ def test_rvr_TCP_DL_ch52_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='5c5943e8-9d91-4270-a5ab-e7018807c64e')
+ def test_rvr_TCP_UL_ch52_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='b52afe89-182f-4bad-8879-cbf7001d28ef')
+ def test_rvr_TCP_DL_ch56_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='f8526241-3b96-463a-9082-a749a8650d5f')
+ def test_rvr_TCP_UL_ch56_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='c3042d7e-7468-4ab8-aec3-9b3088ba3e4c')
+ def test_rvr_TCP_DL_ch60_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='80426542-b035-4fb3-9010-e997f95d4964')
+ def test_rvr_TCP_UL_ch60_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='aa0e7117-390c-4265-adf2-0990f65f8b0b')
+ def test_rvr_TCP_DL_ch64_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='b2fdda85-256b-4368-8e8b-39274062264e')
+ def test_rvr_TCP_UL_ch64_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='48b6590f-1553-4170-83a5-40d3976e9e77')
+ def test_rvr_TCP_DL_ch100_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='2d0525fe-57ce-49d3-826d-4ebedd2ca6d6')
+ def test_rvr_TCP_UL_ch100_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='52da922d-6c2f-4afa-aca3-c19438ae3217')
+ def test_rvr_TCP_DL_ch100_VHT40(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='2c7e7106-88c8-47ba-ac28-362475abec41')
+ def test_rvr_TCP_UL_ch100_VHT40(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='fd4a7118-e9fe-4931-b32c-f69efd3e6493')
+ def test_rvr_TCP_DL_ch100_VHT80(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='146502b2-9cab-4bbe-8a5c-7ec625edc2ef')
+ def test_rvr_TCP_UL_ch100_VHT80(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='a5e185d6-b523-4016-bc8a-2a32cdc67ae0')
+ def test_rvr_TCP_DL_ch104_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='886aed91-0fdc-432d-b47e-ebfa85ac27ad')
+ def test_rvr_TCP_UL_ch104_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='fda3de6e-3183-401b-b98c-1b076da139e1')
+ def test_rvr_TCP_DL_ch108_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='29cc30f5-bbc8-4b64-9789-a56154907af5')
+ def test_rvr_TCP_UL_ch108_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='5c52ccac-8c38-46fa-a7b3-d714b6a814ad')
+ def test_rvr_TCP_DL_ch112_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='cc1c2a0b-71a3-4343-b7ff-489527c839d2')
+ def test_rvr_TCP_UL_ch112_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='11c6ccc3-e347-44ce-9a79-6c90e9dfd0a0')
+ def test_rvr_TCP_DL_ch116_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='29f0fce1-005d-4ad7-97d7-6b43cbdff01b')
+ def test_rvr_TCP_UL_ch116_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='112302b1-8261-479a-b397-916b08fbbdd2')
+ def test_rvr_TCP_DL_ch132_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='3bb0efb8-ddfc-4a0b-b7cf-6d6af1dbb9f4')
+ def test_rvr_TCP_UL_ch132_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='11a4638f-d872-4730-82eb-71d9c64e0e16')
+ def test_rvr_TCP_DL_ch136_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='4d797c24-3bbe-43a6-ac9e-291db1aa732a')
+ def test_rvr_TCP_UL_ch136_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='5d433b44-0395-43cb-b85a-be138390b18b')
+ def test_rvr_TCP_DL_ch140_VHT20(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='47061772-21b1-4330-bd4f-daec21afa0c8')
+ def test_rvr_TCP_UL_ch140_VHT20(self):
+ self._test_rvr()
+
@test_tracker_info(uuid='24aa1e7a-3978-4803-877f-3ac5812ab0ae')
def test_rvr_TCP_DL_ch149_VHT20(self):
self._test_rvr()
@@ -605,6 +738,19 @@
"test_rvr_TCP_DL_ch161_VHT20", "test_rvr_TCP_UL_ch161_VHT20")
+class WifiRvr_SampleDFS_Test(WifiRvrTest):
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.tests = (
+ "test_rvr_TCP_DL_ch64_VHT20", "test_rvr_TCP_UL_ch64_VHT20",
+ "test_rvr_TCP_DL_ch100_VHT20", "test_rvr_TCP_UL_ch100_VHT20",
+ "test_rvr_TCP_DL_ch100_VHT40", "test_rvr_TCP_UL_ch100_VHT40",
+ "test_rvr_TCP_DL_ch100_VHT80", "test_rvr_TCP_UL_ch100_VHT80",
+ "test_rvr_TCP_DL_ch116_VHT20", "test_rvr_TCP_UL_ch116_VHT20",
+ "test_rvr_TCP_DL_ch132_VHT20", "test_rvr_TCP_UL_ch132_VHT20",
+ "test_rvr_TCP_DL_ch140_VHT20", "test_rvr_TCP_UL_ch140_VHT20")
+
+
class WifiRvr_SampleUDP_Test(WifiRvrTest):
def __init__(self, controllers):
base_test.BaseTestClass.__init__(self, controllers)
diff --git a/acts/tests/google/wifi/WifiScannerMultiScanTest.py b/acts/tests/google/wifi/WifiScannerMultiScanTest.py
index 0ff3574..1b33e57 100755
--- a/acts/tests/google/wifi/WifiScannerMultiScanTest.py
+++ b/acts/tests/google/wifi/WifiScannerMultiScanTest.py
@@ -149,15 +149,12 @@
'numUsage': 0,
'SSID': '"wh_ap1_2g"',
'timestamp': 4280078660,
- 'numConnection': 0,
'BSSID': '30:b5:c2:33:f9:05',
'frequency': 2412,
- 'numIpConfigFailures': 0,
'distanceSdCm': 0,
'distanceCm': 0,
'centerFreq1': 0,
'centerFreq0': 0,
- 'blackListTimestamp': 0,
'venueName': '',
'seen': 0,
'operatorFriendlyName': '',
diff --git a/acts/tests/google/wifi/WifiScannerScanTest.py b/acts/tests/google/wifi/WifiScannerScanTest.py
index 9eb6d38..b0d73de 100755
--- a/acts/tests/google/wifi/WifiScannerScanTest.py
+++ b/acts/tests/google/wifi/WifiScannerScanTest.py
@@ -75,12 +75,15 @@
"test_single_scan_while_pno",
"test_wifi_connection_and_pno_while_batch_scan",
"test_wifi_scanner_single_scan_in_isolated",
- "test_wifi_scanner_with_invalid_numBssidsPerScan")
+ "test_wifi_scanner_with_invalid_numBssidsPerScan",
+ "test_wifi_scanner_dual_radio_low_latency",
+ "test_wifi_scanner_dual_radio_low_power",
+ "test_wifi_scanner_dual_radio_high_accuracy")
def setup_class(self):
self.dut = self.android_devices[0]
wutils.wifi_test_device_init(self.dut)
- req_params = ("run_extended_test", "ping_addr", "max_bugreports")
+ req_params = ("run_extended_test", "ping_addr", "max_bugreports", "dbs_supported_models")
opt_param = ["reference_networks"]
self.unpack_userparams(
req_param_names=req_params, opt_param_names=opt_param)
@@ -108,6 +111,7 @@
self.attenuators = wutils.group_attenuators(self.attenuators)
self.attenuators[0].set_atten(0)
self.attenuators[1].set_atten(0)
+ self.dut.droid.wifiEnableWifiConnectivityManager(False)
def teardown_test(self):
base_test.BaseTestClass.teardown_test(self)
@@ -121,6 +125,7 @@
self.dut.cat_adb_log(test_name, begin_time)
def teardown_class(self):
+ self.dut.droid.wifiEnableWifiConnectivityManager(True)
if "AccessPoint" in self.user_params:
del self.user_params["reference_networks"]
del self.user_params["open_network"]
@@ -317,6 +322,8 @@
parameter.
3. Pop all full scan result events occurred earlier.
4. Verify that full scan results match with normal scan results.
+ 5. If the scan type is included in scan_setting, verify that the
+ radioChainInfos length.
Args:
scan_setting: The parameters for the single scan.
@@ -348,12 +355,27 @@
asserts.assert_true(
len(results) >= bssids,
"Full single shot result don't match {}".format(len(results)))
+ if 'type' in scan_setting.keys():
+ for item in results:
+ self.verify_radio_chain_length(scan_setting['type'], item)
except queue.Empty as error:
raise AssertionError(
"Event did not triggered for single shot {}".format(error))
finally:
self.dut.droid.wifiScannerStopScan(idx)
+ def verify_radio_chain_length(self, scan_setting_type, scan_result):
+ llen = len(scan_result[0]["radioChainInfos"])
+ if scan_setting_type == wutils.WifiEnums.SCAN_TYPE_LOW_LATENCY \
+ or scan_setting_type == wutils.WifiEnums.SCAN_TYPE_LOW_POWER:
+ asserts.assert_true(llen == 1,
+ "radioChainInfos len expected:{} "
+ "actual:{}".format(1, llen))
+ else:
+ asserts.assert_true(llen == 2,
+ "radioChainInfos len expected:{} "
+ "actual:{}".format(2, llen))
+
def wifi_scanner_batch_scan_full(self, scan_setting):
"""Common logic for batch scan test case for full scan result.
@@ -953,6 +975,63 @@
wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN}
self.wifi_scanner_single_scan(scan_setting)
+ @test_tracker_info(uuid="7c8da0c4-dec7-4d04-abd4-f8ea467a5c6d")
+ def test_wifi_scanner_dual_radio_low_latency(self):
+ """Test WiFi scanner single scan for mix channel with default setting
+ parameters.
+
+ 1. Start WifiScanner single scan for type = SCAN_TYPE_LOW_LATENCY.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ if self.dut.model not in self.dbs_supported_models:
+ asserts.skip(
+ ("Device %s does not support dual radio scanning.")
+ % self.dut.model)
+ scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+ "type": wutils.WifiEnums.SCAN_TYPE_LOW_LATENCY}
+ self.wifi_scanner_single_scan_full(scan_setting)
+
+ @test_tracker_info(uuid="58b49b01-851b-4e45-b218-9fd27c0be921")
+ def test_wifi_scanner_dual_radio_low_power(self):
+ """Test WiFi scanner single scan for mix channel with default setting
+ parameters.
+
+ 1. Start WifiScanner single scan for type = SCAN_TYPE_LOW_POWER.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ if self.dut.model not in self.dbs_supported_models:
+ asserts.skip(
+ ("Device %s does not support dual radio scanning.")
+ % self.dut.model)
+ scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+ "type": wutils.WifiEnums.SCAN_TYPE_LOW_POWER}
+ self.wifi_scanner_single_scan_full(scan_setting)
+
+ @test_tracker_info(uuid="3e7288bc-45e4-497c-bf3a-977eec4e896e")
+ def test_wifi_scanner_dual_radio_high_accuracy(self):
+ """Test WiFi scanner single scan for mix channel with default setting
+ parameters.
+
+ 1. Start WifiScanner single scan for type = SCAN_TYPE_HIGH_ACCURACY.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ if self.dut.model not in self.dbs_supported_models:
+ asserts.skip(
+ ("Device %s does not support dual radio scanning.")
+ % self.dut.model)
+ scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+ "type": wutils.WifiEnums.SCAN_TYPE_HIGH_ACCURACY}
+ self.wifi_scanner_single_scan_full(scan_setting)
+
@test_tracker_info(uuid="e9f3aaad-4af3-4c54-9829-65dc1d6d4987")
def test_wifi_scanner_batch_scan_channel_sanity(self):
"""Test WiFi scanner batch scan for mix channel with default setting
diff --git a/acts/tests/google/wifi/WifiSoftApAcsTest.py b/acts/tests/google/wifi/WifiSoftApAcsTest.py
new file mode 100755
index 0000000..d377764
--- /dev/null
+++ b/acts/tests/google/wifi/WifiSoftApAcsTest.py
@@ -0,0 +1,602 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import itertools
+import pprint
+import queue
+import sys
+import time
+
+import acts.base_test
+import acts.signals as signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils as utils
+
+from acts import asserts
+from acts.controllers.ap_lib import hostapd_constants
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from threading import Thread
+
+WifiEnums = wutils.WifiEnums
+WIFI_CONFIG_APBAND_AUTO = -1
+
+class WifiSoftApAcsTest(WifiBaseTest):
+ """Tests for Automatic Channel Selection.
+
+ Test Bed Requirement:
+ * Two Android devices and an AP.
+ * 2GHz and 5GHz Wi-Fi network visible to the device.
+ """
+
+ def __init__(self, controllers):
+ WifiBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ self.dut_client = self.android_devices[1]
+ wutils.wifi_test_device_init(self.dut)
+ wutils.wifi_test_device_init(self.dut_client)
+ utils.require_sl4a((self.dut, self.dut_client))
+ utils.sync_device_time(self.dut)
+ utils.sync_device_time(self.dut_client)
+ # Set country code explicitly to "US".
+ self.dut.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+ self.dut_client.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+ # Enable verbose logging on the duts
+ self.dut.droid.wifiEnableVerboseLogging(1)
+ asserts.assert_equal(self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
+ "Failed to enable WiFi verbose logging on the softap dut.")
+ self.dut_client.droid.wifiEnableVerboseLogging(1)
+ asserts.assert_equal(self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
+ "Failed to enable WiFi verbose logging on the client dut.")
+ req_params = []
+ opt_param = ["iperf_server_address", "reference_networks"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server = self.iperf_servers[0]
+ if hasattr(self, 'iperf_server'):
+ self.iperf_server.start()
+
+ def setup_test(self):
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.stop_wifi_tethering(self.dut)
+ wutils.reset_wifi(self.dut)
+ wutils.reset_wifi(self.dut_client)
+ try:
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+ except:
+ pass
+ self.access_points[0].close()
+
+ def teardown_class(self):
+ if hasattr(self, 'iperf_server'):
+ self.iperf_server.stop()
+
+ def on_fail(self, test_name, begin_time):
+ self.dut.take_bug_report(test_name, begin_time)
+ self.dut.cat_adb_log(test_name, begin_time)
+
+ """Helper Functions"""
+
+ def run_iperf_client(self, params):
+ """Run iperf traffic after connection.
+
+ Args:
+ params: A tuple of network info and AndroidDevice object.
+
+ """
+ if "iperf_server_address" in self.user_params:
+ network, ad = params
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic through {}".format(SSID))
+ port_arg = "-p {} -t {}".format(self.iperf_server.port, 3)
+ success, data = ad.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+ self.log.info("Finished iperf traffic through {}".format(SSID))
+
+ def start_softap_and_verify(self, band):
+ """Bring-up softap and verify AP mode and in scan results.
+
+ Args:
+ band: The band to use for softAP.
+
+ """
+ config = wutils.create_softap_config()
+ wutils.start_wifi_tethering(self.dut,
+ config[wutils.WifiEnums.SSID_KEY],
+ config[wutils.WifiEnums.PWD_KEY], band=band)
+ asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut_client, config[wutils.WifiEnums.SSID_KEY])
+ return config
+
+ def get_softap_acs(self, softap):
+ """Connect to the softap on client-dut and get the softap channel
+ information.
+
+ Args:
+ softap: The softap network configuration information.
+
+ """
+ wutils.connect_to_wifi_network(self.dut_client, softap,
+ check_connectivity=False)
+ softap_info = self.dut_client.droid.wifiGetConnectionInfo()
+ self.log.debug("DUT is connected to softAP %s with details: %s" %
+ (softap[wutils.WifiEnums.SSID_KEY], softap_info))
+ frequency = softap_info['frequency']
+ return hostapd_constants.CHANNEL_MAP[frequency]
+
+ def configure_ap(self, channel_2g=None, channel_5g=None):
+ """Configure and bring up AP on required channel.
+
+ Args:
+ channel_2g: The channel number to use for 2GHz network.
+ channel_5g: The channel number to use for 5GHz network.
+
+ """
+ if "AccessPoint" in self.user_params:
+ if not channel_2g:
+ self.legacy_configure_ap_and_start(channel_5g=channel_5g)
+ elif not channel_5g:
+ self.legacy_configure_ap_and_start(channel_2g=channel_2g)
+ else:
+ self.legacy_configure_ap_and_start(channel_2g=channel_2g,
+ channel_5g=chanel_5g)
+
+ def start_traffic_and_softap(self, network, softap_band):
+ """Start iPerf traffic on client dut, during softAP bring-up on dut.
+
+ Args:
+ network: Network information of the network to connect to.
+ softap_band: The band to use for softAP.
+
+ """
+ if not network:
+ # For a clean environment just bring up softap and return channel.
+ softap = self.start_softap_and_verify(softap_band)
+ channel = self.get_softap_acs(softap)
+ return channel
+ # Connect to the AP and start IPerf traffic, while we bring up softap.
+ wutils.connect_to_wifi_network(self.dut_client, network)
+ t = Thread(target=self.run_iperf_client,args=((network,self.dut_client),))
+ t.setDaemon(True)
+ t.start()
+ time.sleep(1)
+ softap = self.start_softap_and_verify(softap_band)
+ t.join()
+ channel = self.get_softap_acs(softap)
+ return channel
+
+ def verify_acs_channel(self, chan, avoid_chan):
+ """Verify ACS algorithm by ensuring that softAP came up on a channel,
+ different than the active channels.
+
+ Args:
+ chan: The channel number softap came-up on.
+ avoid_chan: The channel to avoid during this test.
+
+ """
+ if avoid_chan in range(1,12):
+ avoid_chan2 = hostapd_constants.AP_DEFAULT_CHANNEL_5G
+ elif avoid_chan in range(36, 166):
+ avoid_chan2 = hostapd_constants.AP_DEFAULT_CHANNEL_2G
+ if chan == avoid_chan or chan == avoid_chan2:
+ raise signals.TestFailure("ACS chose the same channel that the "
+ "AP was beaconing on. Channel = %d" % chan)
+
+ """Tests"""
+ @test_tracker_info(uuid="3507bd18-e787-4380-8725-1872916d4267")
+ def test_softap_2G_clean_env(self):
+ """Test to bring up SoftAp on 2GHz in clean environment."""
+ network = None
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ if not chan in range(1, 12):
+ raise signals.TestFailure("ACS chose incorrect channel %d for 2GHz "
+ "band" % chan)
+
+ @test_tracker_info(uuid="3d18da8b-d29a-45f9-8018-5348e10099e9")
+ def test_softap_5G_clean_env(self):
+ """Test to bring up SoftAp on 5GHz in clean environment."""
+ network = None
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ if not chan in range(36, 166):
+ # Note: This does not treat DFS channel separately.
+ raise signals.TestFailure("ACS chose incorrect channel %d for 5GHz "
+ "band" % chan)
+
+ @test_tracker_info(uuid="cc353bda-3831-4d6e-b990-e501b8e4eafe")
+ def test_softap_auto_clean_env(self):
+ """Test to bring up SoftAp on AUTO-band in clean environment."""
+ network = None
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_AUTO)
+ if not chan in range(36, 166):
+ # Note: This does not treat DFS channel separately.
+ raise signals.TestFailure("ACS chose incorrect channel %d for 5GHz "
+ "band" % chan)
+
+ @test_tracker_info(uuid="a5f6a926-76d2-46a7-8136-426e35b5a5a8")
+ def test_softap_2G_avoid_channel_1(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=1)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="757e2019-b027-40bf-a562-2b01f3e5957e")
+ def test_softap_5G_avoid_channel_1(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=1)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="b96e39d1-9041-4662-a55f-22641c2e2b02")
+ def test_softap_2G_avoid_channel_2(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=2)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="941c4e2b-ae35-4b49-aa81-13d3dc44b5b6")
+ def test_softap_5G_avoid_channel_2(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=2)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="444c4a34-7f6b-4f02-9802-2e896e7d1796")
+ def test_softap_2G_avoid_channel_3(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=3)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="eccd06b1-6df5-4144-8fda-1504cb822375")
+ def test_softap_5G_avoid_channel_3(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=3)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="fb257644-2081-4c3d-8394-7a308dde0047")
+ def test_softap_2G_avoid_channel_4(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=4)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="88b9cd16-4541-408a-8607-415fe60001f2")
+ def test_softap_5G_avoid_channel_4(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=4)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="b3626ec8-50e8-412c-bdbe-5c5ade647d7b")
+ def test_softap_2G_avoid_channel_5(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=5)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="45c4396b-9b0c-44f3-adf2-ea9c86fcab1d")
+ def test_softap_5G_avoid_channel_5(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=5)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="f70634e7-c6fd-403d-8cd7-439fbbda6af0")
+ def test_softap_2G_avoid_channel_6(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=6)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="f3341136-10bc-44e2-b9a8-2d27d3284b73")
+ def test_softap_5G_avoid_channel_6(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=6)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="8129594d-1608-448b-8548-5a8c4022f2a1")
+ def test_softap_2G_avoid_channel_7(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=7)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="7b470b82-d19b-438c-8f98-ce697e0eb474")
+ def test_softap_5G_avoid_channel_7(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=7)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="11540182-d471-4bf0-8f8b-add89443c329")
+ def test_softap_2G_avoid_channel_8(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=8)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="1280067c-389e-42e9-aa75-6bfbd61340f3")
+ def test_softap_5G_avoid_channel_8(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=9)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="6feeb83c-2723-49cb-93c1-6297d4a3d853")
+ def test_softap_2G_avoid_channel_9(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=9)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="49a110cd-03e8-4e99-9327-5123eab40902")
+ def test_softap_5G_avoid_channel_9(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=9)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="a03c9e45-8763-4b5c-bead-e574fb9899a2")
+ def test_softap_2G_avoid_channel_10(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=10)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="c1a1d272-a646-4c2d-8425-09d2ae6ae8e6")
+ def test_softap_5G_avoid_channel_10(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=10)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="f38d8911-92d4-4dcd-ba23-1e1667fa1f5a")
+ def test_softap_2G_avoid_channel_11(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=11)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="24cc35ba-45e3-4b7a-9bc9-25b7abe92fa9")
+ def test_softap_5G_avoid_channel_11(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=11)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="85aef720-4f3c-43bb-9de0-615b88c2bfe0")
+ def test_softap_2G_avoid_channel_36(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=36)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="433e8db3-93b5-463e-a83c-0d4b9b9a8700")
+ def test_softap_5G_avoid_channel_36(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=36)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="326e0e42-3219-4e63-a18d-5dc32c58e7d8")
+ def test_softap_2G_avoid_channel_40(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=40)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="45953c03-c978-4775-a39b-fb7e70c8990a")
+ def test_softap_5G_avoid_channel_40(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=40)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="e8e89cec-aa27-4780-8ff8-546d5af820f7")
+ def test_softap_2G_avoid_channel_44(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=44)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="5e386d7d-d4c9-40cf-9333-06da55e11ba1")
+ def test_softap_5G_avoid_channel_44(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=44)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="cb51dfca-f8de-4dfc-b513-e590c838c766")
+ def test_softap_2G_avoid_channel_48(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=48)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="490b8ed1-196c-4941-b06b-5f0721ca440b")
+ def test_softap_5G_avoid_channel_48(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=48)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="c5ab141b-e145-4cc1-b0d7-dd610cbfb462")
+ def test_softap_2G_avoid_channel_149(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=149)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="108d7ef8-6fe7-49ba-b684-3820e881fcf0")
+ def test_softap_5G_avoid_channel_149(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=149)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="f6926f40-0afc-41c5-9b38-c95a99788ff5")
+ def test_softap_2G_avoid_channel_153(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=153)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="3d7b653b-c094-4c57-8e6a-047629b05216")
+ def test_softap_5G_avoid_channel_153(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=153)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="b866ceea-d3ca-45d4-964a-4edea96026e6")
+ def test_softap_2G_avoid_channel_157(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=157)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="03cb9163-bca3-442e-9691-6df82f8c51c7")
+ def test_softap_2G_avoid_channel_157(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=157)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="ae10f23a-da70-43c8-9991-2c2f4a602724")
+ def test_softap_2G_avoid_channel_161(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=161)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="521686c2-acfa-42d1-861b-aa10ac4dad34")
+ def test_softap_5G_avoid_channel_161(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=161)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="77ebecd7-c036-463f-b77d-2cd70d89bc81")
+ def test_softap_2G_avoid_channel_165(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=165)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="85d9386d-fe60-4708-9f91-75bbf8bec54f")
+ def test_softap_5G_avoid_channel_165(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=165)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
diff --git a/acts/tests/google/wifi/WifiSoftApPerformanceTest.py b/acts/tests/google/wifi/WifiSoftApPerformanceTest.py
new file mode 100644
index 0000000..0b20d46
--- /dev/null
+++ b/acts/tests/google/wifi/WifiSoftApPerformanceTest.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import WifiRvrTest
+from acts import base_test
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+
+class WifiSoftApRvrTest(WifiRvrTest.WifiRvrTest):
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.tests = ("test_rvr_TCP_DL_2GHz", "test_rvr_TCP_UL_2GHz",
+ "test_rvr_TCP_DL_5GHz", "test_rvr_TCP_UL_5GHz")
+
+ def get_sap_connection_info(self):
+ info = {}
+ info["client_ip_address"] = self.android_devices[
+ 1].droid.connectivityGetIPv4Addresses('wlan0')[0]
+ info["ap_ip_address"] = self.android_devices[
+ 0].droid.connectivityGetIPv4Addresses('wlan0')[0]
+ info["frequency"] = self.client_dut.adb.shell(
+ "wpa_cli status | grep freq").split("=")[1]
+ info["channel"] = wutils.WifiEnums.freq_to_channel[int(
+ info["frequency"])]
+ return info
+
+ def sap_rvr_test_func(self):
+ """Main function to test Soft AP RvR.
+
+ The function sets up the phones in the correct soft ap and client mode
+ configuration and calls run_rvr to sweep attenuation and measure
+ throughput
+
+ Args:
+ channel: Specifies AP's channel
+ mode: Specifies AP's bandwidth/mode (11g, VHT20, VHT40, VHT80)
+ Returns:
+ rvr_result: dict containing rvr_results and meta data
+ """
+ #Initialize RvR test parameters
+ num_atten_steps = int((self.test_params["rvr_atten_stop"] -
+ self.test_params["rvr_atten_start"]) /
+ self.test_params["rvr_atten_step"])
+ self.rvr_atten_range = [
+ self.test_params["rvr_atten_start"] +
+ x * self.test_params["rvr_atten_step"]
+ for x in range(0, num_atten_steps)
+ ]
+ rvr_result = {}
+ # Reset WiFi on all devices
+ for dev in self.android_devices:
+ wutils.reset_wifi(dev)
+ dev.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+ # Setup Soft AP
+ sap_config = wutils.create_softap_config()
+ wutils.start_wifi_tethering(
+ self.android_devices[0], sap_config[wutils.WifiEnums.SSID_KEY],
+ sap_config[wutils.WifiEnums.PWD_KEY], self.sap_band_enum)
+ self.main_network = {
+ "SSID": sap_config[wutils.WifiEnums.SSID_KEY],
+ "password": sap_config[wutils.WifiEnums.PWD_KEY]
+ }
+ # Set attenuator to 0 dB
+ [self.attenuators[i].set_atten(0) for i in range(self.num_atten)]
+ # Connect DUT to Network
+ wutils.wifi_connect(
+ self.android_devices[1],
+ self.main_network,
+ num_of_tries=5,
+ assert_on_fail=False)
+ connection_info = self.get_sap_connection_info()
+ self.test_params["iperf_server_address"] = connection_info[
+ "ap_ip_address"]
+ # Run RvR and log result
+ rvr_result["test_name"] = self.current_test_name
+ rvr_result["attenuation"] = list(self.rvr_atten_range)
+ rvr_result["fixed_attenuation"] = self.test_params[
+ "fixed_attenuation"][str(connection_info["channel"])]
+ rvr_result["throughput_receive"] = self.rvr_test()
+ self.testclass_results.append(rvr_result)
+ wutils.stop_wifi_tethering(self.android_devices[0])
+ return rvr_result
+
+ def _test_rvr(self):
+ """ Function that gets called for each test case
+
+ The function gets called in each rvr test case. The function customizes
+ the rvr test based on the test name of the test that called it
+ """
+ test_params = self.current_test_name.split("_")
+ self.sap_band = test_params[4]
+ if self.sap_band == "2GHz":
+ self.sap_band_enum = wutils.WifiEnums.WIFI_CONFIG_APBAND_2G
+ else:
+ self.sap_band_enum = wutils.WifiEnums.WIFI_CONFIG_APBAND_5G
+ self.iperf_args = '-i 1 -t {} -J '.format(
+ self.test_params["iperf_duration"])
+ if test_params[2] == "UDP":
+ self.iperf_args = self.iperf_args + "-u -b {}".format(
+ self.test_params["UDP_rates"]["VHT80"])
+ if test_params[3] == "DL":
+ self.iperf_args = self.iperf_args + ' -R'
+ self.use_client_output = True
+ else:
+ self.use_client_output = False
+ rvr_result = self.sap_rvr_test_func()
+ self.post_process_results(rvr_result)
+ self.pass_fail_check(rvr_result)
+
+ #Test cases
+ @test_tracker_info(uuid='7910112e-49fd-4e49-bc5c-f84da0cbb9f6')
+ def test_rvr_TCP_DL_2GHz(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='b3c00814-6fdf-496b-b345-6a719bef657e')
+ def test_rvr_TCP_UL_2GHz(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='a2f727b5-68ba-46e5-a7fe-f86c0a082fc9')
+ def test_rvr_TCP_DL_5GHz(self):
+ self._test_rvr()
+
+ @test_tracker_info(uuid='0cca9352-3f06-4bba-be17-8897a1b42a0f')
+ def test_rvr_TCP_UL_5GHz(self):
+ self._test_rvr()
diff --git a/acts/tests/google/wifi/WifiSoftApTest.py b/acts/tests/google/wifi/WifiSoftApTest.py
index ed8a080..0d722e3 100644
--- a/acts/tests/google/wifi/WifiSoftApTest.py
+++ b/acts/tests/google/wifi/WifiSoftApTest.py
@@ -20,15 +20,16 @@
from acts import asserts
from acts import utils
-from acts import base_test
from acts.test_decorators import test_tracker_info
from acts.test_utils.tel import tel_defines
from acts.test_utils.tel import tel_test_utils as tel_utils
from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_AUTO
from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-class WifiSoftApTest(base_test.BaseTestClass):
+class WifiSoftApTest(WifiBaseTest):
def setup_class(self):
"""It will setup the required dependencies from config file and configure
@@ -39,6 +40,13 @@
"""
self.dut = self.android_devices[0]
self.dut_client = self.android_devices[1]
+ req_params = []
+ opt_param = ["open_network"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start()
+ self.open_network = self.open_network[0]["2g"]
# Do a simple version of init - mainly just sync the time and enable
# verbose logging. This test will fail if the DUT has a sim and cell
# data is disabled. We would also like to test with phones in less
@@ -48,9 +56,8 @@
utils.sync_device_time(self.dut)
utils.sync_device_time(self.dut_client)
# Set country code explicitly to "US".
- self.dut.adb.shell("halutil -country %s" %
- wutils.WifiEnums.CountryCode.US)
-
+ self.dut.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+ self.dut_client.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
# Enable verbose logging on the duts
self.dut.droid.wifiEnableVerboseLogging(1)
asserts.assert_equal(self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
@@ -60,50 +67,18 @@
"Failed to enable WiFi verbose logging on the client dut.")
def teardown_class(self):
+ wutils.stop_wifi_tethering(self.dut)
wutils.reset_wifi(self.dut)
wutils.reset_wifi(self.dut_client)
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
def on_fail(self, test_name, begin_time):
self.dut.take_bug_report(test_name, begin_time)
+ self.dut_client.take_bug_report(test_name, begin_time)
""" Helper Functions """
- def verify_return_to_wifi_enabled(self):
- """Verifies that wifi is enabled
-
- We consider wifi as enabled if two things have happened. First, supplicant
- is started correctly (seen with the supplicant connection change). Second,
- supplicant should initially enter the disconnected state.
- """
- curr_state = "waiting for supplicant to come back up"
- try:
- self.dut.droid.wifiStartTrackingStateChange()
- event = self.dut.ed.pop_event("SupplicantConnectionChanged", 10)
- except queue.Empty:
- self.log.exception("Failed to restart wifi: current state = %s",
- curr_state)
- asserts.fail(curr_state)
- finally:
- self.dut.droid.wifiStopTrackingStateChange()
-
- #TODO(silberst): uncomment and remove loop below when b/30037819 is fixed
- #curr_state = "waiting for wifi to go back into connect mode"
- #try:
- # self.dut.droid.wifiStartTrackingStateChange()
- # event = self.dut.ed.pop_event("WifiNetworkDisconnected", 10)
- # self.dut.droid.wifiStopTrackingStateChange()
- #except queue.Empty:
- # self.log.exception("Failed to restart wifi: current state = %s", curr_state)
- # asserts.fail(curr_state)
- attempt_count = 0
- max_attempts = 3
- while attempt_count < max_attempts:
- if not self.dut.droid.wifiCheckState():
- attempt_count += 1
- time.sleep(5)
- else:
- return
- asserts.fail("failed waiting for wifi to return to connect mode")
-
def create_softap_config(self):
"""Create a softap config with ssid and password."""
ap_ssid = "softap_" + utils.rand_ascii_str(8)
@@ -119,15 +94,17 @@
Args:
ap_ssid: SSID of the ap we are looking for.
"""
- #TODO(silberst): debug and remove the extra scan before submitting this test
- wutils.start_wifi_connection_scan(self.dut_client)
- client_scan_results = self.dut_client.droid.wifiGetScanResults()
- wutils.start_wifi_connection_scan(self.dut_client)
- client_scan_results = self.dut_client.droid.wifiGetScanResults()
- for result in client_scan_results:
- self.dut.log.debug("scan found: %s", result[wutils.WifiEnums.SSID_KEY])
- wutils.assert_network_in_list({wutils.WifiEnums.SSID_KEY: ap_ssid},
- client_scan_results)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut_client, ap_ssid);
+
+ def confirm_softap_not_in_scan_results(self, ap_ssid):
+ """Confirm the ap started by wifi tethering is not seen in scan results.
+
+ Args:
+ ap_ssid: SSID of the ap we are looking for.
+ """
+ wutils.start_wifi_connection_scan_and_ensure_network_not_found(
+ self.dut_client, ap_ssid);
def check_cell_data_and_enable(self):
"""Make sure that cell data is enabled if there is a sim present.
@@ -144,7 +121,7 @@
asserts.assert_true(self.dut.droid.telephonyIsDataEnabled(),
"Failed to enable cell data for softap dut.")
- def validate_full_tether_startup(self, band=None):
+ def validate_full_tether_startup(self, band=None, hidden=None):
"""Test full startup of wifi tethering
1. Report current state.
@@ -162,13 +139,23 @@
config = self.create_softap_config()
wutils.start_wifi_tethering(self.dut,
config[wutils.WifiEnums.SSID_KEY],
- config[wutils.WifiEnums.PWD_KEY], band)
+ config[wutils.WifiEnums.PWD_KEY], band, hidden)
+ if hidden:
+ # First ensure it's not seen in scan results.
+ self.confirm_softap_not_in_scan_results(
+ config[wutils.WifiEnums.SSID_KEY])
+ # If the network is hidden, it should be saved on the client to be
+ # seen in scan results.
+ config[wutils.WifiEnums.HIDDEN_KEY] = True
+ ret = self.dut_client.droid.wifiAddNetwork(config)
+ asserts.assert_true(ret != -1, "Add network %r failed" % config)
+ self.dut_client.droid.wifiEnableNetwork(ret, 0)
self.confirm_softap_in_scan_results(config[wutils.WifiEnums.SSID_KEY])
wutils.stop_wifi_tethering(self.dut)
asserts.assert_false(self.dut.droid.wifiIsApEnabled(),
"SoftAp is still reported as running")
if initial_wifi_state:
- self.verify_return_to_wifi_enabled()
+ wutils.wait_for_wifi_state(self.dut, True)
elif self.dut.droid.wifiCheckState():
asserts.fail("Wifi was disabled before softap and now it is enabled")
@@ -228,6 +215,80 @@
"""
self.validate_full_tether_startup(WIFI_CONFIG_APBAND_5G)
+ @test_tracker_info(uuid="f76ed37a-519a-48b4-b260-ee3fc5a9cae0")
+ def test_full_tether_startup_auto(self):
+ """Test full startup of wifi tethering in auto-band.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_AUTO)
+
+ @test_tracker_info(uuid="d26ee4df-5dcb-4191-829f-05a10b1218a7")
+ def test_full_tether_startup_2G_hidden(self):
+ """Test full startup of wifi tethering in 2G band using hidden AP.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_2G, True)
+
+ @test_tracker_info(uuid="229cd585-a789-4c9a-8948-89fa72de9dd5")
+ def test_full_tether_startup_5G_hidden(self):
+ """Test full startup of wifi tethering in 5G band using hidden AP.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_5G, True)
+
+ @test_tracker_info(uuid="d546a143-6047-4ffd-b3c6-5ec81a38001f")
+ def test_full_tether_startup_auto_hidden(self):
+ """Test full startup of wifi tethering in auto-band using hidden AP.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_AUTO, True)
+
+ @test_tracker_info(uuid="b2f75330-bf33-4cdd-851a-de390f891ef7")
+ def test_tether_startup_while_connected_to_a_network(self):
+ """Test full startup of wifi tethering in auto-band while the device
+ is connected to a network.
+
+ 1. Connect to an open network.
+ 2. Turn on AP mode (in auto band).
+ 3. Verify SoftAP active.
+ 4. Make a client connect to the AP.
+ 5. Shutdown wifi tethering.
+ 6. Ensure that the client disconnected.
+ """
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.wifi_connect(self.dut, self.open_network)
+ config = self.create_softap_config()
+ wutils.start_wifi_tethering(self.dut,
+ config[wutils.WifiEnums.SSID_KEY],
+ config[wutils.WifiEnums.PWD_KEY],
+ WIFI_CONFIG_APBAND_AUTO)
+ asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+ # local hotspot may not have internet connectivity
+ wutils.wifi_connect(self.dut_client, config, check_connectivity=False)
+ wutils.stop_wifi_tethering(self.dut)
+ wutils.wait_for_disconnect(self.dut_client)
+
""" Tests End """
diff --git a/acts/tests/google/wifi/WifiStaApConcurrencyTest.py b/acts/tests/google/wifi/WifiStaApConcurrencyTest.py
new file mode 100755
index 0000000..ffe0eff
--- /dev/null
+++ b/acts/tests/google/wifi/WifiStaApConcurrencyTest.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import itertools
+import pprint
+import queue
+import time
+
+import acts.base_test
+import acts.signals as signals
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils as utils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+# Channels to configure the AP for various test scenarios.
+WIFI_NETWORK_AP_CHANNEL_2G = 1
+WIFI_NETWORK_AP_CHANNEL_5G = 36
+WIFI_NETWORK_AP_CHANNEL_5G_DFS = 132
+
+class WifiStaApConcurrencyTest(WifiBaseTest):
+ """Tests for STA + AP concurrency scenarions.
+
+ Test Bed Requirement:
+ * Two Android devices (For AP)
+ * One Wi-Fi network visible to the device (for STA).
+ """
+
+ def __init__(self, controllers):
+ WifiBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ self.dut_client = self.android_devices[1]
+ wutils.wifi_test_device_init(self.dut)
+ wutils.wifi_test_device_init(self.dut_client)
+ # Do a simple version of init - mainly just sync the time and enable
+ # verbose logging. This test will fail if the DUT has a sim and cell
+ # data is disabled. We would also like to test with phones in less
+ # constrained states (or add variations where we specifically
+ # constrain).
+ utils.require_sl4a((self.dut, self.dut_client))
+ utils.sync_device_time(self.dut)
+ utils.sync_device_time(self.dut_client)
+ # Set country code explicitly to "US".
+ self.dut.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+ self.dut_client.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+ # Enable verbose logging on the duts
+ self.dut.droid.wifiEnableVerboseLogging(1)
+ asserts.assert_equal(self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
+ "Failed to enable WiFi verbose logging on the softap dut.")
+ self.dut_client.droid.wifiEnableVerboseLogging(1)
+ asserts.assert_equal(self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
+ "Failed to enable WiFi verbose logging on the client dut.")
+
+ req_params = ["AccessPoint", "dbs_supported_models"]
+ opt_param = ["iperf_server_address"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+
+ if self.dut.model not in self.dbs_supported_models:
+ asserts.skip(
+ ("Device %s does not support dual interfaces.")
+ % self.dut.model)
+
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server = self.iperf_servers[0]
+ if hasattr(self, 'iperf_server'):
+ self.iperf_server.start()
+
+ # Set the client wifi state to on before the test begins.
+ wutils.wifi_toggle_state(self.dut_client, True)
+
+ def setup_test(self):
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ self.turn_location_off_and_scan_toggle_off()
+ wutils.wifi_toggle_state(self.dut, False)
+
+ def teardown_test(self):
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.stop_wifi_tethering(self.dut)
+ wutils.reset_wifi(self.dut)
+ wutils.reset_wifi(self.dut_client)
+ self.access_points[0].close()
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ def teardown_class(self):
+ if hasattr(self, 'iperf_server'):
+ self.iperf_server.stop()
+
+ def on_fail(self, test_name, begin_time):
+ self.dut.take_bug_report(test_name, begin_time)
+ self.dut.cat_adb_log(test_name, begin_time)
+
+ """Helper Functions"""
+ def configure_ap(self, channel_2g=None, channel_5g=None):
+ """Configure and bring up AP on required channel.
+
+ Args:
+ channel_2g: The channel number to use for 2GHz network.
+ channel_5g: The channel number to use for 5GHz network.
+
+ """
+ if not channel_2g:
+ self.legacy_configure_ap_and_start(channel_5g=channel_5g)
+ elif not channel_5g:
+ self.legacy_configure_ap_and_start(channel_2g=channel_2g)
+ else:
+ self.legacy_configure_ap_and_start(channel_2g=channel_2g,
+ channel_5g=chanel_5g)
+ self.wpapsk_2g = self.reference_networks[0]["2g"]
+ self.wpapsk_5g = self.reference_networks[0]["5g"]
+
+ def turn_location_on_and_scan_toggle_on(self):
+ """ Turns on wifi location scans.
+ """
+ acts.utils.set_location_service(self.dut, True)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(True)
+ msg = "Failed to turn on location service's scan."
+ asserts.assert_true(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def turn_location_off_and_scan_toggle_off(self):
+ """ Turns off wifi location scans.
+ """
+ acts.utils.set_location_service(self.dut, False)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
+ msg = "Failed to turn off location service's scan."
+ asserts.assert_true(not self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def run_iperf_client(self, params):
+ """Run iperf traffic after connection.
+
+ Args:
+ params: A tuple of network info and AndroidDevice object.
+ """
+ if "iperf_server_address" in self.user_params:
+ wait_time = 5
+ network, ad = params
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic through {}".format(SSID))
+ time.sleep(wait_time)
+ port_arg = "-p {}".format(self.iperf_server.port)
+ success, data = ad.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+
+ def connect_to_wifi_network_and_verify(self, params):
+ """Connection logic for open and psk wifi networks.
+
+ Args:
+ params: A tuple of network info and AndroidDevice object.
+ """
+ network, ad = params
+ droid = ad.droid
+ ed = ad.ed
+ SSID = network[WifiEnums.SSID_KEY]
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ ad, SSID);
+ wutils.wifi_connect(ad, network, num_of_tries=3)
+
+ def confirm_softap_in_scan_results(self, ap_ssid):
+ """Confirm the ap started by wifi tethering is seen in scan results.
+
+ Args:
+ ap_ssid: SSID of the ap we are looking for.
+ """
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut_client, ap_ssid);
+
+ def create_softap_config(self):
+ """Create a softap config with ssid and password."""
+ ap_ssid = "softap_" + utils.rand_ascii_str(8)
+ ap_password = utils.rand_ascii_str(8)
+ self.dut.log.info("softap setup: %s %s", ap_ssid, ap_password)
+ config = {wutils.WifiEnums.SSID_KEY: ap_ssid}
+ config[wutils.WifiEnums.PWD_KEY] = ap_password
+ return config
+
+ def start_softap_and_verify(self, band):
+ """Test startup of softap
+
+ 1. Brinup AP mode.
+ 2. Verify SoftAP active using the client device.
+ """
+ config = self.create_softap_config()
+ wutils.start_wifi_tethering(self.dut,
+ config[wutils.WifiEnums.SSID_KEY],
+ config[wutils.WifiEnums.PWD_KEY], band)
+ self.confirm_softap_in_scan_results(config[wutils.WifiEnums.SSID_KEY])
+
+ def connect_to_wifi_network_and_start_softap(self, nw_params, softap_band):
+ """Test concurrenct wifi connection and softap.
+ This helper method first makes a wifi conenction and then starts SoftAp.
+
+ Args:
+ nw_params: Params for network STA connection.
+ softap_band: Band for the AP.
+
+ 1. Bring up wifi.
+ 2. Establish connection to a network.
+ 3. Bring up softap and verify AP is seen on a client device.
+ 4. Run iperf on the wifi connection to the network.
+ """
+ wutils.wifi_toggle_state(self.dut, True)
+ self.connect_to_wifi_network_and_verify((nw_params, self.dut))
+ self.start_softap_and_verify(softap_band)
+ self.run_iperf_client((nw_params, self.dut))
+ # Verify that both softap & wifi is enabled concurrently.
+ self.verify_wifi_and_softap_enabled()
+
+ def start_softap_and_connect_to_wifi_network(self, nw_params, softap_band):
+ """Test concurrenct wifi connection and softap.
+ This helper method first starts SoftAp and then makes a wifi conenction.
+
+ Args:
+ nw_params: Params for network STA connection.
+ softap_band: Band for the AP.
+
+ 1. Bring up softap and verify AP is seen on a client device.
+ 2. Bring up wifi.
+ 3. Establish connection to a network.
+ 4. Run iperf on the wifi connection to the network.
+ """
+ self.start_softap_and_verify(softap_band)
+ wutils.wifi_toggle_state(self.dut, True)
+ self.connect_to_wifi_network_and_verify((nw_params, self.dut))
+ self.run_iperf_client((nw_params, self.dut))
+ # Verify that both softap & wifi is enabled concurrently.
+ self.verify_wifi_and_softap_enabled()
+
+ def verify_wifi_and_softap_enabled(self):
+ """Helper to verify both wifi and softap is enabled
+ """
+ asserts.assert_true(self.dut.droid.wifiCheckState(),
+ "Wifi is not reported as running");
+ asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+
+ """Tests"""
+ @test_tracker_info(uuid="c396e7ac-cf22-4736-a623-aa6d3c50193a")
+ def test_wifi_connection_2G_softap_2G(self):
+ """Tests connection to 2G network followed by bringing up SoftAp on 2G.
+ """
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.wpapsk_2g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="1cd6120d-3db4-4624-9bae-55c976533a48")
+ def test_wifi_connection_5G_softap_5G(self):
+ """Tests connection to 5G network followed by bringing up SoftAp on 5G.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="5f980007-3490-413e-b94e-7700ffab8534")
+ def test_wifi_connection_5G_DFS_softap_5G(self):
+ """Tests connection to 5G DFS network followed by bringing up SoftAp on 5G.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ self.connect_to_wifi_network_and_start_softap(
+ self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="d05d5d44-c738-4372-9f01-ce2a640a2f25")
+ def test_wifi_connection_5G_softap_2G(self):
+ """Tests connection to 5G network followed by bringing up SoftAp on 2G.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="909ac713-1ad3-4dad-9be3-ad60f00ed25e")
+ def test_wifi_connection_5G_DFS_softap_2G(self):
+ """Tests connection to 5G DFS network followed by bringing up SoftAp on 2G.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ self.connect_to_wifi_network_and_start_softap(
+ self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="e8de724a-25d3-4801-94cc-22e9e0ecc8d1")
+ def test_wifi_connection_2G_softap_5G(self):
+ """Tests connection to 2G network followed by bringing up SoftAp on 5G.
+ """
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.wpapsk_2g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="647f4e17-5c7a-4249-98af-f791d163a39f")
+ def test_wifi_connection_5G_softap_2G_with_location_scan_on(self):
+ """Tests connection to 5G network followed by bringing up SoftAp on 2G
+ with location scans turned on.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.turn_location_on_and_scan_toggle_on()
+ self.connect_to_wifi_network_and_start_softap(
+ self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="4aa56c11-e5bc-480b-bd61-4b4ee577a5da")
+ def test_softap_2G_wifi_connection_2G(self):
+ """Tests bringing up SoftAp on 2G followed by connection to 2G network.
+ """
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.start_softap_and_connect_to_wifi_network(
+ self.wpapsk_2g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="5f954957-ad20-4de1-b20c-6c97d0463bdd")
+ def test_softap_5G_wifi_connection_5G(self):
+ """Tests bringing up SoftAp on 5G followed by connection to 5G network.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.start_softap_and_connect_to_wifi_network(
+ self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="1306aafc-a07e-4654-ba78-674f90cf748e")
+ def test_softap_5G_wifi_connection_5G_DFS(self):
+ """Tests bringing up SoftAp on 5G followed by connection to 5G DFS network.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ self.start_softap_and_connect_to_wifi_network(
+ self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="5e28e8b5-3faa-4cff-a782-13a796d7f572")
+ def test_softap_5G_wifi_connection_2G(self):
+ """Tests bringing up SoftAp on 5G followed by connection to 2G network.
+ """
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.start_softap_and_connect_to_wifi_network(
+ self.wpapsk_2g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="a2c62bc6-9ccd-4bc4-8a23-9a1b5d0b4b5c")
+ def test_softap_2G_wifi_connection_5G(self):
+ """Tests bringing up SoftAp on 2G followed by connection to 5G network.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.start_softap_and_connect_to_wifi_network(
+ self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="a2c62bc6-9ccd-4bc4-8a23-9a1b5d0b4b5c")
+ def test_softap_2G_wifi_connection_5G_DFS(self):
+ """Tests bringing up SoftAp on 2G followed by connection to 5G DFS network.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ self.start_softap_and_connect_to_wifi_network(
+ self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="aa23a3fc-31a1-4d5c-8cf5-2eb9fdf9e7ce")
+ def test_softap_5G_wifi_connection_2G_with_location_scan_on(self):
+ """Tests bringing up SoftAp on 5G followed by connection to 2G network
+ with location scans turned on.
+ """
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.turn_location_on_and_scan_toggle_on()
+ self.start_softap_and_connect_to_wifi_network(
+ self.wpapsk_2g, WIFI_CONFIG_APBAND_5G)
diff --git a/acts/tests/google/wifi/WifiStressTest.py b/acts/tests/google/wifi/WifiStressTest.py
new file mode 100755
index 0000000..bb3396b
--- /dev/null
+++ b/acts/tests/google/wifi/WifiStressTest.py
@@ -0,0 +1,331 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 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.
+
+import pprint
+import time
+
+import acts.base_test
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils
+
+from acts import asserts
+from acts import signals
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+WifiEnums = wutils.WifiEnums
+
+WAIT_FOR_AUTO_CONNECT = 40
+WAIT_BEFORE_CONNECTION = 30
+
+TIMEOUT = 1
+PING_ADDR = 'www.google.com'
+
+class WifiStressTest(WifiBaseTest):
+ """WiFi Stress test class.
+
+ Test Bed Requirement:
+ * Two Android device
+ * Several Wi-Fi networks visible to the device, including an open Wi-Fi
+ network.
+ """
+
+ def __init__(self, controllers):
+ WifiBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ self.dut_client = self.android_devices[1]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = []
+ opt_param = [
+ "open_network", "reference_networks", "iperf_server_address",
+ "stress_count", "stress_hours"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start(ap_count=2)
+
+ asserts.assert_true(
+ len(self.reference_networks) > 0,
+ "Need at least one reference network with psk.")
+ self.wpa_2g = self.reference_networks[0]["2g"]
+ self.wpa_5g = self.reference_networks[0]["5g"]
+ self.open_2g = self.open_network[0]["2g"]
+ self.open_5g = self.open_network[0]["5g"]
+ self.networks = [self.wpa_2g, self.wpa_5g, self.open_2g, self.open_5g]
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server = self.iperf_servers[0]
+ if hasattr(self, 'iperf_server'):
+ self.iperf_server.start()
+
+ def setup_test(self):
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def on_fail(self, test_name, begin_time):
+ self.dut.take_bug_report(test_name, begin_time)
+ self.dut.cat_adb_log(test_name, begin_time)
+
+ def teardown_class(self):
+ wutils.reset_wifi(self.dut)
+ if hasattr(self, 'iperf_server'):
+ self.iperf_server.stop()
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """Helper Functions"""
+
+ def scan_and_connect_by_ssid(self, network):
+ """Scan for network and connect using network information.
+
+ Args:
+ network: A dictionary representing the network to connect to.
+
+ """
+ ssid = network[WifiEnums.SSID_KEY]
+ wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut,
+ ssid)
+ wutils.wifi_connect(self.dut, network, num_of_tries=3)
+
+ def scan_and_connect_by_id(self, network, net_id):
+ """Scan for network and connect using network id.
+
+ Args:
+ net_id: Integer specifying the network id of the network.
+
+ """
+ ssid = network[WifiEnums.SSID_KEY]
+ wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut,
+ ssid)
+ wutils.wifi_connect_by_id(self.dut, net_id)
+
+ def run_ping(self, sec):
+ """Run ping for given number of seconds.
+
+ Args:
+ sec: Time in seconds to run teh ping traffic.
+
+ """
+ self.log.info("Running ping for %d seconds" % sec)
+ result = self.dut.adb.shell("ping -w %d %s" %(sec, PING_ADDR),
+ timeout=sec+1)
+ self.log.debug("Ping Result = %s" % result)
+ if "100% packet loss" in result:
+ raise signals.TestFailure("100% packet loss during ping")
+
+ """Tests"""
+
+ @test_tracker_info(uuid="cd0016c6-58cf-4361-b551-821c0b8d2554")
+ def test_stress_toggle_wifi_state(self):
+ """Toggle WiFi state ON and OFF for N times."""
+ for count in range(self.stress_count):
+ """Test toggling wifi"""
+ try:
+ self.log.debug("Going from on to off.")
+ wutils.wifi_toggle_state(self.dut, False)
+ self.log.debug("Going from off to on.")
+ startTime = time.time()
+ wutils.wifi_toggle_state(self.dut, True)
+ startup_time = time.time() - startTime
+ self.log.debug("WiFi was enabled on the device in %s s." %
+ startup_time)
+ except:
+ signals.TestFailure(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %count})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %(count+1)})
+
+ @test_tracker_info(uuid="49e3916a-9580-4bf7-a60d-a0f2545dcdde")
+ def test_stress_connect_traffic_disconnect_5g(self):
+ """Test to connect and disconnect from a network for N times.
+
+ Steps:
+ 1. Scan and connect to a network.
+ 2. Run IPerf to upload data for few seconds.
+ 3. Disconnect.
+ 4. Repeat 1-3.
+
+ """
+ for count in range(self.stress_count):
+ try:
+ net_id = self.dut.droid.wifiAddNetwork(self.wpa_5g)
+ asserts.assert_true(net_id != -1, "Add network %r failed" % self.wpa_5g)
+ self.scan_and_connect_by_id(self.wpa_5g, net_id)
+ # Start IPerf traffic from phone to server.
+ # Upload data for 10s.
+ args = "-p {} -t {}".format(self.iperf_server.port, 10)
+ self.log.info("Running iperf client {}".format(args))
+ result, data = self.dut.run_iperf_client(self.iperf_server_address, args)
+ if not result:
+ self.log.debug("Error occurred in iPerf traffic.")
+ self.run_ping(10)
+ wutils.wifi_forget_network(self.dut,self.wpa_5g[WifiEnums.SSID_KEY])
+ time.sleep(WAIT_BEFORE_CONNECTION)
+ except:
+ raise signals.TestFailure("Network connect-disconnect failed."
+ "Look at logs", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %count})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %(count+1)})
+
+ @test_tracker_info(uuid="e9827dff-0755-43ec-8b50-1f9756958460")
+ def test_stress_connect_long_traffic_5g(self):
+ """Test to connect to network and hold connection for few hours.
+
+ Steps:
+ 1. Scan and connect to a network.
+ 2. Run IPerf to download data for few hours.
+ 3. Verify no WiFi disconnects/data interruption.
+
+ """
+ try:
+ self.scan_and_connect_by_ssid(self.wpa_5g)
+ # Start IPerf traffic from server to phone.
+ # Download data for 5 hours.
+ sec = self.stress_hours * 60 * 60
+ args = "-p {} -t {} -R".format(self.iperf_server.port, sec)
+ self.log.info("Running iperf client {}".format(args))
+ result, data = self.dut.run_iperf_client(self.iperf_server_address,
+ args, timeout=sec+1)
+ if not result:
+ self.log.debug("Error occurred in iPerf traffic.")
+ self.run_ping(sec)
+ except:
+ raise signals.TestFailure("Network long-connect failed."
+ "Look at logs", extras={"Total Hours":"%d" %self.stress_hours,
+ "Seconds Run":"UNKNOWN"})
+ raise signals.TestPass(details="", extras={"Total Hours":"%d" %
+ self.stress_hours, "Seconds":"%d" %sec})
+
+ @test_tracker_info(uuid="d367c83e-5b00-4028-9ed8-f7b875997d13")
+ def test_stress_wifi_failover(self):
+ """This test does aggressive failover to several networks in list.
+
+ Steps:
+ 1. Add and enable few networks.
+ 2. Let device auto-connect.
+ 3. Remove the connected network.
+ 4. Repeat 2-3.
+ 5. Device should connect to a network until all networks are
+ exhausted.
+
+ """
+ for count in range(int(self.stress_count/4)):
+ ssids = list()
+ for network in self.networks:
+ ssids.append(network[WifiEnums.SSID_KEY])
+ ret = self.dut.droid.wifiAddNetwork(network)
+ asserts.assert_true(ret != -1, "Add network %r failed" % network)
+ self.dut.droid.wifiEnableNetwork(ret, 0)
+ time.sleep(WAIT_FOR_AUTO_CONNECT)
+ cur_network = self.dut.droid.wifiGetConnectionInfo()
+ cur_ssid = cur_network[WifiEnums.SSID_KEY]
+ self.log.info("Cur_ssid = %s" % cur_ssid)
+ for i in range(0,len(self.networks)):
+ self.log.debug("Forget network %s" % cur_ssid)
+ wutils.wifi_forget_network(self.dut, cur_ssid)
+ time.sleep(WAIT_FOR_AUTO_CONNECT)
+ cur_network = self.dut.droid.wifiGetConnectionInfo()
+ cur_ssid = cur_network[WifiEnums.SSID_KEY]
+ self.log.info("Cur_ssid = %s" % cur_ssid)
+ if i == len(self.networks) - 1:
+ break
+ if cur_ssid not in ssids:
+ raise signals.TestFailure("Device did not failover to the "
+ "expected network. SSID = %s" % cur_ssid)
+ network_config = self.dut.droid.wifiGetConfiguredNetworks()
+ self.log.info("Network Config = %s" % network_config)
+ if len(network_config):
+ raise signals.TestFailure("All the network configurations were not "
+ "removed. Configured networks = %s" % network_config,
+ extras={"Iterations":"%d" % self.stress_count,
+ "Pass":"%d" %(count*4)})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %((count+1)*4)})
+
+ @test_tracker_info(uuid="2c19e8d1-ac16-4d7e-b309-795144e6b956")
+ def test_stress_softAP_startup_and_stop_5g(self):
+ """Test to bring up softAP and down for N times.
+
+ Steps:
+ 1. Bring up softAP on 5G.
+ 2. Check for softAP on teh client device.
+ 3. Turn ON WiFi.
+ 4. Verify softAP is turned down and WiFi is up.
+
+ """
+ # Set country code explicitly to "US".
+ self.dut.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+ self.dut_client.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+ ap_ssid = "softap_" + utils.rand_ascii_str(8)
+ ap_password = utils.rand_ascii_str(8)
+ self.dut.log.info("softap setup: %s %s", ap_ssid, ap_password)
+ config = {wutils.WifiEnums.SSID_KEY: ap_ssid}
+ config[wutils.WifiEnums.PWD_KEY] = ap_password
+ for count in range(self.stress_count):
+ initial_wifi_state = self.dut.droid.wifiCheckState()
+ wutils.start_wifi_tethering(self.dut,
+ ap_ssid,
+ ap_password,
+ WifiEnums.WIFI_CONFIG_APBAND_5G)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut_client, ap_ssid)
+ wutils.stop_wifi_tethering(self.dut)
+ asserts.assert_false(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp failed to shutdown!")
+ # Give some time for WiFi to come back to previous state.
+ time.sleep(2)
+ cur_wifi_state = self.dut.droid.wifiCheckState()
+ if initial_wifi_state != cur_wifi_state:
+ raise signals.TestFailure("Wifi state was %d before softAP and %d now!" %
+ (initial_wifi_state, cur_wifi_state),
+ extras={"Iterations":"%d" % self.stress_count,
+ "Pass":"%d" %count})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %(count+1)})
+
+ @test_tracker_info(uuid="eb22e26b-95d1-4580-8c76-85dfe6a42a0f")
+ def test_stress_wifi_roaming(self):
+ AP1_network = self.reference_networks[0]["5g"]
+ AP2_network = self.reference_networks[1]["5g"]
+ wutils.set_attns(self.attenuators, "AP1_on_AP2_off")
+ self.scan_and_connect_by_ssid(AP1_network)
+ # Reduce iteration to half because each iteration does two roams.
+ for count in range(int(self.stress_count/2)):
+ self.log.info("Roaming iteration %d, from %s to %s", count,
+ AP1_network, AP2_network)
+ try:
+ wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
+ "AP1_off_AP2_on", AP2_network)
+ self.log.info("Roaming iteration %d, from %s to %s", count,
+ AP2_network, AP1_network)
+ wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
+ "AP1_on_AP2_off", AP1_network)
+ except:
+ raise signals.TestFailure("Roaming failed. Look at logs",
+ extras={"Iterations":"%d" %self.stress_count, "Pass":"%d" %
+ (count*2)})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %((count+1)*2)})
+
diff --git a/acts/tests/google/wifi/WifiTeleCoexTest.py b/acts/tests/google/wifi/WifiTeleCoexTest.py
index b21ce20..3d30640 100644
--- a/acts/tests/google/wifi/WifiTeleCoexTest.py
+++ b/acts/tests/google/wifi/WifiTeleCoexTest.py
@@ -195,6 +195,7 @@
self.connect_to_wifi(self.dut, self.network)
wifi_utils.toggle_wifi_off_and_on(self.dut)
self.validate_cellular_and_wifi()
+ return True
@test_tracker_info(uuid="caf22447-6354-4a2e-99e5-0ff235fc8f20")
@@ -216,6 +217,7 @@
self.connect_to_wifi(self.dut, self.network)
wifi_utils.toggle_airplane_mode_on_and_off(self.dut)
self.validate_cellular_and_wifi()
+ return True
@test_tracker_info(uuid="dd888b35-f820-409a-89af-4b0f6551e4d6")
@@ -239,6 +241,7 @@
self.connect_to_wifi(self.dut, self.network)
self.stress_toggle_airplane_and_wifi(1)
self.validate_cellular_and_wifi()
+ return True
@test_tracker_info(uuid="15db5b7e-827e-4bc8-8e77-7fcce343a323")
@@ -260,6 +263,7 @@
self.connect_to_wifi(self.dut, self.network)
self.stress_toggle_wifi(self.stress_count)
self.validate_cellular_and_wifi()
+ return True
@test_tracker_info(uuid="80a2f1bf-5e41-453a-9b8e-be3b41d4d313")
@@ -281,6 +285,7 @@
self.connect_to_wifi(self.dut, self.network)
self.stress_toggle_airplane(self.stress_count)
self.validate_cellular_and_wifi()
+ return True
@test_tracker_info(uuid="b88ad3e7-6462-4280-ad57-22d0ac91fdd8")
@@ -305,3 +310,4 @@
self.connect_to_wifi(self.dut, self.network)
self.stress_toggle_airplane_and_wifi(self.stress_count)
self.validate_cellular_and_wifi()
+ return True
diff --git a/acts/tests/google/wifi/WifiTetheringTest.py b/acts/tests/google/wifi/WifiTetheringTest.py
index d471290..080f9e4 100644
--- a/acts/tests/google/wifi/WifiTetheringTest.py
+++ b/acts/tests/google/wifi/WifiTetheringTest.py
@@ -23,15 +23,14 @@
from acts import test_runner
from acts.controllers import adb
from acts.test_decorators import test_tracker_info
-from acts.test_utils.tel import tel_data_utils
from acts.test_utils.tel import tel_defines
-from acts.test_utils.tel.tel_data_utils import toggle_airplane_mode
from acts.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
from acts.test_utils.tel.tel_test_utils import get_operator_name
-from acts.test_utils.tel.tel_test_utils import http_file_download_by_chrome
from acts.test_utils.tel.tel_test_utils import verify_http_connection
from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts.test_utils.net import socket_test_utils as sutils
+from acts.test_utils.net import arduino_test_utils as dutils
from acts.test_utils.wifi import wifi_test_utils as wutils
WAIT_TIME = 2
@@ -42,15 +41,11 @@
def setup_class(self):
""" Setup devices for tethering and unpack params """
- self.convert_byte_to_mb = 1024.0 * 1024.0
- self.new_ssid = "wifi_tethering_test2"
- self.data_usage_error = 1
-
self.hotspot_device = self.android_devices[0]
self.tethered_devices = self.android_devices[1:]
- req_params = ("network", "url", "download_file", "file_size")
+ req_params = ("network", "url", "open_network")
self.unpack_userparams(req_params)
- self.file_size = int(self.file_size)
+ self.new_ssid = "wifi_tethering_test2"
wutils.wifi_toggle_state(self.hotspot_device, False)
self.hotspot_device.droid.telephonyToggleDataConnection(True)
@@ -65,24 +60,6 @@
ad.droid.telephonyToggleDataConnection(False)
wutils.wifi_test_device_init(ad)
- # Set chrome browser start with no-first-run verification
- # Give permission to read from and write to storage
- commands = ["pm grant com.android.chrome "
- "android.permission.READ_EXTERNAL_STORAGE",
- "pm grant com.android.chrome "
- "android.permission.WRITE_EXTERNAL_STORAGE",
- "rm /data/local/chrome-command-line",
- "am set-debug-app --persistent com.android.chrome",
- 'echo "chrome --no-default-browser-check --no-first-run '
- '--disable-fre" > /data/local/tmp/chrome-command-line']
- for cmd in commands:
- for dut in self.tethered_devices:
- try:
- dut.adb.shell(cmd)
- except adb.AdbError:
- self.log.warn("adb command %s failed on %s"
- % (cmd, dut.serial))
-
def teardown_class(self):
""" Reset devices """
wutils.wifi_toggle_state(self.hotspot_device, True)
@@ -187,6 +164,56 @@
wutils.wifi_connect(dut, self.network)
device_connected[dut_id] = not device_connected[dut_id]
+ def _connect_disconnect_android_device(self, dut_id, wifi_state):
+ """ Connect or disconnect wifi on android device depending on the
+ current wifi state
+
+ Args:
+ 1. dut_id: tethered device to change the wifi state
+ 2. wifi_state: current wifi state
+ """
+ ad = self.tethered_devices[dut_id]
+ if wifi_state:
+ self.log.info("Disconnecting wifi on android device")
+ wutils.wifi_forget_network(ad, self.network["SSID"])
+ else:
+ self.log.info("Connecting to wifi on android device")
+ wutils.wifi_connect(ad, self.network)
+
+ def _connect_disconnect_wifi_dongle(self, dut_id, wifi_state):
+ """ Connect or disconnect wifi on wifi dongle depending on the
+ current wifi state
+
+ Args:
+ 1. dut_id: wifi dongle to change the wifi state
+ 2. wifi_state: current wifi state
+ """
+ wd = self.arduino_wifi_dongles[dut_id]
+ if wifi_state:
+ self.log.info("Disconnecting wifi on dongle")
+ dutils.disconnect_wifi(wd)
+ else:
+ self.log.info("Connecting to wifi on dongle")
+ dutils.connect_wifi(wd, self.network)
+
+ def _connect_disconnect_tethered_devices(self):
+ """ Connect disconnect tethered devices to wifi hotspot """
+ num_android_devices = len(self.tethered_devices)
+ num_wifi_dongles = 0
+ if self.arduino_wifi_dongles:
+ num_wifi_dongles = len(self.arduino_wifi_dongles)
+ total_devices = num_android_devices + num_wifi_dongles
+ device_connected = [False] * total_devices
+ for _ in range(50):
+ dut_id = random.randint(0, total_devices-1)
+ wifi_state = device_connected[dut_id]
+ if dut_id < num_android_devices:
+ self._connect_disconnect_android_device(dut_id, wifi_state)
+ else:
+ self._connect_disconnect_wifi_dongle(dut_id-num_android_devices,
+ wifi_state)
+ device_connected[dut_id] = not device_connected[dut_id]
+
def _verify_ping(self, dut, ip, isIPv6=False):
""" Verify ping works from the dut to IP/hostname
@@ -216,25 +243,24 @@
return dut.droid.connectivityGetIPv4Addresses(iface_name) + \
dut.droid.connectivityGetIPv6Addresses(iface_name)
- def _test_traffic_between_two_tethered_devices(self, dut1, dut2):
+ def _test_traffic_between_two_tethered_devices(self, ad, wd):
""" Verify pinging interfaces of one DUT from another
Args:
- 1. dut1 - tethered device 1
- 2. dut2 - tethered device 2
+ 1. ad - android device
+ 2. wd - wifi dongle
"""
- wutils.wifi_connect(dut1, self.network)
- wutils.wifi_connect(dut2, self.network)
+ wutils.wifi_connect(ad, self.network)
+ dutils.connect_wifi(wd, self.network)
+ local_ip = ad.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ remote_ip = wd.ip_address()
+ port = 8888
- dut1_ipaddrs = dut1.droid.connectivityGetIPv4Addresses("wlan0") + \
- dut1.droid.connectivityGetIPv6Addresses("wlan0")
- dut2_ipaddrs = dut2.droid.connectivityGetIPv4Addresses("wlan0") + \
- dut2.droid.connectivityGetIPv6Addresses("wlan0")
-
- for ip in dut1_ipaddrs:
- asserts.assert_true(self._verify_ping(dut2, ip), "%s " % ip)
- for ip in dut2_ipaddrs:
- asserts.assert_true(self._verify_ping(dut1, ip), "%s " % ip)
+ time.sleep(6) # wait until UDP packets method is invoked
+ socket = sutils.open_datagram_socket(ad, local_ip, port)
+ sutils.send_recv_data_datagram_sockets(
+ ad, ad, socket, socket, remote_ip, port)
+ sutils.close_datagram_socket(ad, socket)
def _ping_hotspot_interfaces_from_tethered_device(self, dut):
""" Ping hotspot interfaces from tethered device
@@ -262,6 +288,35 @@
return return_result
+ def _save_wifi_softap_configuration(self, ad, config):
+ """ Save soft AP configuration
+
+ Args:
+ 1. dut - device to save configuration on
+ 2. config - soft ap configuration
+ """
+ asserts.assert_true(ad.droid.wifiSetWifiApConfiguration(config),
+ "Failed to set WifiAp Configuration")
+ wifi_ap = ad.droid.wifiGetApConfiguration()
+ asserts.assert_true(wifi_ap[wutils.WifiEnums.SSID_KEY] == config[wutils.WifiEnums.SSID_KEY],
+ "Configured wifi hotspot SSID does not match with the expected SSID")
+
+ def _turn_on_wifi_hotspot(self, ad):
+ """ Turn on wifi hotspot with a config that is already saved
+
+ Args:
+ 1. dut - device to turn wifi hotspot on
+ """
+ ad.droid.wifiStartTrackingTetherStateChange()
+ ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
+ try:
+ ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
+ ad.ed.wait_for_event("TetherStateChanged",
+ lambda x: x["data"]["ACTIVE_TETHER"], 30)
+ except:
+ asserts.fail("Didn't receive wifi tethering starting confirmation")
+ ad.droid.wifiStopTrackingTetherStateChange()
+
""" Test Cases """
@test_tracker_info(uuid="36d03295-bea3-446e-8342-b9f8f1962a32")
@@ -327,7 +382,7 @@
wutils.stop_wifi_tethering(self.hotspot_device)
@test_tracker_info(uuid="110b61d1-8af2-4589-8413-11beac7a3025")
- def wifi_tethering_2ghz_traffic_between_2tethered_devices(self):
+ def test_wifi_tethering_2ghz_traffic_between_2tethered_devices(self):
""" Steps:
1. Start wifi hotspot with 2G band
@@ -337,7 +392,7 @@
wutils.toggle_wifi_off_and_on(self.hotspot_device)
self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
self._test_traffic_between_two_tethered_devices(self.tethered_devices[0],
- self.tethered_devices[1])
+ self.arduino_wifi_dongles[0])
wutils.stop_wifi_tethering(self.hotspot_device)
@test_tracker_info(uuid="953f6e2e-27bd-4b73-85a6-d2eaa4e755d5")
@@ -351,7 +406,7 @@
wutils.toggle_wifi_off_and_on(self.hotspot_device)
self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
self._test_traffic_between_two_tethered_devices(self.tethered_devices[0],
- self.tethered_devices[1])
+ self.arduino_wifi_dongles[0])
wutils.stop_wifi_tethering(self.hotspot_device)
@test_tracker_info(uuid="d7d5aa51-682d-4882-a334-61966d93b68c")
@@ -364,7 +419,7 @@
"""
wutils.toggle_wifi_off_and_on(self.hotspot_device)
self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
- self._connect_disconnect_devices()
+ self._connect_disconnect_tethered_devices()
wutils.stop_wifi_tethering(self.hotspot_device)
@test_tracker_info(uuid="34abd6c9-c7f1-4d89-aa2b-a66aeabed9aa")
@@ -414,106 +469,6 @@
wutils.stop_wifi_tethering(self.hotspot_device)
return result
- @test_tracker_info(uuid="d4e18031-0af0-4b29-a574-8707cd4029b7")
- def test_wifi_tethering_verify_received_bytes(self):
- """ Steps:
-
- 1. Start wifi hotspot and connect tethered device to it
- 2. Get the data usage on hotspot device
- 3. Download data on tethered device
- 4. Get the new data usage on hotspot device
- 5. Verify that hotspot device's data usage
- increased by downloaded file size
- """
- wutils.toggle_wifi_off_and_on(self.hotspot_device)
- dut = self.hotspot_device
- self._start_wifi_tethering()
- wutils.wifi_connect(self.tethered_devices[0], self.network)
- subscriber_id = dut.droid.telephonyGetSubscriberId()
-
- # get data usage limit
- end_time = int(time.time() * 1000)
- bytes_before_download = dut.droid.connectivityGetRxBytesForDevice(
- subscriber_id, 0, end_time)
- self.log.info("Data usage before download: %s MB" %
- (bytes_before_download/self.convert_byte_to_mb))
-
- # download file
- self.log.info("Download file of size %sMB" % self.file_size)
- http_file_download_by_chrome(self.tethered_devices[0],
- self.download_file,
- self.file_size)
-
- # get data usage limit after download
- end_time = int(time.time() * 1000)
- bytes_after_download = dut.droid.connectivityGetRxBytesForDevice(
- subscriber_id, 0, end_time)
- self.log.info("Data usage after download: %s MB" %
- (bytes_after_download/self.convert_byte_to_mb))
-
- bytes_diff = bytes_after_download - bytes_before_download
- wutils.stop_wifi_tethering(self.hotspot_device)
-
- # verify data usage update is correct
- bytes_used = bytes_diff/self.convert_byte_to_mb
- self.log.info("Data usage on the device increased by %s" % bytes_used)
- return bytes_used > self.file_size \
- and bytes_used < self.file_size + self.data_usage_error
-
- @test_tracker_info(uuid="07a00c96-4770-44a1-a9db-b3d02d6a12b6")
- def test_wifi_tethering_data_usage_limit(self):
- """ Steps:
-
- 1. Set the data usage limit to current data usage + 10MB
- 2. Start wifi tethering and connect a dut to the SSID
- 3. Download 20MB data on tethered device
- a. file download should stop
- b. tethered device will lose internet connectivity
- c. data usage limit reached message should be displayed
- on the hotspot device
- 4. Verify data usage limit
- """
- wutils.toggle_wifi_off_and_on(self.hotspot_device)
- dut = self.hotspot_device
- data_usage_inc = 10 * self.convert_byte_to_mb
- subscriber_id = dut.droid.telephonyGetSubscriberId()
-
- self._start_wifi_tethering()
- wutils.wifi_connect(self.tethered_devices[0], self.network)
-
- # get current data usage
- end_time = int(time.time() * 1000)
- old_data_usage = dut.droid.connectivityQuerySummaryForDevice(
- subscriber_id, 0, end_time)
-
- # set data usage limit to current usage limit + 10MB
- dut.droid.connectivitySetDataUsageLimit(
- subscriber_id, str(int(old_data_usage + data_usage_inc)))
-
- # download file - size 20MB
- http_file_download_by_chrome(self.tethered_devices[0],
- self.download_file,
- self.file_size,
- timeout=120)
- end_time = int(time.time() * 1000)
- new_data_usage = dut.droid.connectivityQuerySummaryForDevice(
- subscriber_id, 0, end_time)
-
- # test network connectivity on tethered device
- asserts.assert_true(
- not wutils.validate_connection(self.tethered_devices[0]),
- "Tethered device has internet connectivity after data usage"
- "limit is reached on hotspot device")
- dut.droid.connectivityFactoryResetNetworkPolicies(subscriber_id)
- wutils.stop_wifi_tethering(self.hotspot_device)
-
- old_data_usage = (old_data_usage+data_usage_inc)/self.convert_byte_to_mb
- new_data_usage = new_data_usage/self.convert_byte_to_mb
- self.log.info("Expected data usage: %s MB" % old_data_usage)
- self.log.info("Actual data usage: %s MB" % new_data_usage)
-
- return (new_data_usage-old_data_usage) < self.data_usage_error
-
@test_tracker_info(uuid="2bc344cb-0277-4f06-b6cc-65b3972086ed")
def test_change_wifi_hotspot_ssid_when_hotspot_enabled(self):
""" Steps:
@@ -541,25 +496,11 @@
config = {wutils.WifiEnums.SSID_KEY: self.new_ssid}
config[wutils.WifiEnums.PWD_KEY] = self.network[wutils.WifiEnums.PWD_KEY]
config[wutils.WifiEnums.APBAND_KEY] = WIFI_CONFIG_APBAND_2G
- asserts.assert_true(
- dut.droid.wifiSetWifiApConfiguration(config),
- "Failed to update WifiAp Configuration")
- wifi_ap = dut.droid.wifiGetApConfiguration()
- asserts.assert_true(
- wifi_ap[wutils.WifiEnums.SSID_KEY] == self.new_ssid,
- "Configured wifi hotspot SSID does not match with the expected SSID")
+ self._save_wifi_softap_configuration(dut, config)
# start wifi tethering with new wifi ap configuration
wutils.stop_wifi_tethering(dut)
- dut.droid.wifiStartTrackingTetherStateChange()
- dut.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
- try:
- dut.ed.pop_event("ConnectivityManagerOnTetheringStarted")
- dut.ed.wait_for_event("TetherStateChanged",
- lambda x: x["data"]["ACTIVE_TETHER"], 30)
- except:
- asserts.fail("Didn't receive wifi tethering starting confirmation")
- dut.droid.wifiStopTrackingTetherStateChange()
+ self._turn_on_wifi_hotspot(dut)
# verify dut can connect to new wifi ap configuration
new_network = {wutils.WifiEnums.SSID_KEY: self.new_ssid,
@@ -567,3 +508,81 @@
self.network[wutils.WifiEnums.PWD_KEY]}
wutils.wifi_connect(self.tethered_devices[0], new_network)
wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="4cf7ab26-ca2d-46f6-9d3f-a935c7e04c97")
+ def test_wifi_tethering_open_network_2g(self):
+ """ Steps:
+
+ 1. Start wifi tethering with open network 2G band
+ (Not allowed manually. b/72412729)
+ 2. Connect tethered device to the SSID
+ 3. Verify internet connectivity
+ """
+ wutils.start_wifi_tethering(
+ self.hotspot_device, self.open_network[wutils.WifiEnums.SSID_KEY],
+ None, WIFI_CONFIG_APBAND_2G)
+ wutils.wifi_connect(self.tethered_devices[0], self.open_network)
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="f407049b-1324-40ea-a8d1-f90587933310")
+ def test_wifi_tethering_open_network_5g(self):
+ """ Steps:
+
+ 1. Start wifi tethering with open network 5G band
+ (Not allowed manually. b/72412729)
+ 2. Connect tethered device to the SSID
+ 3. Verify internet connectivity
+ """
+ wutils.start_wifi_tethering(
+ self.hotspot_device, self.open_network[wutils.WifiEnums.SSID_KEY],
+ None, WIFI_CONFIG_APBAND_5G)
+ wutils.wifi_connect(self.tethered_devices[0], self.open_network)
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="d964f2e6-0acb-417c-ada9-eb9fc5a470e4")
+ def test_wifi_tethering_open_network_2g_stress(self):
+ """ Steps:
+
+ 1. Save wifi hotspot configuration with open network 2G band
+ (Not allowed manually. b/72412729)
+ 2. Turn on wifi hotspot
+ 3. Connect tethered device and verify internet connectivity
+ 4. Turn off wifi hotspot
+ 5. Repeat steps 2 to 4
+ """
+ # save open network wifi ap configuration with 2G band
+ config = {wutils.WifiEnums.SSID_KEY:
+ self.open_network[wutils.WifiEnums.SSID_KEY]}
+ config[wutils.WifiEnums.APBAND_KEY] = WIFI_CONFIG_APBAND_2G
+ self._save_wifi_softap_configuration(self.hotspot_device, config)
+
+ # turn on/off wifi hotspot, connect device
+ for _ in range(9):
+ self._turn_on_wifi_hotspot(self.hotspot_device)
+ wutils.wifi_connect(self.tethered_devices[0], self.open_network)
+ wutils.stop_wifi_tethering(self.hotspot_device)
+ time.sleep(1) # wait for some time before turning on hotspot
+
+ @test_tracker_info(uuid="c7ef840c-4003-41fc-80e3-755f9057b542")
+ def test_wifi_tethering_open_network_5g_stress(self):
+ """ Steps:
+
+ 1. Save wifi hotspot configuration with open network 5G band
+ (Not allowed manually. b/72412729)
+ 2. Turn on wifi hotspot
+ 3. Connect tethered device and verify internet connectivity
+ 4. Turn off wifi hotspot
+ 5. Repeat steps 2 to 4
+ """
+ # save open network wifi ap configuration with 5G band
+ config = {wutils.WifiEnums.SSID_KEY:
+ self.open_network[wutils.WifiEnums.SSID_KEY]}
+ config[wutils.WifiEnums.APBAND_KEY] = WIFI_CONFIG_APBAND_5G
+ self._save_wifi_softap_configuration(self.hotspot_device, config)
+
+ # turn on/off wifi hotspot, connect device
+ for _ in range(9):
+ self._turn_on_wifi_hotspot(self.hotspot_device)
+ wutils.wifi_connect(self.tethered_devices[0], self.open_network)
+ wutils.stop_wifi_tethering(self.hotspot_device)
+ time.sleep(1) # wait for some time before turning on hotspot
diff --git a/acts/tests/google/wifi/WifiThroughputStabilityTest.py b/acts/tests/google/wifi/WifiThroughputStabilityTest.py
index 3da256c..326e8e8 100644
--- a/acts/tests/google/wifi/WifiThroughputStabilityTest.py
+++ b/acts/tests/google/wifi/WifiThroughputStabilityTest.py
@@ -29,6 +29,8 @@
from acts.test_utils.wifi import wifi_test_utils as wutils
TEST_TIMEOUT = 10
+SHORT_SLEEP = 1
+MED_SLEEP = 6
class WifiThroughputStabilityTest(base_test.BaseTestClass):
@@ -93,9 +95,13 @@
def setup_class(self):
self.dut = self.android_devices[0]
- req_params = ["test_params", "main_network"]
- opt_params = ["RetailAccessPoints"]
+ req_params = [
+ "throughput_stability_test_params", "testbed_params",
+ "main_network"
+ ]
+ opt_params = ["RetailAccessPoints", "golden_files_list"]
self.unpack_userparams(req_params, opt_params)
+ self.test_params = self.throughput_stability_test_params
self.num_atten = self.attenuators[0].instrument.num_atten
self.iperf_server = self.iperf_servers[0]
self.access_points = retail_ap.create(self.RetailAccessPoints)
@@ -106,8 +112,9 @@
self.access_point.ap_settings))
if not hasattr(self, "golden_files_list"):
self.golden_files_list = [
- os.path.join(self.test_params["golden_results_path"], file)
- for file in os.listdir(self.test_params["golden_results_path"])
+ os.path.join(self.testbed_params["golden_results_path"],
+ file) for file in os.listdir(
+ self.testbed_params["golden_results_path"])
]
def teardown_test(self):
@@ -169,9 +176,8 @@
test_result_dict["ap_settings"] = test_result["ap_settings"].copy()
test_result_dict["attenuation"] = self.atten_level
instantaneous_rates_Mbps = [
- rate * 8
- for rate in test_result["iperf_result"].instantaneous_rates[
- self.test_params["iperf_ignored_interval"]:-1]
+ rate * 8 * (1.024**2) for rate in test_result["iperf_result"]
+ .instantaneous_rates[self.test_params["iperf_ignored_interval"]:-1]
]
test_result_dict["iperf_results"] = {
"instantaneous_rates":
@@ -225,6 +231,14 @@
test_result = {}
# Configure AP
band = self.access_point.band_lookup_by_channel(channel)
+ if "2G" in band:
+ frequency = wutils.WifiEnums.channel_2G_to_freq[channel]
+ else:
+ frequency = wutils.WifiEnums.channel_5G_to_freq[channel]
+ if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
+ self.access_point.set_region(self.testbed_params["DFS_region"])
+ else:
+ self.access_point.set_region(self.testbed_params["default_region"])
self.access_point.set_channel(band, channel)
self.access_point.set_bandwidth(band, mode)
self.log.info("Access Point Configuration: {}".format(
@@ -236,10 +250,11 @@
for i in range(self.num_atten)
]
# Connect DUT to Network
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.reset_wifi(self.dut)
self.main_network[band]["channel"] = channel
- wutils.toggle_wifi_off_and_on(self.dut)
wutils.wifi_connect(self.dut, self.main_network[band], num_of_tries=5)
- time.sleep(5)
+ time.sleep(MED_SLEEP)
# Run test and log result
# Start iperf session
self.log.info("Starting iperf test.")
@@ -247,14 +262,14 @@
try:
client_output = ""
client_status, client_output = self.dut.run_iperf_client(
- self.test_params["iperf_server_address"],
+ self.testbed_params["iperf_server_address"],
self.iperf_args,
timeout=self.test_params["iperf_duration"] + TEST_TIMEOUT)
except:
self.log.warning("TimeoutError: Iperf measurement timed out.")
- client_output_path = os.path.join(
- self.iperf_server.log_path,
- "iperf_client_output_{}".format(self.current_test_name))
+ client_output_path = os.path.join(self.iperf_server.log_path,
+ "iperf_client_output_{}".format(
+ self.current_test_name))
with open(client_output_path, 'w') as out_file:
out_file.write("\n".join(client_output))
self.iperf_server.stop()
diff --git a/acts/tests/google/wifi/aware/functional/AttachTest.py b/acts/tests/google/wifi/aware/functional/AttachTest.py
index 598cca6..37f07e0 100644
--- a/acts/tests/google/wifi/aware/functional/AttachTest.py
+++ b/acts/tests/google/wifi/aware/functional/AttachTest.py
@@ -16,12 +16,13 @@
import time
+from acts import asserts
+from acts import utils
from acts.test_decorators import test_tracker_info
from acts.test_utils.wifi import wifi_test_utils as wutils
from acts.test_utils.wifi.aware import aware_const as aconsts
from acts.test_utils.wifi.aware import aware_test_utils as autils
from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
-from acts.utils import force_airplane_mode
class AttachTest(AwareBaseTest):
@@ -99,8 +100,8 @@
"""Function test case / Attach test cases / attempt to attach with wifi off
Validates that if trying to attach with Wi-Fi disabled will receive the
- expected failure callback. As a side-effect also validates that the broadcast
- for Aware unavailable is received.
+ expected failure callback. As a side-effect also validates that the
+ broadcast for Aware unavailable is received.
"""
dut = self.android_devices[0]
wutils.wifi_toggle_state(dut, False)
@@ -108,6 +109,39 @@
dut.droid.wifiAwareAttach()
autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
+ @test_tracker_info(uuid="7dcc4530-c936-4447-9d22-a7c5b315e2ce")
+ def test_attach_with_doze(self):
+ """Function test case / Attach test cases / attempt to attach with doze on
+
+ Validates that if trying to attach with device in doze mode will receive the
+ expected failure callback. As a side-effect also validates that the
+ broadcast for Aware unavailable is received.
+ """
+ dut = self.android_devices[0]
+ asserts.assert_true(utils.enable_doze(dut), "Can't enable doze")
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+ dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
+ asserts.assert_true(utils.disable_doze(dut), "Can't disable doze")
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+
+ @test_tracker_info(uuid="2574fd01-8974-4dd0-aeb8-a7194461140e")
+ def test_attach_with_location_off(self):
+ """Function test case / Attach test cases / attempt to attach with location
+ mode off.
+
+ Validates that if trying to attach with device location mode off will
+ receive the expected failure callback. As a side-effect also validates that
+ the broadcast for Aware unavailable is received.
+ """
+ dut = self.android_devices[0]
+ utils.set_location_service(dut, False)
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+ dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
+ utils.set_location_service(dut, True)
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+
@test_tracker_info(uuid="7ffde8e7-a010-4b77-97f5-959f263b5249")
def test_attach_apm_toggle_attach_again(self):
"""Validates that enabling Airplane mode while Aware is on resets it
@@ -120,12 +154,12 @@
autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
# enable airplane mode
- force_airplane_mode(dut, True)
+ utils.force_airplane_mode(dut, True)
autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
# wait a few seconds and disable airplane mode
time.sleep(10)
- force_airplane_mode(dut, False)
+ utils.force_airplane_mode(dut, False)
autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
# try enabling Aware again (attach)
diff --git a/acts/tests/google/wifi/aware/functional/DataPathTest.py b/acts/tests/google/wifi/aware/functional/DataPathTest.py
index 7e77c79..8afb2a4 100644
--- a/acts/tests/google/wifi/aware/functional/DataPathTest.py
+++ b/acts/tests/google/wifi/aware/functional/DataPathTest.py
@@ -19,6 +19,7 @@
from acts import asserts
from acts.test_decorators import test_tracker_info
from acts.test_utils.net import connectivity_const as cconsts
+from acts.test_utils.wifi import wifi_test_utils as wutils
from acts.test_utils.wifi.aware import aware_const as aconsts
from acts.test_utils.wifi.aware import aware_test_utils as autils
from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
@@ -78,13 +79,18 @@
network_req = {"TransportType": 5, "NetworkSpecifier": ns}
return dut.droid.connectivityRequestWifiAwareNetwork(network_req)
- def set_up_discovery(self, ptype, stype, get_peer_id):
+ def set_up_discovery(self, ptype, stype, get_peer_id, pub_on_both=False,
+ pub_on_both_same=True):
"""Set up discovery sessions and wait for service discovery.
Args:
ptype: Publish discovery type
stype: Subscribe discovery type
get_peer_id: Send a message across to get the peer's id
+ pub_on_both: If True then set up a publisher on both devices. The second
+ publisher isn't used (existing to test use-case).
+ pub_on_both_same: If True then the second publish uses an identical
+ service name, otherwise a different service name.
"""
p_dut = self.android_devices[0]
p_dut.pretty_name = "Publisher"
@@ -102,6 +108,15 @@
p_disc_id = p_dut.droid.wifiAwarePublish(p_id, self.create_config(ptype))
autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ # Optionally set up a publish session on the Subscriber device
+ if pub_on_both:
+ p2_config = self.create_config(ptype)
+ if not pub_on_both_same:
+ p2_config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = (
+ p2_config[aconsts.DISCOVERY_KEY_SERVICE_NAME] + "-XYZXYZ")
+ s_dut.droid.wifiAwarePublish(s_id, p2_config)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
# Subscriber: start subscribe and wait for confirmation
s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, self.create_config(stype))
autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
@@ -132,7 +147,10 @@
stype,
encr_type,
use_peer_id,
- passphrase_to_use=None):
+ passphrase_to_use=None,
+ pub_on_both=False,
+ pub_on_both_same=True,
+ expect_failure=False):
"""Runs the in-band data-path tests.
Args:
@@ -143,14 +161,23 @@
accept any request
passphrase_to_use: The passphrase to use if encr_type=ENCR_TYPE_PASSPHRASE
If None then use self.PASSPHRASE
+ pub_on_both: If True then set up a publisher on both devices. The second
+ publisher isn't used (existing to test use-case).
+ pub_on_both_same: If True then the second publish uses an identical
+ service name, otherwise a different service name.
+ expect_failure: If True then don't expect NDP formation, otherwise expect
+ NDP setup to succeed.
"""
(p_dut, s_dut, p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
- peer_id_on_pub) = self.set_up_discovery(ptype, stype, use_peer_id)
+ peer_id_on_pub) = self.set_up_discovery(ptype, stype, use_peer_id,
+ pub_on_both=pub_on_both,
+ pub_on_both_same=pub_on_both_same)
passphrase = None
pmk = None
if encr_type == self.ENCR_TYPE_PASSPHRASE:
- passphrase = self.PASSPHRASE if passphrase_to_use == None else passphrase_to_use
+ passphrase = (
+ self.PASSPHRASE if passphrase_to_use == None else passphrase_to_use)
elif encr_type == self.ENCR_TYPE_PMK:
pmk = self.PMK
@@ -166,56 +193,70 @@
s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id, peer_id_on_sub,
passphrase, pmk))
- # Publisher & Subscriber: wait for network formation
- p_net_event = autils.wait_for_event_with_keys(
- p_dut, cconsts.EVENT_NETWORK_CALLBACK,
- autils.EVENT_NDP_TIMEOUT,
- (cconsts.NETWORK_CB_KEY_EVENT,
- cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
- (cconsts.NETWORK_CB_KEY_ID, p_req_key))
- s_net_event = autils.wait_for_event_with_keys(
- s_dut, cconsts.EVENT_NETWORK_CALLBACK,
- autils.EVENT_NDP_TIMEOUT,
- (cconsts.NETWORK_CB_KEY_EVENT,
- cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
- (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+ if expect_failure:
+ # Publisher & Subscriber: fail on network formation
+ time.sleep(autils.EVENT_NDP_TIMEOUT)
+ autils.fail_on_event_with_keys(p_dut, cconsts.EVENT_NETWORK_CALLBACK, 0,
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ autils.fail_on_event_with_keys(s_dut, cconsts.EVENT_NETWORK_CALLBACK, 0,
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+ else:
+ # Publisher & Subscriber: wait for network formation
+ p_net_event = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
- p_aware_if = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
- s_aware_if = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
- self.log.info("Interface names: p=%s, s=%s", p_aware_if, s_aware_if)
+ p_aware_if = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ s_aware_if = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ self.log.info("Interface names: p=%s, s=%s", p_aware_if, s_aware_if)
- p_ipv6 = p_dut.droid.connectivityGetLinkLocalIpv6Address(p_aware_if).split(
- "%")[0]
- s_ipv6 = s_dut.droid.connectivityGetLinkLocalIpv6Address(s_aware_if).split(
- "%")[0]
- self.log.info("Interface addresses (IPv6): p=%s, s=%s", p_ipv6, s_ipv6)
+ p_ipv6 = \
+ p_dut.droid.connectivityGetLinkLocalIpv6Address(p_aware_if).split("%")[0]
+ s_ipv6 = \
+ s_dut.droid.connectivityGetLinkLocalIpv6Address(s_aware_if).split("%")[0]
+ self.log.info("Interface addresses (IPv6): p=%s, s=%s", p_ipv6, s_ipv6)
- # TODO: possibly send messages back and forth, prefer to use netcat/nc
+ # TODO: possibly send messages back and forth, prefer to use netcat/nc
- # terminate sessions and wait for ON_LOST callbacks
- p_dut.droid.wifiAwareDestroy(p_id)
- s_dut.droid.wifiAwareDestroy(s_id)
+ # terminate sessions and wait for ON_LOST callbacks
+ p_dut.droid.wifiAwareDestroy(p_id)
+ s_dut.droid.wifiAwareDestroy(s_id)
- autils.wait_for_event_with_keys(
- p_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
- (cconsts.NETWORK_CB_KEY_EVENT,
- cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, p_req_key))
- autils.wait_for_event_with_keys(
- s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
- (cconsts.NETWORK_CB_KEY_EVENT,
- cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+ autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, s_req_key))
# clean-up
p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
- def run_oob_data_path_test(self, encr_type, use_peer_id):
+ def run_oob_data_path_test(self, encr_type, use_peer_id,
+ setup_discovery_sessions=False, expect_failure=False):
"""Runs the out-of-band data-path tests.
Args:
encr_type: Encryption type, one of ENCR_TYPE_*
use_peer_id: On Responder: True to use peer ID, False to accept any
request
+ setup_discovery_sessions: If True also set up a (spurious) discovery
+ session (pub on both sides, sub on Responder side). Validates a corner
+ case.
+ expect_failure: If True then don't expect NDP formation, otherwise expect
+ NDP setup to succeed.
"""
init_dut = self.android_devices[0]
init_dut.pretty_name = "Initiator"
@@ -240,6 +281,18 @@
# to execute the data-path request)
time.sleep(self.WAIT_FOR_CLUSTER)
+ if setup_discovery_sessions:
+ init_dut.droid.wifiAwarePublish(init_id, self.create_config(
+ aconsts.PUBLISH_TYPE_UNSOLICITED))
+ autils.wait_for_event(init_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ resp_dut.droid.wifiAwarePublish(resp_id, self.create_config(
+ aconsts.PUBLISH_TYPE_UNSOLICITED))
+ autils.wait_for_event(resp_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ resp_dut.droid.wifiAwareSubscribe(resp_id, self.create_config(
+ aconsts.SUBSCRIBE_TYPE_PASSIVE))
+ autils.wait_for_event(resp_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+ autils.wait_for_event(resp_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
passphrase = None
pmk = None
if encr_type == self.ENCR_TYPE_PASSPHRASE:
@@ -260,47 +313,57 @@
init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, passphrase, pmk))
- # Initiator & Responder: wait for network formation
- init_net_event = autils.wait_for_event_with_keys(
- init_dut, cconsts.EVENT_NETWORK_CALLBACK,
- autils.EVENT_NDP_TIMEOUT,
- (cconsts.NETWORK_CB_KEY_EVENT,
- cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
- (cconsts.NETWORK_CB_KEY_ID, init_req_key))
- resp_net_event = autils.wait_for_event_with_keys(
- resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
- autils.EVENT_NDP_TIMEOUT,
- (cconsts.NETWORK_CB_KEY_EVENT,
- cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
- (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+ if expect_failure:
+ # Initiator & Responder: fail on network formation
+ time.sleep(autils.EVENT_NDP_TIMEOUT)
+ autils.fail_on_event_with_keys(resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ 0,
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+ autils.fail_on_event_with_keys(init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ 0,
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ else:
+ # Initiator & Responder: wait for network formation
+ init_net_event = autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ resp_net_event = autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
- init_aware_if = init_net_event["data"][
- cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
- resp_aware_if = resp_net_event["data"][
- cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
- self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if)
+ init_aware_if = init_net_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ resp_aware_if = resp_net_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if)
- init_ipv6 = init_dut.droid.connectivityGetLinkLocalIpv6Address(
- init_aware_if).split("%")[0]
- resp_ipv6 = resp_dut.droid.connectivityGetLinkLocalIpv6Address(
- resp_aware_if).split("%")[0]
- self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
- resp_ipv6)
+ init_ipv6 = init_dut.droid.connectivityGetLinkLocalIpv6Address(
+ init_aware_if).split("%")[0]
+ resp_ipv6 = resp_dut.droid.connectivityGetLinkLocalIpv6Address(
+ resp_aware_if).split("%")[0]
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
- # TODO: possibly send messages back and forth, prefer to use netcat/nc
+ # TODO: possibly send messages back and forth, prefer to use netcat/nc
- # terminate sessions and wait for ON_LOST callbacks
- init_dut.droid.wifiAwareDestroy(init_id)
- resp_dut.droid.wifiAwareDestroy(resp_id)
+ # terminate sessions and wait for ON_LOST callbacks
+ init_dut.droid.wifiAwareDestroy(init_id)
+ resp_dut.droid.wifiAwareDestroy(resp_id)
- autils.wait_for_event_with_keys(
- init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
- (cconsts.NETWORK_CB_KEY_EVENT,
- cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, init_req_key))
- autils.wait_for_event_with_keys(
- resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
- (cconsts.NETWORK_CB_KEY_EVENT,
- cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+ autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
# clean-up
resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
@@ -588,14 +651,104 @@
use_peer_id=False)
#######################################
+ # Positive In-Band (IB) with a publish session running on the subscriber
+ # tests key:
+ #
+ # names is: test_ib_extra_pub_<same|diff>_<pub_type>_<sub_type>
+ # _<encr_type>_<peer_spec>
+ # where:
+ #
+ # same|diff: Whether the extra publish session (on the subscriber) is the same
+ # or different from the primary session.
+ # pub_type: Type of publish discovery session: unsolicited or solicited.
+ # sub_type: Type of subscribe discovery session: passive or active.
+ # encr_type: Encryption type: open, passphrase
+ # peer_spec: Peer specification method: any or specific
+ #
+ # Note: In-Band means using Wi-Fi Aware for discovery and referring to the
+ # peer using the Aware-provided peer handle (as opposed to a MAC address).
+ #######################################
+
+ @test_tracker_info(uuid="e855dd81-45c8-4bb2-a204-7687c48ff843")
+ def test_ib_extra_pub_same_unsolicited_passive_open_specific(self):
+ """Data-path: in-band, unsolicited/passive, open encryption, specific peer.
+
+ Configuration contains a publisher (for the same service) running on *both*
+ devices.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=True,
+ pub_on_both=True,
+ pub_on_both_same=True)
+
+ @test_tracker_info(uuid="57fc9d53-32ae-470f-a8b1-2fe37893687d")
+ def test_ib_extra_pub_same_unsolicited_passive_open_any(self):
+ """Data-path: in-band, unsolicited/passive, open encryption, any peer.
+
+ Configuration contains a publisher (for the same service) running on *both*
+ devices.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ pub_on_both=True,
+ pub_on_both_same=True)
+
+ @test_tracker_info(uuid="7a32f439-d745-4716-a75e-b54109aaaf82")
+ def test_ib_extra_pub_diff_unsolicited_passive_open_specific(self):
+ """Data-path: in-band, unsolicited/passive, open encryption, specific peer.
+
+ Configuration contains a publisher (for a different service) running on
+ *both* devices.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=True,
+ pub_on_both=True,
+ pub_on_both_same=False)
+
+ @test_tracker_info(uuid="a14ddc66-88fd-4b49-ab37-225533867c63")
+ def test_ib_extra_pub_diff_unsolicited_passive_open_any(self):
+ """Data-path: in-band, unsolicited/passive, open encryption, any peer.
+
+ Configuration contains a publisher (for a different service) running on
+ *both* devices.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ pub_on_both=True,
+ pub_on_both_same=False)
+
+ #######################################
# Positive Out-of-Band (OOB) tests key:
#
# names is: test_oob_<encr_type>_<peer_spec>
# where:
#
- # encr_type: Encription type: open, passphrase
+ # encr_type: Encryption type: open, passphrase
# peer_spec: Peer specification method: any or specific
#
+ # Optionally set up an extra discovery session to test coexistence. If so
+ # add "ib_coex" to test name.
+ #
# Note: Out-of-Band means using a non-Wi-Fi Aware mechanism for discovery and
# exchange of MAC addresses and then Wi-Fi Aware for data-path.
#######################################
@@ -660,6 +813,32 @@
encr_type=self.ENCR_TYPE_PMK,
use_peer_id=False)
+ @test_tracker_info(uuid="dd464f24-b404-4eea-955c-d10c9e8adefc")
+ def test_oob_ib_coex_open_specific(self):
+ """Data-path: out-of-band, open encryption, specific peer - in-band coex:
+ set up a concurrent discovery session to verify no impact. The session
+ consists of Publisher on both ends, and a Subscriber on the Responder.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=True,
+ setup_discovery_sessions=True)
+
+ @test_tracker_info(uuid="088fcd3a-b015-4179-a9a5-91f782b03e3b")
+ def test_oob_ib_coex_open_any(self):
+ """Data-path: out-of-band, open encryption, any peer - in-band coex:
+ set up a concurrent discovery session to verify no impact. The session
+ consists of Publisher on both ends, and a Subscriber on the Responder.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ setup_discovery_sessions=True)
+
##############################################################
@test_tracker_info(uuid="1c2c9805-dc1e-43b5-a1b8-315e8c9a4337")
@@ -779,7 +958,8 @@
"""
num_events = 0
while num_events != len(req_keys):
- event = autils.wait_for_event(dut, cconsts.EVENT_NETWORK_CALLBACK)
+ event = autils.wait_for_event(dut, cconsts.EVENT_NETWORK_CALLBACK,
+ timeout=autils.EVENT_NDP_TIMEOUT)
if (event["data"][cconsts.NETWORK_CB_KEY_EVENT] ==
cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
if event["data"][cconsts.NETWORK_CB_KEY_ID] in req_keys:
@@ -808,7 +988,7 @@
resp_ids = []
# Initiator+Responder: attach and wait for confirmation & identity
- # create 10 sessions to be used in the different (but identical) NDPs
+ # create N+M sessions to be used in the different (but identical) NDPs
for i in range(N + M):
id, init_mac = autils.attach_with_identity(init_dut)
init_ids.append(id)
@@ -851,7 +1031,7 @@
self.wait_for_request_responses(resp_dut, resp_req_keys, resp_aware_ifs)
self.wait_for_request_responses(init_dut, init_req_keys, init_aware_ifs)
- # issue N more requests for the same NDPs - tests post-setup multiple NDP
+ # issue M more requests for the same NDPs - tests post-setup multiple NDP
for i in range(M):
# Responder: request network
resp_req_keys.append(autils.request_network(
@@ -909,9 +1089,118 @@
for init_req_key in init_req_keys:
init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+ def test_identical_network_from_both_sides(self):
+ """Validate that requesting two identical NDPs (Open) each being initiated
+ from a different side, results in the same/single NDP.
+
+ Verify that the interface and IPv6 address is the same for all networks.
+ """
+ dut1 = self.android_devices[0]
+ dut2 = self.android_devices[1]
+
+ id1, mac1 = autils.attach_with_identity(dut1)
+ id2, mac2 = autils.attach_with_identity(dut2)
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ # first NDP: DUT1 (Init) -> DUT2 (Resp)
+ req_a_resp = autils.request_network(dut2,
+ dut2.droid.wifiAwareCreateNetworkSpecifierOob(
+ id2, aconsts.DATA_PATH_RESPONDER,
+ mac1))
+
+ req_a_init = autils.request_network(dut1,
+ dut1.droid.wifiAwareCreateNetworkSpecifierOob(
+ id1, aconsts.DATA_PATH_INITIATOR,
+ mac2))
+
+ req_a_resp_event = autils.wait_for_event_with_keys(
+ dut2, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_a_resp))
+ req_a_init_event = autils.wait_for_event_with_keys(
+ dut1, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_a_init))
+
+ req_a_if_resp = req_a_resp_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ req_a_if_init = req_a_init_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ self.log.info("Interface names for A: I=%s, R=%s", req_a_if_init,
+ req_a_if_resp)
+
+ req_a_ipv6_resp = \
+ dut2.droid.connectivityGetLinkLocalIpv6Address(req_a_if_resp).split("%")[0]
+ req_a_ipv6_init = \
+ dut1.droid.connectivityGetLinkLocalIpv6Address(req_a_if_init).split("%")[0]
+ self.log.info("Interface addresses (IPv6) for A: I=%s, R=%s",
+ req_a_ipv6_init, req_a_ipv6_resp)
+
+ # second NDP: DUT2 (Init) -> DUT1 (Resp)
+ req_b_resp = autils.request_network(dut1,
+ dut1.droid.wifiAwareCreateNetworkSpecifierOob(
+ id1, aconsts.DATA_PATH_RESPONDER,
+ mac2))
+
+ req_b_init = autils.request_network(dut2,
+ dut2.droid.wifiAwareCreateNetworkSpecifierOob(
+ id2, aconsts.DATA_PATH_INITIATOR,
+ mac1))
+
+ req_b_resp_event = autils.wait_for_event_with_keys(
+ dut1, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_b_resp))
+ req_b_init_event = autils.wait_for_event_with_keys(
+ dut2, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_b_init))
+
+ req_b_if_resp = req_b_resp_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ req_b_if_init = req_b_init_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ self.log.info("Interface names for B: I=%s, R=%s", req_b_if_init,
+ req_b_if_resp)
+
+ req_b_ipv6_resp = \
+ dut1.droid.connectivityGetLinkLocalIpv6Address(req_b_if_resp).split("%")[0]
+ req_b_ipv6_init = \
+ dut2.droid.connectivityGetLinkLocalIpv6Address(req_b_if_init).split("%")[0]
+ self.log.info("Interface addresses (IPv6) for B: I=%s, R=%s",
+ req_b_ipv6_init, req_b_ipv6_resp)
+
+ # validate equality of NDPs (using interface names & ipv6)
+ asserts.assert_equal(req_a_if_init, req_b_if_resp,
+ "DUT1 NDPs are on different interfaces")
+ asserts.assert_equal(req_a_if_resp, req_b_if_init,
+ "DUT2 NDPs are on different interfaces")
+ asserts.assert_equal(req_a_ipv6_init, req_b_ipv6_resp,
+ "DUT1 NDPs are using different IPv6 addresses")
+ asserts.assert_equal(req_a_ipv6_resp, req_b_ipv6_init,
+ "DUT2 NDPs are using different IPv6 addresses")
+
+ # release requests
+ dut1.droid.connectivityUnregisterNetworkCallback(req_a_init)
+ dut1.droid.connectivityUnregisterNetworkCallback(req_b_resp)
+ dut2.droid.connectivityUnregisterNetworkCallback(req_a_resp)
+ dut2.droid.connectivityUnregisterNetworkCallback(req_b_init)
+
########################################################################
- def run_multiple_ndi(self, sec_configs):
+ def run_multiple_ndi(self, sec_configs, flip_init_resp=False):
"""Validate that the device can create and use multiple NDIs.
The security configuration can be:
@@ -921,127 +1210,716 @@
Args:
sec_configs: list of security configurations
+ flip_init_resp: if True the roles of Initiator and Responder are flipped
+ between the 2 devices, otherwise same devices are always
+ configured in the same role.
"""
- init_dut = self.android_devices[0]
- init_dut.pretty_name = "Initiator"
- resp_dut = self.android_devices[1]
- resp_dut.pretty_name = "Responder"
+ dut1 = self.android_devices[0]
+ dut2 = self.android_devices[1]
- asserts.skip_if(init_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES]
+ asserts.skip_if(dut1.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES]
< len(sec_configs) or
- resp_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES]
+ dut2.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES]
< len(sec_configs),
- "Initiator or Responder do not support multiple NDIs")
+ "DUTs do not support enough NDIs")
- init_id, init_mac = autils.attach_with_identity(init_dut)
- resp_id, resp_mac = autils.attach_with_identity(resp_dut)
+ id1, mac1 = autils.attach_with_identity(dut1)
+ id2, mac2 = autils.attach_with_identity(dut2)
# wait for for devices to synchronize with each other - there are no other
# mechanisms to make sure this happens for OOB discovery (except retrying
# to execute the data-path request)
time.sleep(autils.WAIT_FOR_CLUSTER)
- resp_req_keys = []
- init_req_keys = []
- resp_aware_ifs = []
- init_aware_ifs = []
+ dut2_req_keys = []
+ dut1_req_keys = []
+ dut2_aware_ifs = []
+ dut1_aware_ifs = []
+ dut2_aware_ipv6 = []
+ dut1_aware_ipv6 = []
+ dut2_type = aconsts.DATA_PATH_RESPONDER
+ dut1_type = aconsts.DATA_PATH_INITIATOR
+ dut2_is_responder = True
for sec in sec_configs:
- # Responder: request network
- resp_req_key = autils.request_network(resp_dut,
- autils.get_network_specifier(
- resp_dut, resp_id,
- aconsts.DATA_PATH_RESPONDER,
- init_mac, sec))
- resp_req_keys.append(resp_req_key)
+ if dut2_is_responder:
+ # DUT2 (Responder): request network
+ dut2_req_key = autils.request_network(dut2,
+ autils.get_network_specifier(
+ dut2, id2,
+ dut2_type,
+ mac1, sec))
+ dut2_req_keys.append(dut2_req_key)
- # Initiator: request network
- init_req_key = autils.request_network(init_dut,
- autils.get_network_specifier(
- init_dut, init_id,
- aconsts.DATA_PATH_INITIATOR,
- resp_mac, sec))
- init_req_keys.append(init_req_key)
+ # DUT1 (Initiator): request network
+ dut1_req_key = autils.request_network(dut1,
+ autils.get_network_specifier(
+ dut1, id1,
+ dut1_type,
+ mac2, sec))
+ dut1_req_keys.append(dut1_req_key)
+ else:
+ # DUT1 (Responder): request network
+ dut1_req_key = autils.request_network(dut1,
+ autils.get_network_specifier(
+ dut1, id1,
+ dut1_type,
+ mac2, sec))
+ dut1_req_keys.append(dut1_req_key)
+
+ # DUT2 (Initiator): request network
+ dut2_req_key = autils.request_network(dut2,
+ autils.get_network_specifier(
+ dut2, id2,
+ dut2_type,
+ mac1, sec))
+ dut2_req_keys.append(dut2_req_key)
# Wait for network
- init_net_event = autils.wait_for_event_with_keys(
- init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+ dut1_net_event = autils.wait_for_event_with_keys(
+ dut1, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
(cconsts.NETWORK_CB_KEY_EVENT,
cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
- (cconsts.NETWORK_CB_KEY_ID, init_req_key))
- resp_net_event = autils.wait_for_event_with_keys(
- resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_ID, dut1_req_key))
+ dut2_net_event = autils.wait_for_event_with_keys(
+ dut2, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
(cconsts.NETWORK_CB_KEY_EVENT,
cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
- (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+ (cconsts.NETWORK_CB_KEY_ID, dut2_req_key))
- resp_aware_ifs.append(
- resp_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
- init_aware_ifs.append(
- init_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
+ dut2_aware_if = dut2_net_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ dut1_aware_if = dut1_net_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ dut2_aware_ifs.append(dut2_aware_if)
+ dut1_aware_ifs.append(dut1_aware_if)
+ dut2_aware_ipv6.append(autils.get_ipv6_addr(dut2, dut2_aware_if))
+ dut1_aware_ipv6.append(autils.get_ipv6_addr(dut1, dut1_aware_if))
- # check that we are using 2 NDIs
- init_aware_ifs = list(set(init_aware_ifs))
- resp_aware_ifs = list(set(resp_aware_ifs))
+ if flip_init_resp:
+ if dut2_is_responder:
+ dut2_type = aconsts.DATA_PATH_INITIATOR
+ dut1_type = aconsts.DATA_PATH_RESPONDER
+ else:
+ dut2_type = aconsts.DATA_PATH_RESPONDER
+ dut1_type = aconsts.DATA_PATH_INITIATOR
+ dut2_is_responder = not dut2_is_responder
- self.log.info("Interface names: I=%s, R=%s", init_aware_ifs, resp_aware_ifs)
- self.log.info("Initiator requests: %s", init_req_keys)
- self.log.info("Responder requests: %s", resp_req_keys)
+ # check that we are using 2 NDIs & that they have unique IPv6 addresses
+ dut1_aware_ifs = list(set(dut1_aware_ifs))
+ dut2_aware_ifs = list(set(dut2_aware_ifs))
+ dut1_aware_ipv6 = list(set(dut1_aware_ipv6))
+ dut2_aware_ipv6 = list(set(dut2_aware_ipv6))
+
+ self.log.info("Interface names: DUT1=%s, DUT2=%s", dut1_aware_ifs,
+ dut2_aware_ifs)
+ self.log.info("IPv6 addresses: DUT1=%s, DUT2=%s", dut1_aware_ipv6,
+ dut2_aware_ipv6)
+ self.log.info("DUT1 requests: %s", dut1_req_keys)
+ self.log.info("DUT2 requests: %s", dut2_req_keys)
asserts.assert_equal(
- len(init_aware_ifs), len(sec_configs), "Multiple initiator interfaces")
+ len(dut1_aware_ifs), len(sec_configs), "Multiple DUT1 interfaces")
asserts.assert_equal(
- len(resp_aware_ifs), len(sec_configs), "Multiple responder interfaces")
+ len(dut2_aware_ifs), len(sec_configs), "Multiple DUT2 interfaces")
+ asserts.assert_equal(
+ len(dut1_aware_ipv6), len(sec_configs), "Multiple DUT1 IPv6 addresses")
+ asserts.assert_equal(
+ len(dut2_aware_ipv6), len(sec_configs), "Multiple DUT2 IPv6 addresses")
for i in range(len(sec_configs)):
if_name = "%s%d" % (aconsts.AWARE_NDI_PREFIX, i)
- init_ipv6 = autils.get_ipv6_addr(init_dut, if_name)
- resp_ipv6 = autils.get_ipv6_addr(resp_dut, if_name)
+ dut1_ipv6 = autils.get_ipv6_addr(dut1, if_name)
+ dut2_ipv6 = autils.get_ipv6_addr(dut2, if_name)
asserts.assert_equal(
- init_ipv6 is None, if_name not in init_aware_ifs,
- "Initiator interface %s in unexpected state" % if_name)
+ dut1_ipv6 is None, if_name not in dut1_aware_ifs,
+ "DUT1 interface %s in unexpected state" % if_name)
asserts.assert_equal(
- resp_ipv6 is None, if_name not in resp_aware_ifs,
- "Responder interface %s in unexpected state" % if_name)
+ dut2_ipv6 is None, if_name not in dut2_aware_ifs,
+ "DUT2 interface %s in unexpected state" % if_name)
# release requests
- for resp_req_key in resp_req_keys:
- resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
- for init_req_key in init_req_keys:
- init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+ for dut2_req_key in dut2_req_keys:
+ dut2.droid.connectivityUnregisterNetworkCallback(dut2_req_key)
+ for dut1_req_key in dut1_req_keys:
+ dut1.droid.connectivityUnregisterNetworkCallback(dut1_req_key)
@test_tracker_info(uuid="2d728163-11cc-46ba-a973-c8e1e71397fc")
def test_multiple_ndi_open_passphrase(self):
- """Verify that can between 2 DUTs can create 2 NDPs with different security
+ """Verify that between 2 DUTs can create 2 NDPs with different security
configuration (one open, one using passphrase). The result should use two
different NDIs"""
self.run_multiple_ndi([None, self.PASSPHRASE])
@test_tracker_info(uuid="5f2c32aa-20b2-41f0-8b1e-d0b68df73ada")
def test_multiple_ndi_open_pmk(self):
- """Verify that can between 2 DUTs can create 2 NDPs with different security
+ """Verify that between 2 DUTs can create 2 NDPs with different security
configuration (one open, one using pmk). The result should use two
different NDIs"""
self.run_multiple_ndi([None, self.PMK])
@test_tracker_info(uuid="34467659-bcfb-40cd-ba25-7e50560fca63")
def test_multiple_ndi_passphrase_pmk(self):
- """Verify that can between 2 DUTs can create 2 NDPs with different security
+ """Verify that between 2 DUTs can create 2 NDPs with different security
configuration (one using passphrase, one using pmk). The result should use
two different NDIs"""
self.run_multiple_ndi([self.PASSPHRASE, self.PMK])
@test_tracker_info(uuid="d9194ce6-45b6-41b1-9cc8-ada79968966d")
def test_multiple_ndi_passphrases(self):
- """Verify that can between 2 DUTs can create 2 NDPs with different security
+ """Verify that between 2 DUTs can create 2 NDPs with different security
configuration (using different passphrases). The result should use two
different NDIs"""
self.run_multiple_ndi([self.PASSPHRASE, self.PASSPHRASE2])
@test_tracker_info(uuid="879df795-62d2-40d4-a862-bd46d8f7e67f")
def test_multiple_ndi_pmks(self):
- """Verify that can between 2 DUTs can create 2 NDPs with different security
+ """Verify that between 2 DUTs can create 2 NDPs with different security
configuration (using different PMKS). The result should use two different
NDIs"""
self.run_multiple_ndi([self.PMK, self.PMK2])
+
+ @test_tracker_info(uuid="397d380a-8e41-466e-9ccb-cf8f413d83ba")
+ def test_multiple_ndi_open_passphrase_flip(self):
+ """Verify that between 2 DUTs can create 2 NDPs with different security
+ configuration (one open, one using passphrase). The result should use two
+ different NDIs.
+
+ Flip Initiator and Responder roles.
+ """
+ self.run_multiple_ndi([None, self.PASSPHRASE], flip_init_resp=True)
+
+ @test_tracker_info(uuid="b3a4300b-1514-4cb8-a814-9c2baa449700")
+ def test_multiple_ndi_open_pmk_flip(self):
+ """Verify that between 2 DUTs can create 2 NDPs with different security
+ configuration (one open, one using pmk). The result should use two
+ different NDIs
+
+ Flip Initiator and Responder roles.
+ """
+ self.run_multiple_ndi([None, self.PMK], flip_init_resp=True)
+
+ @test_tracker_info(uuid="0bfea9e4-e57d-417f-8db4-245741e9bbd5")
+ def test_multiple_ndi_passphrase_pmk_flip(self):
+ """Verify that between 2 DUTs can create 2 NDPs with different security
+ configuration (one using passphrase, one using pmk). The result should use
+ two different NDIs
+
+ Flip Initiator and Responder roles.
+ """
+ self.run_multiple_ndi([self.PASSPHRASE, self.PMK], flip_init_resp=True)
+
+ @test_tracker_info(uuid="74023483-5417-431b-a362-991ad4a03ab8")
+ def test_multiple_ndi_passphrases_flip(self):
+ """Verify that between 2 DUTs can create 2 NDPs with different security
+ configuration (using different passphrases). The result should use two
+ different NDIs
+
+ Flip Initiator and Responder roles.
+ """
+ self.run_multiple_ndi([self.PASSPHRASE, self.PASSPHRASE2],
+ flip_init_resp=True)
+
+ @test_tracker_info(uuid="873b2d91-28a1-403f-ae9c-d756bb2f59ee")
+ def test_multiple_ndi_pmks_flip(self):
+ """Verify that between 2 DUTs can create 2 NDPs with different security
+ configuration (using different PMKS). The result should use two different
+ NDIs
+
+ Flip Initiator and Responder roles.
+ """
+ self.run_multiple_ndi([self.PMK, self.PMK2], flip_init_resp=True)
+
+ #######################################
+
+ @test_tracker_info(uuid="2f10a9df-7fbd-490d-a238-3523f47ab54c")
+ def test_ib_responder_any_usage(self):
+ """Verify that configuring an in-band (Aware discovery) Responder to receive
+ an NDP request from any peer is not permitted by current API level. Override
+ API check to validate that possible (i.e. that failure at current API level
+ is due to an API check and not some underlying failure).
+ """
+
+ # configure all devices to override API check and allow a Responder from ANY
+ for ad in self.android_devices:
+ autils.configure_ndp_allow_any_override(ad, True)
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False)
+
+ # configure all devices to respect API check - i.e. disallow a Responder
+ # from ANY
+ for ad in self.android_devices:
+ autils.configure_ndp_allow_any_override(ad, False)
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ expect_failure=True)
+
+ @test_tracker_info(uuid="5889cd41-0a72-4b7b-ab82-5b9168b9b5b8")
+ def test_oob_responder_any_usage(self):
+ """Verify that configuring an out-of-band (Aware discovery) Responder to
+ receive an NDP request from any peer is not permitted by current API level.
+ Override API check to validate that possible (i.e. that failure at current
+ API level is due to an API check and not some underlying failure).
+ """
+
+ # configure all devices to override API check and allow a Responder from ANY
+ for ad in self.android_devices:
+ autils.configure_ndp_allow_any_override(ad, True)
+ self.run_oob_data_path_test(
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False)
+
+ # configure all devices to respect API check - i.e. disallow a Responder
+ # from ANY
+ for ad in self.android_devices:
+ autils.configure_ndp_allow_any_override(ad, False)
+ self.run_oob_data_path_test(
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ expect_failure=True)
+
+ #######################################
+
+ def run_multiple_regulatory_domains(self, use_ib, init_domain, resp_domain):
+ """Verify that a data-path setup with two conflicting regulatory domains
+ works (the result should be run in Channel 6 - but that is not tested).
+
+ Args:
+ use_ib: True to use in-band discovery, False to use out-of-band discovery.
+ init_domain: The regulatory domain of the Initiator/Subscriber.
+ resp_domain: The regulator domain of the Responder/Publisher.
+ """
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ init_dut.droid.wifiSetCountryCode(init_domain)
+ resp_dut.droid.wifiSetCountryCode(resp_domain)
+
+ if use_ib:
+ (resp_req_key, init_req_key, resp_aware_if, init_aware_if, resp_ipv6,
+ init_ipv6) = autils.create_ib_ndp(resp_dut, init_dut,
+ autils.create_discovery_config(
+ "GoogleTestXyz",
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ autils.create_discovery_config(
+ "GoogleTestXyz",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ self.device_startup_offset)
+ else:
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6,
+ resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut)
+
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if)
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
+
+ # clean-up
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+
+ @test_tracker_info(uuid="eff53739-35c5-47a6-81f0-d70b51d89c3b")
+ def test_multiple_regulator_domains_ib_us_jp(self):
+ """Verify data-path setup across multiple regulator domains.
+
+ - Uses in-band discovery
+ - Subscriber=US, Publisher=JP
+ """
+ self.run_multiple_regulatory_domains(
+ use_ib=True,
+ init_domain=wutils.WifiEnums.CountryCode.US,
+ resp_domain=wutils.WifiEnums.CountryCode.JAPAN)
+
+ @test_tracker_info(uuid="19af47cc-3204-40ef-b50f-14cf7b89cf4a")
+ def test_multiple_regulator_domains_ib_jp_us(self):
+ """Verify data-path setup across multiple regulator domains.
+
+ - Uses in-band discovery
+ - Subscriber=JP, Publisher=US
+ """
+ self.run_multiple_regulatory_domains(
+ use_ib=True,
+ init_domain=wutils.WifiEnums.CountryCode.JAPAN,
+ resp_domain=wutils.WifiEnums.CountryCode.US)
+
+ @test_tracker_info(uuid="65285ab3-977f-4dbd-b663-d5a02f4fc663")
+ def test_multiple_regulator_domains_oob_us_jp(self):
+ """Verify data-path setup across multiple regulator domains.
+
+ - Uses out-f-band discovery
+ - Initiator=US, Responder=JP
+ """
+ self.run_multiple_regulatory_domains(
+ use_ib=False,
+ init_domain=wutils.WifiEnums.CountryCode.US,
+ resp_domain=wutils.WifiEnums.CountryCode.JAPAN)
+
+ @test_tracker_info(uuid="8a417e24-aaf6-44b9-a089-a07c3ba8d954")
+ def test_multiple_regulator_domains_oob_jp_us(self):
+ """Verify data-path setup across multiple regulator domains.
+
+ - Uses out-of-band discovery
+ - Initiator=JP, Responder=US
+ """
+ self.run_multiple_regulatory_domains(
+ use_ib=False,
+ init_domain=wutils.WifiEnums.CountryCode.JAPAN,
+ resp_domain=wutils.WifiEnums.CountryCode.US)
+
+ ########################################################################
+
+ def run_mix_ib_oob(self, same_request, ib_first, inits_on_same_dut):
+ """Validate that multiple network requests issued using both in-band and
+ out-of-band discovery behave as expected.
+
+ The same_request parameter controls whether identical single NDP is
+ expected, if True, or whether multiple NDPs on different NDIs are expected,
+ if False.
+
+ Args:
+ same_request: Issue canonically identical requests (same NMI peer, same
+ passphrase) if True, if False use different passphrases.
+ ib_first: If True then the in-band network is requested first, otherwise
+ (if False) then the out-of-band network is requested first.
+ inits_on_same_dut: If True then the Initiators are run on the same device,
+ otherwise (if False) then the Initiators are run on
+ different devices. Note that Subscribe == Initiator.
+ """
+ if not same_request:
+ asserts.skip_if(self.android_devices[0].aware_capabilities[
+ aconsts.CAP_MAX_NDI_INTERFACES] < 2 or
+ self.android_devices[1].aware_capabilities[
+ aconsts.CAP_MAX_NDI_INTERFACES] < 2,
+ "DUTs do not support enough NDIs")
+
+ (p_dut, s_dut, p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+ peer_id_on_pub_null) = self.set_up_discovery(
+ aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE, False)
+
+ p_id2, p_mac = autils.attach_with_identity(p_dut)
+ s_id2, s_mac = autils.attach_with_identity(s_dut)
+
+ if inits_on_same_dut:
+ resp_dut = p_dut
+ resp_id = p_id2
+ resp_mac = p_mac
+
+ init_dut = s_dut
+ init_id = s_id2
+ init_mac = s_mac
+ else:
+ resp_dut = s_dut
+ resp_id = s_id2
+ resp_mac = s_mac
+
+ init_dut = p_dut
+ init_id = p_id2
+ init_mac = p_mac
+
+ passphrase = None if same_request else self.PASSPHRASE
+
+ if ib_first:
+ # request in-band network (to completion)
+ p_req_key = self.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, None))
+ s_req_key = self.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id,
+ peer_id_on_sub))
+
+ # Publisher & Subscriber: wait for network formation
+ p_net_event = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ # request out-of-band network
+ resp_req_key = autils.request_network(resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, passphrase))
+ init_req_key = autils.request_network(init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, passphrase))
+
+ resp_net_event = autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+ init_net_event = autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+
+ if not ib_first:
+ # request in-band network (to completion)
+ p_req_key = self.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, None))
+ s_req_key = self.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id,
+ peer_id_on_sub))
+
+ # Publisher & Subscriber: wait for network formation
+ p_net_event = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ # extract net info
+ pub_interface = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ sub_interface = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ resp_interface = resp_net_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ init_interface = init_net_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ self.log.info(
+ "Interface names: Pub=%s, Sub=%s, Resp=%s, Init=%s", pub_interface,
+ sub_interface, resp_interface, init_interface)
+
+ pub_ipv6 = \
+ p_dut.droid.connectivityGetLinkLocalIpv6Address(pub_interface).split("%")[0]
+ sub_ipv6 = \
+ s_dut.droid.connectivityGetLinkLocalIpv6Address(sub_interface).split("%")[0]
+ resp_ipv6 = \
+ resp_dut.droid.connectivityGetLinkLocalIpv6Address(resp_interface).split(
+ "%")[0]
+ init_ipv6 = \
+ init_dut.droid.connectivityGetLinkLocalIpv6Address(init_interface).split(
+ "%")[0]
+
+ self.log.info(
+ "Interface addresses (IPv6): Pub=%s, Sub=%s, Resp=%s, Init=%s", pub_ipv6,
+ sub_ipv6, resp_ipv6, init_ipv6)
+
+ # validate NDP/NDI conditions (using interface names & ipv6)
+ if same_request:
+ asserts.assert_equal(pub_interface,
+ resp_interface if inits_on_same_dut else init_interface,
+ "NDP interfaces don't match on Pub/other")
+ asserts.assert_equal(sub_interface,
+ init_interface if inits_on_same_dut else resp_interface,
+ "NDP interfaces don't match on Sub/other")
+
+ asserts.assert_equal(pub_ipv6,
+ resp_ipv6 if inits_on_same_dut else init_ipv6,
+ "NDP IPv6 don't match on Pub/other")
+ asserts.assert_equal(sub_ipv6,
+ init_ipv6 if inits_on_same_dut else resp_ipv6,
+ "NDP IPv6 don't match on Sub/other")
+ else:
+ asserts.assert_false(pub_interface == (
+ resp_interface if inits_on_same_dut else init_interface),
+ "NDP interfaces match on Pub/other")
+ asserts.assert_false(sub_interface == (
+ init_interface if inits_on_same_dut else resp_interface),
+ "NDP interfaces match on Sub/other")
+
+ asserts.assert_false(pub_ipv6 ==
+ (resp_ipv6 if inits_on_same_dut else init_ipv6),
+ "NDP IPv6 match on Pub/other")
+ asserts.assert_false(sub_ipv6 ==
+ (init_ipv6 if inits_on_same_dut else resp_ipv6),
+ "NDP IPv6 match on Sub/other")
+
+ # release requests
+ p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+ s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+
+ @test_tracker_info(uuid="d8a0839d-4ba0-43f2-af93-3cf1382f9f16")
+ def test_identical_ndps_mix_ib_oob_ib_first_same_polarity(self):
+ """Validate that a single NDP is created for multiple identical requests
+ which are issued through either in-band (ib) or out-of-band (oob) APIs.
+
+ The in-band request is issued first. Both Initiators (Sub == Initiator) are
+ run on the same device.
+ """
+ self.run_mix_ib_oob(same_request=True,
+ ib_first=True,
+ inits_on_same_dut=True)
+
+ @test_tracker_info(uuid="70bbb811-0bed-4a19-96b3-f2446e777c8a")
+ def test_identical_ndps_mix_ib_oob_oob_first_same_polarity(self):
+ """Validate that a single NDP is created for multiple identical requests
+ which are issued through either in-band (ib) or out-of-band (oob) APIs.
+
+ The out-of-band request is issued first. Both Initiators (Sub == Initiator)
+ are run on the same device.
+ """
+ self.run_mix_ib_oob(same_request=True,
+ ib_first=False,
+ inits_on_same_dut=True)
+
+ @test_tracker_info(uuid="d9796da5-f96a-4a51-be0f-89d6f5bfe3ad")
+ def test_identical_ndps_mix_ib_oob_ib_first_diff_polarity(self):
+ """Validate that a single NDP is created for multiple identical requests
+ which are issued through either in-band (ib) or out-of-band (oob) APIs.
+
+ The in-band request is issued first. Initiators (Sub == Initiator) are
+ run on different devices.
+ """
+ self.run_mix_ib_oob(same_request=True,
+ ib_first=True,
+ inits_on_same_dut=False)
+
+ @test_tracker_info(uuid="72b16cbf-53ad-4f98-8dcf-a8cc5fa812e3")
+ def test_identical_ndps_mix_ib_oob_oob_first_diff_polarity(self):
+ """Validate that a single NDP is created for multiple identical requests
+ which are issued through either in-band (ib) or out-of-band (oob) APIs.
+
+ The out-of-band request is issued first. Initiators (Sub == Initiator) are
+ run on different devices.
+ """
+ self.run_mix_ib_oob(same_request=True,
+ ib_first=False,
+ inits_on_same_dut=False)
+
+ @test_tracker_info(uuid="51f9581e-c5ee-48a7-84d2-adff4876c3d7")
+ def test_multiple_ndis_mix_ib_oob_ib_first_same_polarity(self):
+ """Validate that multiple NDIs are created for NDPs which are requested with
+ different security configurations. Use a mix of in-band and out-of-band APIs
+ to request the different NDPs.
+
+ The in-band request is issued first. Initiators (Sub == Initiator) are
+ run on the same device.
+ """
+ self.run_mix_ib_oob(same_request=False,
+ ib_first=True,
+ inits_on_same_dut=True)
+
+ @test_tracker_info(uuid="b1e3070e-4d38-4b31-862d-39b82e0f2853")
+ def test_multiple_ndis_mix_ib_oob_oob_first_same_polarity(self):
+ """Validate that multiple NDIs are created for NDPs which are requested with
+ different security configurations. Use a mix of in-band and out-of-band APIs
+ to request the different NDPs.
+
+ The out-of-band request is issued first. Initiators (Sub == Initiator) are
+ run on the same device.
+ """
+ self.run_mix_ib_oob(same_request=False,
+ ib_first=False,
+ inits_on_same_dut=True)
+
+ @test_tracker_info(uuid="b1e3070e-4d38-4b31-862d-39b82e0f2853")
+ def test_multiple_ndis_mix_ib_oob_ib_first_diff_polarity(self):
+ """Validate that multiple NDIs are created for NDPs which are requested with
+ different security configurations. Use a mix of in-band and out-of-band APIs
+ to request the different NDPs.
+
+ The in-band request is issued first. Initiators (Sub == Initiator) are
+ run on different devices.
+ """
+ self.run_mix_ib_oob(same_request=False,
+ ib_first=True,
+ inits_on_same_dut=False)
+
+ @test_tracker_info(uuid="596caadf-028e-494b-bbce-8304ccec2cbb")
+ def test_multiple_ndis_mix_ib_oob_oob_first_diff_polarity(self):
+ """Validate that multiple NDIs are created for NDPs which are requested with
+ different security configurations. Use a mix of in-band and out-of-band APIs
+ to request the different NDPs.
+
+ The out-of-band request is issued first. Initiators (Sub == Initiator) are
+ run on different devices.
+ """
+ self.run_mix_ib_oob(same_request=False,
+ ib_first=False,
+ inits_on_same_dut=False)
+
+ ########################################################################
+
+ def test_ndp_loop(self):
+ """Validate that can create a loop (chain) of N NDPs between N devices,
+ where N >= 3, e.g.
+
+ A - B
+ B - C
+ C - A
+
+ The NDPs are all OPEN (no encryption).
+ """
+ asserts.assert_true(len(self.android_devices) >= 3,
+ 'A minimum of 3 devices is needed to run the test, have %d' %
+ len(self.android_devices))
+
+ duts = self.android_devices
+ loop_len = len(duts)
+ ids = []
+ macs = []
+ reqs = [[], [], []]
+ ifs = [[], [], []]
+ ipv6s = [[], [], []]
+
+ for i in range(loop_len):
+ duts[i].pretty_name = chr(ord("A") + i)
+
+ # start-up 3 devices (attach w/ identity)
+ for i in range(loop_len):
+ ids.append(duts[i].droid.wifiAwareAttach(True))
+ autils.wait_for_event(duts[i], aconsts.EVENT_CB_ON_ATTACHED)
+ ident_event = autils.wait_for_event(duts[i],
+ aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ macs.append(ident_event['data']['mac'])
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ # create the N NDPs: i to (i+1) % N
+ for i in range(loop_len):
+ peer_device = (i + 1) % loop_len
+
+ (init_req_key, resp_req_key, init_aware_if,
+ resp_aware_if, init_ipv6, resp_ipv6) = autils.create_oob_ndp_on_sessions(
+ duts[i], duts[peer_device],
+ ids[i], macs[i], ids[peer_device], macs[peer_device])
+
+ reqs[i].append(init_req_key)
+ reqs[peer_device].append(resp_req_key)
+ ifs[i].append(init_aware_if)
+ ifs[peer_device].append(resp_aware_if)
+ ipv6s[i].append(init_ipv6)
+ ipv6s[peer_device].append(resp_ipv6)
+
+ # clean-up
+ for i in range(loop_len):
+ for req in reqs[i]:
+ duts[i].droid.connectivityUnregisterNetworkCallback(req)
+
+ # info
+ self.log.info("MACs: %s", macs)
+ self.log.info("Interface names: %s", ifs)
+ self.log.info("IPv6 addresses: %s", ipv6s)
+ asserts.explicit_pass("NDP loop test",
+ extras={"macs": macs, "ifs": ifs, "ipv6s": ipv6s})
diff --git a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
index 8732dbb..c6f75b0 100644
--- a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
+++ b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
@@ -829,3 +829,207 @@
s_type=aconsts.SUBSCRIBE_TYPE_ACTIVE,
p_mf_1="hello there string",
s_mf_1="goodbye there string")
+
+ #######################################
+ # Multiple concurrent services
+ #######################################
+
+ def run_multiple_concurrent_services(self, type_x, type_y):
+ """Validate multiple identical discovery services running on both devices:
+ - DUT1 & DUT2 running Publish for X
+ - DUT1 & DUT2 running Publish for Y
+ - DUT1 Subscribes for X
+ - DUT2 Subscribes for Y
+ Message exchanges.
+
+ Note: test requires that devices support 2 publish sessions concurrently.
+ The test will be skipped if the devices are not capable.
+
+ Args:
+ type_x, type_y: A list of [ptype, stype] of the publish and subscribe
+ types for services X and Y respectively.
+ """
+ dut1 = self.android_devices[0]
+ dut2 = self.android_devices[1]
+
+ X_SERVICE_NAME = "ServiceXXX"
+ Y_SERVICE_NAME = "ServiceYYY"
+
+ asserts.skip_if(dut1.aware_capabilities[aconsts.CAP_MAX_PUBLISHES] < 2 or
+ dut2.aware_capabilities[aconsts.CAP_MAX_PUBLISHES] < 2,
+ "Devices do not support 2 publish sessions")
+
+ # attach and wait for confirmation
+ id1 = dut1.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut1, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ id2 = dut2.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut2, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # DUT1 & DUT2: start publishing both X & Y services and wait for
+ # confirmations
+ dut1_x_pid = dut1.droid.wifiAwarePublish(id1,
+ autils.create_discovery_config(
+ X_SERVICE_NAME, type_x[0]))
+ event = autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut1_x_pid,
+ "Unexpected DUT1 X publish session discovery ID")
+
+ dut1_y_pid = dut1.droid.wifiAwarePublish(id1,
+ autils.create_discovery_config(
+ Y_SERVICE_NAME, type_y[0]))
+ event = autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut1_y_pid,
+ "Unexpected DUT1 Y publish session discovery ID")
+
+ dut2_x_pid = dut2.droid.wifiAwarePublish(id2,
+ autils.create_discovery_config(
+ X_SERVICE_NAME, type_x[0]))
+ event = autils.wait_for_event(dut2, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut2_x_pid,
+ "Unexpected DUT2 X publish session discovery ID")
+
+ dut2_y_pid = dut2.droid.wifiAwarePublish(id2,
+ autils.create_discovery_config(
+ Y_SERVICE_NAME, type_y[0]))
+ event = autils.wait_for_event(dut2, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut2_y_pid,
+ "Unexpected DUT2 Y publish session discovery ID")
+
+ # DUT1: start subscribing for X
+ dut1_x_sid = dut1.droid.wifiAwareSubscribe(id1,
+ autils.create_discovery_config(
+ X_SERVICE_NAME, type_x[1]))
+ autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # DUT2: start subscribing for Y
+ dut2_y_sid = dut2.droid.wifiAwareSubscribe(id2,
+ autils.create_discovery_config(
+ Y_SERVICE_NAME, type_y[1]))
+ autils.wait_for_event(dut2, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # DUT1 & DUT2: wait for service discovery
+ event = autils.wait_for_event(dut1,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut1_x_sid,
+ "Unexpected DUT1 X subscribe session discovery ID")
+ dut1_peer_id_for_dut2_x = event["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ event = autils.wait_for_event(dut2,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut2_y_sid,
+ "Unexpected DUT2 Y subscribe session discovery ID")
+ dut2_peer_id_for_dut1_y = event["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ # DUT1.X send message to DUT2
+ x_msg = "Hello X on DUT2!"
+ dut1.droid.wifiAwareSendMessage(dut1_x_sid, dut1_peer_id_for_dut2_x,
+ self.get_next_msg_id(), x_msg,
+ self.msg_retx_count)
+ autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ event = autils.wait_for_event(dut2, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut2_x_pid,
+ "Unexpected publish session ID on DUT2 for meesage "
+ "received on service X")
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], x_msg,
+ "Message on service X from DUT1 to DUT2 not received correctly")
+
+ # DUT2.Y send message to DUT1
+ y_msg = "Hello Y on DUT1!"
+ dut2.droid.wifiAwareSendMessage(dut2_y_sid, dut2_peer_id_for_dut1_y,
+ self.get_next_msg_id(), y_msg,
+ self.msg_retx_count)
+ autils.wait_for_event(dut2, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ event = autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut1_y_pid,
+ "Unexpected publish session ID on DUT1 for meesage "
+ "received on service Y")
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], y_msg,
+ "Message on service Y from DUT2 to DUT1 not received correctly")
+
+ @test_tracker_info(uuid="eef80cf3-1fd2-4526-969b-6af2dce785d7")
+ def test_multiple_concurrent_services_both_unsolicited_passive(self):
+ """Validate multiple concurrent discovery sessions running on both devices.
+ - DUT1 & DUT2 running Publish for X
+ - DUT1 & DUT2 running Publish for Y
+ - DUT1 Subscribes for X
+ - DUT2 Subscribes for Y
+ Message exchanges.
+
+ Both sessions are Unsolicited/Passive.
+
+ Note: test requires that devices support 2 publish sessions concurrently.
+ The test will be skipped if the devices are not capable.
+ """
+ self.run_multiple_concurrent_services(
+ type_x=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE],
+ type_y=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE])
+
+ @test_tracker_info(uuid="46739f04-ab2b-4556-b1a4-9aa2774869b5")
+ def test_multiple_concurrent_services_both_solicited_active(self):
+ """Validate multiple concurrent discovery sessions running on both devices.
+ - DUT1 & DUT2 running Publish for X
+ - DUT1 & DUT2 running Publish for Y
+ - DUT1 Subscribes for X
+ - DUT2 Subscribes for Y
+ Message exchanges.
+
+ Both sessions are Solicited/Active.
+
+ Note: test requires that devices support 2 publish sessions concurrently.
+ The test will be skipped if the devices are not capable.
+ """
+ self.run_multiple_concurrent_services(
+ type_x=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE],
+ type_y=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE])
+
+ @test_tracker_info(uuid="5f8f7fd2-4a0e-4cca-8cbb-6d54353f2baa")
+ def test_multiple_concurrent_services_mix_unsolicited_solicited(self):
+ """Validate multiple concurrent discovery sessions running on both devices.
+ - DUT1 & DUT2 running Publish for X
+ - DUT1 & DUT2 running Publish for Y
+ - DUT1 Subscribes for X
+ - DUT2 Subscribes for Y
+ Message exchanges.
+
+ Session A is Unsolicited/Passive.
+ Session B is Solicited/Active.
+
+ Note: test requires that devices support 2 publish sessions concurrently.
+ The test will be skipped if the devices are not capable.
+ """
+ self.run_multiple_concurrent_services(
+ type_x=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE],
+ type_y=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE])
+
+ #########################################################
+
+ @test_tracker_info(uuid="908ec896-fc7a-4ee4-b633-a2f042b74448")
+ def test_upper_lower_service_name_equivalence(self):
+ """Validate that Service Name is case-insensitive. Publish a service name
+ with mixed case, subscribe to the same service name with alternative case
+ and verify that discovery happens."""
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ pub_service_name = "GoogleAbCdEf"
+ sub_service_name = "GoogleaBcDeF"
+
+ autils.create_discovery_pair(p_dut, s_dut,
+ p_config=autils.create_discovery_config(
+ pub_service_name,
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ s_config=autils.create_discovery_config(
+ sub_service_name,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ device_startup_offset=self.device_startup_offset)
diff --git a/acts/tests/google/wifi/aware/functional/MacRandomTest.py b/acts/tests/google/wifi/aware/functional/MacRandomTest.py
index 329ead4..af1503b 100644
--- a/acts/tests/google/wifi/aware/functional/MacRandomTest.py
+++ b/acts/tests/google/wifi/aware/functional/MacRandomTest.py
@@ -57,6 +57,10 @@
(NAN data-interface) on each enable/disable cycle"""
dut = self.android_devices[0]
+ # re-enable randomization interval (since if disabled it may also disable
+ # the 'randomize on enable' feature).
+ autils.configure_mac_random_interval(dut, 1800)
+
# DUT: attach and wait for confirmation & identity 10 times
mac_addresses = {}
for i in range(self.NUM_ITERATIONS):
@@ -108,9 +112,8 @@
dut = self.android_devices[0]
- # set randomization interval to 5 seconds
- dut.adb.shell("cmd wifiaware native_api set mac_random_interval_sec %d" %
- RANDOM_INTERVAL)
+ # set randomization interval to 120 seconds
+ autils.configure_mac_random_interval(dut, RANDOM_INTERVAL)
# attach and wait for first identity
id = dut.droid.wifiAwareAttach(True)
diff --git a/acts/tests/google/wifi/aware/performance/LatencyTest.py b/acts/tests/google/wifi/aware/performance/LatencyTest.py
index bde9ff4..bfadebc 100644
--- a/acts/tests/google/wifi/aware/performance/LatencyTest.py
+++ b/acts/tests/google/wifi/aware/performance/LatencyTest.py
@@ -92,8 +92,8 @@
s_dut.pretty_name = "Subscriber"
# override the default DW configuration
- autils.config_dw_all_modes(p_dut, dw_24ghz, dw_5ghz)
- autils.config_dw_all_modes(s_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
latencies = []
failed_discoveries = 0
@@ -174,8 +174,8 @@
s_dut.pretty_name = "Subscriber"
# override the default DW configuration
- autils.config_dw_all_modes(p_dut, dw_24ghz, dw_5ghz)
- autils.config_dw_all_modes(s_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
# Publisher+Subscriber: attach and wait for confirmation
p_id = p_dut.droid.wifiAwareAttach(False)
@@ -253,8 +253,8 @@
s_dut = self.android_devices[1]
# override the default DW configuration
- autils.config_dw_all_modes(p_dut, dw_24ghz, dw_5ghz)
- autils.config_dw_all_modes(s_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
# Start up a discovery session
(p_id, s_id, p_disc_id, s_disc_id,
@@ -341,8 +341,8 @@
resp_dut.pretty_name = 'Responder'
# override the default DW configuration
- autils.config_dw_all_modes(init_dut, dw_24ghz, dw_5ghz)
- autils.config_dw_all_modes(resp_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(init_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(resp_dut, dw_24ghz, dw_5ghz)
# Initiator+Responder: attach and wait for confirmation & identity
init_id = init_dut.droid.wifiAwareAttach(True)
@@ -427,6 +427,209 @@
dw_5ghz))
results[key_avail]["ndp_setup_failures"] = ndp_setup_failures
+ def run_end_to_end_latency(self, results, dw_24ghz, dw_5ghz, num_iterations,
+ startup_offset, include_setup):
+ """Measure the latency for end-to-end communication link setup:
+ - Start Aware
+ - Discovery
+ - Message from Sub -> Pub
+ - Message from Pub -> Sub
+ - NDP setup
+
+ Args:
+ results: Result array to be populated - will add results (not erase it)
+ dw_24ghz: DW interval in the 2.4GHz band.
+ dw_5ghz: DW interval in the 5GHz band.
+ startup_offset: The start-up gap (in seconds) between the two devices
+ include_setup: True to include the cluster setup in the latency
+ measurements.
+ """
+ key = "dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
+ results[key] = {}
+ results[key]["num_iterations"] = num_iterations
+
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # override the default DW configuration
+ autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
+
+ latencies = []
+
+ # allow for failures here since running lots of samples and would like to
+ # get the partial data even in the presence of errors
+ failures = 0
+
+ if not include_setup:
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ for i in range(num_iterations):
+ while (True): # for pseudo-goto/finalize
+ timestamp_start = time.perf_counter()
+
+ if include_setup:
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # start publish
+ p_disc_id, p_disc_event = self.start_discovery_session(
+ p_dut, p_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED)
+
+ # start subscribe
+ s_disc_id, s_session_event = self.start_discovery_session(
+ s_dut, s_id, False, aconsts.SUBSCRIBE_TYPE_PASSIVE)
+
+ # wait for discovery (allow for failures here since running lots of
+ # samples and would like to get the partial data even in the presence of
+ # errors)
+ try:
+ event = s_dut.ed.pop_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ autils.EVENT_TIMEOUT)
+ s_dut.log.info("[Subscriber] SESSION_CB_ON_SERVICE_DISCOVERED: %s",
+ event["data"])
+ peer_id_on_sub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] Timed out while waiting for "
+ "SESSION_CB_ON_SERVICE_DISCOVERED")
+ failures = failures + 1
+ break
+
+ # message from Sub -> Pub
+ msg_s2p = "Message Subscriber -> Publisher #%d" % i
+ next_msg_id = self.get_next_msg_id()
+ s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, next_msg_id,
+ msg_s2p, 0)
+
+ # wait for Tx confirmation
+ try:
+ s_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_SENT,
+ autils.EVENT_TIMEOUT)
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_SENT")
+ failures = failures + 1
+ break
+
+ # wait for Rx confirmation (and validate contents)
+ try:
+ event = p_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+ autils.EVENT_TIMEOUT)
+ peer_id_on_pub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+ if (event["data"][
+ aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING] != msg_s2p):
+ p_dut.log.info("[Publisher] Corrupted input message - %s", event)
+ failures = failures + 1
+ break
+ except queue.Empty:
+ p_dut.log.info("[Publisher] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_RECEIVED")
+ failures = failures + 1
+ break
+
+ # message from Pub -> Sub
+ msg_p2s = "Message Publisher -> Subscriber #%d" % i
+ next_msg_id = self.get_next_msg_id()
+ p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub, next_msg_id,
+ msg_p2s, 0)
+
+ # wait for Tx confirmation
+ try:
+ p_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_SENT,
+ autils.EVENT_TIMEOUT)
+ except queue.Empty:
+ p_dut.log.info("[Publisher] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_SENT")
+ failures = failures + 1
+ break
+
+ # wait for Rx confirmation (and validate contents)
+ try:
+ event = s_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+ autils.EVENT_TIMEOUT)
+ if (event["data"][
+ aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING] != msg_p2s):
+ s_dut.log.info("[Subscriber] Corrupted input message - %s", event)
+ failures = failures + 1
+ break
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_RECEIVED")
+ failures = failures + 1
+ break
+
+ # create NDP
+
+ # Publisher: request network
+ p_req_key = autils.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id,
+ peer_id_on_pub, None))
+
+ # Subscriber: request network
+ s_req_key = autils.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id,
+ peer_id_on_sub, None))
+
+ # Publisher & Subscriber: wait for network formation
+ try:
+ p_net_event = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT, (
+ cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT, (
+ cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+ except:
+ failures = failures + 1
+ break
+
+ p_aware_if = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ s_aware_if = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ p_ipv6 = \
+ p_dut.droid.connectivityGetLinkLocalIpv6Address(p_aware_if).split("%")[
+ 0]
+ s_ipv6 = \
+ s_dut.droid.connectivityGetLinkLocalIpv6Address(s_aware_if).split("%")[
+ 0]
+
+ p_dut.log.info("[Publisher] IF=%s, IPv6=%s", p_aware_if, p_ipv6)
+ s_dut.log.info("[Subscriber] IF=%s, IPv6=%s", s_aware_if, s_ipv6)
+
+ latencies.append(time.perf_counter() - timestamp_start)
+ break
+
+ # destroy sessions
+ p_dut.droid.wifiAwareDestroyDiscoverySession(p_disc_id)
+ s_dut.droid.wifiAwareDestroyDiscoverySession(s_disc_id)
+ if include_setup:
+ p_dut.droid.wifiAwareDestroy(p_id)
+ s_dut.droid.wifiAwareDestroy(s_id)
+
+ autils.extract_stats(
+ p_dut,
+ data=latencies,
+ results=results[key],
+ key_prefix="",
+ log_prefix="End-to-End(dw24=%d, dw5=%d)" % (dw_24ghz, dw_5ghz))
+ results[key]["failures"] = failures
+
########################################################################
@@ -438,8 +641,8 @@
self.run_synchronization_latency(
results=results,
do_unsolicited_passive=True,
- dw_24ghz=aconsts.DW_24_INTERACTIVE,
- dw_5ghz=aconsts.DW_5_INTERACTIVE,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
num_iterations=10,
startup_offset=startup_offset,
timeout_period=20)
@@ -454,8 +657,8 @@
self.run_synchronization_latency(
results=results,
do_unsolicited_passive=True,
- dw_24ghz=aconsts.DW_24_NON_INTERACTIVE,
- dw_5ghz=aconsts.DW_5_NON_INTERACTIVE,
+ dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
num_iterations=10,
startup_offset=startup_offset,
timeout_period=20)
@@ -469,8 +672,8 @@
self.run_discovery_latency(
results=results,
do_unsolicited_passive=True,
- dw_24ghz=aconsts.DW_24_INTERACTIVE,
- dw_5ghz=aconsts.DW_5_INTERACTIVE,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
num_iterations=100)
asserts.explicit_pass(
"test_discovery_latency_default_parameters finished", extras=results)
@@ -482,8 +685,8 @@
self.run_discovery_latency(
results=results,
do_unsolicited_passive=True,
- dw_24ghz=aconsts.DW_24_NON_INTERACTIVE,
- dw_5ghz=aconsts.DW_5_NON_INTERACTIVE,
+ dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
num_iterations=100)
asserts.explicit_pass(
"test_discovery_latency_non_interactive_dws finished", extras=results)
@@ -510,8 +713,8 @@
results = {}
self.run_message_latency(
results=results,
- dw_24ghz=aconsts.DW_24_INTERACTIVE,
- dw_5ghz=aconsts.DW_5_INTERACTIVE,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
num_iterations=100)
asserts.explicit_pass(
"test_message_latency_default_dws finished", extras=results)
@@ -524,8 +727,8 @@
results = {}
self.run_message_latency(
results=results,
- dw_24ghz=aconsts.DW_24_NON_INTERACTIVE,
- dw_5ghz=aconsts.DW_5_NON_INTERACTIVE,
+ dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
num_iterations=100)
asserts.explicit_pass(
"test_message_latency_non_interactive_dws finished", extras=results)
@@ -536,8 +739,8 @@
results = {}
self.run_ndp_oob_latency(
results=results,
- dw_24ghz=aconsts.DW_24_INTERACTIVE,
- dw_5ghz=aconsts.DW_5_INTERACTIVE,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
num_iterations=100)
asserts.explicit_pass(
"test_ndp_setup_latency_default_dws finished", extras=results)
@@ -549,8 +752,49 @@
results = {}
self.run_ndp_oob_latency(
results=results,
- dw_24ghz=aconsts.DW_24_NON_INTERACTIVE,
- dw_5ghz=aconsts.DW_5_NON_INTERACTIVE,
+ dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
num_iterations=100)
asserts.explicit_pass(
"test_ndp_setup_latency_non_interactive_dws finished", extras=results)
+
+ def test_end_to_end_latency_default_dws(self):
+ """Measure the latency for end-to-end communication link setup:
+ - Start Aware
+ - Discovery
+ - Message from Sub -> Pub
+ - Message from Pub -> Sub
+ - NDP setup
+ """
+ results = {}
+ self.run_end_to_end_latency(
+ results,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+ num_iterations=10,
+ startup_offset=0,
+ include_setup=True)
+ asserts.explicit_pass(
+ "test_end_to_end_latency_default_dws finished", extras=results)
+
+ def test_end_to_end_latency_post_attach_default_dws(self):
+ """Measure the latency for end-to-end communication link setup without
+ the initial synchronization:
+ - Start Aware & synchronize initially
+ - Loop:
+ - Discovery
+ - Message from Sub -> Pub
+ - Message from Pub -> Sub
+ - NDP setup
+ """
+ results = {}
+ self.run_end_to_end_latency(
+ results,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+ num_iterations=10,
+ startup_offset=0,
+ include_setup=False)
+ asserts.explicit_pass(
+ "test_end_to_end_latency_post_attach_default_dws finished",
+ extras=results)
diff --git a/acts/tests/google/wifi/aware/performance/ThroughputTest.py b/acts/tests/google/wifi/aware/performance/ThroughputTest.py
index 6cf1046..ddb6d15 100644
--- a/acts/tests/google/wifi/aware/performance/ThroughputTest.py
+++ b/acts/tests/google/wifi/aware/performance/ThroughputTest.py
@@ -36,7 +36,7 @@
PASSPHRASE2 = "This is some random passphrase - very very secure - but diff!!"
def __init__(self, controllers):
- AwareBaseTest.__init__(self, controllers)
+ super(ThroughputTest, self).__init__(controllers)
def request_network(self, dut, ns):
"""Request a Wi-Fi Aware network.
@@ -302,12 +302,12 @@
# Wait for network
init_net_event = autils.wait_for_event_with_keys(
- init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
(cconsts.NETWORK_CB_KEY_EVENT,
cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
(cconsts.NETWORK_CB_KEY_ID, init_req_key))
resp_net_event = autils.wait_for_event_with_keys(
- resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
(cconsts.NETWORK_CB_KEY_EVENT,
cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
(cconsts.NETWORK_CB_KEY_ID, resp_req_key))
diff --git a/acts/tests/google/wifi/aware/stress/DataPathStressTest.py b/acts/tests/google/wifi/aware/stress/DataPathStressTest.py
index 9a862cb..f718007 100644
--- a/acts/tests/google/wifi/aware/stress/DataPathStressTest.py
+++ b/acts/tests/google/wifi/aware/stress/DataPathStressTest.py
@@ -18,6 +18,7 @@
import time
from acts import asserts
+from acts.test_decorators import test_tracker_info
from acts.test_utils.net import connectivity_const as cconsts
from acts.test_utils.wifi.aware import aware_const as aconsts
from acts.test_utils.wifi.aware import aware_test_utils as autils
@@ -30,16 +31,29 @@
ATTACH_ITERATIONS = 2
# Number of iterations on create/destroy NDP in each discovery session.
- NDP_ITERATIONS = 20
+ NDP_ITERATIONS = 50
+
+ # Maximum percentage of NDP setup failures over all iterations
+ MAX_FAILURE_PERCENTAGE = 1
def __init__(self, controllers):
AwareBaseTest.__init__(self, controllers)
################################################################
- def test_oob_ndp_stress(self):
+ def run_oob_ndp_stress(self, attach_iterations, ndp_iterations,
+ trigger_failure_on_index=None):
"""Run NDP (NAN data-path) stress test creating and destroying Aware
- attach sessions, discovery sessions, and NDPs."""
+ attach sessions, discovery sessions, and NDPs.
+
+ Args:
+ attach_iterations: Number of attach sessions.
+ ndp_iterations: Number of NDP to be attempted per attach session.
+ trigger_failure_on_index: Trigger a failure on this NDP iteration (the
+ mechanism is to request NDP on Initiator
+ before issuing the requeest on the Responder).
+ If None then no artificial failure triggered.
+ """
init_dut = self.android_devices[0]
init_dut.pretty_name = 'Initiator'
resp_dut = self.android_devices[1]
@@ -50,7 +64,7 @@
ndp_resp_setup_success = 0
ndp_resp_setup_failures = 0
- for attach_iter in range(self.ATTACH_ITERATIONS):
+ for attach_iter in range(attach_iterations):
init_id = init_dut.droid.wifiAwareAttach(True)
autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
init_ident_event = autils.wait_for_event(
@@ -68,18 +82,41 @@
# to execute the data-path request)
time.sleep(autils.WAIT_FOR_CLUSTER)
- for ndp_iteration in range(self.NDP_ITERATIONS):
- # Responder: request network
- resp_req_key = autils.request_network(
- resp_dut,
- resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
- resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
+ for ndp_iteration in range(ndp_iterations):
+ if trigger_failure_on_index != ndp_iteration:
+ # Responder: request network
+ resp_req_key = autils.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
- # Initiator: request network
- init_req_key = autils.request_network(
- init_dut,
- init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
- init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
+ # Wait a minimal amount of time to let the Responder configure itself
+ # and be ready for the request. While calling it first may be
+ # sufficient there are no guarantees that a glitch may slow the
+ # Responder slightly enough to invert the setup order.
+ time.sleep(1)
+
+ # Initiator: request network
+ init_req_key = autils.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
+ else:
+ # Initiator: request network
+ init_req_key = autils.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
+
+ # Wait a minimal amount of time to let the Initiator configure itself
+ # to guarantee failure!
+ time.sleep(2)
+
+ # Responder: request network
+ resp_req_key = autils.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
# Initiator: wait for network formation
got_on_available = False
@@ -138,9 +175,31 @@
results['ndp_init_setup_failures'] = ndp_init_setup_failures
results['ndp_resp_setup_success'] = ndp_resp_setup_success
results['ndp_resp_setup_failures'] = ndp_resp_setup_failures
- asserts.assert_equal(
- ndp_init_setup_failures + ndp_resp_setup_failures,
- 0,
- 'test_oob_ndp_stress finished',
- extras=results)
- asserts.explicit_pass("test_oob_ndp_stress done", extras=results)
+ max_failures = (
+ self.MAX_FAILURE_PERCENTAGE * attach_iterations * ndp_iterations / 100)
+ if max_failures == 0:
+ max_failures = 1
+ if trigger_failure_on_index is not None:
+ max_failures = max_failures + 1 # for the triggered failure
+ asserts.assert_true(
+ (ndp_init_setup_failures + ndp_resp_setup_failures) < (2 * max_failures),
+ 'NDP setup failure rate exceeds threshold', extras=results)
+ asserts.explicit_pass("test_oob_ndp_stress* done", extras=results)
+
+ @test_tracker_info(uuid="a20a96ba-e71f-4d31-b850-b88a75381981")
+ def test_oob_ndp_stress(self):
+ """Run NDP (NAN data-path) stress test creating and destroying Aware
+ attach sessions, discovery sessions, and NDPs."""
+ self.run_oob_ndp_stress(self.ATTACH_ITERATIONS, self.NDP_ITERATIONS)
+
+ @test_tracker_info(uuid="1fb4a383-bf1a-411a-a904-489dd9e29c6a")
+ def test_oob_ndp_stress_failure_case(self):
+ """Run NDP (NAN data-path) stress test creating and destroying Aware
+ attach sessions, discovery sessions, and NDPs.
+
+ Verify recovery from failure by triggering an artifical failure and
+ verifying that all subsequent iterations succeed.
+ """
+ self.run_oob_ndp_stress(attach_iterations=1,
+ ndp_iterations=10,
+ trigger_failure_on_index=3)
diff --git a/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py b/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py
index 8b3d925..eaa5d19 100644
--- a/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py
+++ b/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py
@@ -15,9 +15,9 @@
# limitations under the License.
import queue
-import time
from acts import asserts
+from acts.test_decorators import test_tracker_info
from acts.test_utils.wifi.aware import aware_const as aconsts
from acts.test_utils.wifi.aware import aware_test_utils as autils
from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
@@ -37,6 +37,7 @@
####################################################################
+ @test_tracker_info(uuid="783791e5-7726-44e0-ac5b-98c1dbf493cb")
def test_discovery_stress(self):
"""Create and destroy a random array of discovery sessions, up to the
limit of capabilities."""
@@ -101,9 +102,6 @@
results = {}
results['discovery_setup_success'] = discovery_setup_success
results['discovery_setup_fail'] = discovery_setup_fail
- asserts.assert_equal(
- discovery_setup_fail,
- 0,
- 'test_discovery_stress finished',
- extras=results)
+ asserts.assert_equal(discovery_setup_fail, 0,
+ 'Discovery setup failures', extras=results)
asserts.explicit_pass('test_discovery_stress done', extras=results)
diff --git a/acts/tests/google/wifi/aware/stress/MessagesStressTest.py b/acts/tests/google/wifi/aware/stress/MessagesStressTest.py
index 5871d61..34827f1 100644
--- a/acts/tests/google/wifi/aware/stress/MessagesStressTest.py
+++ b/acts/tests/google/wifi/aware/stress/MessagesStressTest.py
@@ -17,6 +17,7 @@
import queue
from acts import asserts
+from acts.test_decorators import test_tracker_info
from acts.test_utils.wifi.aware import aware_const as aconsts
from acts.test_utils.wifi.aware import aware_test_utils as autils
from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
@@ -29,7 +30,19 @@
class MessagesStressTest(AwareBaseTest):
"""Set of stress tests for Wi-Fi Aware L2 (layer 2) message exchanges."""
+
+ # Number of iterations in the stress test (number of messages)
NUM_ITERATIONS = 100
+
+ # Maximum permitted percentage of messages which fail to be transmitted
+ # correctly
+ MAX_TX_FAILURE_PERCENTAGE = 2
+
+ # Maximum permitted percentage of messages which are received more than once
+ # (indicating, most likely, that the ACK wasn't received and the message was
+ # retransmitted)
+ MAX_DUPLICATE_RX_PERCENTAGE = 2
+
SERVICE_NAME = "GoogleTestServiceXY"
def __init__(self, controllers):
@@ -184,6 +197,7 @@
#######################################################################
+ @test_tracker_info(uuid="e88c060f-4ca7-41c1-935a-d3d62878ec0b")
def test_stress_message(self):
"""Stress test for bi-directional message transmission and reception."""
p_dut = self.android_devices[0]
@@ -250,6 +264,9 @@
# clear errors
asserts.assert_equal(results["tx_unknown_ids"], 0, "Message ID corruption",
results)
+ asserts.assert_equal(results["tx_count_neither"], 0,
+ "Tx message with no success or fail indication",
+ results)
asserts.assert_equal(results["tx_count_duplicate_fail"], 0,
"Duplicate Tx fail messages", results)
asserts.assert_equal(results["tx_count_duplicate_success"], 0,
@@ -266,4 +283,14 @@
asserts.assert_equal(results["rx_count_fail_tx_indication"], 0,
"Message received but Tx didn't get ACK", results)
- asserts.explicit_pass("test_stress_message done", extras=results)
+ # permissible failures based on thresholds
+ asserts.assert_true(results["tx_count_fail"] <= (
+ self.MAX_TX_FAILURE_PERCENTAGE * self.NUM_ITERATIONS / 100),
+ "Number of Tx failures exceeds threshold",
+ extras=results)
+ asserts.assert_true(results["rx_count_duplicate"] <= (
+ self.MAX_DUPLICATE_RX_PERCENTAGE * self.NUM_ITERATIONS / 100),
+ "Number of duplicate Rx exceeds threshold",
+ extras=results)
+
+ asserts.explicit_pass("test_stress_message done", extras=results)
\ No newline at end of file
diff --git a/acts/tests/google/wifi/example_config.json b/acts/tests/google/wifi/example_config.json
new file mode 100644
index 0000000..42b0be7
--- /dev/null
+++ b/acts/tests/google/wifi/example_config.json
@@ -0,0 +1,38 @@
+{
+ "_description": "This and example IOT WiFi testbed.",
+ "testbed": [
+ {
+ "_description": "WiFi testbed with 1 devices",
+ "name": "<test station name>",
+ "AndroidDevice": [
+ "<device serial>"
+ ],
+ "IPerfServer": [
+ 5005
+ ]
+ }
+ ],
+ "logpath": "/tmp/ACTS_logs",
+ "testpaths": [
+ "<path to acts root>/tools/test/connectivity/acts/tests/google/wifi"
+ ],
+ "iot_networks": [
+ {
+ "SSID": "<your SSID 2G>",
+ "password": "<your password>"
+ },
+ {
+ "SSID": "<your SSID 5G>",
+ "password": "<your password>"
+ },
+ {
+ "SSID": "<your SSID 2G 2>",
+ "password": "<your password>"
+ },
+ {
+ "SSID": "<your SSID 5G 2>",
+ "password": "<your password>"
+ }
+ ],
+ "iperf_server_address": "<your IP address>"
+}
\ No newline at end of file
diff --git a/acts/tests/google/wifi/rtt/README.md b/acts/tests/google/wifi/rtt/README.md
new file mode 100644
index 0000000..639c3d8
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/README.md
@@ -0,0 +1,56 @@
+# Wi-Fi RTT (IEEE 802.11mc) Integrated (ACTS/sl4a) Test Suite
+
+This directory contains ACTS/sl4a test scripts to verify and characterize
+the Wi-Fi RTT (IEEE 802.11mc) implementation in Android.
+
+There are 2 groups of tests (in 2 sub-directories):
+
+* functional: Functional tests that each implementation must pass. These
+are pass/fail tests.
+* stress: Tests which run through a large number of iterations to stress
+test the implementation. Considering that some failures are expected,
+especially in an over-the-air situation, pass/fail criteria are either
+not provided or may not apply to all implementations or test environments.
+
+The tests can be executed using:
+
+`act.py -c <config> -tc {<test_class>|<test_class>:<test_name>}`
+
+Where a test file is any of the `.py` files in any of the test sub-directories.
+If a test class is specified, then all tests within that test class are executed.
+
+## Test Beds
+The Wi-Fi RTT tests support several different test scenarios which require different test bed
+configuration. The test beds and their corresponding test files are:
+
+* Device Under Test + AP which supports IEEE 802.11mc
+ * functional/RangeApSupporting11McTest.py
+ * functional/RttRequestManagementTest.py
+ * functional/RttDisableTest.py
+ * stress/StressRangeApTest.py
+* Device Under Test + AP which does **not** support IEEE 802.11mc
+ * functional/RangeApNonSupporting11McTest.py
+* 2 Devices Under Test
+ * functional/RangeAwareTest.py
+ * functional/AwareDiscoveryWithRangingTest.py
+ * functional/RangeSoftApTest.py
+ * stress/StressRangeAwareTest.py
+
+## Test Configurations
+The test configuration, the `<config>` in the commands above, is stored in
+the *config* sub-directory. The configuration simply uses all connected
+devices without listing specific serial numbers. Note that some tests use a
+single device while others use 2 devices.
+
+The only provided configuration is *wifi_rtt.json*.
+
+The configuration defines the following keys to configure the test:
+
+* **lci_reference**, **lcr_reference**: Arrays of bytes used to validate that the *correct* LCI and
+LCR were received from the AP. These are empty by default and should be configured to match the
+configuration of the AP used in the test.
+* **rtt_reference_distance_mm**: The reference distance, in mm, between the test device and the test
+AP or between the two test devices (for Aware ranging tests).
+* **stress_test_min_iteration_count**, **stress_test_target_run_time_sec**: Parameters used to
+control the length and duration of the stress tests. The stress test runs for the specified number
+of iterations or for the specified duration - whichever is longer.
diff --git a/acts/tests/google/wifi/rtt/config/wifi_rtt.json b/acts/tests/google/wifi/rtt/config/wifi_rtt.json
new file mode 100644
index 0000000..41f77dc
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/config/wifi_rtt.json
@@ -0,0 +1,20 @@
+{
+ "_description": "This is a test configuration file for Wi-Fi RTT tests.",
+ "testbed":
+ [
+ {
+ "_description": "Wi-Fi RTT testbed: auto-detect all attached devices",
+ "name": "WifiRttAllAttached",
+ "AndroidDevice": "*"
+ }
+ ],
+ "logpath": "~/logs",
+ "testpaths": ["./tools/test/connectivity/acts/tests/google/wifi"],
+ "adb_logcat_param": "-b all",
+ "aware_default_power_mode": "INTERACTIVE",
+ "lci_reference": [],
+ "lcr_reference": [],
+ "rtt_reference_distance_mm": 100,
+ "stress_test_min_iteration_count": 100,
+ "stress_test_target_run_time_sec" : 30
+}
diff --git a/acts/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py b/acts/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
new file mode 100644
index 0000000..f6d7c8d
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
@@ -0,0 +1,1567 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2017 - 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.
+
+import sys
+import time
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.net import connectivity_const as cconsts
+from acts.test_utils.wifi.aware import aware_const as aconsts
+from acts.test_utils.wifi.aware import aware_test_utils as autils
+from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class AwareDiscoveryWithRangingTest(AwareBaseTest, RttBaseTest):
+ """Set of tests for Wi-Fi Aware discovery configured with ranging (RTT)."""
+
+ SERVICE_NAME = "GoogleTestServiceRRRRR"
+
+ # Flag indicating whether the device has a limitation that does not allow it
+ # to execute Aware-based Ranging (whether direct or as part of discovery)
+ # whenever NDP is enabled.
+ RANGING_NDP_CONCURRENCY_LIMITATION = True
+
+ # Flag indicating whether the device has a limitation that does not allow it
+ # to execute Aware-based Ranging (whether direct or as part of discovery)
+ # for both Initiators and Responders. Only the first mode works.
+ RANGING_INITIATOR_RESPONDER_CONCURRENCY_LIMITATION = True
+
+ def __init__(self, controllers):
+ AwareBaseTest.__init__(self, controllers)
+ RttBaseTest.__init__(self, controllers)
+
+ def setup_test(self):
+ """Manual setup here due to multiple inheritance: explicitly execute the
+ setup method from both parents."""
+ AwareBaseTest.setup_test(self)
+ RttBaseTest.setup_test(self)
+
+ def teardown_test(self):
+ """Manual teardown here due to multiple inheritance: explicitly execute the
+ teardown method from both parents."""
+ AwareBaseTest.teardown_test(self)
+ RttBaseTest.teardown_test(self)
+
+ #########################################################################
+
+ def run_discovery(self, p_config, s_config, expect_discovery,
+ expect_range=False):
+ """Run discovery on the 2 input devices with the specified configurations.
+
+ Args:
+ p_config, s_config: Publisher and Subscriber discovery configuration.
+ expect_discovery: True or False indicating whether discovery is expected
+ with the specified configurations.
+ expect_range: True if we expect distance results (i.e. ranging to happen).
+ Only relevant if expect_discovery is True.
+ Returns:
+ p_dut, s_dut: Publisher/Subscribe DUT
+ p_disc_id, s_disc_id: Publisher/Subscribe discovery session ID
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Publisher: start publish and wait for confirmation
+ p_disc_id = p_dut.droid.wifiAwarePublish(p_id, p_config)
+ autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Subscriber: start subscribe and wait for confirmation
+ s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, s_config)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # Subscriber: wait or fail on service discovery
+ if expect_discovery:
+ event = autils.wait_for_event(s_dut,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ if expect_range:
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging expected!")
+ else:
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging NOT expected!")
+ else:
+ autils.fail_on_event(s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+ # (single) sleep for timeout period and then verify that no further events
+ time.sleep(autils.EVENT_TIMEOUT)
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ return p_dut, s_dut, p_disc_id, s_disc_id
+
+ def run_discovery_update(self, p_dut, s_dut, p_disc_id, s_disc_id, p_config,
+ s_config, expect_discovery, expect_range=False):
+ """Run discovery on the 2 input devices with the specified update
+ configurations. I.e. update the existing discovery sessions with the
+ configurations.
+
+ Args:
+ p_dut, s_dut: Publisher/Subscriber DUTs.
+ p_disc_id, s_disc_id: Publisher/Subscriber discovery session IDs.
+ p_config, s_config: Publisher and Subscriber discovery configuration.
+ expect_discovery: True or False indicating whether discovery is expected
+ with the specified configurations.
+ expect_range: True if we expect distance results (i.e. ranging to happen).
+ Only relevant if expect_discovery is True.
+ """
+
+ # try to perform reconfiguration at same time (and wait once for all
+ # confirmations)
+ if p_config is not None:
+ p_dut.droid.wifiAwareUpdatePublish(p_disc_id, p_config)
+ if s_config is not None:
+ s_dut.droid.wifiAwareUpdateSubscribe(s_disc_id, s_config)
+
+ if p_config is not None:
+ autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED)
+ if s_config is not None:
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED)
+
+ # Subscriber: wait or fail on service discovery
+ if expect_discovery:
+ event = autils.wait_for_event(s_dut,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ if expect_range:
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging expected!")
+ else:
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging NOT expected!")
+ else:
+ autils.fail_on_event(s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+ # (single) sleep for timeout period and then verify that no further events
+ time.sleep(autils.EVENT_TIMEOUT)
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ def run_discovery_prange_sminmax_outofrange(self, is_unsolicited_passive):
+ """Run discovery with ranging:
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that out of range (min=large,
+ max=large+1)
+
+ Expected: no discovery
+
+ This is a baseline test for the update-configuration tests.
+
+ Args:
+ is_unsolicited_passive: True for Unsolicited/Passive, False for
+ Solicited/Active.
+ Returns: the return arguments of the run_discovery.
+ """
+ pub_type = (aconsts.PUBLISH_TYPE_UNSOLICITED if is_unsolicited_passive
+ else aconsts.PUBLISH_TYPE_SOLICITED)
+ sub_type = (aconsts.SUBSCRIBE_TYPE_PASSIVE if is_unsolicited_passive
+ else aconsts.SUBSCRIBE_TYPE_ACTIVE)
+ return self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME, pub_type,
+ ssi=self.getname(2)),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME, sub_type,
+ ssi=self.getname(2)),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001),
+ expect_discovery=False)
+
+ def getname(self, level=1):
+ """Python magic to return the name of the *calling* function.
+
+ Args:
+ level: How many levels up to go for the method name. Default = calling
+ method.
+ """
+ return sys._getframe(level).f_code.co_name
+
+ #########################################################################
+ # Run discovery with ranging configuration.
+ #
+ # Names: test_ranged_discovery_<ptype>_<stype>_<p_range>_<s_range>_<ref_dist>
+ #
+ # where:
+ # <ptype>_<stype>: unsolicited_passive or solicited_active
+ # <p_range>: prange or pnorange
+ # <s_range>: smin or smax or sminmax or snorange
+ # <ref_distance>: inrange or outoforange
+ #########################################################################
+
+ @test_tracker_info(uuid="3a216e9a-7a57-4741-89c0-84456975e1ac")
+ def test_ranged_discovery_unsolicited_passive_prange_snorange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber disables ranging
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="859a321e-18e2-437b-aa7a-2a45a42ee737")
+ def test_ranged_discovery_solicited_active_prange_snorange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber disables ranging
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="12a4f899-4f70-4641-8f3c-351004669b71")
+ def test_ranged_discovery_unsolicited_passive_pnorange_smax_inrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher disables ranging
+ - Subscriber enables ranging with max such that always within range (large
+ max)
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=False),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="b7f90793-113d-4355-be20-856d92ac939f")
+ def test_ranged_discovery_solicited_active_pnorange_smax_inrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher disables ranging
+ - Subscriber enables ranging with max such that always within range (large
+ max)
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=False),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="da3ab6df-58f9-44ae-b7be-8200d9e1bb76")
+ def test_ranged_discovery_unsolicited_passive_pnorange_smin_outofrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher disables ranging
+ - Subscriber enables ranging with min such that always out of range (large
+ min)
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=False),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="275e0806-f266-4fa6-9ca0-1cfd7b65a6ca")
+ def test_ranged_discovery_solicited_active_pnorange_smin_outofrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher disables ranging
+ - Subscriber enables ranging with min such that always out of range (large
+ min)
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=False),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="8cd0aa1e-6866-4a5d-a550-f25483eebea1")
+ def test_ranged_discovery_unsolicited_passive_prange_smin_inrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min such that in range (min=0)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="97c22c54-669b-4f7a-bf51-2f484e5f3e74")
+ def test_ranged_discovery_unsolicited_passive_prange_smax_inrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with max such that in range (max=large)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="616673d7-9d0b-43de-a378-e5e949b51b32")
+ def test_ranged_discovery_unsolicited_passive_prange_sminmax_inrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that in range (min=0,
+ max=large)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="2bf84912-dcad-4a8f-971f-e445a07f05ce")
+ def test_ranged_discovery_solicited_active_prange_smin_inrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min such that in range (min=0)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="5cfd7961-9665-4742-a1b5-2d1fc97f9795")
+ def test_ranged_discovery_solicited_active_prange_smax_inrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with max such that in range (max=large)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="5cf650ad-0b42-4b7d-9e05-d5f45fe0554d")
+ def test_ranged_discovery_solicited_active_prange_sminmax_inrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that in range (min=0,
+ max=large)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="5277f418-ac35-43ce-9b30-3c895272898e")
+ def test_ranged_discovery_unsolicited_passive_prange_smin_outofrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min such that out of range (min=large)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="8a7e6ab1-acf4-41a7-a5fb-8c164d593b5f")
+ def test_ranged_discovery_unsolicited_passive_prange_smax_outofrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with max such that in range (max=0)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=0),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="b744f5f9-2641-4373-bf86-3752e2f9aace")
+ def test_ranged_discovery_unsolicited_passive_prange_sminmax_outofrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that out of range (min=large,
+ max=large+1)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="d2e94199-b2e6-4fa5-a347-24594883c801")
+ def test_ranged_discovery_solicited_active_prange_smin_outofrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min such that out of range (min=large)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="a5619835-496a-4244-a428-f85cba3d4115")
+ def test_ranged_discovery_solicited_active_prange_smax_outofrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with max such that out of range (max=0)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=0),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="12ebd91f-a973-410b-8ee1-0bd86024b921")
+ def test_ranged_discovery_solicited_active_prange_sminmax_outofrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that out of range (min=large,
+ max=large+1)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001),
+ expect_discovery=False)
+
+ #########################################################################
+ # Run discovery with ranging configuration & update configurations after
+ # first run.
+ #
+ # Names: test_ranged_updated_discovery_<ptype>_<stype>_<scenario>
+ #
+ # where:
+ # <ptype>_<stype>: unsolicited_passive or solicited_active
+ # <scenario>: test scenario (details in name)
+ #########################################################################
+
+ @test_tracker_info(uuid="59442180-4a6c-428f-b926-86000e8339b4")
+ def test_ranged_updated_discovery_unsolicited_passive_oor_to_ir(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Ranging enabled, min/max such that in range (min=0,
+ max=large)
+
+ Expect: discovery + ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="60188508-104d-42d5-ac3a-3605093c45d7")
+ def test_ranged_updated_discovery_unsolicited_passive_pub_unrange(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Publisher disables ranging
+
+ Expect: discovery w/o ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ s_config=None, # no updates
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="f96b434e-751d-4eb5-ae01-0c5c3a6fb4a2")
+ def test_ranged_updated_discovery_unsolicited_passive_sub_unrange(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Ranging disabled
+
+ Expect: discovery w/o ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="78970de8-9362-4647-931a-3513bcf58e80")
+ def test_ranged_updated_discovery_unsolicited_passive_sub_oor(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: different out-of-range setting
+
+ Expect: no discovery after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=100000,
+ max_distance_mm=100001),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="0841ad05-4899-4521-bd24-04a8e2e345ac")
+ def test_ranged_updated_discovery_unsolicited_passive_pub_same(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Publisher with same settings (ranging enabled)
+
+ Expect: no discovery after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=None, # no updates
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="ec6ca57b-f115-4516-813a-4572b930c8d3")
+ def test_ranged_updated_discovery_unsolicited_passive_multi_step(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Expect: no discovery
+ - Reconfigured to: Ranging enabled, min/max such that in-range (min=0)
+ - Expect: discovery with ranging
+ - Reconfigured to: Ranging enabled, min/max such that out-of-range
+ (min=large)
+ - Expect: no discovery
+ - Reconfigured to: Ranging disabled
+ - Expect: discovery without ranging
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=True)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=False)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="bbaac63b-000c-415f-bf19-0906f04031cd")
+ def test_ranged_updated_discovery_solicited_active_oor_to_ir(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Ranging enabled, min/max such that in range (min=0,
+ max=large)
+
+ Expect: discovery + ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="c385b361-7955-4f34-9109-8d8ca81cb4cc")
+ def test_ranged_updated_discovery_solicited_active_pub_unrange(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Publisher disables ranging
+
+ Expect: discovery w/o ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ s_config=None, # no updates
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="ec5120ea-77ec-48c6-8820-48b82ad3dfd4")
+ def test_ranged_updated_discovery_solicited_active_sub_unrange(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Ranging disabled
+
+ Expect: discovery w/o ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="6231cb42-91e4-48d3-b9db-b37efbe8537c")
+ def test_ranged_updated_discovery_solicited_active_sub_oor(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: different out-of-range setting
+
+ Expect: no discovery after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=100000,
+ max_distance_mm=100001),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="ec999420-6a50-455e-b624-f4c9b4cb7ea5")
+ def test_ranged_updated_discovery_solicited_active_pub_same(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Publisher with same settings (ranging enabled)
+
+ Expect: no discovery after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=None, # no updates
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="ec6ca57b-f115-4516-813a-4572b930c8d3")
+ def test_ranged_updated_discovery_solicited_active_multi_step(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Expect: no discovery
+ - Reconfigured to: Ranging enabled, min/max such that in-range (min=0)
+ - Expect: discovery with ranging
+ - Reconfigured to: Ranging enabled, min/max such that out-of-range
+ (min=large)
+ - Expect: no discovery
+ - Reconfigured to: Ranging disabled
+ - Expect: discovery without ranging
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=True)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=False)
+ self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ #########################################################################
+
+ @test_tracker_info(uuid="6edc47ab-7300-4bff-b7dd-5de83f58928a")
+ def test_ranged_discovery_multi_session(self):
+ """Verify behavior with multiple concurrent discovery session with different
+ configurations:
+
+ Device A (Publisher):
+ Publisher AA: ranging enabled
+ Publisher BB: ranging enabled
+ Publisher CC: ranging enabled
+ Publisher DD: ranging disabled
+ Device B (Subscriber):
+ Subscriber AA: ranging out-of-range -> no match
+ Subscriber BB: ranging in-range -> match w/range
+ Subscriber CC: ranging disabled -> match w/o range
+ Subscriber DD: ranging out-of-range -> match w/o range
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Subscriber: start sessions
+ aa_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("AA",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000, max_distance_mm=1000001),
+ True)
+ bb_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("BB",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=0, max_distance_mm=1000000),
+ True)
+ cc_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.create_discovery_config("CC", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ True)
+ dd_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("DD",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000, max_distance_mm=1000001),
+ True)
+
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, aa_s_disc_id))
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, bb_s_disc_id))
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, cc_s_disc_id))
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, dd_s_disc_id))
+
+ # Publisher: start sessions
+ aa_p_disc_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config("AA",
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True),
+ True)
+ bb_p_disc_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config("BB",
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True),
+ True)
+ cc_p_disc_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config("CC",
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True),
+ True)
+ dd_p_disc_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.create_discovery_config("DD", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ True)
+
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, aa_p_disc_id))
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, bb_p_disc_id))
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, cc_p_disc_id))
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, dd_p_disc_id))
+
+ # Expected and unexpected service discovery
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, bb_s_disc_id))
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for BB expected!")
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, cc_s_disc_id))
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for CC NOT expected!")
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, dd_s_disc_id))
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for DD NOT expected!")
+ autils.fail_on_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, aa_s_disc_id))
+
+ # (single) sleep for timeout period and then verify that no further events
+ time.sleep(autils.EVENT_TIMEOUT)
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ #########################################################################
+
+ @test_tracker_info(uuid="deede47f-a54c-46d9-88bb-f4482fbd8470")
+ def test_ndp_concurrency(self):
+ """Verify the behavior of Wi-Fi Aware Ranging whenever an NDP is created -
+ for those devices that have a concurrency limitation that does not allow
+ Aware Ranging, whether direct or as part of discovery.
+
+ Publisher: start 3 services
+ AA w/o ranging
+ BB w/ ranging
+ CC w/ ranging
+ DD w/ ranging
+ Subscriber: start 2 services
+ AA w/o ranging
+ BB w/ ranging out-of-range
+ (do not start CC!)
+ DD w/ ranging in-range
+ Expect AA discovery, DD discovery w/range, but no BB
+ Start NDP in context of AA
+ IF NDP_CONCURRENCY_LIMITATION:
+ Verify discovery on BB w/o range
+ Start EE w/ranging out-of-range
+ Start FF w/ranging in-range
+ IF NDP_CONCURRENCY_LIMITATION:
+ Verify discovery on EE w/o range
+ Verify discovery on FF w/o range
+ Else:
+ Verify discovery on FF w/ range
+ Tear down NDP
+ Subscriber
+ Start CC w/ ranging out-of-range
+ Wait to verify that do not get match
+ Update configuration to be in-range
+ Verify that get match with ranging information
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Publisher: AA w/o ranging, BB w/ ranging, CC w/ ranging, DD w/ ranging
+ aa_p_id = p_dut.droid.wifiAwarePublish(p_id,
+ autils.create_discovery_config("AA", aconsts.PUBLISH_TYPE_SOLICITED),
+ True)
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, aa_p_id))
+ bb_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("BB", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, bb_p_id))
+ cc_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("CC", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, cc_p_id))
+ dd_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("DD", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, dd_p_id))
+
+ # Subscriber: AA w/o ranging, BB w/ranging out-of-range,
+ # DD w /ranging in-range
+ aa_s_id = s_dut.droid.wifiAwareSubscribe(s_id,
+ autils.create_discovery_config("AA", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+ True)
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, aa_s_id))
+ bb_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("BB", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000, max_distance_mm=1000001), True)
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, bb_s_id))
+ dd_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("DD", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None, max_distance_mm=1000000), True)
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, dd_s_id))
+
+ # verify: AA discovered, BB not discovered, DD discovery w/range
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, aa_s_id))
+ asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for AA NOT expected!")
+ aa_peer_id_on_sub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+ autils.fail_on_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, bb_s_id))
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, dd_s_id))
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for DD expected!")
+
+ # start NDP in context of AA:
+
+ # Publisher: request network (from ANY)
+ p_req_key = autils.request_network(p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(aa_p_id, None))
+
+ # Subscriber: request network
+ s_req_key = autils.request_network(s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(aa_s_id, aa_peer_id_on_sub))
+
+ # Publisher & Subscriber: wait for network formation
+ p_net_event = autils.wait_for_event_with_keys(p_dut,
+ cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_TIMEOUT, (
+ cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID,
+ p_req_key))
+ s_net_event = autils.wait_for_event_with_keys(s_dut,
+ cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_TIMEOUT, (
+ cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID,
+ s_req_key))
+
+ p_aware_if = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ s_aware_if = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ p_ipv6 = p_dut.droid.connectivityGetLinkLocalIpv6Address(p_aware_if).split(
+ "%")[0]
+ s_ipv6 = s_dut.droid.connectivityGetLinkLocalIpv6Address(s_aware_if).split(
+ "%")[0]
+
+ self.log.info("AA NDP Interface names: P=%s, S=%s", p_aware_if, s_aware_if)
+ self.log.info("AA NDP Interface addresses (IPv6): P=%s, S=%s", p_ipv6,
+ s_ipv6)
+
+ if self.RANGING_NDP_CONCURRENCY_LIMITATION:
+ # Expect BB to now discover w/o ranging
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, bb_s_id))
+ asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for BB NOT expected!")
+
+ # Publishers: EE, FF w/ ranging
+ ee_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("EE", aconsts.PUBLISH_TYPE_SOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, ee_p_id))
+ ff_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("FF", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(p_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, ff_p_id))
+
+ # Subscribers: EE out-of-range, FF in-range
+ ee_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("EE", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+ min_distance_mm=1000000, max_distance_mm=1000001), True)
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, ee_s_id))
+ ff_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("FF", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None, max_distance_mm=1000000), True)
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, ff_s_id))
+
+ if self.RANGING_NDP_CONCURRENCY_LIMITATION:
+ # Expect EE & FF discovery w/o range
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ee_s_id))
+ asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for EE NOT expected!")
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ff_s_id))
+ asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for FF NOT expected!")
+ else:
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ff_s_id))
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for FF expected!")
+
+ # tear down NDP
+ p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+ s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+
+ time.sleep(5) # give time for NDP termination to finish
+
+ # Subscriber: start CC out-of-range - no discovery expected!
+ cc_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("CC", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000, max_distance_mm=1000001), True)
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, cc_s_id))
+ autils.fail_on_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, cc_s_id))
+
+ # Subscriber: modify CC to in-range - expect discovery w/ range
+ s_dut.droid.wifiAwareUpdateSubscribe(cc_s_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("CC", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None, max_distance_mm=1000001))
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED, cc_s_id))
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, cc_s_id))
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for CC expected!")
+
+ @test_tracker_info(uuid="d94dac91-4090-4c03-a867-6dfac6558ba3")
+ def test_role_concurrency(self):
+ """Verify the behavior of Wi-Fi Aware Ranging (in the context of discovery)
+ when the device has concurrency limitations which do not permit concurrent
+ Initiator and Responder roles on the same device. In such case it is
+ expected that normal discovery without ranging is executed AND that ranging
+ is restored whenever the concurrency constraints are removed.
+
+ Note: all Subscribers are in-range.
+
+ DUT1: start multiple services
+ Publish AA w/ ranging (unsolicited)
+ Subscribe BB w/ ranging (active)
+ Publish CC w/ ranging (unsolicited)
+ Publish DD w/o ranging (solicited)
+ Subscribe EE w/ ranging (passive)
+ Subscribe FF w/ ranging (active)
+ DUT2: start multiple services
+ Subscribe AA w/ ranging (passive)
+ Publish BB w/ ranging (solicited)
+ Subscribe DD w/o ranging (active)
+ Expect
+ DUT2: AA match w/ range information
+ DUT1: BB match w/o range information (concurrency disables ranging)
+ DUT2: DD match w/o range information
+ DUT1: Terminate AA
+ DUT2:
+ Terminate AA
+ Start Publish EE w/ ranging (unsolicited)
+ DUT1: expect EE w/o ranging
+ DUT1: Terminate CC
+ DUT2: Start Publish FF w/ ranging (solicited)
+ DUT1: expect FF w/ ranging information - should finally be back up
+ """
+ dut1 = self.android_devices[0]
+ dut1.pretty_name = "DUT1"
+ dut2 = self.android_devices[1]
+ dut2.pretty_name = "DUT2"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ dut1_id = dut1.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut1, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ dut2_id = dut2.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut2, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # DUT1: initial service bringup
+ aa_p_id = dut1.droid.wifiAwarePublish(dut1_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("AA", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, aa_p_id))
+ bb_s_id = dut1.droid.wifiAwareSubscribe(dut1_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("BB", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+ min_distance_mm=None, max_distance_mm=1000000), True)
+ autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, bb_s_id))
+ cc_p_id = dut1.droid.wifiAwarePublish(dut1_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("CC", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, cc_p_id))
+ dd_p_id = dut1.droid.wifiAwarePublish(dut1_id,
+ autils.create_discovery_config("DD", aconsts.PUBLISH_TYPE_SOLICITED),
+ True)
+ autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, dd_p_id))
+ ee_s_id = dut1.droid.wifiAwareSubscribe(dut1_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("EE", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None, max_distance_mm=1000000), True)
+ autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, ee_s_id))
+ ff_s_id = dut1.droid.wifiAwareSubscribe(dut1_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("FF", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+ min_distance_mm=None, max_distance_mm=1000000), True)
+ autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, ff_s_id))
+
+ # DUT2: initial service bringup
+ aa_s_id = dut2.droid.wifiAwareSubscribe(dut2_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("AA", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None, max_distance_mm=1000000), True)
+ autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, aa_s_id))
+ bb_p_id = dut2.droid.wifiAwarePublish(dut2_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("BB", aconsts.PUBLISH_TYPE_SOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, bb_p_id))
+ dd_s_id = dut2.droid.wifiAwareSubscribe(dut2_id,
+ autils.create_discovery_config("AA", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+ True)
+ autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, dd_s_id))
+
+ # Initial set of discovery events for AA, BB, and DD (which are up)
+ event = autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, aa_s_id))
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for AA expected!")
+ event = autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, bb_s_id))
+ if self.RANGING_INITIATOR_RESPONDER_CONCURRENCY_LIMITATION:
+ asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for BB NOT expected!")
+ else:
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for BB expected!")
+ event = autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, dd_s_id))
+ asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for DD NOT expected!")
+
+ # DUT1/DUT2: terminate AA
+ dut1.droid.wifiAwareDestroyDiscoverySession(aa_p_id)
+ dut2.droid.wifiAwareDestroyDiscoverySession(aa_s_id)
+
+ time.sleep(5) # guarantee that session terminated (and host recovered?)
+
+ # DUT2: try EE service - ranging still disabled
+ ee_p_id = dut2.droid.wifiAwarePublish(dut2_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("EE", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, ee_p_id))
+
+ event = autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ee_s_id))
+ if self.RANGING_INITIATOR_RESPONDER_CONCURRENCY_LIMITATION:
+ asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for EE NOT expected!")
+ else:
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for EE expected!")
+
+ # DUT1: terminate CC - last publish w/ ranging on DUT!
+ dut1.droid.wifiAwareDestroyDiscoverySession(cc_p_id)
+
+ time.sleep(5) # guarantee that session terminated (and host recovered?)
+
+ # DUT2: try FF service - ranging should now function
+ ff_p_id = dut2.droid.wifiAwarePublish(dut2_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("FF", aconsts.PUBLISH_TYPE_SOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, ff_p_id))
+
+ event = autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ff_s_id))
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for FF expected!")
+
+
+ @test_tracker_info(uuid="6700eab8-a172-43cd-aed3-e6577ce8fd89")
+ def test_discovery_direct_concurrency(self):
+ """Verify the behavior of Wi-Fi Aware Ranging used as part of discovery and
+ as direct ranging to a peer device.
+
+ Process:
+ - Start YYY service with ranging in-range
+ - Start XXX service with ranging out-of-range
+ - Start performing direct Ranging
+ - While above going on update XXX to be in-range
+ - Keep performing direct Ranging in context of YYY
+ - Stop direct Ranging and look for XXX to discover
+ """
+ dut1 = self.android_devices[0]
+ dut1.pretty_name = "DUT1"
+ dut2 = self.android_devices[1]
+ dut2.pretty_name = "DUT2"
+
+ # DUTs: attach and wait for confirmation
+ dut1_id = dut1.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut1, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ dut2_id = dut2.droid.wifiAwareAttach(True)
+ event = autils.wait_for_event(dut2, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ dut2_mac = event['data']['mac']
+
+ # DUT1: publishers bring-up
+ xxx_p_id = dut1.droid.wifiAwarePublish(dut1_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("XXX", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, xxx_p_id))
+ yyy_p_id = dut1.droid.wifiAwarePublish(dut1_id, autils.add_ranging_to_pub(
+ autils.create_discovery_config("YYY", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(dut1, autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, yyy_p_id))
+
+ # DUT2: subscribers bring-up
+ xxx_s_id = dut2.droid.wifiAwareSubscribe(dut2_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("XXX", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000, max_distance_mm=1000001), True)
+ autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, xxx_s_id))
+ yyy_s_id = dut2.droid.wifiAwareSubscribe(dut2_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("YYY", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None, max_distance_mm=1000000), True)
+ autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, yyy_s_id))
+
+ # Service discovery: YYY (with range info), but no XXX
+ event = autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, yyy_s_id))
+ asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for YYY expected!")
+ yyy_peer_id_on_sub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ autils.fail_on_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, xxx_s_id))
+
+ # Direct ranging
+ results21 = []
+ for iter in range(10):
+ id = dut2.droid.wifiRttStartRangingToAwarePeerId(yyy_peer_id_on_sub)
+ event = autils.wait_for_event(dut2, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, id))
+ results21.append(event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0])
+
+ time.sleep(5) # while switching roles
+
+ results12 = []
+ for iter in range(10):
+ id = dut1.droid.wifiRttStartRangingToAwarePeerMac(dut2_mac)
+ event = autils.wait_for_event(dut1, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, id))
+ results12.append(event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0])
+
+ stats = [rutils.extract_stats(results12, 0, 0, 0),
+ rutils.extract_stats(results21, 0, 0, 0)]
+
+ # Update XXX to be within range
+ dut2.droid.wifiAwareUpdateSubscribe(xxx_s_id, autils.add_ranging_to_sub(
+ autils.create_discovery_config("XXX", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None, max_distance_mm=1000000))
+ autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED, xxx_s_id))
+
+ # Expect discovery on XXX - wait until discovery with ranging:
+ # - 0 or more: without ranging info (due to concurrency limitations)
+ # - 1 or more: with ranging (once concurrency limitation relieved)
+ num_events = 0
+ while True:
+ event = autils.wait_for_event(dut2, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, xxx_s_id))
+ if aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"]:
+ break
+ num_events = num_events + 1
+ asserts.assert_true(num_events < 10, # arbitrary safety valve
+ "Way too many discovery events without ranging!")
+
+ asserts.explicit_pass("Discovery/Direct RTT Concurrency Pass", extras=stats)
\ No newline at end of file
diff --git a/acts/tests/google/wifi/rtt/functional/RangeApMiscTest.py b/acts/tests/google/wifi/rtt/functional/RangeApMiscTest.py
new file mode 100644
index 0000000..dd5560d
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeApMiscTest.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2017 - 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.
+
+from acts import asserts
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeApMiscTest(RttBaseTest):
+ """Test class for RTT ranging to Access Points - miscellaneous tests which
+ do not fit into the strict IEEE 802.11mc supporting or non-supporting test
+ beds - e.g. a mixed test."""
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ # Time gap (in seconds) between iterations
+ TIME_BETWEEN_ITERATIONS = 0
+
+ def __init__(self, controllers):
+ RttBaseTest.__init__(self, controllers)
+
+ #############################################################################
+
+ def test_rtt_mixed_80211mc_supporting_aps_wo_privilege(self):
+ """Scan for APs and perform RTT on one supporting and one non-supporting
+ IEEE 802.11mc APs with the device not having privilege access (expect
+ failures)."""
+ dut = self.android_devices[0]
+ rutils.config_privilege_override(dut, True)
+ rtt_aps = rutils.scan_with_rtt_support_constraint(dut, True)
+ non_rtt_aps = rutils.scan_with_rtt_support_constraint(dut, False)
+ mix_list = [rtt_aps[0], non_rtt_aps[0]]
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", mix_list)
+ events = rutils.run_ranging(dut, mix_list, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(stat['num_no_results'] == 0,
+ "Missing (timed-out) results", extras=stats)
+ if bssid == rtt_aps[0][wutils.WifiEnums.BSSID_KEY]:
+ asserts.assert_false(stat['any_lci_mismatch'],
+ "LCI mismatch", extras=stats)
+ asserts.assert_false(stat['any_lcr_mismatch'],
+ "LCR mismatch", extras=stats)
+ asserts.assert_equal(stat['num_invalid_rssi'], 0, "Invalid RSSI",
+ extras=stats)
+ asserts.assert_true(stat['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage
+ * stat['num_results'] / 100,
+ "Failure rate is too high", extras=stats)
+ asserts.assert_true(stat['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+ * stat['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=stats)
+ else:
+ asserts.assert_true(stat['num_failures'] == self.NUM_ITER,
+ "All one-sided RTT requests must fail when executed without privilege",
+ extras=stats)
+ for code in stat['status_codes']:
+ asserts.assert_true(code ==
+ rconsts.EVENT_CB_RANGING_STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC,
+ "Expected non-support error code", extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
diff --git a/acts/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py b/acts/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
new file mode 100644
index 0000000..65b67d2
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2017 - 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.
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeApNonSupporting11McTest(WifiBaseTest, RttBaseTest):
+ """Test class for RTT ranging to Access Points which do not support IEEE
+ 802.11mc"""
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ # Time gap (in seconds) between iterations
+ TIME_BETWEEN_ITERATIONS = 0
+
+ def __init__(self, controllers):
+ WifiBaseTest.__init__(self, controllers)
+ RttBaseTest.__init__(self, controllers)
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start()
+
+ #############################################################################
+
+ @test_tracker_info(uuid="cde756e9-11f3-43da-b9ae-9edf85764f82")
+ def test_rtt_non_80211mc_supporting_aps(self):
+ """Scan for APs and perform RTT on non-IEEE 802.11mc supporting APs"""
+ dut = self.android_devices[0]
+ non_rtt_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, False), select_count=1)
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+ asserts.assert_true(len(non_rtt_aps) > 0, "Need at least one AP!")
+ events = rutils.run_ranging(dut, non_rtt_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(stat['num_no_results'] == 0,
+ "Missing (timed-out) results", extras=stats)
+ asserts.assert_false(stat['any_lci_mismatch'],
+ "LCI mismatch", extras=stats)
+ asserts.assert_false(stat['any_lcr_mismatch'],
+ "LCR mismatch", extras=stats)
+ asserts.assert_equal(stat['num_invalid_rssi'], 0, "Invalid RSSI",
+ extras=stats)
+ asserts.assert_true(stat['num_failures'] <=
+ self.rtt_max_failure_rate_one_sided_rtt_percentage
+ * stat['num_results'] / 100,
+ "Failure rate is too high", extras=stats)
+ asserts.assert_true(stat['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_one_sided_rtt_percentage
+ * stat['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
+
+ @test_tracker_info(uuid="c9e22185-16d4-4fe6-894f-5823587b3288")
+ def test_rtt_non_80211mc_supporting_aps_wo_privilege(self):
+ """Scan for APs and perform RTT on non-IEEE 802.11mc supporting APs with the
+ device not having privilege access (expect failures)."""
+ dut = self.android_devices[0]
+ rutils.config_privilege_override(dut, True)
+ non_rtt_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, False), select_count=1)
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+ asserts.assert_true(len(non_rtt_aps) > 0, "Need at least one AP!")
+ events = rutils.run_ranging(dut, non_rtt_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(stat['num_no_results'] == 0,
+ "Missing (timed-out) results", extras=stats)
+ asserts.assert_true(stat['num_failures'] == self.NUM_ITER,
+ "All one-sided RTT requests must fail when executed without privilege",
+ extras=stats)
+ for code in stat['status_codes']:
+ asserts.assert_true(code ==
+ rconsts.EVENT_CB_RANGING_STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC,
+ "Expected non-support error code", extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
+
+ @test_tracker_info(uuid="e117af56-bd3f-40ae-a2fd-4175f0daa7fa")
+ def test_rtt_non_80211mc_supporting_ap_faked_as_supporting(self):
+ """Scan for APs which do not support IEEE 802.11mc, maliciously modify the
+ Responder config to indicate support and pass-through to service. Verify
+ that get an error result.
+ """
+ dut = self.android_devices[0]
+ non_rtt_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, False), select_count=1)
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+ asserts.assert_true(len(non_rtt_aps) > 0, "Need at least one AP!")
+ non_rtt_aps = non_rtt_aps[0:1] # pick first
+ non_rtt_aps[0][rconsts.SCAN_RESULT_KEY_RTT_RESPONDER] = True # falsify
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+ events = rutils.run_ranging(dut, non_rtt_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(stat['num_no_results'] == 0,
+ "Missing (timed-out) results", extras=stats)
+ asserts.assert_true(stat['num_failures'] == self.NUM_ITER,
+ "Failures expected for falsified responder config",
+ extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
diff --git a/acts/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py b/acts/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
new file mode 100644
index 0000000..d889a22
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
@@ -0,0 +1,187 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2017 - 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.
+
+import queue
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeApSupporting11McTest(RttBaseTest):
+ """Test class for RTT ranging to Access Points which support IEEE 802.11mc"""
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ # Time gap (in seconds) between iterations
+ TIME_BETWEEN_ITERATIONS = 0
+
+ def __init__(self, controllers):
+ RttBaseTest.__init__(self, controllers)
+
+ #############################################################################
+
+ @test_tracker_info(uuid="6705270f-924b-4bef-b50a-0f0a7eb9ce52")
+ def test_rtt_80211mc_supporting_aps(self):
+ """Scan for APs and perform RTT only to those which support 802.11mc"""
+ dut = self.android_devices[0]
+ rtt_supporting_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+ select_count=2)
+ dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+ events = rutils.run_ranging(dut, rtt_supporting_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(stat['num_no_results'] == 0,
+ "Missing (timed-out) results", extras=stats)
+ asserts.assert_false(stat['any_lci_mismatch'],
+ "LCI mismatch", extras=stats)
+ asserts.assert_false(stat['any_lcr_mismatch'],
+ "LCR mismatch", extras=stats)
+ asserts.assert_false(stat['invalid_num_attempted'],
+ "Invalid (0) number of attempts", extras=stats)
+ asserts.assert_false(stat['invalid_num_successful'],
+ "Invalid (0) number of successes", extras=stats)
+ asserts.assert_equal(stat['num_invalid_rssi'], 0, "Invalid RSSI",
+ extras=stats)
+ asserts.assert_true(stat['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage
+ * stat['num_results'] / 100,
+ "Failure rate is too high", extras=stats)
+ asserts.assert_true(stat['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+ * stat['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high", extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
+
+ #########################################################################
+ #
+ # LEGACY API test code
+ #
+ #########################################################################
+
+ @test_tracker_info(uuid="18be9737-2f03-4e35-9a23-f722dea7b82d")
+ def test_legacy_rtt_80211mc_supporting_aps(self):
+ """Scan for APs and perform RTT only to those which support 802.11mc - using
+ the LEGACY API!"""
+ dut = self.android_devices[0]
+ rtt_supporting_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+ select_count=2)
+ dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+
+ rtt_configs = []
+ for ap in rtt_supporting_aps:
+ rtt_configs.append(self.rtt_config_from_scan_result(ap))
+ dut.log.debug("RTT configs=%s", rtt_configs)
+
+ results = []
+ num_missing = 0
+ num_failed_aborted = 0
+ for i in range(self.NUM_ITER):
+ idx = dut.droid.wifiRttStartRanging(rtt_configs)
+ event = None
+ try:
+ events = dut.ed.pop_events("WifiRttRanging%d" % idx, 30)
+ dut.log.debug("Event=%s", events)
+ for event in events:
+ if rconsts.EVENT_CB_RANGING_KEY_RESULTS in event["data"]:
+ results.append(
+ event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS])
+ else:
+ self.log.info("RTT failed/aborted - %s", event)
+ results.append([])
+ num_failed_aborted = num_failed_aborted + 1
+ except queue.Empty:
+ self.log.debug("Waiting for RTT event timed out.")
+ results.append([])
+ num_missing = num_missing + 1
+
+ # basic error checking:
+ # 1. no missing
+ # 2. no full failed/aborted (i.e. operation not even tried)
+ # 3. overall (all BSSIDs) success rate > threshold
+ asserts.assert_equal(num_missing, 0,
+ "Missing results (timeout waiting for event)",
+ extras={"data":results})
+ asserts.assert_equal(num_failed_aborted, 0,
+ "Failed or aborted operations (not tried)",
+ extras={"data":results})
+
+ num_results = 0
+ num_errors = 0
+ for result_group in results:
+ num_results = num_results + len(result_group)
+ for result in result_group:
+ if result["status"] != 0:
+ num_errors = num_errors + 1
+
+ extras = [results, {"num_results": num_results, "num_errors": num_errors}]
+ asserts.assert_true(
+ num_errors <= self.rtt_max_failure_rate_two_sided_rtt_percentage
+ * num_results / 100,
+ "Failure rate is too high", extras={"data":extras})
+ asserts.explicit_pass("RTT test done", extras={"data": extras})
+
+ def rtt_config_from_scan_result(self, scan_result):
+ """Creates an Rtt configuration based on the scan result of a network.
+ """
+ WifiEnums = wutils.WifiEnums
+ ScanResult = WifiEnums.ScanResult
+ RttParam = WifiEnums.RttParam
+ RttBW = WifiEnums.RttBW
+ RttPreamble = WifiEnums.RttPreamble
+ RttType = WifiEnums.RttType
+
+ scan_result_channel_width_to_rtt = {
+ ScanResult.CHANNEL_WIDTH_20MHZ: RttBW.BW_20_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_40MHZ: RttBW.BW_40_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_80MHZ: RttBW.BW_80_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_160MHZ: RttBW.BW_160_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: RttBW.BW_160_SUPPORT
+ }
+ p = {}
+ freq = scan_result[RttParam.frequency]
+ p[RttParam.frequency] = freq
+ p[RttParam.BSSID] = scan_result[WifiEnums.BSSID_KEY]
+ if freq > 5000:
+ p[RttParam.preamble] = RttPreamble.PREAMBLE_VHT
+ else:
+ p[RttParam.preamble] = RttPreamble.PREAMBLE_HT
+ cf0 = scan_result[RttParam.center_freq0]
+ if cf0 > 0:
+ p[RttParam.center_freq0] = cf0
+ cf1 = scan_result[RttParam.center_freq1]
+ if cf1 > 0:
+ p[RttParam.center_freq1] = cf1
+ cw = scan_result["channelWidth"]
+ p[RttParam.channel_width] = cw
+ p[RttParam.bandwidth] = scan_result_channel_width_to_rtt[cw]
+ if scan_result["is80211McRTTResponder"]:
+ p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
+ else:
+ p[RttParam.request_type] = RttType.TYPE_ONE_SIDED
+ return p
diff --git a/acts/tests/google/wifi/rtt/functional/RangeAwareTest.py b/acts/tests/google/wifi/rtt/functional/RangeAwareTest.py
new file mode 100644
index 0000000..d4b7d41
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeAwareTest.py
@@ -0,0 +1,409 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2017 - 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.
+
+import queue
+import time
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.aware import aware_const as aconsts
+from acts.test_utils.wifi.aware import aware_test_utils as autils
+from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeAwareTest(AwareBaseTest, RttBaseTest):
+ """Test class for RTT ranging to Wi-Fi Aware peers"""
+ SERVICE_NAME = "GoogleTestServiceXY"
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ # Time gap (in seconds) between iterations
+ TIME_BETWEEN_ITERATIONS = 0
+
+ # Time gap (in seconds) when switching between Initiator and Responder
+ TIME_BETWEEN_ROLES = 4
+
+ def __init__(self, controllers):
+ AwareBaseTest.__init__(self, controllers)
+ RttBaseTest.__init__(self, controllers)
+
+ def setup_test(self):
+ """Manual setup here due to multiple inheritance: explicitly execute the
+ setup method from both parents."""
+ AwareBaseTest.setup_test(self)
+ RttBaseTest.setup_test(self)
+
+ def teardown_test(self):
+ """Manual teardown here due to multiple inheritance: explicitly execute the
+ teardown method from both parents."""
+ AwareBaseTest.teardown_test(self)
+ RttBaseTest.teardown_test(self)
+
+ #############################################################################
+
+ def run_rtt_discovery(self, init_dut, resp_mac=None, resp_peer_id=None):
+ """Perform single RTT measurement, using Aware, from the Initiator DUT to
+ a Responder. The RTT Responder can be specified using its MAC address
+ (obtained using out- of-band discovery) or its Peer ID (using Aware
+ discovery).
+
+ Args:
+ init_dut: RTT Initiator device
+ resp_mac: MAC address of the RTT Responder device
+ resp_peer_id: Peer ID of the RTT Responder device
+ """
+ asserts.assert_true(resp_mac is not None or resp_peer_id is not None,
+ "One of the Responder specifications (MAC or Peer ID)"
+ " must be provided!")
+ if resp_mac is not None:
+ id = init_dut.droid.wifiRttStartRangingToAwarePeerMac(resp_mac)
+ else:
+ id = init_dut.droid.wifiRttStartRangingToAwarePeerId(resp_peer_id)
+ try:
+ event = init_dut.ed.pop_event(rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, id), rutils.EVENT_TIMEOUT)
+ result = event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0]
+ if resp_mac is not None:
+ rutils.validate_aware_mac_result(result, resp_mac, "DUT")
+ else:
+ rutils.validate_aware_peer_id_result(result, resp_peer_id, "DUT")
+ return result
+ except queue.Empty:
+ return None
+
+ def run_rtt_ib_discovery_set(self, do_both_directions, iter_count,
+ time_between_iterations, time_between_roles):
+ """Perform a set of RTT measurements, using in-band (Aware) discovery.
+
+ Args:
+ do_both_directions: False - perform all measurements in one direction,
+ True - perform 2 measurements one in both directions.
+ iter_count: Number of measurements to perform.
+ time_between_iterations: Number of seconds to wait between iterations.
+ time_between_roles: Number of seconds to wait when switching between
+ Initiator and Responder roles (only matters if
+ do_both_directions=True).
+
+ Returns: a list of the events containing the RTT results (or None for a
+ failed measurement). If both directions are tested then returns a list of
+ 2 elements: one set for each direction.
+ """
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ (p_id, s_id, p_disc_id, s_disc_id,
+ peer_id_on_sub, peer_id_on_pub) = autils.create_discovery_pair(
+ p_dut,
+ s_dut,
+ p_config=autils.add_ranging_to_pub(autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED), True),
+ s_config=autils.add_ranging_to_pub(autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE), True),
+ device_startup_offset=self.device_startup_offset,
+ msg_id=self.get_next_msg_id())
+
+ resultsPS = []
+ resultsSP = []
+ for i in range(iter_count):
+ if i != 0 and time_between_iterations != 0:
+ time.sleep(time_between_iterations)
+
+ # perform RTT from pub -> sub
+ resultsPS.append(
+ self.run_rtt_discovery(p_dut, resp_peer_id=peer_id_on_pub))
+
+ if do_both_directions:
+ if time_between_roles != 0:
+ time.sleep(time_between_roles)
+
+ # perform RTT from sub -> pub
+ resultsSP.append(
+ self.run_rtt_discovery(s_dut, resp_peer_id=peer_id_on_sub))
+
+ return resultsPS if not do_both_directions else [resultsPS, resultsSP]
+
+ def run_rtt_oob_discovery_set(self, do_both_directions, iter_count,
+ time_between_iterations, time_between_roles):
+ """Perform a set of RTT measurements, using out-of-band discovery.
+
+ Args:
+ do_both_directions: False - perform all measurements in one direction,
+ True - perform 2 measurements one in both directions.
+ iter_count: Number of measurements to perform.
+ time_between_iterations: Number of seconds to wait between iterations.
+ time_between_roles: Number of seconds to wait when switching between
+ Initiator and Responder roles (only matters if
+ do_both_directions=True).
+ enable_ranging: True to enable Ranging, False to disable.
+
+ Returns: a list of the events containing the RTT results (or None for a
+ failed measurement). If both directions are tested then returns a list of
+ 2 elements: one set for each direction.
+ """
+ dut0 = self.android_devices[0]
+ dut1 = self.android_devices[1]
+
+ id0, mac0 = autils.attach_with_identity(dut0)
+ id1, mac1 = autils.attach_with_identity(dut1)
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ # start publisher(s) on the Responder(s) with ranging enabled
+ p_config = autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True)
+ dut1.droid.wifiAwarePublish(id1, p_config)
+ autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ if do_both_directions:
+ dut0.droid.wifiAwarePublish(id0, p_config)
+ autils.wait_for_event(dut0, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ results01 = []
+ results10 = []
+ for i in range(iter_count):
+ if i != 0 and time_between_iterations != 0:
+ time.sleep(time_between_iterations)
+
+ # perform RTT from dut0 -> dut1
+ results01.append(
+ self.run_rtt_discovery(dut0, resp_mac=mac1))
+
+ if do_both_directions:
+ if time_between_roles != 0:
+ time.sleep(time_between_roles)
+
+ # perform RTT from dut1 -> dut0
+ results10.append(
+ self.run_rtt_discovery(dut1, resp_mac=mac0))
+
+ return results01 if not do_both_directions else [results01, results10]
+
+ def verify_results(self, results, results_reverse_direction=None):
+ """Verifies the results of the RTT experiment.
+
+ Args:
+ results: List of RTT results.
+ results_reverse_direction: List of RTT results executed in the
+ reverse direction. Optional.
+ """
+ stats = rutils.extract_stats(results, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm)
+ stats_reverse_direction = None
+ if results_reverse_direction is not None:
+ stats_reverse_direction = rutils.extract_stats(results_reverse_direction,
+ self.rtt_reference_distance_mm, self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm)
+ self.log.debug("Stats: %s", stats)
+ if stats_reverse_direction is not None:
+ self.log.debug("Stats in reverse direction: %s", stats_reverse_direction)
+
+ extras = stats if stats_reverse_direction is None else {
+ "forward": stats,
+ "reverse": stats_reverse_direction}
+
+ asserts.assert_true(stats['num_no_results'] == 0,
+ "Missing (timed-out) results", extras=extras)
+ asserts.assert_false(stats['any_lci_mismatch'],
+ "LCI mismatch", extras=extras)
+ asserts.assert_false(stats['any_lcr_mismatch'],
+ "LCR mismatch", extras=extras)
+ asserts.assert_false(stats['invalid_num_attempted'],
+ "Invalid (0) number of attempts", extras=stats)
+ asserts.assert_false(stats['invalid_num_successful'],
+ "Invalid (0) number of successes", extras=stats)
+ asserts.assert_equal(stats['num_invalid_rssi'], 0, "Invalid RSSI",
+ extras=extras)
+ asserts.assert_true(
+ stats['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage
+ * stats['num_results'] / 100,
+ "Failure rate is too high", extras=extras)
+ asserts.assert_true(
+ stats['num_range_out_of_margin']
+ <= self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+ * stats['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high", extras=extras)
+
+ if stats_reverse_direction is not None:
+ asserts.assert_true(stats_reverse_direction['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=extras)
+ asserts.assert_false(stats['any_lci_mismatch'],
+ "LCI mismatch", extras=extras)
+ asserts.assert_false(stats['any_lcr_mismatch'],
+ "LCR mismatch", extras=extras)
+ asserts.assert_equal(stats['num_invalid_rssi'], 0, "Invalid RSSI",
+ extras=extras)
+ asserts.assert_true(
+ stats_reverse_direction['num_failures']
+ <= self.rtt_max_failure_rate_two_sided_rtt_percentage
+ * stats['num_results'] / 100,
+ "Failure rate is too high", extras=extras)
+ asserts.assert_true(
+ stats_reverse_direction['num_range_out_of_margin']
+ <= self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+ * stats['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=extras)
+
+ asserts.explicit_pass("RTT Aware test done", extras=extras)
+
+ #############################################################################
+
+ @test_tracker_info(uuid="9e4e7ab4-2254-498c-9788-21e15ed9a370")
+ def test_rtt_oob_discovery_one_way(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
+ to communicate the MAC addresses to the peer. Test one-direction RTT only.
+ """
+ rtt_results = self.run_rtt_oob_discovery_set(do_both_directions=False,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results)
+
+ @test_tracker_info(uuid="22edba77-eeb2-43ee-875a-84437550ad84")
+ def test_rtt_oob_discovery_both_ways(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
+ to communicate the MAC addresses to the peer. Test RTT both-ways:
+ switching rapidly between Initiator and Responder.
+ """
+ rtt_results1, rtt_results2 = self.run_rtt_oob_discovery_set(
+ do_both_directions=True, iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results1, rtt_results2)
+
+ @test_tracker_info(uuid="18cef4be-95b4-4f7d-a140-5165874e7d1c")
+ def test_rtt_ib_discovery_one_way(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use in-band (Aware) discovery
+ to communicate the MAC addresses to the peer. Test one-direction RTT only.
+ """
+ rtt_results = self.run_rtt_ib_discovery_set(do_both_directions=False,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results)
+
+ @test_tracker_info(uuid="c67c8e70-c417-42d9-9bca-af3a89f1ddd9")
+ def test_rtt_ib_discovery_both_ways(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use in-band (Aware) discovery
+ to communicate the MAC addresses to the peer. Test RTT both-ways:
+ switching rapidly between Initiator and Responder.
+ """
+ rtt_results1, rtt_results2 = self.run_rtt_ib_discovery_set(
+ do_both_directions=True, iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results1, rtt_results2)
+
+ @test_tracker_info(uuid="54f9693d-45e5-4979-adbb-1b875d217c0c")
+ def test_rtt_without_initiator_aware(self):
+ """Try to perform RTT operation when there is no local Aware session (on the
+ Initiator). The Responder is configured normally: Aware on and a Publisher
+ with Ranging enable. Should FAIL."""
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ # Enable a Responder and start a Publisher
+ resp_id = resp_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ resp_ident_event = autils.wait_for_event(resp_dut,
+ aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ resp_mac = resp_ident_event['data']['mac']
+
+ resp_config = autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True)
+ resp_dut.droid.wifiAwarePublish(resp_id, resp_config)
+ autils.wait_for_event(resp_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Initiate an RTT to Responder (no Aware started on Initiator!)
+ results = []
+ num_no_responses = 0
+ num_successes = 0
+ for i in range(self.NUM_ITER):
+ result = self.run_rtt_discovery(init_dut, resp_mac=resp_mac)
+ self.log.debug("result: %s", result)
+ results.append(result)
+ if result is None:
+ num_no_responses = num_no_responses + 1
+ elif (result[rconsts.EVENT_CB_RANGING_KEY_STATUS]
+ == rconsts.EVENT_CB_RANGING_STATUS_SUCCESS):
+ num_successes = num_successes + 1
+
+ asserts.assert_equal(num_no_responses, 0, "No RTT response?",
+ extras={"data":results})
+ asserts.assert_equal(num_successes, 0, "Aware RTT w/o Aware should FAIL!",
+ extras={"data":results})
+ asserts.explicit_pass("RTT Aware test done", extras={"data":results})
+
+ @test_tracker_info(uuid="87a69053-8261-4928-8ec1-c93aac7f3a8d")
+ def test_rtt_without_responder_aware(self):
+ """Try to perform RTT operation when there is no peer Aware session (on the
+ Responder). Should FAIL."""
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ # Enable a Responder and start a Publisher
+ resp_id = resp_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ resp_ident_event = autils.wait_for_event(resp_dut,
+ aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ resp_mac = resp_ident_event['data']['mac']
+
+ resp_config = autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True)
+ resp_dut.droid.wifiAwarePublish(resp_id, resp_config)
+ autils.wait_for_event(resp_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Disable Responder
+ resp_dut.droid.wifiAwareDestroy(resp_id)
+
+ # Enable the Initiator
+ init_id = init_dut.droid.wifiAwareAttach()
+ autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Initiate an RTT to Responder (no Aware started on Initiator!)
+ results = []
+ num_no_responses = 0
+ num_successes = 0
+ for i in range(self.NUM_ITER):
+ result = self.run_rtt_discovery(init_dut, resp_mac=resp_mac)
+ self.log.debug("result: %s", result)
+ results.append(result)
+ if result is None:
+ num_no_responses = num_no_responses + 1
+ elif (result[rconsts.EVENT_CB_RANGING_KEY_STATUS]
+ == rconsts.EVENT_CB_RANGING_STATUS_SUCCESS):
+ num_successes = num_successes + 1
+
+ asserts.assert_equal(num_no_responses, 0, "No RTT response?",
+ extras={"data":results})
+ asserts.assert_equal(num_successes, 0, "Aware RTT w/o Aware should FAIL!",
+ extras={"data":results})
+ asserts.explicit_pass("RTT Aware test done", extras={"data":results})
diff --git a/acts/tests/google/wifi/rtt/functional/RangeSoftApTest.py b/acts/tests/google/wifi/rtt/functional/RangeSoftApTest.py
new file mode 100644
index 0000000..f0c4f4c
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeSoftApTest.py
@@ -0,0 +1,95 @@
+#!/usr/bin/python3.4
+#
+# Copyright 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.
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeSoftApTest(RttBaseTest):
+ """Test class for RTT ranging to an Android Soft AP."""
+
+ # Soft AP SSID
+ SOFT_AP_SSID = "RTT_TEST_SSID"
+
+ # Soft AP Password (irrelevant)
+ SOFT_AP_PASSWORD = "ABCDEFGH"
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ def __init__(self, controllers):
+ RttBaseTest.__init__(self, controllers)
+
+ #########################################################################
+
+ @test_tracker_info(uuid="578f0725-31e3-4e60-ad62-0212d93cf5b8")
+ def test_rtt_to_soft_ap(self):
+ """Set up a Soft AP on one device and try performing an RTT ranging to it
+ from another device. The attempt must fail - RTT on Soft AP must be
+ disabled."""
+ sap = self.android_devices[0]
+ sap.pretty_name = "SoftAP"
+ client = self.android_devices[1]
+ client.pretty_name = "Client"
+
+ # start Soft AP
+ wutils.start_wifi_tethering(sap, self.SOFT_AP_SSID, self.SOFT_AP_PASSWORD,
+ band=WIFI_CONFIG_APBAND_5G, hidden=False)
+
+ try:
+ # start scanning on the client
+ wutils.start_wifi_connection_scan_and_ensure_network_found(client,
+ self.SOFT_AP_SSID)
+ scans = client.droid.wifiGetScanResults()
+ scanned_softap = None
+ for scanned_ap in scans:
+ if scanned_ap[wutils.WifiEnums.SSID_KEY] == self.SOFT_AP_SSID:
+ scanned_softap = scanned_ap
+ break
+
+ asserts.assert_false(scanned_softap == None, "Soft AP not found in scan!",
+ extras=scans)
+
+ # validate that Soft AP does not advertise 802.11mc support
+ asserts.assert_false(
+ rconsts.SCAN_RESULT_KEY_RTT_RESPONDER in scanned_softap and
+ scanned_softap[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER],
+ "Soft AP advertises itself as supporting 802.11mc!",
+ extras=scanned_softap)
+
+ # falsify the SoftAP's support for IEEE 802.11 so we try a 2-sided RTT
+ scanned_softap[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER] = True # falsify
+
+ # actually try ranging to the Soft AP
+ events = rutils.run_ranging(client, [scanned_softap], self.NUM_ITER, 0)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+
+ asserts.assert_equal(
+ stats[scanned_ap[wutils.WifiEnums.BSSID_KEY]]['num_failures'],
+ self.NUM_ITER, "Some RTT operations to Soft AP succeed!?",
+ extras=stats)
+
+ asserts.explicit_pass("SoftAP + RTT validation done", extras=events)
+ finally:
+ wutils.stop_wifi_tethering(sap)
diff --git a/acts/tests/google/wifi/rtt/functional/RttDisableTest.py b/acts/tests/google/wifi/rtt/functional/RttDisableTest.py
new file mode 100644
index 0000000..1816cd5
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RttDisableTest.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2017 - 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.
+
+from acts import asserts
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+
+class RttDisableTest(WifiBaseTest, RttBaseTest):
+ """Test class for RTT ranging enable/disable flows."""
+
+ MODE_DISABLE_WIFI = 0
+ MODE_ENABLE_DOZE = 1
+ MODE_DISABLE_LOCATIONING = 2
+
+ def __init__(self, controllers):
+ WifiBaseTest.__init__(self, controllers)
+ RttBaseTest.__init__(self, controllers)
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start()
+
+ def run_disable_rtt(self, disable_mode):
+ """Validate the RTT disabled flows: whether by disabling Wi-Fi or entering
+ doze mode.
+
+ Args:
+ disable_mode: The particular mechanism in which RTT is disabled. One of
+ the MODE_* constants.
+ """
+ dut = self.android_devices[0]
+
+ # validate start-up conditions
+ asserts.assert_true(dut.droid.wifiIsRttAvailable(), "RTT is not available")
+
+ # scan to get some APs to be used later
+ all_aps = rutils.select_best_scan_results(rutils.scan_networks(dut),
+ select_count=1)
+ asserts.assert_true(len(all_aps) > 0, "Need at least one visible AP!")
+
+ # disable RTT and validate broadcast & API
+ if disable_mode == self.MODE_DISABLE_WIFI:
+ # disabling Wi-Fi is not sufficient: since scan mode (and hence RTT) will
+ # remain enabled - we need to disable the Wi-Fi chip aka Airplane Mode
+ asserts.assert_true(utils.force_airplane_mode(dut, True),
+ "Can not turn on airplane mode on: %s" % dut.serial)
+ elif disable_mode == self.MODE_ENABLE_DOZE:
+ asserts.assert_true(utils.enable_doze(dut), "Can't enable doze")
+ elif disable_mode == self.MODE_DISABLE_LOCATIONING:
+ utils.set_location_service(dut, False)
+
+ rutils.wait_for_event(dut, rconsts.BROADCAST_WIFI_RTT_NOT_AVAILABLE)
+ asserts.assert_false(dut.droid.wifiIsRttAvailable(), "RTT is available")
+
+ # request a range and validate error
+ id = dut.droid.wifiRttStartRangingToAccessPoints(all_aps[0:1])
+ event = rutils.wait_for_event(dut, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_FAIL, id))
+ asserts.assert_equal(event["data"][rconsts.EVENT_CB_RANGING_KEY_STATUS],
+ rconsts.RANGING_FAIL_CODE_RTT_NOT_AVAILABLE,
+ "Invalid error code")
+
+ # enable RTT and validate broadcast & API
+ if disable_mode == self.MODE_DISABLE_WIFI:
+ asserts.assert_true(utils.force_airplane_mode(dut, False),
+ "Can not turn off airplane mode on: %s" % dut.serial)
+ elif disable_mode == self.MODE_ENABLE_DOZE:
+ asserts.assert_true(utils.disable_doze(dut), "Can't disable doze")
+ elif disable_mode == self.MODE_DISABLE_LOCATIONING:
+ utils.set_location_service(dut, True)
+
+ rutils.wait_for_event(dut, rconsts.BROADCAST_WIFI_RTT_AVAILABLE)
+ asserts.assert_true(dut.droid.wifiIsRttAvailable(), "RTT is not available")
+
+ ############################################################################
+
+ @test_tracker_info(uuid="498c49ab-a188-4612-998d-c47b35ff285e")
+ def test_disable_wifi(self):
+ """Validate that getting expected broadcast when Wi-Fi is disabled and that
+ any range requests are rejected."""
+ self.run_disable_rtt(self.MODE_DISABLE_WIFI)
+
+ @test_tracker_info(uuid="f71f731f-4aaf-402b-8595-db94b625b544")
+ def test_enable_doze(self):
+ """Validate that getting expected broadcast when RTT is disabled due to doze
+ mode and that any range requests are rejected."""
+ self.run_disable_rtt(self.MODE_ENABLE_DOZE)
+
+ @test_tracker_info(uuid="6a1c83a8-9eaf-49db-b547-5131cba0eafe")
+ def test_disable_location(self):
+ """Validate that getting expected broadcast when locationing is disabled and
+ that any range requests are rejected."""
+ self.run_disable_rtt(self.MODE_DISABLE_LOCATIONING)
diff --git a/acts/tests/google/wifi/rtt/functional/RttRequestManagementTest.py b/acts/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
new file mode 100644
index 0000000..82c1058
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2017 - 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.
+
+import random
+import time
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RttRequestManagementTest(RttBaseTest):
+ """Test class for RTT request management flows."""
+
+ SPAMMING_LIMIT = 20
+
+ def __init__(self, controllers):
+ RttBaseTest.__init__(self, controllers)
+
+ #############################################################################
+
+ @test_tracker_info(uuid="29ff4a02-2952-47df-bf56-64f30c963093")
+ def test_cancel_ranging(self):
+ """Request a 'large' number of range operations with various UIDs (using the
+ work-source API), then cancel some of them.
+
+ We can't guarantee a reaction time - it is possible that a cancelled test
+ was already finished and it's results dispatched back. The test therefore
+ stacks the request queue. The sequence is:
+
+ - Request:
+ - 50 tests @ UIDs = {uid1, uid2, uid3}
+ - 2 tests @ UIDs = {uid2, uid3}
+ - 1 test2 @ UIDs = {uid1, uid2, uid3}
+ - Cancel UIDs = {uid2, uid3}
+
+ Expect to receive only 51 results.
+ """
+ dut = self.android_devices[0]
+ max_peers = dut.droid.wifiRttMaxPeersInRequest()
+
+ all_uids = [1000, 20, 30] # 1000 = System Server (makes requests foreground)
+ some_uids = [20, 30]
+
+ aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+ select_count=1)
+ dut.log.info("RTT Supporting APs=%s", aps)
+
+ asserts.assert_true(
+ len(aps) > 0,
+ "Need at least one AP which supports 802.11mc!")
+ if len(aps) > max_peers:
+ aps = aps[0:max_peers]
+
+ group1_ids = []
+ group2_ids = []
+ group3_ids = []
+
+ # step 1: request <spam_limit> ranging operations on [uid1, uid2, uid3]
+ for i in range(self.SPAMMING_LIMIT):
+ group1_ids.append(
+ dut.droid.wifiRttStartRangingToAccessPoints(aps, all_uids))
+
+ # step 2: request 2 ranging operations on [uid2, uid3]
+ for i in range(2):
+ group2_ids.append(
+ dut.droid.wifiRttStartRangingToAccessPoints(aps, some_uids))
+
+ # step 3: request 1 ranging operation on [uid1, uid2, uid3]
+ for i in range(1):
+ group3_ids.append(
+ dut.droid.wifiRttStartRangingToAccessPoints(aps, all_uids))
+
+ # step 4: cancel ranging requests on [uid2, uid3]
+ dut.droid.wifiRttCancelRanging(some_uids)
+
+ # collect results
+ for i in range(len(group1_ids)):
+ rutils.wait_for_event(dut, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, group1_ids[i]))
+ time.sleep(rutils.EVENT_TIMEOUT) # optimize time-outs below to single one
+ for i in range(len(group2_ids)):
+ rutils.fail_on_event(dut, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, group2_ids[i]), 0)
+ for i in range(len(group3_ids)):
+ rutils.wait_for_event(dut, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, group3_ids[i]))
+
+ @test_tracker_info(uuid="48297480-c026-4780-8c13-476e7bea440c")
+ def test_throttling(self):
+ """Request sequential range operations using a bogus UID (which will
+ translate as a throttled process) and similarly using the ACTS/sl4a as
+ the source (a foreground/unthrottled process)."""
+ dut = self.android_devices[0]
+ max_peers = dut.droid.wifiRttMaxPeersInRequest()
+
+ # Need to use a random number since the system keeps states and so the
+ # background uid will be throttled on the next run of this script
+ fake_uid = [random.randint(10, 9999)]
+
+ aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+ select_count=1)
+ dut.log.info("RTT Supporting APs=%s", aps)
+
+ asserts.assert_true(
+ len(aps) > 0,
+ "Need at least one AP which supports 802.11mc!")
+ if len(aps) > max_peers:
+ aps = aps[0:max_peers]
+
+ id1 = dut.droid.wifiRttStartRangingToAccessPoints(aps) # as ACTS/sl4a
+ id2 = dut.droid.wifiRttStartRangingToAccessPoints(aps, fake_uid)
+ id3 = dut.droid.wifiRttStartRangingToAccessPoints(aps, fake_uid)
+ id4 = dut.droid.wifiRttStartRangingToAccessPoints(aps) # as ACTS/sl4a
+
+ rutils.wait_for_event(dut, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, id1))
+ rutils.wait_for_event(dut, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, id2))
+ rutils.wait_for_event(dut, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_FAIL, id3))
+ rutils.wait_for_event(dut, rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, id4))
diff --git a/acts/tests/google/wifi/rtt/stress/StressRangeApTest.py b/acts/tests/google/wifi/rtt/stress/StressRangeApTest.py
new file mode 100644
index 0000000..497c125
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/stress/StressRangeApTest.py
@@ -0,0 +1,79 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2017 - 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.
+
+from acts import asserts
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class StressRangeApTest(RttBaseTest):
+ """Test class for stress testing of RTT ranging to Access Points"""
+
+ def __init__(self, controllers):
+ BaseTestClass.__init__(self, controllers)
+
+ #############################################################################
+
+ def test_rtt_supporting_ap_only(self):
+ """Scan for APs and perform RTT only to those which support 802.11mc.
+
+ Stress test: repeat ranging to the same AP. Verify rate of success and
+ stability of results.
+ """
+ dut = self.android_devices[0]
+ rtt_supporting_aps = rutils.scan_with_rtt_support_constraint(dut, True,
+ repeat=10)
+ dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+
+ num_iter = self.stress_test_min_iteration_count
+
+ max_peers = dut.droid.wifiRttMaxPeersInRequest()
+ asserts.assert_true(
+ len(rtt_supporting_aps) > 0,
+ "Need at least one AP which supports 802.11mc!")
+ if len(rtt_supporting_aps) > max_peers:
+ rtt_supporting_aps = rtt_supporting_aps[0:max_peers]
+
+ events = rutils.run_ranging(dut, rtt_supporting_aps, num_iter, 0,
+ self.stress_test_target_run_time_sec)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference,
+ summary_only=True)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(stat['num_no_results'] == 0,
+ "Missing (timed-out) results", extras=stats)
+ asserts.assert_false(stat['any_lci_mismatch'],
+ "LCI mismatch", extras=stats)
+ asserts.assert_false(stat['any_lcr_mismatch'],
+ "LCR mismatch", extras=stats)
+ asserts.assert_equal(stat['num_invalid_rssi'], 0, "Invalid RSSI",
+ extras=stats)
+ asserts.assert_true(stat['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage
+ * stat['num_results'] / 100,
+ "Failure rate is too high", extras=stats)
+ asserts.assert_true(stat['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+ * stat['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
+
diff --git a/acts/tests/google/wifi/rtt/stress/StressRangeAwareTest.py b/acts/tests/google/wifi/rtt/stress/StressRangeAwareTest.py
new file mode 100644
index 0000000..3073898
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/stress/StressRangeAwareTest.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python3.4
+#
+# Copyright 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.
+
+import queue
+import time
+
+from acts import asserts
+from acts.test_utils.wifi.aware import aware_const as aconsts
+from acts.test_utils.wifi.aware import aware_test_utils as autils
+from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class StressRangeAwareTest(AwareBaseTest, RttBaseTest):
+ """Test class for stress testing of RTT ranging to Wi-Fi Aware peers."""
+ SERVICE_NAME = "GoogleTestServiceXY"
+
+ def __init__(self, controllers):
+ AwareBaseTest.__init__(self, controllers)
+ RttBaseTest.__init__(self, controllers)
+
+ def setup_test(self):
+ """Manual setup here due to multiple inheritance: explicitly execute the
+ setup method from both parents."""
+ AwareBaseTest.setup_test(self)
+ RttBaseTest.setup_test(self)
+
+ def teardown_test(self):
+ """Manual teardown here due to multiple inheritance: explicitly execute the
+ teardown method from both parents."""
+ AwareBaseTest.teardown_test(self)
+ RttBaseTest.teardown_test(self)
+
+ #############################################################################
+
+ def run_rtt_discovery(self, init_dut, resp_mac=None, resp_peer_id=None):
+ """Perform single RTT measurement, using Aware, from the Initiator DUT to
+ a Responder. The RTT Responder can be specified using its MAC address
+ (obtained using out- of-band discovery) or its Peer ID (using Aware
+ discovery).
+
+ Args:
+ init_dut: RTT Initiator device
+ resp_mac: MAC address of the RTT Responder device
+ resp_peer_id: Peer ID of the RTT Responder device
+ """
+ asserts.assert_true(resp_mac is not None or resp_peer_id is not None,
+ "One of the Responder specifications (MAC or Peer ID)"
+ " must be provided!")
+ if resp_mac is not None:
+ id = init_dut.droid.wifiRttStartRangingToAwarePeerMac(resp_mac)
+ else:
+ id = init_dut.droid.wifiRttStartRangingToAwarePeerId(resp_peer_id)
+ try:
+ event = init_dut.ed.pop_event(rutils.decorate_event(
+ rconsts.EVENT_CB_RANGING_ON_RESULT, id), rutils.EVENT_TIMEOUT)
+ result = event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0]
+ if resp_mac is not None:
+ rutils.validate_aware_mac_result(result, resp_mac, "DUT")
+ else:
+ rutils.validate_aware_peer_id_result(result, resp_peer_id, "DUT")
+ return result
+ except queue.Empty:
+ return None
+
+ def test_stress_rtt_ib_discovery_set(self):
+ """Perform a set of RTT measurements, using in-band (Aware) discovery, and
+ switching Initiator and Responder roles repeatedly.
+
+ Stress test: repeat ranging operations. Verify rate of success and
+ stability of results.
+ """
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ (p_id, s_id, p_disc_id, s_disc_id,
+ peer_id_on_sub, peer_id_on_pub) = autils.create_discovery_pair(
+ p_dut,
+ s_dut,
+ p_config=autils.add_ranging_to_pub(autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED), True),
+ s_config=autils.add_ranging_to_pub(autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE), True),
+ device_startup_offset=self.device_startup_offset,
+ msg_id=self.get_next_msg_id())
+
+ results = []
+ start_clock = time.time()
+ iterations_done = 0
+ run_time = 0
+ while iterations_done < self.stress_test_min_iteration_count or (
+ self.stress_test_target_run_time_sec != 0
+ and run_time < self.stress_test_target_run_time_sec):
+ results.append(self.run_rtt_discovery(p_dut, resp_peer_id=peer_id_on_pub))
+ results.append(self.run_rtt_discovery(s_dut, resp_peer_id=peer_id_on_sub))
+
+ iterations_done = iterations_done + 1
+ run_time = time.time() - start_clock
+
+ stats = rutils.extract_stats(results, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ summary_only=True)
+ self.log.debug("Stats: %s", stats)
+ asserts.assert_true(stats['num_no_results'] == 0,
+ "Missing (timed-out) results", extras=stats)
+ asserts.assert_false(stats['any_lci_mismatch'],
+ "LCI mismatch", extras=stats)
+ asserts.assert_false(stats['any_lcr_mismatch'],
+ "LCR mismatch", extras=stats)
+ asserts.assert_equal(stats['num_invalid_rssi'], 0, "Invalid RSSI",
+ extras=stats)
+ asserts.assert_true(
+ stats['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage
+ * stats['num_results'] / 100,
+ "Failure rate is too high", extras=stats)
+ asserts.assert_true(
+ stats['num_range_out_of_margin']
+ <= self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+ * stats['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high", extras=stats)
diff --git a/acts/tests/sample/ConfigurableAccessPointTest.py b/acts/tests/sample/ConfigurableAccessPointTest.py
index ccef716..de07b1e 100644
--- a/acts/tests/sample/ConfigurableAccessPointTest.py
+++ b/acts/tests/sample/ConfigurableAccessPointTest.py
@@ -14,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
import logging
from acts import base_test
@@ -35,8 +34,7 @@
def test_setup_access_point(self):
config = hostapd_config.HostapdConfig(
- channel=6,
- ssid='ImagineYourNetworkHere')
+ channel=6, ssid='ImagineYourNetworkHere')
self.ap.start_ap(config)
logging.info('Your test logic should go here!')
diff --git a/acts/tests/sample/RelayDeviceSampleTest.py b/acts/tests/sample/RelayDeviceSampleTest.py
index 040ef62..a9304bc 100644
--- a/acts/tests/sample/RelayDeviceSampleTest.py
+++ b/acts/tests/sample/RelayDeviceSampleTest.py
@@ -14,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from acts import base_test
-from acts import test_runner
from acts.controllers.relay_lib.relay import SynchronizeRelays
@@ -98,7 +97,3 @@
# For more fine control over the wait time of relays, you can set
# Relay.transition_wait_time. This is not recommended unless you are
# using solid state relays, or async calls.
-
-
-if __name__ == "__main__":
- test_runner.main()
diff --git a/tools/commit_message_check.py b/tools/commit_message_check.py
deleted file mode 100755
index 9304ca5..0000000
--- a/tools/commit_message_check.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - 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.
-from __future__ import print_function
-
-import logging
-import os
-import re
-import sys
-
-from acts.libs.proc import job
-
-GLOBAL_KEYWORDS_FILEPATH = 'vendor/google_testing/comms/framework/etc/' \
- 'commit_keywords'
-LOCAL_KEYWORDS_FILEPATH = '~/.repo_acts_commit_keywords'
-
-FIND_COMMIT_KEYWORDS = 'git log @{u}..| grep -i %s'
-GET_EMAIL_ADDRESS = 'git log --format=%ce -1'
-
-
-def main(argv):
- file_path = os.path.join(
- os.path.dirname(__file__), "../../../../%s" % GLOBAL_KEYWORDS_FILEPATH)
-
- if not os.path.isfile(file_path):
- file_path = os.path.expanduser(LOCAL_KEYWORDS_FILEPATH)
- if not os.path.exists(file_path) or not os.path.isfile(file_path):
- result = job.run(GET_EMAIL_ADDRESS)
- if result.stdout.endswith('@google.com'):
- logging.error(
- 'You do not have the necessary file %s. Please run '
- 'tools/ignore_commit_keywords.sh, or link it with the '
- 'following command:\n ln -sf <internal_master_root>/%s %s'
- % (LOCAL_KEYWORDS_FILEPATH, GLOBAL_KEYWORDS_FILEPATH,
- LOCAL_KEYWORDS_FILEPATH))
- exit(1)
- return
-
- with open(file_path) as file:
- # Places every line within quotes with -e before them.
- # i.e. '-e "line1" -e "line2" -e "line3"'
- grep_args = re.sub('^(.+?)$\n?', ' -e "\\1"',
- file.read(), 0, re.MULTILINE)
-
- result = job.run(FIND_COMMIT_KEYWORDS % grep_args, ignore_status=True)
-
- if result.stdout:
- logging.error('Your commit message contains at least one keyword.')
- logging.error('Keyword(s) found in the following line(s):')
- logging.error(result.stdout)
- logging.error('Please fix/remove these before committing.')
- exit(1)
-
-
-if __name__ == '__main__':
- main(sys.argv[1:])
diff --git a/tools/keyword_check.py b/tools/keyword_check.py
new file mode 100755
index 0000000..e124d29
--- /dev/null
+++ b/tools/keyword_check.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - 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.
+import logging
+import os
+import re
+import shellescape
+
+from acts.libs.proc import job
+
+GLOBAL_KEYWORDS_FILEPATH = 'vendor/google_testing/comms/framework/etc/' \
+ 'commit_keywords'
+LOCAL_KEYWORDS_FILEPATH = '~/.repo_acts_commit_keywords'
+
+GIT_FILE_ADDITIONS = r'git diff --unified=0 %s^! | grep "^+" | ' \
+ r'grep -Ev "^(\+\+\+ b/)" | cut -c 2-'
+
+GIT_FILE_NAMES = r'git diff-tree --no-commit-id --name-only -r %s'
+
+GIT_DIFF_REGION_FOUND = 'git diff %s^! | grep -A10 -B10 %s'
+COMMIT_ID_ENV_KEY = 'PREUPLOAD_COMMIT'
+
+FIND_COMMIT_KEYWORDS = 'git diff %s^! | grep -i %s'
+GET_EMAIL_ADDRESS = 'git log --format=%ce -1'
+
+
+def find_in_commit_message(keyword_list):
+ """Looks for keywords in commit messages.
+
+ Args:
+ keyword_list: A list of keywords
+ """
+ grep_args = ''
+ for keyword in keyword_list:
+ grep_args += '-e %s' % keyword
+
+ result = job.run(
+ FIND_COMMIT_KEYWORDS % (os.environ[COMMIT_ID_ENV_KEY], grep_args),
+ ignore_status=True)
+
+ if result.stdout:
+ logging.error('Your commit message contains at least one keyword.')
+ logging.error('Keyword(s) found in the following line(s):')
+ logging.error(result.stdout)
+ logging.error('Please fix/remove these before committing.')
+ exit(1)
+
+
+def get_words(string):
+ """Splits a string into an array of alphabetical words."""
+ s1 = re.sub('(.)([A-Z][a-z]+)', r'\1 \2', string)
+ s1 = re.sub('([a-z0-9])([A-Z])', r'\1 \2', s1).lower()
+ s1 = re.sub('[^a-z ]', ' ', s1)
+ return s1.split()
+
+
+def find_in_file_names(keyword_list):
+ """Looks for keywords in file names.
+
+ Args:
+ keyword_list: a list of keywords
+ """
+ changed_files = job.run(GIT_FILE_NAMES % os.environ[COMMIT_ID_ENV_KEY])
+
+ keyword_set = set(keyword_list)
+
+ for file_name in changed_files.stdout.split('\n'):
+ words = set(get_words(file_name))
+ if len(keyword_set.intersection(words)) > 0:
+ logging.error('Your commit has a file name that contain at least '
+ 'one keyword: %s.' % file_name)
+ logging.error('Please update or remove this before committing.')
+ exit(1)
+
+
+def find_in_code_additions(keyword_list):
+ """Looks for keywords in code additions.
+
+ Args:
+ keyword_list: a list of keywords
+ """
+ all_additions = job.run(GIT_FILE_ADDITIONS % os.environ[COMMIT_ID_ENV_KEY])
+
+ keyword_set = set(keyword_list)
+
+ for line in all_additions.stdout.split('\n'):
+ words = set(get_words(line))
+ if len(keyword_set.intersection(words)) > 0:
+ result = job.run(GIT_DIFF_REGION_FOUND %
+ (os.environ[COMMIT_ID_ENV_KEY],
+ shellescape.quote(line)))
+ logging.error('Your commit has code changes that contain at least '
+ 'one keyword. Below is an excerpt from the commit '
+ 'diff:')
+ logging.error(result.stdout)
+ logging.error('Please update or remove these before committing.')
+ exit(1)
+
+
+def main():
+ if COMMIT_ID_ENV_KEY not in os.environ:
+ logging.error('Missing commit id in environment.')
+ exit(1)
+ keyword_file = os.path.join(
+ os.path.dirname(__file__), '../../../../%s' % GLOBAL_KEYWORDS_FILEPATH)
+
+ if not os.path.isfile(keyword_file):
+ keyword_file = os.path.expanduser(LOCAL_KEYWORDS_FILEPATH)
+ if not os.path.exists(keyword_file) or not os.path.isfile(keyword_file):
+ result = job.run(GET_EMAIL_ADDRESS)
+ if result.stdout.endswith('@google.com'):
+ logging.error(
+ 'You do not have the necessary file %s. Please run '
+ 'tools/ignore_commit_keywords.sh, or link it with the '
+ 'following command:\n ln -sf <internal_master_root>/%s %s'
+ % (LOCAL_KEYWORDS_FILEPATH, GLOBAL_KEYWORDS_FILEPATH,
+ LOCAL_KEYWORDS_FILEPATH))
+ exit(1)
+ return
+
+ with open(keyword_file) as file:
+ keyword_list = file.read().lower().split()
+
+ find_in_code_additions(keyword_list)
+ find_in_commit_message(keyword_list)
+ find_in_file_names(keyword_list)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/lab/README.md b/tools/lab/README.md
index 5c5eee4..0fe5502 100644
--- a/tools/lab/README.md
+++ b/tools/lab/README.md
@@ -29,9 +29,10 @@
* num_users:
num_users: number of users (int)
* process_time:
- pid_times: a list of (time, PID) tuples where time is a string
- representing time elapsed in D-HR:MM:SS format and PID is a string
- representing the pid (string, string)
+ adb_processes, fastboot_processes: a list of (PID, serialnumber)
+ tuples
+ num_adb_processes, num_fastboot_processes: the number of tuples
+ in the previous lists
* ram:
total: total physical RAM available in KB (int)
used: total RAM used by system in KB (int)
@@ -45,19 +46,28 @@
load_avg_1_min: system load average for past 1 min (float)
load_avg_5_min: system load average for past 5 min (float)
load_avg_15_min: system load average for past 15 min (float)
+* time:
+ date_time: system date and time (string)
+* time_sync:
+ is_synchronized: whether NTP synchronized (bool)
* uptime:
time_seconds: uptime in seconds (float)
* usb:
devices: a list of Device objects, each with name of device, number of bytes
transferred, and the usb bus number/device id.
* verify:
- device serial number as key: device status as value
+ unauthorized: list of phone sn's that are unauthorized
+ offline: list of phone sn's that are offline
+ recovery: list of phone sn's that are in recovery mode
+ question: list of phone sn's in ??? mode
+ device: list of phone sn's that are in device mode
+ total: total number of offline, recovery, question or unauthorized devices
* version:
fastboot_version: which version of fastboot (string)
adb_version: which version of adb (string)
python_version: which version of python (string)
kernel_version: which version of kernel (string)
* zombie:
- adb_zombies: list of adb zombie processes (PID, state, name)
- fastboot_zombies: list of fastboot zombie processes (PID, state, name)
- other_zombies: list of other zombie processes (PID, state, name)
+ adb_zombies, fastboot_zombies, other_zombies: lists of
+ (PID, serial number) tuples
+ num_adb_zombies, num_fastboot_zombies, num_other_zombies: int
diff --git a/tools/lab/config.json b/tools/lab/config.json
index 5d9bf76..5ef8ff0 100644
--- a/tools/lab/config.json
+++ b/tools/lab/config.json
@@ -22,16 +22,16 @@
}
},
"time_sync": {
- "is_synchronized": {
- "constant": true,
- "compare":"EQUALS"
- }
+ "is_synchronized": {
+ "constant": true,
+ "compare": "EQUALS"
+ }
},
"network": {
- "connected": {
- "constant": true,
- "compare": "EQUALS_DICT"
- }
+ "connected": {
+ "constant": true,
+ "compare": "EQUALS_DICT"
+ }
},
"process_time": {
"num_adb_processes": {
@@ -40,9 +40,15 @@
}
},
"verify": {
- "devices": {
- "constant": "device",
- "compare": "EQUALS_DICT"
+ "total_unhealthy": {
+ "constant": "0",
+ "compare": "EQUALS"
+ }
+ },
+ "kernel_version": {
+ "kernel_release": {
+ "constant": "3.19",
+ "compare": "STARTS_WITH"
}
},
"zombie": {
@@ -51,5 +57,4 @@
"compare": "EQUALS"
}
}
-
}
diff --git a/tools/lab/health/constant_health_analyzer.py b/tools/lab/health/constant_health_analyzer.py
index 97e92a9..d7f9794 100644
--- a/tools/lab/health/constant_health_analyzer.py
+++ b/tools/lab/health/constant_health_analyzer.py
@@ -75,3 +75,16 @@
True if result is equal to constant
"""
return metric_results[self.key] == self._constant
+
+
+class HealthyIfStartsWith(ConstantHealthAnalyzer):
+ def is_healthy(self, metric_results):
+ """Returns whether result starts with a constant
+
+ Args:
+ metric_results: a dictionary of metric results.
+
+ Returns:
+ True if result starts with a constant
+ """
+ return metric_results[self.key].startswith(str(self._constant))
diff --git a/tools/lab/health_checker.py b/tools/lab/health_checker.py
index 4f9a87b..a917fe7 100644
--- a/tools/lab/health_checker.py
+++ b/tools/lab/health_checker.py
@@ -14,9 +14,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import logging
+
from health.constant_health_analyzer import HealthyIfGreaterThanConstantNumber
from health.constant_health_analyzer import HealthyIfLessThanConstantNumber
from health.constant_health_analyzer import HealthyIfEquals
+from health.constant_health_analyzer import HealthyIfStartsWith
from health.custom_health_analyzer import HealthyIfNotIpAddress
from health.constant_health_analyzer_wrapper import HealthyIfValsEqual
@@ -27,7 +30,6 @@
_analyzers: a list of metric, analyzer tuples where metric is a string
representing a metric name ('DiskMetric') and analyzer is a
constant_health_analyzer object
- _comparer_constructor: a dict that maps strings to comparer objects
config:
a dict formatted as follows:
{
@@ -47,7 +49,8 @@
'LESS_THAN': lambda k, c: HealthyIfLessThanConstantNumber(k, c),
'EQUALS': lambda k, c: HealthyIfEquals(k, c),
'IP_ADDR': lambda k, c: HealthyIfNotIpAddress(k),
- 'EQUALS_DICT': lambda k, c: HealthyIfValsEqual(k, c)
+ 'EQUALS_DICT': lambda k, c: HealthyIfValsEqual(k, c),
+ 'STARTS_WITH': lambda k, c: HealthyIfStartsWith(k, c)
}
def __init__(self, config):
@@ -77,7 +80,13 @@
# loop through and check if healthy
unhealthy_metrics = []
for (metric, analyzer) in self._analyzers:
- # if not healthy, add to list so value can be reported
- if not analyzer.is_healthy(response_dict[metric]):
- unhealthy_metrics.append(metric)
+ try:
+ # if not healthy, add to list so value can be reported
+ if not analyzer.is_healthy(response_dict[metric]):
+ unhealthy_metrics.append(metric)
+ # don't exit whole program if error in config file, just report
+ except KeyError as e:
+ logging.warning(
+ 'Error in config file, "%s" not a health metric\n' % e)
+
return unhealthy_metrics
diff --git a/tools/lab/lab_upload_hooks.py b/tools/lab/lab_upload_hooks.py
new file mode 100755
index 0000000..d1bfa5f
--- /dev/null
+++ b/tools/lab/lab_upload_hooks.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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.
+
+import test_main
+from acts.libs.proc import job
+
+# Get the names of all files that have been modified from last upstream sync.
+GIT_MODIFIED_FILES = 'git show --pretty="" --name-only @{u}..'
+
+
+def main():
+ files = job.run(GIT_MODIFIED_FILES, ignore_status=True).stdout
+ for file in files.split():
+ if file.startswith('tools/lab/'):
+ test_main.main()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/lab/main.py b/tools/lab/main.py
index 6e26e1a..da517ac 100755
--- a/tools/lab/main.py
+++ b/tools/lab/main.py
@@ -34,6 +34,7 @@
from metrics.ram_metric import RamMetric
from metrics.read_metric import ReadMetric
from metrics.system_load_metric import SystemLoadMetric
+from metrics.time_metric import TimeMetric
from metrics.time_sync_metric import TimeSyncMetric
from metrics.uptime_metric import UptimeMetric
from metrics.usb_metric import UsbMetric
@@ -50,8 +51,8 @@
class RunnerFactory(object):
_reporter_constructor = {
- 'logger': lambda param: [LoggerReporter(param)],
- 'json': lambda param: [JsonReporter(param)]
+ 'logger': lambda param, output: [LoggerReporter(param)],
+ 'json': lambda param, output: [JsonReporter(param, output)]
}
_metric_constructor = {
@@ -78,6 +79,7 @@
RamMetric(),
ReadMetric(),
SystemLoadMetric(),
+ TimeMetric(),
TimeSyncMetric(),
UptimeMetric(),
UsbMetric(),
@@ -102,12 +104,13 @@
reporters = []
# Get health config file, if specified
- config_file = arg_dict.pop('config', None)
# If not specified, default to 'config.json'
- if not config_file:
- config_file = os.path.join(sys.path[0], 'config.json')
- else:
- config_file = config_file[0]
+ config_file = arg_dict.pop('config',
+ os.path.join(sys.path[0], 'config.json'))
+ # Get output file path, if specified
+ # If not specified, default to 'output.json'
+ output_file = arg_dict.pop('output', 'output.json')
+
try:
with open(config_file) as json_data:
health_config = json.load(json_data)
@@ -120,7 +123,8 @@
rep_list = arg_dict.pop('reporter')
if rep_list is not None:
for rep_type in rep_list:
- reporters += cls._reporter_constructor[rep_type](checker)
+ reporters += cls._reporter_constructor[rep_type](checker,
+ output_file)
else:
# If no reporter specified, default to logger.
reporters += [LoggerReporter(checker)]
@@ -216,10 +220,18 @@
parser.add_argument(
'-c',
'--config',
- nargs=1,
- default=None,
+ nargs='?',
+ default='config.json',
metavar="<PATH>",
help='Path to health configuration file, defaults to `config.json`')
+ parser.add_argument(
+ '-o',
+ '--output',
+ nargs='?',
+ default='output.json',
+ metavar="<PATH>",
+ help='Path to where output file will be written, if applicable,'
+ ' defaults to `output.json`')
return parser
diff --git a/tools/lab/metrics/process_time_metric.py b/tools/lab/metrics/process_time_metric.py
index 8071f72..bc7194b 100644
--- a/tools/lab/metrics/process_time_metric.py
+++ b/tools/lab/metrics/process_time_metric.py
@@ -32,13 +32,9 @@
"""Returns ADB and Fastboot processes and their time elapsed
Returns:
- A dict with the following fields:
- adb_processes, fastboot_processes: a list of (PID, serialnumber)
- tuples where PID is a string representing the pid and
- serialnumber is serial number as a string, or NONE if number
- wasn't in command
- num_adb_processes, num_fastboot_processes: the number of tuples
- in the previous lists
+ A dictionary with adb/fastboot_processes as a list of serial nums or
+ NONE if number wasn't in command. num_adb/fastboot_processes as
+ the number of serials in list.
"""
# Get the process ids
pids = self.get_adb_fastboot_pids()
@@ -73,9 +69,9 @@
serial_number = spl_ln[sn_index + 1]
# append to proper list
if 'fastboot' in output:
- fastboot_processes.append((str(pid), serial_number))
+ fastboot_processes.append(serial_number)
elif 'adb' in output:
- adb_processes.append((str(pid), serial_number))
+ adb_processes.append(serial_number)
# Create response dictionary
response = {
@@ -93,7 +89,7 @@
A list of PID strings
"""
# Get ids of processes with 'adb' or 'fastboot' in name
- adb_result = self._shell.get_pids('adb')
- fastboot_result = self._shell.get_pids('fastboot')
+ adb_result = self._shell.get_command_pids('adb')
+ fastboot_result = self._shell.get_command_pids('fastboot')
# concatenate two generator objects, return as list
return list(itertools.chain(adb_result, fastboot_result))
diff --git a/tools/lab/metrics/time_metric.py b/tools/lab/metrics/time_metric.py
new file mode 100644
index 0000000..2d9daf2
--- /dev/null
+++ b/tools/lab/metrics/time_metric.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+#
+# Copyright 2017 - 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.
+
+from metrics.metric import Metric
+
+
+class TimeMetric(Metric):
+
+ COMMAND = "date"
+ # Fields for response dictionary
+ DATE_TIME = 'date_time'
+
+ def gather_metric(self):
+ """Returns the system date and time
+
+ Returns:
+ A dict with the following fields:
+ date_time: String stystem date and time
+
+ """
+ # Run shell command
+ result = self._shell.run(self.COMMAND).stdout
+ # Example stdout:
+ # Wed Jul 19 16:53:15 PDT 2017
+
+ response = {
+ self.DATE_TIME: result,
+ }
+ return response
diff --git a/tools/lab/metrics/time_sync_metric.py b/tools/lab/metrics/time_sync_metric.py
index 450f300..f2f7e99 100644
--- a/tools/lab/metrics/time_sync_metric.py
+++ b/tools/lab/metrics/time_sync_metric.py
@@ -28,7 +28,7 @@
Returns:
A dict with the following fields:
- is_synchronized: True if synchronized, fales otherwise
+ is_synchronized: True if synchronized
"""
# Run shell command
diff --git a/tools/lab/metrics/uptime_metric.py b/tools/lab/metrics/uptime_metric.py
index 5683e8d..edf5c2a 100644
--- a/tools/lab/metrics/uptime_metric.py
+++ b/tools/lab/metrics/uptime_metric.py
@@ -18,27 +18,34 @@
class UptimeMetric(Metric):
-
- COMMAND = "cat /proc/uptime"
+ SECONDS_COMMAND = 'cat /proc/uptime | cut -f1 -d\' \''
+ READABLE_COMMAND = 'uptime -p | cut -f2- -d\' \''
# Fields for response dictionary
TIME_SECONDS = 'time_seconds'
+ TIME_READABLE = 'time_readable'
+
+ def get_readable(self):
+ # Example stdout:
+ # 2 days, 8 hours, 19 minutes
+ return self._shell.run(self.READABLE_COMMAND).stdout
+
+ def get_seconds(self):
+ # Example stdout:
+ # 358350.70
+ return self._shell.run(self.SECONDS_COMMAND).stdout
def gather_metric(self):
"""Tells how long system has been running
Returns:
A dict with the following fields:
- time_seconds: float uptime in total seconds
+ time_seconds: uptime in total seconds
+ time_readable: time in human readable format
"""
- # Run shell command
- result = self._shell.run(self.COMMAND).stdout
- # Example stdout:
- # 358350.70 14241538.06
- # Get only first number (total time)
- seconds = float(result.split()[0])
response = {
- self.TIME_SECONDS: seconds,
+ self.TIME_SECONDS: self.get_seconds(),
+ self.TIME_READABLE: self.get_readable()
}
return response
diff --git a/tools/lab/metrics/usb_metric.py b/tools/lab/metrics/usb_metric.py
index 2de56fb..cd4bb7a 100644
--- a/tools/lab/metrics/usb_metric.py
+++ b/tools/lab/metrics/usb_metric.py
@@ -17,7 +17,7 @@
import io
import os
import subprocess
-
+import logging
import sys
from metrics.metric import Metric
@@ -59,7 +59,7 @@
try:
self._shell.run(self.USBMON_CHECK_COMMAND)
except job.Error:
- print('Kernel module not loaded, attempting to load usbmon')
+ logging.info('Kernel module not loaded, attempting to load usbmon')
try:
self._shell.run(self.USBMON_INSTALL_COMMAND)
except job.Error as error:
diff --git a/tools/lab/metrics/verify_metric.py b/tools/lab/metrics/verify_metric.py
index e4ad572..49569cf 100644
--- a/tools/lab/metrics/verify_metric.py
+++ b/tools/lab/metrics/verify_metric.py
@@ -20,17 +20,32 @@
class VerifyMetric(Metric):
"""Gathers the information of connected devices via ADB"""
COMMAND = r"adb devices | sed '1d;$d'"
- DEVICES = 'devices'
+ UNAUTHORIZED = 'unauthorized'
+ OFFLINE = 'offline'
+ RECOVERY = 'recovery'
+ QUESTION = 'question'
+ DEVICE = 'device'
+ TOTAL_UNHEALTHY = 'total_unhealthy'
def gather_metric(self):
""" Gathers device info based on adb output.
Returns:
- A dictionary with the field:
- devices: a dict with device serial number as key and device status as
- value.
+ A dictionary with the fields:
+ unauthorized: list of phone sn's that are unauthorized
+ offline: list of phone sn's that are offline
+ recovery: list of phone sn's that are in recovery mode
+ question: list of phone sn's in ??? mode
+ device: list of phone sn's that are in device mode
+ total: total number of offline, recovery, question or unauthorized
+ devices
"""
- device_dict = {}
+ offline_list = list()
+ unauth_list = list()
+ recovery_list = list()
+ question_list = list()
+ device_list = list()
+
# Delete first and last line of output of adb.
output = self._shell.run(self.COMMAND).stdout
@@ -40,6 +55,32 @@
for line in output.split('\n'):
spl_line = line.split('\t')
# spl_line[0] is serial, [1] is status. See example line.
- device_dict[spl_line[0]] = spl_line[1]
+ phone_sn = spl_line[0]
+ phone_state = spl_line[1]
- return {self.DEVICES: device_dict}
+ if phone_state == 'device':
+ device_list.append(phone_sn)
+ elif phone_state == 'unauthorized':
+ unauth_list.append(phone_sn)
+ elif phone_state == 'recovery':
+ recovery_list.append(phone_sn)
+ elif '?' in phone_state:
+ question_list.append(phone_sn)
+ elif phone_state == 'offline':
+ offline_list.append(phone_sn)
+
+ return {
+ self.UNAUTHORIZED:
+ unauth_list,
+ self.OFFLINE:
+ offline_list,
+ self.RECOVERY:
+ recovery_list,
+ self.QUESTION:
+ question_list,
+ self.DEVICE:
+ device_list,
+ self.TOTAL_UNHEALTHY:
+ len(unauth_list) + len(offline_list) + len(recovery_list) +
+ len(question_list)
+ }
diff --git a/tools/lab/metrics/version_metric.py b/tools/lab/metrics/version_metric.py
index 32c7910..e0b1445 100644
--- a/tools/lab/metrics/version_metric.py
+++ b/tools/lab/metrics/version_metric.py
@@ -22,7 +22,7 @@
FASTBOOT_COMMAND = 'fastboot --version'
FASTBOOT_VERSION = 'fastboot_version'
- # String to return if Fastboot version is too old
+ # String to return if fastboot version is too old
FASTBOOT_ERROR_MESSAGE = ('this version is older than versions that'
'return versions properly')
@@ -34,10 +34,10 @@
fastboot_version: string representing version of fastboot
Older versions of fastboot do not have a version flag. On an
older version, this metric will print 'this version is older
- than versions that return veresions properly'
+ than versions that return versions properly'
"""
- result = self._shell.run(self.FASTBOOT_COMMAND)
+ result = self._shell.run(self.FASTBOOT_COMMAND, ignore_status=True)
# If '--version' flag isn't recognized, will print to stderr
if result.stderr:
version = self.FASTBOOT_ERROR_MESSAGE
@@ -67,8 +67,10 @@
result = self._shell.run(self.ADB_COMMAND)
stdout = result.stdout.splitlines()
adb_version = stdout[0].split()[-1]
- # Revision information will always be in next line
- adb_revision = stdout[1].split()[1]
+ adb_revision = ""
+ # Old versions of ADB do not have revision numbers.
+ if len(stdout) > 1:
+ adb_revision = stdout[1].split()[1]
response = {
self.ADB_VERSION: adb_version,
diff --git a/tools/lab/metrics/zombie_metric.py b/tools/lab/metrics/zombie_metric.py
index 7711333..647b2ce 100644
--- a/tools/lab/metrics/zombie_metric.py
+++ b/tools/lab/metrics/zombie_metric.py
@@ -32,11 +32,9 @@
If process does not have serial, None is returned instead.
Returns:
- A dict with the following fields:
- adb_zombies, fastboot_zombies, other_zombies: lists of
- (PID, serial number) tuples
- num_adb_zombies, num_fastboot_zombies, num_other_zombies: int
- representing the number of tuples in the respective list
+ A dict with the following fields: adb/fastboot/other_zombies; lists
+ of serial numbers and num_adb/fastboot/other_zombies; ints
+ representing the number of entries in the respective list
"""
adb_zombies, fastboot_zombies, other_zombies = [], [], []
result = self._shell.run(self.COMMAND).stdout
@@ -46,8 +44,8 @@
output = result.splitlines()
for ln in output:
- spl_ln = ln.split()
# spl_ln looks like ['1xx', 'Z+', 'adb', '<defunct'>, ...]
+ spl_ln = ln.split()
pid, state, name = spl_ln[:3]
if '-s' in spl_ln:
@@ -57,15 +55,15 @@
sn = None
else:
sn = spl_ln[sn_idx + 1]
- zombie = (pid, sn)
+ zombie = sn
else:
- zombie = (pid, None)
+ zombie = None
if 'adb' in ln:
adb_zombies.append(zombie)
elif 'fastboot' in ln:
fastboot_zombies.append(zombie)
else:
- other_zombies.append(zombie)
+ other_zombies.append(pid)
return {
self.ADB_ZOMBIES: adb_zombies,
diff --git a/tools/lab/reporters/json_reporter.py b/tools/lab/reporters/json_reporter.py
index 7aa779f..4a8bcf7 100644
--- a/tools/lab/reporters/json_reporter.py
+++ b/tools/lab/reporters/json_reporter.py
@@ -16,12 +16,25 @@
import json
-import health_checker
from metrics.usb_metric import Device
from reporters.reporter import Reporter
class JsonReporter(Reporter):
+ """Reporter class that reports information in JSON format to a file.
+
+ This defaults to writing to the current working directory with name
+ output.json
+
+ Attributes:
+ health_checker: a HealthChecker object
+ file_name: Path of file to write to.
+ """
+
+ def __init__(self, h_checker, file_name='output.json'):
+ super(JsonReporter, self).__init__(h_checker)
+ self.file_name = file_name
+
def report(self, metric_responses):
unhealthy_metrics = self.health_checker.get_unhealthy(metric_responses)
for metric_name in metric_responses:
@@ -29,7 +42,13 @@
metric_responses[metric_name]['is_healthy'] = True
else:
metric_responses[metric_name]['is_healthy'] = False
- print(json.dumps(metric_responses, indent=4, cls=AutoJsonEncoder))
+ # add a total unhealthy score
+ metric_responses['total_unhealthy'] = {
+ 'total_unhealthy': len(set(unhealthy_metrics))
+ }
+ with open(self.file_name, 'w') as outfile:
+ json.dump(
+ metric_responses, indent=4, cls=AutoJsonEncoder, fp=outfile)
class AutoJsonEncoder(json.JSONEncoder):
diff --git a/tools/lab/setup.py b/tools/lab/setup.py
index c2c77ba..20e9daa 100755
--- a/tools/lab/setup.py
+++ b/tools/lab/setup.py
@@ -74,7 +74,7 @@
def main():
setuptools.setup(
name='LabHealth',
- version='0.1',
+ version='0.3',
description='Android Test Lab Health',
license='Apache2.0',
cmdclass={
diff --git a/tools/lab/test_main.py b/tools/lab/test_main.py
index 5f7b644..23c14cd 100755
--- a/tools/lab/test_main.py
+++ b/tools/lab/test_main.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3.4
#
# Copyright 2017 - The Android Open Source Project
#
@@ -17,11 +17,16 @@
import sys
import unittest
-if __name__ == "__main__":
+
+def main():
suite = unittest.TestLoader().discover(
start_dir='./tools/lab', pattern='*_test.py')
runner = unittest.TextTestRunner()
- # Return exit code of tests, so preupload hook fails if tests don't pass
+ # Pass the return status of the tests to the exit code.
ret = not runner.run(suite).wasSuccessful()
sys.exit(ret)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/lab/tests/constant_health_analyzer_test.py b/tools/lab/tests/constant_health_analyzer_test.py
index ebf38b2..756b488 100644
--- a/tools/lab/tests/constant_health_analyzer_test.py
+++ b/tools/lab/tests/constant_health_analyzer_test.py
@@ -61,5 +61,29 @@
self.assertFalse(analyzer.is_healthy(sample_metric))
+class HealthIfStartsWithTest(unittest.TestCase):
+ def test_starts_with_true_str(self):
+ sample_metric = {'kernel_release': "3.19-generic-random-12"}
+ analyzer = ha.HealthyIfStartsWith(
+ key='kernel_release', constant="3.19")
+ self.assertTrue(analyzer.is_healthy(sample_metric))
+
+ def test_starts_with_false_str(self):
+ sample_metric = {'kernel_release': "3.19-generic-random-12"}
+ analyzer = ha.HealthyIfStartsWith(
+ key='kernel_release', constant="4.04")
+ self.assertFalse(analyzer.is_healthy(sample_metric))
+
+ def test_starts_with_true_non_str(self):
+ sample_metric = {'kernel_release': "3.19-generic-random-12"}
+ analyzer = ha.HealthyIfStartsWith(key='kernel_release', constant=3.19)
+ self.assertTrue(analyzer.is_healthy(sample_metric))
+
+ def test_starts_with_false_non_str(self):
+ sample_metric = {'kernel_release': "3.19-generic-random-12"}
+ analyzer = ha.HealthyIfStartsWith(key='kernel_release', constant=4.04)
+ self.assertFalse(analyzer.is_healthy(sample_metric))
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/tools/lab/tests/fake.py b/tools/lab/tests/fake.py
index 07aa4e0..be22e2a 100644
--- a/tools/lab/tests/fake.py
+++ b/tools/lab/tests/fake.py
@@ -57,7 +57,7 @@
else:
return self._fake_result
- def get_pids(self, identifier):
+ def get_command_pids(self, identifier):
"""Returns a generator of fake pids
Args:
diff --git a/tools/lab/tests/health_checker_test.py b/tools/lab/tests/health_checker_test.py
index 28d2e64..67b4504 100644
--- a/tools/lab/tests/health_checker_test.py
+++ b/tools/lab/tests/health_checker_test.py
@@ -16,6 +16,7 @@
import io
import mock
+import sys
import unittest
from health_checker import HealthChecker
@@ -159,6 +160,50 @@
set(checker.get_unhealthy(fake_metric_response)),
set(expected_unhealthy))
+ def test_catch_key_error_metric_name(self):
+ fake_config = {
+ "not_verify": {
+ "devices": {
+ "constant": "device",
+ "compare": "EQUALS_DICT"
+ }
+ }
+ }
+ checker = HealthChecker(fake_config)
+ fake_metric_response = {
+ 'verify': {
+ 'devices': {
+ 'serialnumber': 'device'
+ }
+ }
+ }
+ try:
+ checker.get_unhealthy(fake_metric_response)
+ except KeyError as error:
+ self.fail('did not catch %s' % error)
+
+ def test_catch_key_error_metric_field(self):
+ fake_config = {
+ "verify": {
+ "not_devices": {
+ "constant": "device",
+ "compare": "EQUALS_DICT"
+ }
+ }
+ }
+ checker = HealthChecker(fake_config)
+ fake_metric_response = {
+ 'verify': {
+ 'devices': {
+ 'serialnumber': 'device'
+ }
+ }
+ }
+ try:
+ checker.get_unhealthy(fake_metric_response)
+ except KeyError as error:
+ self.fail('get_unhealthy did not internally handle %s' % error)
+
if __name__ == '__main__':
unittest.main()
diff --git a/tools/lab/tests/process_time_metric_test.py b/tools/lab/tests/process_time_metric_test.py
index f3e4c8f..c9dbec3 100644
--- a/tools/lab/tests/process_time_metric_test.py
+++ b/tools/lab/tests/process_time_metric_test.py
@@ -60,7 +60,7 @@
process_time_metric.ProcessTimeMetric.NUM_ADB_PROCESSES:
0,
process_time_metric.ProcessTimeMetric.FASTBOOT_PROCESSES:
- [("456", 'FA6BM0305019')],
+ ['FA6BM0305019'],
process_time_metric.ProcessTimeMetric.NUM_FASTBOOT_PROCESSES:
1
}
@@ -80,7 +80,7 @@
metric_obj = process_time_metric.ProcessTimeMetric(shell=fake_shell)
expected_result = {
process_time_metric.ProcessTimeMetric.ADB_PROCESSES:
- [('123', 'FAKESN'), ('456', 'FAKESN2')],
+ ['FAKESN', 'FAKESN2'],
process_time_metric.ProcessTimeMetric.NUM_ADB_PROCESSES:
2,
process_time_metric.ProcessTimeMetric.FASTBOOT_PROCESSES: [],
diff --git a/tools/lab/tests/time_metric_test.py b/tools/lab/tests/time_metric_test.py
new file mode 100755
index 0000000..7d96299
--- /dev/null
+++ b/tools/lab/tests/time_metric_test.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+#
+# Copyright 2017 - 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.
+
+import unittest
+
+from metrics import time_metric
+from tests import fake
+
+
+class TimeMetricTest(unittest.TestCase):
+ """Class for testing UptimeMetric."""
+
+ def test_correct_uptime(self):
+ # Create sample stdout string ShellCommand.run() would return
+ stdout_string = "Wed Jul 19 16:53:15 PDT 2017"
+ FAKE_RESULT = fake.FakeResult(stdout=stdout_string)
+ fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
+ metric_obj = time_metric.TimeMetric(shell=fake_shell)
+
+ expected_result = {
+ time_metric.TimeMetric.DATE_TIME: "Wed Jul 19 16:53:15 PDT 2017",
+ }
+ self.assertEqual(expected_result, metric_obj.gather_metric())
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/lab/tests/uptime_metric_test.py b/tools/lab/tests/uptime_metric_test.py
index 5c73523..185c4b1 100755
--- a/tools/lab/tests/uptime_metric_test.py
+++ b/tools/lab/tests/uptime_metric_test.py
@@ -23,17 +23,23 @@
class UptimeMetricTest(unittest.TestCase):
"""Class for testing UptimeMetric."""
- def test_correct_uptime(self):
- # Create sample stdout string ShellCommand.run() would return
- stdout_string = "358350.70 14241538.06"
+ def test_get_seconds(self):
+ stdout_string = '358350.70'
FAKE_RESULT = fake.FakeResult(stdout=stdout_string)
fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
metric_obj = uptime_metric.UptimeMetric(shell=fake_shell)
- expected_result = {
- uptime_metric.UptimeMetric.TIME_SECONDS: 358350.70,
- }
- self.assertEqual(expected_result, metric_obj.gather_metric())
+ expected_result = '358350.70'
+ self.assertEqual(expected_result, metric_obj.get_seconds())
+
+ def test_get_readable(self):
+ stdout_string = '2 days, 8 hours, 19 minutes'
+ FAKE_RESULT = fake.FakeResult(stdout=stdout_string)
+ fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
+ metric_obj = uptime_metric.UptimeMetric(shell=fake_shell)
+
+ expected_result = '2 days, 8 hours, 19 minutes'
+ self.assertEqual(expected_result, metric_obj.get_readable())
if __name__ == '__main__':
diff --git a/tools/lab/tests/verify_metric_test.py b/tools/lab/tests/verify_metric_test.py
index 67a08bc..8d5fa04 100644
--- a/tools/lab/tests/verify_metric_test.py
+++ b/tools/lab/tests/verify_metric_test.py
@@ -21,27 +21,102 @@
class VerifyMetricTest(unittest.TestCase):
+ def test_offline(self):
+ mock_output = '00serial01\toffline'
+ FAKE_RESULT = fake.FakeResult(stdout=mock_output)
+ fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
+ metric_obj = verify_metric.VerifyMetric(shell=fake_shell)
+
+ expected_result = {
+ verify_metric.VerifyMetric.TOTAL_UNHEALTHY: 1,
+ verify_metric.VerifyMetric.UNAUTHORIZED: [],
+ verify_metric.VerifyMetric.RECOVERY: [],
+ verify_metric.VerifyMetric.OFFLINE: ['00serial01'],
+ verify_metric.VerifyMetric.QUESTION: [],
+ verify_metric.VerifyMetric.DEVICE: []
+ }
+ self.assertEquals(metric_obj.gather_metric(), expected_result)
+
+ def test_unauth(self):
+ mock_output = '00serial01\tunauthorized'
+ FAKE_RESULT = fake.FakeResult(stdout=mock_output)
+ fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
+ metric_obj = verify_metric.VerifyMetric(shell=fake_shell)
+
+ expected_result = {
+ verify_metric.VerifyMetric.TOTAL_UNHEALTHY: 1,
+ verify_metric.VerifyMetric.UNAUTHORIZED: ['00serial01'],
+ verify_metric.VerifyMetric.RECOVERY: [],
+ verify_metric.VerifyMetric.OFFLINE: [],
+ verify_metric.VerifyMetric.QUESTION: [],
+ verify_metric.VerifyMetric.DEVICE: []
+ }
+ self.assertEquals(metric_obj.gather_metric(), expected_result)
+
+ def test_device(self):
+ mock_output = '00serial01\tdevice'
+ FAKE_RESULT = fake.FakeResult(stdout=mock_output)
+ fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
+ metric_obj = verify_metric.VerifyMetric(shell=fake_shell)
+
+ expected_result = {
+ verify_metric.VerifyMetric.TOTAL_UNHEALTHY: 0,
+ verify_metric.VerifyMetric.UNAUTHORIZED: [],
+ verify_metric.VerifyMetric.RECOVERY: [],
+ verify_metric.VerifyMetric.OFFLINE: [],
+ verify_metric.VerifyMetric.QUESTION: [],
+ verify_metric.VerifyMetric.DEVICE: ['00serial01']
+ }
+ self.assertEquals(metric_obj.gather_metric(), expected_result)
+
+ def test_both(self):
+ mock_output = '00serial01\toffline\n' \
+ '01serial00\tunauthorized\n' \
+ '0regan0\tdevice'
+ FAKE_RESULT = fake.FakeResult(stdout=mock_output)
+ fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
+ metric_obj = verify_metric.VerifyMetric(shell=fake_shell)
+
+ expected_result = {
+ verify_metric.VerifyMetric.TOTAL_UNHEALTHY: 2,
+ verify_metric.VerifyMetric.UNAUTHORIZED: ['01serial00'],
+ verify_metric.VerifyMetric.RECOVERY: [],
+ verify_metric.VerifyMetric.QUESTION: [],
+ verify_metric.VerifyMetric.OFFLINE: ['00serial01'],
+ verify_metric.VerifyMetric.DEVICE: ['0regan0']
+ }
+
+ self.assertEquals(metric_obj.gather_metric(), expected_result)
+
def test_gather_device_empty(self):
mock_output = ''
FAKE_RESULT = fake.FakeResult(stdout=mock_output)
fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
metric_obj = verify_metric.VerifyMetric(shell=fake_shell)
- expected_result = {verify_metric.VerifyMetric.DEVICES: {}}
+ expected_result = {
+ verify_metric.VerifyMetric.TOTAL_UNHEALTHY: 0,
+ verify_metric.VerifyMetric.UNAUTHORIZED: [],
+ verify_metric.VerifyMetric.RECOVERY: [],
+ verify_metric.VerifyMetric.OFFLINE: [],
+ verify_metric.VerifyMetric.QUESTION: [],
+ verify_metric.VerifyMetric.DEVICE: []
+ }
self.assertEquals(metric_obj.gather_metric(), expected_result)
- def test_gather_device_two(self):
- mock_output = '00serial01\toffline\n' \
- '01serial00\tdevice'
+ def test_gather_device_question(self):
+ mock_output = '00serial01\t???'
FAKE_RESULT = fake.FakeResult(stdout=mock_output)
fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
metric_obj = verify_metric.VerifyMetric(shell=fake_shell)
expected_result = {
- verify_metric.VerifyMetric.DEVICES: {
- '00serial01': 'offline',
- '01serial00': 'device',
- }
+ verify_metric.VerifyMetric.TOTAL_UNHEALTHY: 1,
+ verify_metric.VerifyMetric.UNAUTHORIZED: [],
+ verify_metric.VerifyMetric.RECOVERY: [],
+ verify_metric.VerifyMetric.OFFLINE: [],
+ verify_metric.VerifyMetric.QUESTION: ['00serial01'],
+ verify_metric.VerifyMetric.DEVICE: []
}
self.assertEquals(metric_obj.gather_metric(), expected_result)
diff --git a/tools/lab/tests/version_metric_test.py b/tools/lab/tests/version_metric_test.py
index 777d9bd..c069e1c 100755
--- a/tools/lab/tests/version_metric_test.py
+++ b/tools/lab/tests/version_metric_test.py
@@ -78,6 +78,19 @@
}
self.assertEqual(expected_result, metric_obj.gather_metric())
+ def test_get_adb_revision_does_not_exist(self):
+ stdout_str = ('Android Debug Bridge version 1.0.39\n')
+
+ FAKE_RESULT = fake.FakeResult(stdout=stdout_str)
+ fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
+ metric_obj = version_metric.AdbVersionMetric(shell=fake_shell)
+
+ expected_result = {
+ version_metric.AdbVersionMetric.ADB_VERSION: '1.0.39',
+ version_metric.AdbVersionMetric.ADB_REVISION: ''
+ }
+ self.assertEqual(expected_result, metric_obj.gather_metric())
+
def test_get_python_version(self):
stdout_str = 'Python 2.7.6'
FAKE_RESULT = fake.FakeResult(stdout=stdout_str)
diff --git a/tools/lab/tests/zombie_metric_test.py b/tools/lab/tests/zombie_metric_test.py
index 0392f4c..3314d1b 100755
--- a/tools/lab/tests/zombie_metric_test.py
+++ b/tools/lab/tests/zombie_metric_test.py
@@ -30,7 +30,7 @@
metric_obj = zombie_metric.ZombieMetric(shell=fake_shell)
expected_result = {
- zombie_metric.ZombieMetric.ADB_ZOMBIES: [('30888', None)],
+ zombie_metric.ZombieMetric.ADB_ZOMBIES: [None],
zombie_metric.ZombieMetric.NUM_ADB_ZOMBIES: 1,
zombie_metric.ZombieMetric.FASTBOOT_ZOMBIES: [],
zombie_metric.ZombieMetric.NUM_FASTBOOT_ZOMBIES: 0,
@@ -46,7 +46,7 @@
metric_obj = zombie_metric.ZombieMetric(shell=fake_shell)
expected_result = {
- zombie_metric.ZombieMetric.ADB_ZOMBIES: [('30888', None)],
+ zombie_metric.ZombieMetric.ADB_ZOMBIES: [None],
zombie_metric.ZombieMetric.NUM_ADB_ZOMBIES: 1,
zombie_metric.ZombieMetric.FASTBOOT_ZOMBIES: [],
zombie_metric.ZombieMetric.NUM_FASTBOOT_ZOMBIES: 0,
@@ -63,16 +63,12 @@
metric_obj = zombie_metric.ZombieMetric(shell=fake_shell)
expected_result = {
- zombie_metric.ZombieMetric.ADB_ZOMBIES: [('99999', 'OR3G4N0')],
- zombie_metric.ZombieMetric.NUM_ADB_ZOMBIES:
- 1,
- zombie_metric.ZombieMetric.FASTBOOT_ZOMBIES: [('12345',
- 'M4RKY_M4RK')],
- zombie_metric.ZombieMetric.NUM_FASTBOOT_ZOMBIES:
- 1,
+ zombie_metric.ZombieMetric.ADB_ZOMBIES: ['OR3G4N0'],
+ zombie_metric.ZombieMetric.NUM_ADB_ZOMBIES: 1,
+ zombie_metric.ZombieMetric.FASTBOOT_ZOMBIES: ['M4RKY_M4RK'],
+ zombie_metric.ZombieMetric.NUM_FASTBOOT_ZOMBIES: 1,
zombie_metric.ZombieMetric.OTHER_ZOMBIES: [],
- zombie_metric.ZombieMetric.NUM_OTHER_ZOMBIES:
- 0
+ zombie_metric.ZombieMetric.NUM_OTHER_ZOMBIES: 0
}
self.assertEquals(metric_obj.gather_metric(), expected_result)
@@ -83,15 +79,28 @@
metric_obj = zombie_metric.ZombieMetric(shell=fake_shell)
expected_result = {
- zombie_metric.ZombieMetric.ADB_ZOMBIES: [('99999', None)],
+ zombie_metric.ZombieMetric.ADB_ZOMBIES: [None],
zombie_metric.ZombieMetric.NUM_ADB_ZOMBIES: 1,
- zombie_metric.ZombieMetric.FASTBOOT_ZOMBIES: [('12345', None)],
+ zombie_metric.ZombieMetric.FASTBOOT_ZOMBIES: [None],
zombie_metric.ZombieMetric.NUM_FASTBOOT_ZOMBIES: 1,
zombie_metric.ZombieMetric.OTHER_ZOMBIES: [],
zombie_metric.ZombieMetric.NUM_OTHER_ZOMBIES: 0
}
self.assertEquals(metric_obj.gather_metric(), expected_result)
+ def test_gather_metric_no_adb_fastboot(self):
+ stdout_string = '12345 Z+ otters'
+ FAKE_RESULT = fake.FakeResult(stdout=stdout_string)
+ fake_shell = fake.MockShellCommand(fake_result=FAKE_RESULT)
+ metric_obj = zombie_metric.ZombieMetric(shell=fake_shell)
+ metric_obj_res = metric_obj.gather_metric()
+ exp_num = 1
+ exp_pid = ['12345']
+
+ self.assertEquals(metric_obj_res[metric_obj.NUM_OTHER_ZOMBIES],
+ exp_num)
+ self.assertEquals(metric_obj_res[metric_obj.OTHER_ZOMBIES], exp_pid)
+
if __name__ == '__main__':
unittest.main()
diff --git a/tools/lab/utils/shell.py b/tools/lab/utils/shell.py
index d1e8c48..02735e3 100644
--- a/tools/lab/utils/shell.py
+++ b/tools/lab/utils/shell.py
@@ -35,7 +35,7 @@
Args:
runner: The object that will run the shell commands.
working_dir: The directory that all commands should work in,
- if none then the runners enviroment default is used.
+ if none then the runners environment default is used.
"""
self._runner = runner
self._working_dir = working_dir
@@ -67,7 +67,7 @@
def is_alive(self, identifier):
"""Checks to see if a program is alive.
- Checks to see if a program is alive on the shells enviroment. This can
+ Checks to see if a program is alive on the shells environment. This can
be used to check on generic programs, or a specific program using
a pid.
@@ -91,6 +91,29 @@
except job.Error:
return False
+ def get_command_pids(self, identifier):
+ """Gets the pids of a program, based only upon the starting command
+
+ Searches for a program with a specific name and grabs the pids for all
+ programs that match only the command that started it. The arguments of
+ the command are ignored.
+
+ Args:
+ identifier: The search term that identifies the program.
+
+ Returns:
+ An array of ints of all pids that matched the identifier.
+ """
+ result = self.run(
+ 'ps -C %s --no-heading -o pid:1' % identifier, ignore_status=True)
+
+ # Output looks like pids on separate lines:
+ # 1245
+ # 5001
+
+ pids = result.stdout.splitlines()
+ return [int(pid) for pid in result.stdout.splitlines()]
+
def get_pids(self, identifier):
"""Gets the pids of a program.
@@ -195,7 +218,7 @@
that match the identifier until either all are dead or the timeout
finishes.
- Programs are guranteed to be killed after running this command.
+ Programs are guaranteed to be killed after running this command.
Args:
identifier: A string used to identify the program.
@@ -230,7 +253,7 @@
Args:
pid: The process id of the program to kill.
- sig: The singal to send.
+ sig: The signal to send.
Raises:
job.Error: Raised when the signal fail to reach
diff --git a/tools/yapf_checker.py b/tools/yapf_checker.py
index c9bf2c8..9c45a55 100755
--- a/tools/yapf_checker.py
+++ b/tools/yapf_checker.py
@@ -25,10 +25,9 @@
YAPF_COMMAND = 'yapf -d -p %s'
YAPF_OLD_COMMAND = 'yapf -d %s'
YAPF_INPLACE_FORMAT = 'yapf -p -i %s'
-YAPF_INPLACE_FORMAT_OLD = 'yapf -i %s'
-def main(argv):
+def main():
if COMMIT_ID_ENV_KEY not in os.environ:
logging.error('Missing commit id in environment.')
exit(1)
@@ -49,18 +48,14 @@
result = job.run(YAPF_COMMAND % files_param_string, ignore_status=True)
yapf_inplace_format = YAPF_INPLACE_FORMAT
- if result.exit_status:
- logging.warning('Using an old version of yapf. Please update soon.')
- result = job.run(YAPF_OLD_COMMAND % files_param_string)
- yapf_inplace_format = YAPF_INPLACE_FORMAT_OLD
if result.stdout:
logging.error(result.stdout)
logging.error('INVALID FORMATTING.')
- logging.error('Consider run:')
- logging.error(yapf_inplace_format % files_param_string)
+ logging.error('Please run:\n'
+ '%s' % yapf_inplace_format % files_param_string)
exit(1)
if __name__ == '__main__':
- main(sys.argv[1:])
+ main()