Merge "Adding ability to use ADB devices as IPerfServers" am: 56cebe6c17 am: 7438d48088 am: 3e831f45b9
am: 9c9ad0f8d4

Change-Id: Ic639034dc6f60a7dbe6dcb5c1213aecf3682ff89
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/base_test.py b/acts/framework/acts/base_test.py
index 7be85a9..f66058a 100755
--- a/acts/framework/acts/base_test.py
+++ b/acts/framework/acts/base_test.py
@@ -13,20 +13,17 @@
 # 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 acts import asserts
-from acts import keys
 from acts import logger
 from acts import records
 from acts import signals
 from acts import tracelogger
 from acts import utils
-from acts.test_utils.tel.tel_test_utils import run_multithread_func
+from concurrent.futures import ThreadPoolExecutor
 
 # Macro strings for test result reporting
 TEST_CASE_TOKEN = "[Test Case]"
@@ -179,9 +176,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:
+            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)
             pass
         return self.setup_test()
 
@@ -195,6 +198,7 @@
 
         Implementation is optional.
         """
+        return True
 
     def _teardown_test(self, test_name):
         """Proxy function to guarantee the base implementation of teardown_test
@@ -229,7 +233,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 +257,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 +279,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 +301,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 +320,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 +372,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
@@ -425,15 +430,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:
@@ -679,17 +680,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 +740,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..ac6380e 100755
--- a/acts/framework/acts/config_parser.py
+++ b/acts/framework/acts/config_parser.py
@@ -100,6 +100,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 +112,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 +144,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 +159,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 +179,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 +216,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 +273,19 @@
                     '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)
 
+    # 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/access_point.py b/acts/framework/acts/controllers/access_point.py
index 4b7919f..d293ec6 100755
--- a/acts/framework/acts/controllers/access_point.py
+++ b/acts/framework/acts/controllers/access_point.py
@@ -16,18 +16,16 @@
 
 import collections
 import ipaddress
-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
 from acts.controllers.ap_lib import dhcp_server
 from acts.controllers.ap_lib import hostapd
-from acts.controllers.ap_lib import hostapd_config
 from acts.controllers.utils_lib.commands import ip
 from acts.controllers.utils_lib.commands import route
-from acts.controllers.utils_lib.commands import shell
 from acts.controllers.utils_lib.ssh import connection
 from acts.controllers.utils_lib.ssh import settings
 from acts.libs.proc import job
@@ -96,7 +94,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 +104,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']
@@ -130,7 +131,7 @@
         self.bridge = bridge_interface.BridgeInterface(self)
         self.interfaces = ap_get_interface.ApInterfaces(self)
 
-        # Get needed interface names and initialize the unneccessary ones.
+        # Get needed interface names and initialize the unnecessary ones.
         self.wan = self.interfaces.get_wan_interface()
         self.wlan = self.interfaces.get_wlan_interface()
         self.wlan_2g = self.wlan[0]
@@ -146,21 +147,35 @@
         """
         # 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)
+            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 +189,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.
@@ -236,9 +251,8 @@
             counter = 1
             for bss in hostapd_config.bss_lookup:
                 if interface_mac_orig:
-                    hostapd_config.bss_lookup[
-                        bss].bssid = interface_mac_orig.stdout[:-1] + str(
-                            counter)
+                    hostapd_config.bss_lookup[bss].bssid = (
+                            interface_mac_orig.stdout[:-1] + str(counter))
                 self._route_cmd.clear_routes(net_interface=str(bss))
                 if interface is self.wlan_2g:
                     starting_ip_range = self._AP_2G_SUBNET_STR
@@ -321,7 +335,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 +365,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..01a6712 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)
@@ -391,7 +393,9 @@
         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 +417,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()
@@ -658,9 +662,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 +675,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 +708,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 +750,13 @@
             extra_params = self.adb_logcat_param
         else:
             extra_params = "-b all"
-
+        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 -T 1 -v year {} >> {}".format(
-            self.serial, extra_params, logcat_file_path)
+        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 +767,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
 
@@ -854,7 +870,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 +884,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
@@ -889,19 +907,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 +943,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 +965,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 +973,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 +1017,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 +1126,31 @@
         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")
+        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 +1170,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),
@@ -1294,6 +1372,16 @@
         self.adb.shell(
             "am start -n com.google.android.setupwizard/.SetupWizardExitActivity"
         )
+        # Wait up to 5 seconds for user_setup_complete to be updated
+        for _ in range(5):
+            if self.is_user_setup_complete():
+                return
+            time.sleep(1)
+        # If fail to exit setup wizard, set local.prop and reboot
+        if not self.is_user_setup_complete():
+            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)
 
 
 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/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/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/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..083d44c 100644
--- a/acts/framework/acts/keys.py
+++ b/acts/framework/acts/keys.py
@@ -37,6 +37,7 @@
     key_test_case_iterations = "test_case_iterations"
     # 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"
@@ -54,6 +55,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"
@@ -68,9 +70,16 @@
 
     # 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,
     ]
 
     # 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/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/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 91%
rename from acts/tests/google/bt/pts/ble_lib.py
rename to acts/framework/acts/test_utils/bt/ble_lib.py
index f8ff402..379f182 100644
--- a/acts/tests/google/bt/pts/ble_lib.py
+++ b/acts/framework/acts/test_utils/bt/ble_lib.py
@@ -76,22 +76,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 +154,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)
 
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..db2aed6
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/bt_coc_test_utils.py
@@ -0,0 +1,253 @@
+#/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_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_interval_time_step
+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):
+    """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 = (
+                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
+    if (le_connection_interval != 0) and is_ble:
+        client_ad.log.info(
+            "Adjusting connection interval={}".format(le_connection_interval))
+        try:
+            bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection(
+                client_ad,
+                mac_address,
+                False,
+                transport=gatt_transport['le'],
+                opportunistic=False)
+        except GattTestUtilsError as err:
+            client_ad.log.error(err)
+            return False, None, None
+        client_ad.log.info("setup_gatt_connection returns success")
+        minInterval = le_connection_interval / le_connection_interval_time_step
+        maxInterval = le_connection_interval / le_connection_interval_time_step
+        return_status = client_ad.droid.gattClientRequestLeConnectionParameters(
+            bluetooth_gatt_1, minInterval, maxInterval, 0,
+            le_default_supervision_timeout)
+        if not return_status:
+            client_ad.log.error(
+                "gattClientRequestLeConnectionParameters returns failure")
+            return False, None, None
+        client_ad.log.info(
+            "gattClientRequestLeConnectionParameters returns success. Interval={}"
+            .format(minInterval))
+        # 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 not test_result:
+        client_ad.log.error("Failed to establish an CoC connection")
+        return False, 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")
+
+    # Get the conn_id
+    client_conn_id = client_ad.droid.bluetoothGetLastConnId()
+    server_conn_id = server_ad.droid.bluetoothGetLastConnId()
+    client_ad.log.info(
+        "orchestrate_coc_connection: client conn id={}, server conn id={}".
+        format(client_conn_id, server_conn_id))
+
+    if (le_connection_interval != 0) and is_ble:
+        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..0d038b5 100644
--- a/acts/framework/acts/test_utils/bt/bt_constants.py
+++ b/acts/framework/acts/test_utils/bt/bt_constants.py
@@ -18,10 +18,29 @@
 
 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 = 1.25
+le_default_supervision_timeout = 2000
+default_le_data_length = 23
+
+# 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"
@@ -72,6 +91,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 +181,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 +274,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 +559,32 @@
 }
 
 ### 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 ###
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..f3fb6bc 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:
@@ -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..f51e476 100644
--- a/acts/framework/acts/test_utils/bt/bt_test_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_test_utils.py
@@ -57,12 +57,14 @@
 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 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 +105,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 +143,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
 
 
@@ -229,7 +231,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.")
@@ -356,8 +358,8 @@
                                            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":
@@ -405,8 +407,9 @@
             max_tries = 3
             #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
@@ -508,8 +511,8 @@
         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
@@ -647,8 +650,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 +714,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 +725,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
@@ -842,34 +844,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)
@@ -892,8 +894,8 @@
         if state == bt_profile_states['connected'] and \
             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
@@ -951,8 +953,8 @@
         if state == bt_profile_states['disconnected'] and \
             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 +1000,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 +1028,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 +1091,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 +1147,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 +1280,33 @@
         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"))
diff --git a/acts/framework/acts/test_utils/bt/bta_lib.py b/acts/framework/acts/test_utils/bt/bta_lib.py
new file mode 100644
index 0000000..723dfdb
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/bta_lib.py
@@ -0,0 +1,107 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 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.
+"""
+Bluetooth adapter libraries
+"""
+
+from acts.test_utils.bt.bt_constants import bt_scan_mode_types
+from acts.test_utils.bt.bt_test_utils import set_bt_scan_mode
+
+import pprint
+
+
+class BtaLib():
+    def __init__(self, log, mac_addr, dut):
+        self.advertisement_list = []
+        self.dut = dut
+        self.log = log
+        self.mac_addr = mac_addr
+
+    def set_scan_mode(self, scan_mode):
+        """Set the Scan mode of the Bluetooth Adapter"""
+        set_bt_scan_mode(self.dut, bt_scan_mode_types[scan_mode])
+
+    def set_device_name(self, line):
+        """Set Bluetooth Adapter Name"""
+        self.dut.droid.bluetoothSetLocalName(line)
+
+    def enable(self):
+        """Enable Bluetooth Adapter"""
+        self.dut.droid.bluetoothToggleState(True)
+
+    def disable(self):
+        """Disable Bluetooth Adapter"""
+        self.dut.droid.bluetoothToggleState(False)
+
+    def init_bond(self):
+        """Initiate bond to PTS device"""
+        self.dut.droid.bluetoothDiscoverAndBond(self.mac_addr)
+
+    def start_discovery(self):
+        """Start BR/EDR Discovery"""
+        self.dut.droid.bluetoothStartDiscovery()
+
+    def stop_discovery(self):
+        """Stop BR/EDR Discovery"""
+        self.dut.droid.bluetoothCancelDiscovery()
+
+    def get_discovered_devices(self):
+        """Get Discovered Br/EDR Devices"""
+        if self.dut.droid.bluetoothIsDiscovering():
+            self.dut.droid.bluetoothCancelDiscovery()
+        self.log.info(
+            pprint.pformat(self.dut.droid.bluetoothGetDiscoveredDevices()))
+
+    def bond(self):
+        """Bond to PTS device"""
+        self.dut.droid.bluetoothBond(self.mac_addr)
+
+    def disconnect(self):
+        """BTA disconnect"""
+        self.dut.droid.bluetoothDisconnectConnected(self.mac_addr)
+
+    def unbond(self):
+        """Unbond from PTS device"""
+        self.dut.droid.bluetoothUnbond(self.mac_addr)
+
+    def start_pairing_helper(self, line):
+        """Start or stop Bluetooth Pairing Helper"""
+        if line:
+            self.dut.droid.bluetoothStartPairingHelper(bool(line))
+        else:
+            self.dut.droid.bluetoothStartPairingHelper()
+
+    def push_pairing_pin(self, line):
+        """Push pairing pin to the Android Device"""
+        self.dut.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
+                                 line)
+
+    def get_pairing_pin(self):
+        """Get pairing PIN"""
+        self.log.info(
+            self.dut.ed.pop_event("BluetoothActionPairingRequest", 1))
+
+    def fetch_uuids_with_sdp(self):
+        """BTA fetch UUIDS with SDP"""
+        self.log.info(self.dut.droid.bluetoothFetchUuidsWithSdp(self.mac_addr))
+
+    def connect_profiles(self):
+        """Connect available profiles"""
+        self.dut.droid.bluetoothConnectBonded(self.mac_addr)
+
+    def tts_speak(self):
+        """Open audio channel by speaking characters"""
+        self.dut.droid.ttsSpeak(self.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 94%
rename from acts/tests/google/bt/pts/gattc_lib.py
rename to acts/framework/acts/test_utils/bt/gattc_lib.py
index 4bcb0f4..4dfc076 100644
--- a/acts/tests/google/bt/pts/gattc_lib.py
+++ b/acts/framework/acts/test_utils/bt/gattc_lib.py
@@ -130,6 +130,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()
@@ -393,8 +406,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 +437,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 +475,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 """
diff --git a/acts/tests/google/bt/pts/gatts_lib.py b/acts/framework/acts/test_utils/bt/gatts_lib.py
similarity index 89%
rename from acts/tests/google/bt/pts/gatts_lib.py
rename to acts/framework/acts/test_utils/bt/gatts_lib.py
index 45a7a8d..7916a92 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 = []
@@ -128,8 +131,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(
@@ -145,12 +148,12 @@
             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 ('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 +186,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 +227,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 +260,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 +272,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 +331,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 +345,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 90%
rename from acts/tests/google/bt/pts/rfcomm_lib.py
rename to acts/framework/acts/test_utils/bt/rfcomm_lib.py
index 257c929..49b35b6 100644
--- a/acts/tests/google/bt/pts/rfcomm_lib.py
+++ b/acts/framework/acts/test_utils/bt/rfcomm_lib.py
@@ -36,8 +36,8 @@
         if len(line) > 0:
             uuid = line
         if uuid:
-            self.dut.droid.bluetoothRfcommBeginConnectThread(self.mac_addr,
-                                                             uuid)
+            self.dut.droid.bluetoothRfcommBeginConnectThread(
+                self.mac_addr, uuid)
         else:
             self.dut.droid.bluetoothRfcommBeginConnectThread(self.mac_addr)
 
@@ -69,8 +69,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"""
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/connectivity_const.py b/acts/framework/acts/test_utils/net/connectivity_const.py
index 89bdb2c..64bd8b0 100644
--- a/acts/framework/acts/test_utils/net/connectivity_const.py
+++ b/acts/framework/acts/test_utils/net/connectivity_const.py
@@ -53,6 +53,16 @@
 # 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
+
 # Constants for VpnProfile
 class VpnProfile(object):
     """ This class contains all the possible
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 f7bbe07..189c644 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,6 +157,7 @@
 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
@@ -247,41 +259,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 +325,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 +349,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 +372,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 +499,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 +541,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 +840,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 +854,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 +895,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 +941,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 +999,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 +1033,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 +1082,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 +1100,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 +1171,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 +1197,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 +1217,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 +1302,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 +1326,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 +1495,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 +1606,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 +1708,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 +1718,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 +1734,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 +1745,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 +1766,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 +1825,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 +1867,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 +1881,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 +1943,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 +1955,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 +1977,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 +2008,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 +2026,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 +2046,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 +2108,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 +2186,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 +2255,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 +2272,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 +2285,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 +2359,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 +2456,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 +2470,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 +2493,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 +2512,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 +2526,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 +2559,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 +2572,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 +2582,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 +2593,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 +2607,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 +2621,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 +2636,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 +2677,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 +2823,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 +2959,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 +2998,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 +3196,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 +3434,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 +3451,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 +3475,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 +3500,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 +3548,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 +3569,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 +3602,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 +3679,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 +3750,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 +3807,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 +3837,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 +3877,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 +3936,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 +3996,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 +4018,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 +4041,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 +4089,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 +4100,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 +4122,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 +4145,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 +4169,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 +4188,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 +4203,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 +4275,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 +4407,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 +4452,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 +4508,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 +4567,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 +4576,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 +4611,7 @@
     """
     ad.droid.wifiFactoryReset()
     ad.droid.wifiToggleState(False)
+    ad.on_mobile_data = True
 
 
 def wifi_toggle_state(log, ad, state, retries=3):
@@ -4142,6 +4627,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 +4685,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 +4708,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 +4737,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 +4829,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 +5103,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 +5281,16 @@
 
     """
     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")
+    else:
+        ad.adb.shell("rm -rf /sdcard/tcpdump/*", ignore_status=True)
+
     begin_time = epoch_to_log_line_timestamp(get_current_epoch_time())
     begin_time = normalize_log_line_timestamp(begin_time)
 
@@ -4663,11 +5305,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
 
@@ -4677,13 +5318,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
 
 
@@ -4697,7 +5343,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")
@@ -4714,6 +5360,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")
@@ -4723,8 +5370,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.
@@ -4732,12 +5377,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)
@@ -4763,8 +5437,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):
@@ -4772,8 +5445,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
@@ -4791,8 +5473,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)
@@ -4800,11 +5488,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):
@@ -4815,14 +5508,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)
@@ -4896,7 +5591,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")
@@ -4909,10 +5603,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..95c7b95 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,
@@ -214,7 +254,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 +271,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..1e8f9d0 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,8 @@
       self.reset_device_parameters(ad)
       self.reset_device_statistics(ad)
       self.set_power_mode_parameters(ad)
-
+      utils.set_regulatory_domain(ad, "US")
+      autils.configure_ndp_allow_any_override(ad, True)
 
   def teardown_test(self):
     for ad in self.android_devices:
@@ -85,9 +88,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 "
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..4be392e 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
@@ -392,74 +392,122 @@
   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_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 +543,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..8f86cad
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/RttBaseTest.py
@@ -0,0 +1,64 @@
+#!/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)
+
+  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])
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..bd82a86
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/rtt_test_utils.py
@@ -0,0 +1,419 @@
+#!/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 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.
+
+  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
+
+  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_measurements.append(
+      result[rconsts.EVENT_CB_RANGING_KEY_NUM_ATTEMPTED_MEASUREMENTS])
+    num_successful_measurements.append(
+        result[rconsts.EVENT_CB_RANGING_KEY_NUM_SUCCESSFUL_MEASUREMENTS])
+
+    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_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
index e5e9e5a..92054ca 100755
--- a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
@@ -50,6 +50,7 @@
     PWD_KEY = "password"
     frequency_key = "frequency"
     APBAND_KEY = "apBand"
+    HIDDEN_KEY = "hiddenSSID"
 
     WIFI_CONFIG_APBAND_2G = 0
     WIFI_CONFIG_APBAND_5G = 1
@@ -211,6 +212,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]
@@ -688,6 +693,7 @@
     Args:
         ad: An AndroidDevice object.
     """
+    ad.ed.clear_all_events()
     ad.droid.wifiStartScan()
     try:
         ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
@@ -695,6 +701,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 +810,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 +819,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 +829,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")
@@ -754,7 +856,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",
@@ -926,24 +1027,25 @@
         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):
     """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)
 
 
 def connect_to_wifi_network_with_id(ad, network_id, network_ssid):
@@ -957,12 +1059,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]
@@ -1454,3 +1551,15 @@
         asserts.fail(("Either two or four attenuators are required for this "
                       "test, but found %s") % num_of_attns)
     return [attn0, attn1]
+
+
+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..2447548 100755
--- a/acts/framework/acts/utils.py
+++ b/acts/framework/acts/utils.py
@@ -728,6 +728,16 @@
         1 if new_state else 0))
 
 
+def set_regulatory_domain(ad, domain):
+    """Set the Wi-Fi regulatory domain
+
+    Args:
+      ad: android device object.
+      domain: regulatory domain
+    """
+    ad.adb.shell("iw reg set %s" % domain)
+
+
 def bypass_setup_wizard(ad, bypass_wait_time=3):
     """Bypass the setup wizard on an input Android device
 
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
index 48c71bb..37fdcb8 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, ):
@@ -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/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..a477672
--- /dev/null
+++ b/acts/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
@@ -0,0 +1,480 @@
+#/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 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 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)
+
+    def _run_coc_connection_throughput_2_conn(
+            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.
+        num_iterations = 10
+        number_buffers = 100
+        # Note: A 117 octets buffer size would fix nicely to a 123 bytes Data Length
+        buffer_size = 117
+
+        # 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={}".format(is_secured, le_connection_interval,
+                                          buffer_size, le_tx_data_length))
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        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..07c8e1b 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:
@@ -989,3 +1002,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)
\ No newline at end of file
diff --git a/acts/tests/google/bt/RfcommTest.py b/acts/tests/google/bt/RfcommTest.py
index cdd53ee..846827b 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')
@@ -846,8 +834,8 @@
         TAGS: Classic, RFCOMM
         Priority: 3
         """
-        return self._test_rfcomm_connection_with_uuid(bt_rfcomm_uuids[
-            'mcap_data_channel'])
+        return self._test_rfcomm_connection_with_uuid(
+            bt_rfcomm_uuids['mcap_data_channel'])
 
     @BluetoothBaseTest.bt_test_wrap
     @test_tracker_info(uuid='d6c7523d-9247-480e-8154-edd51ae1be50')
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/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/hid/HidDeviceTest.py b/acts/tests/google/bt/hid/HidDeviceTest.py
new file mode 100644
index 0000000..3df837a
--- /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 not self.device_ad.droid.bluetoothGetBondedDevices():
+            self.log.error("HID device unbonded host on virtual_cable_unplug")
+            test_result = False
+
+        if not self.host_ad.droid.bluetoothGetBondedDevices():
+            self.log.error("HID host unbonded device 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..10d2839 100644
--- a/acts/tests/google/bt/pts/cmd_input.py
+++ b/acts/tests/google/bt/pts/cmd_input.py
@@ -16,18 +16,18 @@
 """
 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.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
 
 import time
 import cmd
-import gatt_test_database
 """Various Global Strings"""
 CMD_LOG = "CMD {} result: {}"
 FAILURE = "CMD {} threw exception: {}"
@@ -173,6 +173,14 @@
         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.gattc_lib.write_char_by_instance_id_value(line)
+        except Exception as err:
+            self.log.info(FAILURE.format(cmd, err))
+
     def do_gattc_mod_write_char_by_instance_id(self, line):
         """GATT Client Write to Char that doesn't have write permission"""
         cmd = "GATT Client Write to Char that doesn't have write permission"
@@ -369,8 +377,8 @@
 
     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 +389,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 +400,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 +411,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
 
@@ -633,7 +641,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:
@@ -916,8 +924,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 +958,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 +966,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))
 
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..f789c82 100644
--- a/acts/tests/google/bt/pts/instructions/GATT_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/GATT_PTS_INSTRUCTIONS
@@ -693,13 +693,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]
 
@@ -977,7 +977,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
 
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..a865f45 100644
--- a/acts/tests/google/bt/pts/instructions/L2CAP_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/L2CAP_PTS_INSTRUCTIONS
@@ -102,19 +102,19 @@
   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
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/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..bd823a3
--- /dev/null
+++ b/acts/tests/google/net/DataCostTest.py
@@ -0,0 +1,175 @@
+#
+#   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 """
+
+        self.dut = self.android_devices[0]
+        req_params = ("wifi_network", "download_file")
+        self.unpack_userparams(req_params)
+        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")
+        self.cell_network = self.dut.droid.connectivityGetActiveNetwork()
+        self.log.info("cell network %s" % self.cell_network)
+        wutils.wifi_connect(self.dut, self.wifi_network)
+        self.wifi_network = self.dut.droid.connectivityGetActiveNetwork()
+        self.log.info("wifi network %s" % self.wifi_network)
+        self.sub_id = str(self.dut.droid.telephonyGetSubscriberId())
+
+    def teardown_class(self):
+        wutils.reset_wifi(self.dut)
+        self.dut.droid.telephonyToggleDataConnection(True)
+
+    """ Helper functions """
+
+    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 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 = self.dut.droid.connectivityQuerySummaryForDevice(
+            conn_type, self.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:
+            1. val - multipath preference for the network
+            2. 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, wifi_pref, cell_pref):
+        """ Verify mutlipath preferences for wifi and cell networks
+
+        Args:
+            wifi_pref: Expected multipath value for wifi network
+            cell_pref: Expected multipath value for cell network
+        """
+        wifi_multipath = \
+            self.dut.droid.connectivityGetMultipathPreferenceForNetwork(
+                self.wifi_network)
+        cell_multipath = \
+            self.dut.droid.connectivityGetMultipathPreferenceForNetwork(
+                self.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
+        """
+        # verify multipath preference values
+        self._verify_multipath_preferences(RELIABLE, RELIABLE)
+
+        # set low data limit on mobile data
+        total_pre = self._get_total_data_usage_for_device(0)
+        self.log.info("Setting data usage limit to %sMB" % (total_pre + 5))
+        self.dut.droid.connectivitySetDataUsageLimit(
+            self.sub_id, str(int((total_pre + 5) * 1000.0 * 1000.0)))
+
+        # reset data limit
+        self.dut.droid.connectivityFactoryResetNetworkPolicies(self.sub_id)
+
+        # verify multipath preference values
+        self._verify_multipath_preferences(RELIABLE, NONE)
+
+    @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
+        """
+        # verify multipath preference for wifi and cell networks
+        self._verify_multipath_preferences(RELIABLE, RELIABLE)
+
+        # download file with cell network
+        self.dut.droid.connectivityNetworkOpenConnection(self.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(self.dut, file_path):
+            self.log.info("File exists. Removing file %s" % file_name)
+            self.dut.adb.shell("rm -rf %s%s" % (DOWNLOAD_PATH, file_name))
+
+        #  verify multipath preference values
+        self._verify_multipath_preferences(RELIABLE, NONE)
+
+    # 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/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/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..18b2163
--- /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="")
+    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="")
+    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="")
+    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="")
+    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..1f3da05 100755
--- a/acts/tests/google/wifi/WifiIOTTest.py
+++ b/acts/tests/google/wifi/WifiIOTTest.py
@@ -37,7 +37,6 @@
     """
 
     def __init__(self, controllers):
-        self.attenuators = None
         WifiBaseTest.__init__(self, controllers)
 
     def setup_class(self):
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/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/WifiSoftApTest.py b/acts/tests/google/wifi/WifiSoftApTest.py
index ed8a080..6b8e83e 100644
--- a/acts/tests/google/wifi/WifiSoftApTest.py
+++ b/acts/tests/google/wifi/WifiSoftApTest.py
@@ -48,9 +48,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,11 +59,13 @@
             "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)
 
     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):
@@ -119,15 +120,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 +147,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,7 +165,17 @@
         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(),
@@ -228,6 +241,30 @@
         """
         self.validate_full_tether_startup(WIFI_CONFIG_APBAND_5G)
 
+    @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)
+
     """ Tests End """
 
 
diff --git a/acts/tests/google/wifi/WifiStaApConcurrencyTest.py b/acts/tests/google/wifi/WifiStaApConcurrencyTest.py
new file mode 100755
index 0000000..916519d
--- /dev/null
+++ b/acts/tests/google/wifi/WifiStaApConcurrencyTest.py
@@ -0,0 +1,322 @@
+#!/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
+
+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 = ["reference_networks"]
+        opt_param = ["iperf_server_address"]
+        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) >= 1,
+            "Need at least 1 reference network with psk.")
+        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.")
+        if "iperf_server_address" in self.user_params:
+            self.iperf_server = self.iperf_servers[0]
+        self.wpapsk_2g = self.reference_networks[0]["2g"]
+        self.wpapsk_5g = self.reference_networks[0]["5g"]
+        if hasattr(self, 'iperf_server'):
+            self.iperf_server.start()
+
+    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)
+        wutils.wifi_toggle_state(self.dut_client, 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)
+
+    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)
+
+    def teardown_class(self):
+        if "AccessPoint" in self.user_params:
+            del self.user_params["reference_networks"]
+            del self.user_params["open_network"]
+
+    """Helper Functions"""
+    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)
+        wutils.wifi_toggle_state(self.dut_client, True)
+        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_false(self.dut.droid.wifiIsApEnabled(),
+                             "SoftAp is not reported as running")
+
+    """Tests"""
+    @test_tracker_info(uuid="")
+    def test_wifi_connection_2G_softap_2G(self):
+        """Tests connection to 2G network followed by bringing up SoftAp on 2G.
+        """
+        self.connect_to_wifi_network_and_start_softap(
+            self.wpapsk_2g, WIFI_CONFIG_APBAND_2G)
+
+    @test_tracker_info(uuid="")
+    def test_wifi_connection_5G_softap_5G(self):
+        """Tests connection to 5G network followed by bringing up SoftAp on 5G.
+        """
+        self.connect_to_wifi_network_and_start_softap(
+            self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+
+    @test_tracker_info(uuid="")
+    def test_wifi_connection_5G_softap_2G(self):
+        """Tests connection to 5G network followed by bringing up SoftAp on 2G.
+        """
+        self.connect_to_wifi_network_and_start_softap(
+            self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+
+    @test_tracker_info(uuid="")
+    def test_wifi_connection_2G_softap_5G(self):
+        """Tests connection to 2G network followed by bringing up SoftAp on 5G.
+        """
+        self.connect_to_wifi_network_and_start_softap(
+            self.wpapsk_2g, WIFI_CONFIG_APBAND_5G)
+
+    @test_tracker_info(uuid="")
+    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.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="")
+    def test_softap_2G_wifi_connection_2G(self):
+        """Tests bringing up SoftAp on 2G followed by connection to 2G network.
+        """
+        self.start_softap_and_connect_to_wifi_network(
+            self.wpapsk_2g, WIFI_CONFIG_APBAND_2G)
+
+    @test_tracker_info(uuid="")
+    def test_softap_5G_wifi_connection_5G(self):
+        """Tests bringing up SoftAp on 5G followed by connection to 5G network.
+        """
+        self.start_softap_and_connect_to_wifi_network(
+            self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+
+    @test_tracker_info(uuid="")
+    def test_softap_5G_wifi_connection_2G(self):
+        """Tests bringing up SoftAp on 5G followed by connection to 2G network.
+        """
+        self.start_softap_and_connect_to_wifi_network(
+            self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+
+    @test_tracker_info(uuid="")
+    def test_softap_2G_wifi_connection_5G(self):
+        """Tests bringing up SoftAp on 2G followed by connection to 5G network.
+        """
+        self.start_softap_and_connect_to_wifi_network(
+            self.wpapsk_2g, WIFI_CONFIG_APBAND_5G)
+
+    @test_tracker_info(uuid="")
+    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.turn_location_on_and_scan_toggle_on()
+        self.start_softap_and_connect_to_wifi_network(
+            self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
diff --git a/acts/tests/google/wifi/WifiStressTest.py b/acts/tests/google/wifi/WifiStressTest.py
new file mode 100755
index 0000000..bb574fb
--- /dev/null
+++ b/acts/tests/google/wifi/WifiStressTest.py
@@ -0,0 +1,263 @@
+#!/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
+
+
+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"]
+        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.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()
+
+    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)
+        pass
+
+    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)
+
+
+    """Tests"""
+
+    @test_tracker_info(uuid="")
+    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"""
+            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="")
+    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.
+
+        """
+        net_id = self.dut.droid.wifiAddNetwork(self.wpa_5g)
+        asserts.assert_true(net_id != -1, "Add network %r failed" % self.wpa_5g)
+        self.dut.droid.wifiEnableNetwork(net_id, 0)
+        for count in range(self.stress_count):
+            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)
+            self.dut.droid.wifiDisconnect()
+            time.sleep(WAIT_BEFORE_CONNECTION)
+            if not result:
+                self.log.debug("Error occurred in iPerf traffic.")
+                raise signals.TestFailure("Error occurred in iPerf traffic. Current"
+                    " WiFi state = %d" % self.dut.droid.wifiCheckState())
+
+    @test_tracker_info(uuid="")
+    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.
+
+        """
+        self.scan_and_connect_by_ssid(self.wpa_5g)
+        # Start IPerf traffic from server to phone.
+        # Download data for 5 hours.
+        sec = 5*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)
+        self.dut.droid.wifiDisconnect()
+        if not result:
+            self.log.debug("Error occurred in iPerf traffic.")
+            raise signals.TestFailure("Error occurred in iPerf traffic. Current"
+                " WiFi state = %d" % self.dut.droid.wifiCheckState())
+
+    @test_tracker_info(uuid="")
+    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(self.stress_count):
+            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]
+            for count 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]
+                if count == 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()
+            if len(network_config):
+                raise signals.TestFailure("All the network configurations were not "
+                        "removed. Configured networks = %s" % network_config)
+
+    @test_tracker_info(uuid="")
+    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.
+
+        """
+        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)
+            # Toggle WiFi ON, which inturn calls softAP teardown.
+            wutils.wifi_toggle_state(self.dut, True)
+            time.sleep(TIMEOUT)
+            asserts.assert_false(self.dut.droid.wifiIsApEnabled(),
+                                 "SoftAp failed to shutdown!")
+            time.sleep(TIMEOUT)
+            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))
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..9b66112 100644
--- a/acts/tests/google/wifi/WifiTetheringTest.py
+++ b/acts/tests/google/wifi/WifiTetheringTest.py
@@ -23,12 +23,9 @@
 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
@@ -42,15 +39,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")
         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 +58,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)
@@ -414,106 +389,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:
diff --git a/acts/tests/google/wifi/aware/functional/AttachTest.py b/acts/tests/google/wifi/aware/functional/AttachTest.py
index 598cca6..a1a420b 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,37 @@
     dut.droid.wifiAwareAttach()
     autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
 
+  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)
+
+  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 +152,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..120fc88 100644
--- a/acts/tests/google/wifi/aware/functional/DataPathTest.py
+++ b/acts/tests/google/wifi/aware/functional/DataPathTest.py
@@ -17,6 +17,7 @@
 import time
 
 from acts import asserts
+from acts import utils
 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
@@ -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,100 @@
         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).
+  #######################################
+
+  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)
+
+  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)
+
+  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)
+
+  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 +809,30 @@
         encr_type=self.ENCR_TYPE_PMK,
         use_peer_id=False)
 
+  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)
+
+  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")
@@ -808,7 +981,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 +1024,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 +1082,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 +1203,611 @@
 
     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_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_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_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_ifs.append(
+          dut2_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
+      dut1_aware_ifs.append(
+          dut1_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
+
+      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
 
     # check that we are using 2 NDIs
-    init_aware_ifs = list(set(init_aware_ifs))
-    resp_aware_ifs = list(set(resp_aware_ifs))
+    dut1_aware_ifs = list(set(dut1_aware_ifs))
+    dut2_aware_ifs = list(set(dut2_aware_ifs))
 
-    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)
+    self.log.info("Interface names: DUT1=%s, DUT2=%s", dut1_aware_ifs,
+                  dut2_aware_ifs)
+    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")
 
     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])
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  #######################################
+
+  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)
+
+  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]
+
+    utils.set_regulatory_domain(init_dut, init_domain)
+    utils.set_regulatory_domain(resp_dut, 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)
+
+  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="US",
+                                         resp_domain="JP")
+
+  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="JP",
+                                         resp_domain="US")
+
+  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="US",
+                                         resp_domain="JP")
+
+  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="JP",
+                                         resp_domain="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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
diff --git a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
index 8732dbb..9182f39 100644
--- a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
+++ b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
@@ -829,3 +829,203 @@
         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")
+
+  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])
+
+  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])
+
+  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])
+
+  #########################################################
+
+  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/performance/LatencyTest.py b/acts/tests/google/wifi/aware/performance/LatencyTest.py
index bde9ff4..9f2a5bf 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)
@@ -438,8 +438,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 +454,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 +469,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 +482,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 +510,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 +524,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 +536,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 +549,8 @@
     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)
diff --git a/acts/tests/google/wifi/aware/performance/ThroughputTest.py b/acts/tests/google/wifi/aware/performance/ThroughputTest.py
index 6cf1046..7ee6e08 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.
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..6025624
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
@@ -0,0 +1,1055 @@
+#!/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_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.RttBaseTest import RttBaseTest
+
+
+class AwareDiscoveryWithRangingTest(AwareBaseTest, RttBaseTest):
+  """Set of tests for Wi-Fi Aware discovery configured with ranging (RTT)."""
+
+  SERVICE_NAME = "GoogleTestServiceRRRRR"
+
+  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
+  #########################################################################
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+  #########################################################################
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  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)
+
+  #########################################################################
+
+  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)
\ 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..f0b4be2
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
@@ -0,0 +1,129 @@
+#!/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 RangeApNonSupporting11McTest(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):
+    RttBaseTest.__init__(self, controllers)
+
+  #############################################################################
+
+  @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.scan_with_rtt_support_constraint(dut, False)
+    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_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.scan_with_rtt_support_constraint(dut, False)
+    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,
+        "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.scan_with_rtt_support_constraint(dut, False)
+    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..523fd0c
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
@@ -0,0 +1,181 @@
+#!/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.scan_with_rtt_support_constraint(dut, True,
+                                                                 repeat=10)
+    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_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.scan_with_rtt_support_constraint(dut, True,
+                                                                 repeat=10)
+    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..a2d5286
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeAwareTest.py
@@ -0,0 +1,405 @@
+#!/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_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/RttDisableTest.py b/acts/tests/google/wifi/rtt/functional/RttDisableTest.py
new file mode 100644
index 0000000..d8dd1a1
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RttDisableTest.py
@@ -0,0 +1,100 @@
+#!/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_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 RttDisableTest(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):
+    RttBaseTest.__init__(self, controllers)
+
+  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.scan_networks(dut)
+    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")
+
+  ############################################################################
+
+  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)
+
+  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)
+
+  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..8370290
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
@@ -0,0 +1,136 @@
+#!/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.scan_with_rtt_support_constraint(dut, True, repeat=10)
+    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.scan_with_rtt_support_constraint(dut, True, repeat=10)
+    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()