Merge "Catch more exception in monsoon.py" am: af568482b8 am: fddaa3d01f am: 964fd8594c
am: 65b1ecfab9

Change-Id: I2d4378c388684c9ca9fb8af2c8db810b116bf9cf
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..231ce00 100755
--- a/acts/framework/acts/controllers/access_point.py
+++ b/acts/framework/acts/controllers/access_point.py
@@ -96,7 +96,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.
     """
 
@@ -174,7 +174,7 @@
         Args:
             hostapd_config: hostapd_config.HostapdConfig, The configurations
                             to use when starting up the ap.
-            additional_parameters: A dicitonary of parameters that can sent
+            additional_parameters: A dictionary of parameters that can sent
                                    directly into the hostapd config file.  This
                                    can be used for debugging and or adding one
                                    off parameters into the config.
@@ -321,7 +321,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 +351,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..6439267 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,6 +393,7 @@
         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)
 
     def clean_up(self):
@@ -413,13 +416,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 +661,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 +674,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 +707,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'
@@ -854,7 +859,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 +873,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 +896,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 +932,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 +954,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 +962,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 +1006,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 +1115,14 @@
         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 search_logcat(self, matching_string, begin_time=None):
         """Search logcat message with given string.
 
         Args:
@@ -1093,7 +1142,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 +1344,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/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/monsoon.py b/acts/framework/acts/controllers/monsoon.py
index d91f644..17e8017 100644
--- a/acts/framework/acts/controllers/monsoon.py
+++ b/acts/framework/acts/controllers/monsoon.py
@@ -302,7 +302,7 @@
         while 1:  # loop until we get data or a timeout
             _bytes = self._ReadPacket()
             if not _bytes:
-                return None
+                raise MonsoonError("Data collection failed due to empty data")
             if len(_bytes) < 4 + 8 + 1 or _bytes[0] < 0x20 or _bytes[0] > 0x2F:
                 logging.warning("Wanted data, dropped type=0x%02x, len=%d",
                                 _bytes[0], len(_bytes))
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..3f55940 100644
--- a/acts/framework/acts/controllers/relay_lib/relay_rig.py
+++ b/acts/framework/acts/controllers/relay_lib/relay_rig.py
@@ -15,9 +15,13 @@
 #   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.ak_xb10_speaker import AkXB10Speaker
 from acts.controllers.relay_lib.dongles import SingleButtonDongle
@@ -45,13 +49,20 @@
     # 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),
         'AkXB10Speaker': lambda x, rig: AkXB10Speaker(x, rig),
         'SingleButtonDongle': lambda x, rig: SingleButtonDongle(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/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/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/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..962c0db 100644
--- a/acts/framework/acts/controllers/utils_lib/ssh/connection.py
+++ b/acts/framework/acts/controllers/utils_lib/ssh/connection.py
@@ -28,11 +28,11 @@
 
 
 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.
         """
@@ -88,11 +89,12 @@
     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.
@@ -159,7 +161,7 @@
             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.
 
         Returns:
@@ -191,9 +193,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.
@@ -261,7 +262,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:
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/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..0d9c5f0 100644
--- a/acts/framework/acts/test_utils/bt/bt_constants.py
+++ b/acts/framework/acts/test_utils/bt/bt_constants.py
@@ -18,10 +18,21 @@
 
 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
 
+# 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 +83,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 +173,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 +266,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 +551,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..1e4d5b9 100644
--- a/acts/framework/acts/test_utils/bt/bt_gatt_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_gatt_utils.py
@@ -61,9 +61,9 @@
             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
 
 
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..f75ff6b
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/bta_lib.py
@@ -0,0 +1,110 @@
+#/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"""
+        for mode in bt_scan_mode_types:
+            if scan_mode == mode.name:
+                set_bt_scan_mode(self.dut, mode.value)
+                return
+
+    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 90%
rename from acts/tests/google/bt/pts/gatts_lib.py
rename to acts/framework/acts/test_utils/bt/gatts_lib.py
index 45a7a8d..c341d1a 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
 
@@ -57,14 +57,19 @@
         """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 +89,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 +133,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 +150,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 +188,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 +229,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'])
@@ -268,7 +273,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 +332,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 +346,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..6a290f6
--- /dev/null
+++ b/acts/framework/acts/test_utils/coex/coex_test_utils.py
@@ -0,0 +1,908 @@
+# /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 pandas
+import re
+import subprocess
+import time
+
+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):
+    """Parses the output and writes in spreadsheet.
+
+    Args:
+        pri_ad: An android device.
+        test_result: final output of testrun.
+    """
+    test_result = json.loads(test_result)
+    file_name = '/'.join((pri_ad.log_path,
+                          test_result["Results"][0]["Test Class"] + ".xlsx"))
+    result = [(i["Test Name"], i["Result"], ' '.join(i["Extras"]),
+               throughput_pass_fail_check(i["Extras"]))
+              for i in test_result["Results"]]
+
+    try:
+        data_obj = pandas.DataFrame(
+            result,
+            columns=[
+                "Test Name", "Result", "Iperf Throughput", "Throughput Result"
+            ])
+    except Exception as err:
+        logging.info("Error in writing into xlsheet {}".format(err))
+        return False
+
+    writer = pandas.ExcelWriter(file_name, engine='xlsxwriter')
+    data_obj.to_excel(writer, sheet_name='Sheet1')
+    workbook = writer.book
+    for column in (1, 4):
+        writer.sheets['Sheet1'].set_column(column, column, 40)
+    workbook.add_format({
+        'bold': True,
+        'text_wrap': True,
+    })
diff --git a/acts/framework/acts/test_utils/net/connectivity_const.py b/acts/framework/acts/test_utils/net/connectivity_const.py
index 89bdb2c..59c8d47 100644
--- a/acts/framework/acts/test_utils/net/connectivity_const.py
+++ b/acts/framework/acts/test_utils/net/connectivity_const.py
@@ -53,6 +53,10 @@
 # This is a random value as of now
 VPN_TIMEOUT = 15
 
+# Connectiivty Manager constants
+TYPE_MOBILE = 0
+TYPE_WIFI = 1
+
 # 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..b300492 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,42 @@
         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")
+            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 +111,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 +265,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 +307,80 @@
         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..9459d4d 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,8 +1718,10 @@
     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)
 
     try:
         if not initiate_call(
@@ -1665,7 +1730,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 +1741,17 @@
                 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):
+            if not wait_for_in_call_active(ad):
+                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 +1760,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 +1819,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 +1861,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 +1875,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
 
 
@@ -1875,7 +1953,6 @@
     # 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,12 +1968,10 @@
         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:
-        return (http_file_download_by_curl, (ad, url, output_path, file_size,
-                                             True, timeout))
+    url = "http://ipv4.download.thinkbroadband.com/" + file_name + ".zip"
+    #url = "http://146.148.91.8/download/" + file_name + ".zip"
+    return (http_file_download_by_sl4a, (ad, url, output_path, file_size, True,
+                                         timeout))
 
 
 def active_file_download_test(log, ad, file_name="5MB"):
@@ -1914,9 +1989,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 +2007,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 +2027,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 +2089,56 @@
     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
+    begin_time = int(time.time() * 1000 - 2 * 60 * 60 * 1000)
+    end_time = int(time.time() * 1000 + 2 * 60 * 60 * 1000)
     try:
+        data_accounting = {
+            "mobile_rx_bytes":
+            ad.droid.getMobileRxBytes(),
+            "subscriber_mobile_data_usage":
+            get_mobile_data_usage(ad, None, None, begin_time, end_time),
+            "curl_mobile_data_usage":
+            get_mobile_data_usage(ad, None, accounting_apk, begin_time,
+                                  end_time)
+        }
+        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, begin_time, end_time),
+                "curl_mobile_data_usage":
+                get_mobile_data_usage(ad, None, accounting_apk, begin_time,
+                                      end_time)
+            }
+            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 +2171,72 @@
                                  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)
+    begin_time = int(time.time() * 1000 - 2 * 60 * 60 * 1000)
+    end_time = int(time.time() * 1000 + 2 * 60 * 60 * 1000)
+    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, begin_time, end_time),
+        "chrome_mobile_data_usage":
+        get_mobile_data_usage(ad, None, chrome_apk, begin_time, end_time)
+    }
+    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, begin_time, end_time),
+                "chrome_mobile_data_usage":
+                get_mobile_data_usage(ad, None, chrome_apk, begin_time,
+                                      end_time)
+            }
+            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 +2246,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 +2263,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 +2276,72 @@
     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
+    begin_time = int(time.time() * 1000 - 2 * 60 * 60 * 1000)
+    end_time = int(time.time() * 1000 + 2 * 60 * 60 * 1000)
     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, begin_time, end_time),
+            "sl4a_mobile_data_usage":
+            get_mobile_data_usage(ad, None, accounting_apk, begin_time,
+                                  end_time)
+        }
+        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, begin_time, end_time),
+                "sl4a_mobile_data_usage":
+                get_mobile_data_usage(ad, None, accounting_apk, begin_time,
+                                      end_time)
+            }
+            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 +2354,80 @@
             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,
+                          begin_time=None,
+                          end_time=None):
+    if not subscriber_id:
+        subscriber_id = ad.droid.telephonyGetSubscriberId()
+    if begin_time is None:
+        begin_time = 0
+    if end_time is None:
+        end_time = int(time.time() * 1000 + 2 * 60 * 60 * 1000)
+    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.cconnectivityQueryDetailsForUid(
+                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.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 +2447,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 +2461,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 +2484,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 +2503,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 +2517,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 +2550,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 +2563,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 +2573,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 +2584,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 +2598,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 +2612,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 +2627,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 +2668,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 +2814,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 +2950,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 +2989,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 +3187,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 +3425,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 +3442,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 +3466,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 +3491,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 +3539,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 +3560,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 +3593,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(0.1)  #sleep 100ms after starting event tracking
+            ad_tx.messaging_droid.smsSendTextMessage(phonenumber_rx, text,
+                                                     True)
             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 +3670,57 @@
         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 SL4A %s sent succeessful log message" % type)
+        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" %
+                        type)
+                    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" % type)
+        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" % type)
+            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 +3738,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 +3795,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 +3825,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 +3865,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 +3924,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 +3984,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 +4006,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 +4029,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 +4077,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 +4088,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 +4110,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 +4133,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 +4157,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 +4176,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 +4191,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 +4263,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 +4395,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 +4440,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 +4496,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 +4555,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 +4564,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 +4599,7 @@
     """
     ad.droid.wifiFactoryReset()
     ad.droid.wifiToggleState(False)
+    ad.on_mobile_data = True
 
 
 def wifi_toggle_state(log, ad, state, retries=3):
@@ -4142,6 +4615,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 +4673,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 +4696,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 +4725,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 +4817,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 +5091,166 @@
         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.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 +5266,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 +5290,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 +5303,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 +5328,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 +5345,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 +5355,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 +5362,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 +5422,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 +5430,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 +5458,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 +5473,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 +5493,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 +5576,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 +5588,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..bd62853 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'):
             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/aware_const.py b/acts/framework/acts/test_utils/wifi/aware/aware_const.py
index a7574a4..e94c9bb 100644
--- a/acts/framework/acts/test_utils/wifi/aware/aware_const.py
+++ b/acts/framework/acts/test_utils/wifi/aware/aware_const.py
@@ -90,7 +90,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"
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 0b96a77..f48ec92 100755
--- a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
@@ -754,7 +754,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",
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..654ba5f 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,13 @@
     # 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',
 ]
 
 if sys.version_info < (3, ):
@@ -122,7 +123,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 +168,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
index dbaa2ab..a663ab3 100644
--- 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
index ded5f65..378eeae 100644
--- 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
 #
diff --git a/acts/framework/tests/controllers/sl4a_lib/sl4a_session_test.py b/acts/framework/tests/controllers/sl4a_lib/sl4a_session_test.py
index d16fb99..f056d2a 100644
--- 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..fff4136
--- /dev/null
+++ b/acts/framework/tests/libs/ota/ota_updater_test.py
@@ -0,0 +1,98 @@
+#!/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
+
+
+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..c319a23
--- /dev/null
+++ b/acts/tests/google/ble/concurrency/ConcurrentGattConnectTest.py
@@ -0,0 +1,145 @@
+#/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 time
+from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
+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_decorators import test_tracker_info
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+
+
+class ConcurrentGattConnectTest(BluetoothBaseTest):
+    bt_default_timeout = 10
+    max_connections = 5
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.pri_dut = self.android_devices[0]
+
+    @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
+        """
+        # Create 4 advertisements from different android devices
+        # List of tuples (android_device, advertise_callback)
+        advertise_callbacks = []
+        # List of tuples (android_deivce, gatt_server_callback)
+        gatt_server_callbacks = []
+        # List of tubles (android_device, gatt_server)
+        gatt_servers = []
+        advertisement_names = []
+        for i in range(1, self.max_connections + 1):
+            # Set device name
+            ad = self.android_devices[i]
+            name = "test_adv_{}".format(i)
+            advertisement_names.append(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)
+            advertise_callbacks.append((ad, advertise_callback))
+            # Setup generic Gatt server
+            gatt_server_callback = ad.droid.gattServerCreateGattServerCallback(
+            )
+            gatt_server_callbacks.append((ad, gatt_server_callback))
+            gatt_server = ad.droid.gattServerOpenGattServer(
+                gatt_server_callback)
+            gatt_servers.append((ad, gatt_server))
+
+        # 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 name in 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 = []
+        scan_timeout = 20
+        end_time = time.time() + scan_timeout
+        while time.time() < end_time and len(address_list) < len(
+                self.android_devices) - 1:
+            try:
+                event = self.pri_dut.ed.pop_event(
+                    "BleScan{}onScanResults".format(scan_callback),
+                    self.bt_default_timeout)
+                mac_address = event['data']['Result']['deviceInfo']['address']
+                if mac_address not in address_list:
+                    self.log.info(
+                        "Found new mac address: {}".format(mac_address))
+                    address_list.append(mac_address)
+            except Empty as err:
+                self.log.error("Failed to find any scan results.")
+                return False
+        if len(address_list) < self.max_connections:
+            self.log.error("Could not find all necessary advertisements.")
+            return False
+
+        # Connect to all addresses
+        for address in address_list:
+            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
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..ed7bff5
--- /dev/null
+++ b/acts/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
@@ -0,0 +1,477 @@
+#/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_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()
+
+    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..1c2e7e3
--- /dev/null
+++ b/acts/tests/google/ble/conn_oriented_chan/BleCocTest.py
@@ -0,0 +1,581 @@
+#/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_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()
+
+    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..cfdd052
--- /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, mac_addr=None, 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..1016855
--- /dev/null
+++ b/acts/tests/google/bt/hid/HidDeviceTest.py
@@ -0,0 +1,325 @@
+#!/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
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5315ccc2-0869-4b63-9b02-6ea349acabc8')
+    def test_hid_device_unplug(self):
+        """Test HID Device 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 device sends virtual_cable_unplug command to the HID host.
+
+        Expected Result:
+        HID profile connection is successfully established; After the HID device
+        sends virtual_cable_unplug command to the HID host, 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("Device: virtual unplug")
+        self.device_ad.droid.bluetoothHidDeviceVirtualUnplug(host_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/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..3ef2cb5 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 = [e.name for e in bt_scan_mode_types]
         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/GAP_PTS_INSTRUCTIONS b/acts/tests/google/bt/pts/instructions/GAP_PTS_INSTRUCTIONS
index 47da157..e18d9c1 100644
--- a/acts/tests/google/bt/pts/instructions/GAP_PTS_INSTRUCTIONS
+++ b/acts/tests/google/bt/pts/instructions/GAP_PTS_INSTRUCTIONS
@@ -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
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/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/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..4e8dd4e 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,77 @@
         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 crash_report:
+            fail_count["crashes"] += 1
+        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 +625,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 +639,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 +747,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 +765,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 +797,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 +812,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 +1025,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..6456cd8 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,54 @@
 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 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)
+        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", "+17124325335")
+        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 +94,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 +108,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 +169,408 @@
             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 i in range(3):
+                try:
+                    ad.droid.logI(log_msg)
+                    break
+                except Exception as e:
+                    if i == 2:
+                        ad.log.info("SL4A error: %s", e)
+                        raise
+                    else:
+                        time.sleep(5)
+        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:
+            ad.droid.logI(log_msg)
+        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
+            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.tcpdump_proc = None
+        self.dut.log.info(dict(self.result_info))
+        selection = random.randrange(0, len(file_names))
+        file_name = file_names[selection]
+        if self.result_info["File Download Failure"] < 1:
+            self.tcpdump_proc = start_adb_tcpdump(
+                self.dut, "%s_file_download" % self.test_name, mask="all")
+        self.result_info["File Download Total"] += 1
+        if not active_file_download_test(self.log, self.dut, file_name):
+            self.result_info["File Download Failure"] += 1
+            if self.result_info["File Download Failure"] == 1:
+                if self.tcpdump_proc is not None:
+                    stop_adb_tcpdump(
+                        self.dut, self.tcpdump_proc, True,
+                        "%s_file_download_failure" % self.test_name)
+                    self._take_bug_report(
+                        "%s_file_download_failure" % self.test_name,
+                        begin_time)
+            return False
+        else:
+            self.result_info["File Download Success"] += 1
+            if self.tcpdump_proc is not None:
+                stop_adb_tcpdump(self.dut, self.tcpdump_proc, False)
+            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 +584,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/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/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/WifiManagerTest.py b/acts/tests/google/wifi/WifiManagerTest.py
index b2a7b53..3c81053 100755
--- a/acts/tests/google/wifi/WifiManagerTest.py
+++ b/acts/tests/google/wifi/WifiManagerTest.py
@@ -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,7 +70,8 @@
         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()
@@ -87,7 +83,8 @@
         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)
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/DataPathTest.py b/acts/tests/google/wifi/aware/functional/DataPathTest.py
index 7e77c79..66ec715 100644
--- a/acts/tests/google/wifi/aware/functional/DataPathTest.py
+++ b/acts/tests/google/wifi/aware/functional/DataPathTest.py
@@ -78,13 +78,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 +107,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 +146,9 @@
       stype,
       encr_type,
       use_peer_id,
-      passphrase_to_use=None):
+      passphrase_to_use=None,
+      pub_on_both=False,
+      pub_on_both_same=True):
     """Runs the in-band data-path tests.
 
     Args:
@@ -143,14 +159,21 @@
                    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.
     """
     (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
 
@@ -209,13 +232,17 @@
     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):
     """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.
     """
     init_dut = self.android_devices[0]
     init_dut.pretty_name = "Initiator"
@@ -240,6 +267,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:
@@ -588,6 +627,89 @@
         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: Encription 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>
@@ -596,6 +718,9 @@
   # encr_type: Encription 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 +785,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 +957,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 +1000,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 +1058,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,95 +1179,126 @@
 
     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):
@@ -1045,3 +1334,49 @@
     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 can 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 can 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 can 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 can 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 can 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)
diff --git a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
index 8732dbb..1784d12 100644
--- a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
+++ b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
@@ -829,3 +829,182 @@
         s_type=aconsts.SUBSCRIBE_TYPE_ACTIVE,
         p_mf_1="hello there string",
         s_mf_1="goodbye there string")
+
+  #######################################
+  # Multiple concurrent services key
+  #######################################
+
+  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])
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()