Revert "Revert "Re-structure ACTS to Match New Projects""

This reverts commit 842ad4b8bc3e0aeda7117f1242902922feb16821.

Change-Id: I3007e53cb4955a80f2fbd3d416bce8e45101a8f3
diff --git a/acts/framework/MANIFEST.in b/acts/framework/MANIFEST.in
new file mode 100644
index 0000000..d16f272
--- /dev/null
+++ b/acts/framework/MANIFEST.in
@@ -0,0 +1,5 @@
+include setup.py README.txt sample_config.json
+recursive-include acts *.py
+global-exclude .DS_Store
+global-exclude *.pyc
+
diff --git a/acts/framework/README.txt b/acts/framework/README.txt
new file mode 100755
index 0000000..18853d2
--- /dev/null
+++ b/acts/framework/README.txt
@@ -0,0 +1,15 @@
+This package includes the Android Comms Testing Suite (ACTS) alpha release
+
+Dependencies:
+adb
+python3.4+
+python3.4-setuptools
+pyserial in Python3.4+
+
+Setup:
+    1. Install the dependencies.
+       On Ubuntu, sudo apt-get install Python3.4 python3-setuptools python3-serial
+    2. Run "python3.4 setup.py install" with elevated permissions
+
+To verify ACTS is ready to go, at the location for README, run
+    act.py -c sample_config.txt -tb SampleTestBed -tc SampleTest
\ No newline at end of file
diff --git a/acts/framework/acts/__init__.py b/acts/framework/acts/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/__init__.py
diff --git a/acts/framework/acts/act.py b/acts/framework/acts/act.py
new file mode 100755
index 0000000..ea3fa7b
--- /dev/null
+++ b/acts/framework/acts/act.py
@@ -0,0 +1,321 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 argparse
+import multiprocessing
+import signal
+import sys
+import traceback
+
+from acts.base_test import validate_test_name
+from acts.controllers.android_device import list_adb_devices
+from acts.keys import Config
+from acts.signals import TestAbortAll
+from acts.test_runner import TestRunner
+from acts.test_runner import USERError
+from acts.utils import abs_path
+from acts.utils import concurrent_exec
+from acts.utils import load_config
+from acts.utils import valid_filename_chars
+
+
+def _validate_test_config(test_config):
+    """Validates the raw configuration loaded from the config file.
+
+    Making sure all the required fields exist.
+    """
+    for k in Config.reserved_keys.value:
+        if k not in test_config:
+            raise USERError(("Required key {} missing in test "
+            "config.").format(k))
+
+def _validate_testbed_name(name):
+    """Validates the name of a test bed.
+
+    Since test bed names are used as part of the test run id, it needs to meet
+    certain requirements.
+
+    Args:
+        name: The test bed's name specified in config file.
+
+    Raises:
+        If the name does not meet any criteria, USERError is raised.
+    """
+    if not name:
+        raise USERError("Test bed names can't be empty.")
+    if type(name) is not str:
+        raise USERError("Test bed names have to be string.")
+    for l in name:
+        if l not in valid_filename_chars:
+            raise USERError("Char '%s' is not allowed in test bed names." % l)
+
+def _validate_testbed_configs(testbed_configs):
+    """Validates the testbed configurations.
+
+    Assign android device serials if necessary.
+    Checks for resource conflicts.
+
+    Args:
+        testbed_configs: A list of testbed configuration json objects.
+
+    Raises:
+        If any part of the configuration is invalid, USERError is raised.
+    """
+    connected_androids = list_adb_devices()
+    seen_androids = {}
+    seen_names = {}
+    # Cross checks testbed configs for resource conflicts.
+    for config in testbed_configs:
+        # Check for conflicts between multiple concurrent testbed configs.
+        # No need to call it if there's only one testbed config.
+        name = config[Config.key_testbed_name.value]
+        _validate_testbed_name(name)
+        # Test bed names should be unique.
+        if name in seen_names:
+            raise USERError("Duplicate testbed name {} found.".format(name))
+        seen_names[name] = 0
+        # If no android device specified, pick up all.
+        if Config.key_android_device.value not in config:
+            if len(testbed_configs) > 1:
+                raise USERError(("Test bed %s is picking up all Android device"
+                    "s, so it can't be selected with other test beds.") % name)
+            config[Config.key_android_device.value] = connected_androids
+        for c in config[Config.key_android_device.value]:
+            if isinstance(c, str):
+                serial = c
+            elif "serial" in c:
+                serial = c["serial"]
+            else:
+                raise USERError(('Required value "serial" is missing in '
+                    'AndroidDevice config %s.') % c)
+            if serial not in connected_androids:
+                raise USERError(("Could not find android device {} "
+                 "specified in test config {}.").format(serial, name))
+            # One android device should not be in more than one configs.
+            if serial in seen_androids:
+                raise USERError(("Android device {} is referred in both {} and"
+                    " {}.").format(serial, name, seen_androids[serial]))
+            seen_androids[serial] = name
+
+def _parse_one_test_specifier(item):
+    tokens = item.split(':')
+    if len(tokens) > 2:
+        raise USERError("Syntax error in test specifier {}".format(item))
+    if len(tokens) == 1:
+        # This should be considered a test class name
+        test_cls_name = tokens[0]
+        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 = []
+        for elem in test_case_names.split(','):
+            test_case = elem.strip()
+            validate_test_name(test_case)
+            clean_names.append(test_case)
+        return (test_cls_name, clean_names)
+
+def parse_test_list(test_list):
+    """Parse user provided test list into internal format for test_runner.
+
+    Args:
+        test_list: A list of test classes/cases.
+    """
+    result = []
+    for elem in test_list:
+        result.append(_parse_one_test_specifier(elem))
+    return result
+
+def load_test_config_file(test_config_path, tb_filters=None):
+    """Processes the test configuration file provied by user.
+
+    Loads the configuration file into a json object, unpacks each testbed
+    config into its own json object, and validate the configuration in the
+    process.
+
+    Args:
+        test_config_path: Path to the test configuration file.
+
+    Returns:
+        A list of test configuration json objects to be passed to TestRunner.
+    """
+    try:
+        configs = load_config(test_config_path)
+        if tb_filters:
+            tbs = []
+            for tb in configs[Config.key_testbed.value]:
+                if tb[Config.key_testbed_name.value] in tb_filters:
+                    tbs.append(tb)
+            if len(tbs) != len(tb_filters):
+                print("Expect to find %d test bed configs, found %d." % (
+                    len(tb_filters), len(tbs)))
+                print("Check if you have the correct test bed names.")
+                return None
+            configs[Config.key_testbed.value] = tbs
+        _validate_test_config(configs)
+        _validate_testbed_configs(configs[Config.key_testbed.value])
+        k_log_path = Config.key_log_path.value
+        configs[k_log_path] = abs_path(configs[k_log_path])
+        tps = configs[Config.key_test_paths.value]
+    except USERError as e:
+        print("Something is wrong in the test configurations.")
+        print(str(e))
+        return None
+    except Exception as e:
+        print("Error loading test config {}".format(test_config_path))
+        print(traceback.format_exc())
+        return None
+    # Unpack testbeds into separate json objects.
+    beds = configs.pop(Config.key_testbed.value)
+    config_jsons = []
+    for b in beds:
+        j = dict(configs)
+        j[Config.key_testbed.value] = b
+        # Custom keys in each test bed config will be moved up an level to be
+        # picked up for user_params. If the key already exists in the upper
+        # level, the local one defined in test bed config overwrites the
+        # general one.
+        for k in list(b.keys()):
+            if k in j:
+                j[k] = b[k]
+                del b[k]
+            elif k not in Config.tb_config_reserved_keys.value:
+                j[k] = b[k]
+                del b[k]
+        config_jsons.append(j)
+    return config_jsons
+
+def _run_test(test_runner, repeat=1):
+    """Instantiate and runs TestRunner.
+
+    This is the function to start separate processes with.
+
+    Args:
+        test_runner: The test_runner instance to be executed.
+        repeat: Number of times to iterate the specified tests.
+    """
+    try:
+        for i in range(repeat):
+            test_runner.run()
+    except TestAbortAll:
+        return
+    except:
+        print("Exception when executing {}, iteration {}.".format(
+            test_runner.testbed_name, i))
+        print(traceback.format_exc())
+    finally:
+        test_runner.stop()
+
+def _gen_term_signal_handler(test_runners):
+    def termination_sig_handler(signal_num, frame):
+        for t in test_runners:
+            t.stop()
+        sys.exit(1)
+    return termination_sig_handler
+
+def _run_tests_parallel(process_args):
+    print("Executing {} concurrent test runs.".format(len(process_args)))
+    concurrent_exec(_run_test, process_args)
+
+def _run_tests_sequential(process_args):
+    for args in process_args:
+        _run_test(*args)
+
+def _parse_test_file(fpath):
+    try:
+        with open(fpath, 'r') as f:
+            tf = []
+            for line in f:
+                line = line.strip()
+                if not line:
+                    continue
+                if len(tf) and (tf[-1].endswith(':') or tf[-1].endswith(',')):
+                    tf[-1] += line
+                else:
+                    tf.append(line)
+            return tf
+    except:
+        print("Error loading test file.")
+        raise
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description=("Specify tests to run. If "
+                 "nothing specified, run all test cases found."))
+    parser.add_argument('-c', '--config', nargs=1, type=str, required=True,
+        metavar="<PATH>", help="Path to the test configuration file.")
+    parser.add_argument('--test_args', nargs='+', type=str,
+        metavar="Arg1 Arg2 ...",
+        help=("Command-line arguments to be passed to every test case in a "
+              "test run. Use with caution."))
+    parser.add_argument('-d', '--debug', action="store_true",
+        help=("Set this flag if manual debugging is required."))
+    parser.add_argument('-p', '--parallel', action="store_true",
+        help=("If set, tests will be executed on all testbeds in parallel. "
+              "Otherwise, tests are executed iteratively testbed by testbed."))
+    parser.add_argument('-r', '--repeat', type=int,
+        metavar="<NUMBER>",
+        help="Number of times to run the specified test cases.")
+    parser.add_argument('-tb', '--testbed', nargs='+', type=str,
+        metavar="[<TEST BED NAME1> <TEST BED NAME2> ...]",
+        help="Specify which test beds to run tests on.")
+    group = parser.add_mutually_exclusive_group(required=True)
+    group.add_argument('-tc', '--testclass', nargs='+', type=str,
+        metavar="[TestClass1 TestClass2:test_xxx ...]",
+        help="A list of test classes/cases to run.")
+    group.add_argument('-tf', '--testfile', nargs=1, type=str,
+        metavar="<PATH>",
+        help=("Path to a file containing a comma delimited list of test "
+              "classes to run."))
+
+    args = parser.parse_args()
+    test_list = None
+    repeat = 1
+    if args.testfile:
+        test_list = _parse_test_file(args.testfile[0])
+    elif args.testclass:
+        test_list = args.testclass
+    if args.repeat:
+        repeat = args.repeat
+    parsed_configs = load_test_config_file(args.config[0], args.testbed)
+    if not parsed_configs:
+        print("Encountered error when parsing the config file, abort!")
+        sys.exit(1)
+    # Prepare args for test runs
+    test_identifiers = parse_test_list(test_list)
+    test_runners = []
+    process_args = []
+    try:
+        for c in parsed_configs:
+            c[Config.ikey_cli_args.value] = args.test_args
+            t = TestRunner(c, test_identifiers)
+            test_runners.append(t)
+            process_args.append((t, repeat))
+    except:
+        print("Failed to instantiate test runner, abort.")
+        print(traceback.format_exc())
+        sys.exit(1)
+    # Register handler for term signals if in -i mode.
+    if not args.debug:
+        handler = _gen_term_signal_handler(test_runners)
+        signal.signal(signal.SIGTERM, handler)
+        signal.signal(signal.SIGINT, handler)
+    # Execute test runners.
+    if args.parallel and len(process_args) > 1:
+        _run_tests_parallel(process_args)
+    else:
+        _run_tests_sequential(process_args)
+    sys.exit(0)
diff --git a/acts/framework/acts/base_test.py b/acts/framework/acts/base_test.py
new file mode 100644
index 0000000..8f4c93c
--- /dev/null
+++ b/acts/framework/acts/base_test.py
@@ -0,0 +1,757 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2014 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 acts.logger as logger
+
+from acts.keys import Config
+from acts.records import TestResult
+from acts.records import TestResultRecord
+from acts.signals import TestAbortClass
+from acts.signals import TestAbortAll
+from acts.signals import TestFailure
+from acts.signals import TestPass
+from acts.signals import TestSkip
+from acts.signals import TestSilent
+from acts.utils import concurrent_exec
+from acts.utils import create_dir
+from acts.utils import get_current_human_time
+
+MAX_FILENAME_LEN = 255
+DEFAULT_ADB_LOG_OFFSET = 5
+
+# Macro strings for test result reporting
+TEST_CASE_TOKEN = "[Test Case]"
+RESULT_LINE_TEMPLATE = TEST_CASE_TOKEN + " %s %s"
+
+def validate_test_name(name):
+    """Checks if a test name is valid.
+
+    To be valid, a test name needs to follow the naming convention: starts
+    with "test_". Also, the test class needs to actually have a function
+    named after the test.
+
+    Args:
+        name: name of a test case.
+
+    Raises:
+        BaseTestError is raised if the name is invalid.
+    """
+    if len(name) < 5 or name[:5] != "test_":
+        raise BaseTestError("Invalid test case name found: {}.".format(name))
+
+class BaseTestError(Exception):
+    """Raised for exceptions that occured in BaseTestClass."""
+
+class BaseTestClass(object):
+    """Base class for all test classes to inherit from.
+
+    This class gets all the controller objects from test_runner and executes
+    the test cases requested within itself.
+
+    Most attributes of this class are set at runtime based on the configuration
+    provided.
+
+    Attributes:
+        tests: A list of strings, each representing a test case name.
+        TAG: A string used to refer to a test class. Default is the test class
+            name.
+        droids: A list of SL4A client objects for convenience. Do NOT use, to
+            be deprecated.
+        eds: A list of event_dispatcher objects. Do NOT use, to be deprecated.
+        log: A logger object used for logging.
+        results: A TestResult object for aggregating test results from the
+            execution of test cases.
+    """
+
+    TAG = None
+
+    def __init__(self, configs):
+        self.tests = []
+        if not self.TAG:
+            self.TAG = self.__class__.__name__
+        # Set default value for optional config params.
+        if Config.key_adb_log_time_offset.value not in configs:
+            configs[Config.key_adb_log_time_offset.value] = DEFAULT_ADB_LOG_OFFSET
+        # Set all the controller objects and params.
+        for name, value in configs.items():
+            setattr(self, name, value)
+        # Set convenience references for android_device objects.
+        # TODO(angli): remove these and force tests to use the droids in ad
+        # objs directly.
+        if Config.ikey_android_device.value in configs:
+            self.droids = []
+            self.eds = []
+            for ad in self.android_devices:
+                self.droids.append(ad.droid)
+                self.eds.append(ad.ed)
+            if self.android_devices:
+                self.droid = self.droids[0]
+                self.ed = self.eds[0]
+        else:
+            self.log.warning("No attached android device found.")
+        self.results = TestResult()
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *args):
+        self._exec_func(self.clean_up)
+
+    def unpack_userparams(self, req_param_names, opt_param_names=[]):
+        """Unpacks user defined parameters in test config into individual
+        variables.
+
+        Instead of accessing the user param with self.user_params["xxx"], the
+        variable can be directly accessed with self.xxx.
+
+        All missing required params will be logged in error. If an optional
+        param is missing, log a note and continue. You can assert on the return
+        value of this funtion in setup_class to ensure the required user params
+        are found in test config and set.
+
+        Args:
+            req_param_names: A list of names of the required user params.
+            opt_param_names: A list of names of the optional user params.
+
+        Returns:
+            True if all required user params were set. False otherwise.
+        """
+        missing = False
+        for name in req_param_names:
+            if name not in self.user_params:
+                missing = True
+                self.log.error(("Missing required user param '%s' in "
+                    "configuration!") % name)
+                continue
+            setattr(self, name, self.user_params[name])
+        for name in opt_param_names:
+            if name not in self.user_params:
+                self.log.info(("Missing optional user param '%s' in "
+                    "configuration, continue.") % name)
+            else:
+                setattr(self, name, self.user_params[name])
+        return not missing
+
+    def _setup_class(self):
+        """Proxy function to guarantee the base implementation of setup_class
+        is called.
+        """
+        try:
+            return self.setup_class()
+        except TestAbortClass:
+            return False
+
+    def setup_class(self):
+        """Setup function that will be called before executing any test case in
+        the test class.
+
+        Implementation is optional.
+        """
+        return True
+
+    def teardown_class(self):
+        """Teardown function that will be called after all the selected test
+        cases in the test class have been executed.
+
+        Implementation is optional.
+        """
+        pass
+
+    def _setup_test(self, test_name):
+        """Proxy function to guarantee the base implementation of setup_test is
+        called.
+        """
+        try:
+            # Write test start token to adb log if android device is attached.
+            for ad in self.android_devices:
+                ad.droid.logV("%s BEGIN %s" % (TEST_CASE_TOKEN, test_name))
+        except:
+            pass
+        return self.setup_test()
+
+    def setup_test(self):
+        """Setup function that will be called every time before executing each
+        test case in the test class.
+
+        Implementation is optional.
+        """
+        return True
+
+    def _teardown_test(self, test_name):
+        """Proxy function to guarantee the base implementation of teardown_test
+        is called.
+        """
+        try:
+            # Write test end token to adb log if android device is attached.
+            for ad in self.android_devices:
+                ad.droid.logV("%s END %s" % (TEST_CASE_TOKEN, test_name))
+        except:
+            pass
+        return self.teardown_test()
+
+    def teardown_test(self):
+        """Teardown function that will be called every time a test case has
+        been executed.
+
+        Implementation is optional.
+        """
+        pass
+
+    def _on_fail(self, record):
+        """Proxy function to guarantee the base implementation of on_fail is
+        called.
+
+        Args:
+            record: The TestResultRecord object for the failed test case.
+        """
+        test_name = record.test_name
+        begin_time = logger.epoch_to_log_line_timestamp(record.begin_time)
+        end_time = logger.get_log_line_timestamp(self.adb_log_time_offset)
+        self.log.error(record.details)
+        self.log.info(RESULT_LINE_TEMPLATE % (test_name, record.result))
+        try:
+            self.cat_adb_log(test_name, begin_time, end_time)
+        except AttributeError:
+            pass
+        self.on_fail(test_name, begin_time)
+
+    def on_fail(self, test_name, begin_time):
+        """A function that is executed upon a test case failure.
+
+        User implementation is optional.
+
+        This should be the primary place to call take_bug_reports, e.g.
+        self.take_bug_reports(test_name, self.android_devices[0])
+
+        Args:
+            test_name: Name of the test that triggered this function.
+            begin_time: Logline format timestamp taken when the test started.
+        """
+        pass
+
+    def _on_success(self, record):
+        """Proxy function to guarantee the base implementation of on_success is
+        called.
+
+        Args:
+            record: The TestResultRecord object for the passed test case.
+        """
+        test_name = record.test_name
+        begin_time = logger.epoch_to_log_line_timestamp(record.begin_time)
+        msg = record.details
+        if msg:
+            self.log.info(msg)
+        self.log.info(RESULT_LINE_TEMPLATE % (test_name, record.result))
+        self.on_success(test_name, begin_time)
+
+    def on_success(self, test_name, begin_time):
+        """A function that is executed upon a test case passing.
+
+        Implementation is optional.
+
+        Args:
+            test_name: Name of the test that triggered this function.
+            begin_time: Logline format timestamp taken when the test started.
+        """
+        pass
+
+    def _on_skip(self, record):
+        """Proxy function to guarantee the base implementation of on_skip is
+        called.
+
+        Args:
+            record: The TestResultRecord object for the skipped test case.
+        """
+        test_name = record.test_name
+        begin_time = logger.epoch_to_log_line_timestamp(record.begin_time)
+        self.log.info(RESULT_LINE_TEMPLATE % (test_name, record.result))
+        self.log.info("Reason to skip: %s" % record.details)
+        self.on_skip(test_name, begin_time)
+
+    def on_skip(self, test_name, begin_time):
+        """A function that is executed upon a test case being skipped.
+
+        Implementation is optional.
+
+        Args:
+            test_name: Name of the test that triggered this function.
+            begin_time: Logline format timestamp taken when the test started.
+        """
+        pass
+
+    def on_exception(self, test_name, begin_time):
+        """A function that is executed upon an unhandled exception from a test
+        case.
+
+        Implementation is optional.
+
+        Args:
+            test_name: Name of the test that triggered this function.
+            begin_time: Logline format timestamp taken when the test started.
+        """
+        pass
+
+    def fail(self, msg, extras=None):
+        """Explicitly fail a test case.
+
+        Args:
+            msg: A string explaining the datails of the failure.
+            extras: An optional field for extra information to be included in
+                test result.
+
+        Raises:
+            TestFailure is raised to mark a test case as failed.
+        """
+        raise TestFailure(msg, extras)
+
+    def explicit_pass(self, msg, extras=None):
+        """Explicitly pass a test case.
+
+        A test with not uncaught exception will pass implicitly so the usage of
+        this is optional. It is intended for reporting extra information when a
+        test passes.
+
+        Args:
+            msg: A string explaining the details of the passed test.
+            extras: An optional field for extra information to be included in
+                test result.
+
+        Raises:
+            TestPass is raised to mark a test case as passed.
+        """
+        raise TestPass(msg, extras)
+
+    def assert_true(self, expr, msg, extras=None):
+        """Assert an expression evaluates to true, otherwise fail the test.
+
+        Args:
+            expr: The expression that is evaluated.
+            msg: A string explaining the datails in case of failure.
+            extras: An optional field for extra information to be included in
+                test result.
+        """
+        if not expr:
+            self.fail(msg, extras)
+
+    def skip(self, reason, extras=None):
+        """Skip a test case.
+
+        Args:
+            reason: The reason this test is skipped.
+            extras: An optional field for extra information to be included in
+                test result.
+
+        Raises:
+            TestSkip is raised to mark a test case as skipped.
+        """
+        raise TestSkip(reason, extras)
+
+    def skip_if(self, expr, reason, extras=None):
+        """Skip a test case if expression evaluates to True.
+
+        Args:
+            expr: The expression that is evaluated.
+            reason: The reason this test is skipped.
+            extras: An optional field for extra information to be included in
+                test result.
+        """
+        if expr:
+            self.skip(reason, extras)
+
+    def abort_class(self, reason, extras=None):
+        """Abort all subsequent test cases within the same test class in one
+        iteration.
+
+        If one test class is requested multiple times in a test run, this can
+        only abort one of the requested executions, NOT all.
+
+        Args:
+            reason: The reason to abort.
+            extras: An optional field for extra information to be included in
+                test result.
+
+        Raises:
+            TestAbortClass is raised to abort all subsequent tests in the test
+            class.
+        """
+        self.log.warning(("Abort %s, remaining test cases within the class"
+                " will not be executed. Reason: %s") % (self.TAG, str(reason)))
+        raise TestAbortClass(reason, extras)
+
+    def abort_class_if(self, expr, reason, extras=None):
+        """Abort all subsequent test cases within the same test class in one
+        iteration, if expression evaluates to True.
+
+        If one test class is requested multiple times in a test run, this can
+        only abort one of the requested executions, NOT all.
+
+        Args:
+            expr: The expression that is evaluated.
+            reason: The reason to abort.
+            extras: An optional field for extra information to be included in
+                test result.
+
+        Raises:
+            TestAbortClass is raised to abort all subsequent tests in the test
+            class.
+        """
+        if expr:
+            self.abort_class(reason, extras)
+
+    def abort_all(self, reason, extras=None):
+        """Abort all subsequent test cases, including the ones not in this test
+        class or iteration.
+
+        Args:
+            reason: The reason to abort.
+            extras: An optional field for extra information to be included in
+                test result.
+
+        Raises:
+            TestAbortAll is raised to abort all subsequent tests.
+        """
+        self.log.warning(("Abort test run, remaining test cases will not be "
+                "executed. Reason: %s") % (str(reason)))
+        raise TestAbortAll(reason, extras)
+
+    def abort_all_if(self, expr, reason, extras=None):
+        """Abort all subsequent test cases, if the expression evaluates to
+        True.
+
+        Args:
+            expr: The expression that is evaluated.
+            reason: The reason to abort.
+            extras: An optional field for extra information to be included in
+                test result.
+
+        Raises:
+            TestAbortAll is raised to abort all subsequent tests.
+        """
+        if expr:
+            self.abort_all(reason, extras)
+
+    def _is_timestamp_in_range(self, target, begin_time, end_time):
+        low = logger.logline_timestamp_comparator(begin_time, target) <= 0
+        high = logger.logline_timestamp_comparator(end_time, target) >= 0
+        return low and high
+
+    def cat_adb_log(self, tag, begin_time, end_time):
+        """Takes logs from adb logcat log.
+
+        Goes through adb logcat log and excerpt the log lines recorded during a
+        certain time period. The lines are saved into a file in the test
+        class's log directory.
+
+        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.
+            end_time: Logline format timestamp of the end of the time period.
+        """
+        self.log.debug("Extracting adb log from logcat.")
+        adb_excerpt_path = os.path.join(self.log_path, "AdbLogExcerpts")
+        create_dir(adb_excerpt_path)
+        for f_name in self.adb_logcat_files:
+            out_name = f_name.replace("adblog,", "").replace(".txt", "")
+            out_name = ",{},{}.txt".format(begin_time, out_name)
+            tag_len = MAX_FILENAME_LEN - len(out_name)
+            tag = tag[:tag_len]
+            out_name = tag + out_name
+            full_adblog_path = os.path.join(adb_excerpt_path, out_name)
+            with open(full_adblog_path, 'w', encoding='utf-8') as out:
+                in_file = os.path.join(self.adb_logcat_path, f_name)
+                with open(in_file, 'r', encoding='utf-8', errors='replace') as f:
+                    in_range = False
+                    while True:
+                        line = None
+                        try:
+                            line = f.readline()
+                            if not line:
+                                break
+                        except:
+                            continue
+                        line_time = line[:logger.log_line_timestamp_len]
+                        if not logger.is_valid_logline_timestamp(line_time):
+                            continue
+                        if self._is_timestamp_in_range(line_time, begin_time,
+                            end_time):
+                            in_range = True
+                            out.write(line + '\n')
+                        else:
+                            if in_range:
+                                break
+
+    def take_bug_reports(self, test_name, begin_time, android_devices):
+        """Takes bug report on a list of devices and stores it in the log
+        directory of the test class.
+
+        If you want to take a bug report, call this function with a list of
+        android_device objects in on_fail. But reports will be taken on all the
+        devices in the list concurrently. Bug report takes a relative long
+        time to take, so use this cautiously.
+
+        Args:
+            test_name: Name of the test case that triggered this bug report.
+            begin_time: Logline format timestamp taken when the test started.
+            android_devices: android_device instances to take bugreport on.
+        """
+        br_path = os.path.join(self.log_path, "BugReports")
+        begin_time = logger.normalize_log_line_timestamp(begin_time)
+        create_dir(br_path)
+        args = [(test_name, begin_time, ad) for ad in android_devices]
+        concurrent_exec(self._take_bug_report, args)
+
+    def _take_bug_report(self, test_name, begin_time, ad):
+        """Takes a bug report on a device and stores it in the log directory of
+        the test class.
+
+        Args:
+            test_name: Name of the test case that triggered this bug report.
+            begin_time: Logline format timestamp taken when the test started.
+            ad: The AndroidDevice instance to take bugreport on.
+        """
+        serial = ad.serial
+        br_path = os.path.join(self.log_path, "BugReports")
+        base_name = ",{},{}.txt".format(begin_time, serial)
+        test_name_len = MAX_FILENAME_LEN - len(base_name)
+        out_name = test_name[:test_name_len] + base_name
+        full_out_path = os.path.join(br_path, out_name.replace(' ', '\ '))
+        self.log.info("Taking bugreport for test case {} on {}".
+            format(test_name, serial))
+        ad.adb.bugreport(" > %s" % full_out_path)
+        self.log.info("Finished taking bugreport on {}".format(serial))
+
+    def exec_one_testcase(self, test_name, test_func, pms=None):
+        """Executes one test case and update test results.
+
+        Executes one test case, create a TestResultRecord object with the
+        execution information, and add the record to the test class's test
+        results.
+
+        Args:
+            test_name: Name of the test.
+            test_func: The test function.
+            pms: Params to be passed to the test function.
+        """
+        is_generate_trigger = False
+        tr_record = TestResultRecord(test_name)
+        tr_record.test_begin()
+        self.log.info("[Test Case] %s" % test_name)
+        verdict = None
+        try:
+            self.skip_if(not self._exec_func(self._setup_test, test_name),
+                "Setup for %s failed." % test_name)
+            try:
+                if pms:
+                        verdict = test_func(*pms)
+                else:
+                    verdict = test_func()
+            except TypeError as e:
+                e_str = str(e)
+                if test_name in e_str:
+                    raise TestSkip(e_str + ". Got args: {}".format(pms))
+                raise e
+        except (TestFailure, AssertionError) as e:
+            tr_record.test_fail(e)
+            self._exec_func(self._on_fail, tr_record)
+        except TestSkip as e:
+            # Test skipped.
+            tr_record.test_skip(e)
+            self._exec_func(self._on_skip, tr_record)
+        except (TestAbortClass, TestAbortAll) as e:
+            # Abort signals, pass along.
+            tr_record.test_skip(e)
+            raise e
+        except TestPass as e:
+            # Explicit test pass.
+            tr_record.test_pass(e)
+            self._exec_func(self._on_success, tr_record)
+        except TestSilent as e:
+            # This is a trigger test for generated tests, suppress reporting.
+            is_generate_trigger = True
+            self.results.requested.remove(test_name)
+        except Exception as e:
+            # Exception happened during test.
+            self.log.exception("Exception in " + test_name)
+            tr_record.test_fail(e)
+            bt = logger.epoch_to_log_line_timestamp(tr_record.begin_time)
+            self._exec_func(self.on_exception, tr_record.test_name, bt)
+            self._exec_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_func(self._on_success, tr_record)
+                return
+            # Test failed because it didn't return True.
+            # This should be removed eventually.
+            tr_record.test_fail()
+            self._exec_func(self._on_fail, tr_record)
+        finally:
+            self._exec_func(self._teardown_test, test_name)
+            if not is_generate_trigger:
+                self.results.add_record(tr_record)
+                self.reporter.write(repr(tr_record) + '\n')
+
+    def run_generated_testcases(self, test_func, settings, *args, tag="",
+                                name_func=None):
+        """Runs generated test cases.
+
+        Generated test cases are not written down as functions, but as a list
+        of parameter sets. This way we reduce code repetition and improve
+        test case scalability.
+
+        Args:
+            test_func: The common logic shared by all these generated test
+                cases. This function should take at least one argument, which
+                is a parameter set.
+            settings: A list of strings representing parameter sets. These are
+                usually json strings that get loaded in the test_func.
+            args: Additional args to be passed to the test_func.
+            tag: Name of this group of generated test cases. Ignored if
+                name_func is provided and operate properly
+            name_func: A function that takes a test setting and generates a
+                proper test name. The test name should be shorter than
+                MAX_FILENAME_LEN. Names over the limit will be truncated.
+
+        Returns:
+            A list of settings that did not pass.
+        """
+        failed_settings = []
+        for s in settings:
+            test_name = "{} {}".format(tag, s)
+            if name_func:
+                try:
+                    test_name = name_func(s, *args)
+                except:
+                    msg = ("Failed to get test name from test_func. Fall back "
+                        "to default %s") % test_name
+                    self.log.exception(msg)
+            self.results.requested.append(test_name)
+            if len(test_name) > MAX_FILENAME_LEN:
+                test_name = test_name[:MAX_FILENAME_LEN]
+            previous_success_cnt = len(self.results.passed)
+            self.exec_one_testcase(test_name, test_func, (s,) + args)
+            if len(self.results.passed) - previous_success_cnt != 1:
+                failed_settings.append(s)
+        return failed_settings
+
+    def _exec_func(self, func, *args):
+        """Executes a function with exception safeguard.
+
+        This will let TestAbortAll through so abort_all works in all procedure
+        functions.
+
+        Args:
+            func: Function to be executed.
+            args: Arguments to be passed to the function.
+
+        Returns:
+            Whatever the function returns, or False if unhandled exception
+            occured.
+        """
+        try:
+            return func(*args)
+        except TestAbortAll:
+            raise
+        except:
+            msg = "Exception happened when executing {} in {}.".format(
+                func.__name__, self.TAG)
+            self.log.exception(msg)
+            return False
+
+    def _get_test_funcs(self, test_case_names):
+        # All tests are selected if test_cases list is None.
+        test_names = self.tests
+        if test_case_names:
+            test_names = test_case_names
+        # Load functions based on test names. Also find the longest test name.
+        test_funcs = []
+        for test_name in test_names:
+            try:
+                # Validate test_name's format.
+                validate_test_name(test_name)
+                test_funcs.append((test_name, getattr(self, test_name)))
+            except AttributeError:
+                self.log.warning("%s does not have test case %s." % (
+                    self.TAG, test_name))
+            except BaseTestError as e:
+                self.log.warning(str(e))
+        return test_funcs
+
+    def run(self, test_names=None):
+        """Runs test cases within a test class by the order they
+        appear in the test list.
+
+        Being in the test_names list makes the test case "requested". If its
+        name passes validation, then it'll be executed, otherwise the name will
+        be skipped.
+
+        Args:
+            test_names: A list of names of the requested test cases. If None,
+                all test cases in the class are considered requested.
+
+        Returns:
+            A tuple of: The number of requested test cases, the number of test
+            cases executed, and the number of test cases passed.
+        """
+        # Total number of test cases requested by user.
+        if test_names:
+            self.results.requested = list(test_names)
+        elif self.tests:
+            self.results.requested = list(self.tests)
+        else:
+            # No test case specified and no default list, abort.
+            return self.results
+        # Setup for the class.
+        if not self._exec_func(self._setup_class):
+            self.log.error("Failed to setup {}, skipping.".format(self.TAG))
+            skip_signal = TestSkip("Test class %s failed to setup." % self.TAG)
+            self.results.skip_all(skip_signal)
+            self._exec_func(self.teardown_class)
+            return self.results
+        self.log.info("==========> %s <==========" % self.TAG)
+        tests = self._get_test_funcs(test_names)
+
+        # Run tests in order.
+        try:
+            for test_name, test_func in tests:
+                self.exec_one_testcase(test_name, test_func, self.cli_args)
+            return self.results
+        except TestAbortClass:
+            return self.results
+        except TestAbortAll as e:
+            # Piggy-back test results on this exception object so we don't lose
+            # results from this test class.
+            setattr(e, "results", self.results)
+            raise e
+        finally:
+            self._exec_func(self.teardown_class)
+            self.log.info("Summary for test class %s: %s" % (self.TAG,
+                self.results.summary_str()))
+
+    def clean_up(self):
+        """A function that is executed upon completion of all tests cases
+        selected in the test class.
+
+        This function should clean up objects initialized in the constructor by
+        user.
+        """
+        pass
diff --git a/acts/framework/acts/controllers/__init__.py b/acts/framework/acts/controllers/__init__.py
new file mode 100644
index 0000000..e2f5b49
--- /dev/null
+++ b/acts/framework/acts/controllers/__init__.py
@@ -0,0 +1,33 @@
+"""Modules under acts.controllers provide interfaces to hardware/software
+resources that ACTS manages.
+
+Top level controllers module are controller modules that need to be explicitly
+specified by users in test configuration files. Top level controller modules
+should have the following module level functions:
+
+def create(configs, logger):
+    '''Instantiates the controller class with the input configs.
+    Args:
+        configs: A list of dicts each representing config for one controller
+            object.
+        logger: The main logger used in the current test run.
+    Returns:
+        A list of controller objects.
+
+def destroy(objs):
+    '''Destroys a list of controller objects created by the "create" function
+    and releases all the resources.
+
+    Args:
+        objs: A list of controller objects created from this module.
+    '''
+"""
+
+"""This is a list of all the top level controller modules"""
+__all__ = [
+    "android_device",
+    "attenuator",
+    "monsoon",
+    "access_point",
+    "iperf_server"
+]
\ No newline at end of file
diff --git a/acts/framework/acts/controllers/access_point.py b/acts/framework/acts/controllers/access_point.py
new file mode 100755
index 0000000..f8d862b
--- /dev/null
+++ b/acts/framework/acts/controllers/access_point.py
@@ -0,0 +1,510 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - Google, Inc.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import acts.jsonrpc as jsonrpc
+from acts.test_utils.wifi_test_utils import WifiEnums
+
+def create(configs, logger):
+    results = []
+    for c in configs:
+        addr = c[Config.key_address.value]
+        port = 80
+        if Config.key_port.value in c:
+            port = c[Config.key_port.value]
+        results.append(AP(addr, port))
+    return results
+
+def destroy(objs):
+    return
+
+class ServerError(Exception):
+    pass
+
+class ClientError(Exception):
+    pass
+
+"""
+Controller for OpenWRT routers.
+"""
+class AP():
+    """Interface to OpenWRT using the LuCI interface.
+
+    Works via JSON-RPC over HTTP. A generic access method is provided, as well
+    as more specialized methods.
+
+    Can also call LuCI methods generically:
+
+        ap_instance.sys.loadavg()
+        ap_instance.sys.dmesg()
+        ap_instance.fs.stat("/etc/hosts")
+    """
+    IFACE_DEFAULTS = {"mode": "ap", "disabled": "0",
+                      "encryption": "psk2", "network": "lan"}
+    RADIO_DEFAULTS = {"disabled": "0"}
+
+    def __init__(self, addr, port=80):
+        self._client = jsonrpc.JSONRPCClient(
+                        "http://""{}:{}/cgi-bin/luci/rpc/".format(addr, port))
+        self.RADIO_NAMES = []
+        keys = self._client.get_all("wireless").keys()
+        if "radio0" in keys:
+            self.RADIO_NAMES.append("radio0")
+        if "radio1" in keys:
+            self.RADIO_NAMES.append("radio1")
+
+    def section_id_lookup(self, cfg_name, key, value):
+        """Looks up the section id of a section.
+
+        Finds the section ids of the sections that have the specified key:value
+        pair in them.
+
+        Args:
+            cfg_name: Name of the configuration file to look in.
+            key: Key of the pair.
+            value: Value of the pair.
+
+        Returns:
+            A list of the section ids found.
+        """
+        section_ids = []
+        sections = self._client.get_all(cfg_name)
+        for section_id, section_cfg in sections.items():
+            if key in section_cfg and section_cfg[key] == value:
+                section_ids.append(section_id)
+        return section_ids
+
+    def _section_option_lookup(self, cfg_name, conditions, *target_keys):
+        """Looks up values of options in sections that match the conditions.
+
+        To match a condition, a section needs to have all the key:value pairs
+        specified in conditions.
+
+        Args:
+            cfg_name: Name of the configuration file to look in.
+            key: Key of the pair.
+            value: Value of the pair.
+            target_key: Key of the options we want to retrieve values from.
+
+        Returns:
+            A list of the values found.
+        """
+        results = []
+        sections = self._client.get_all(cfg_name)
+        for section_cfg in sections.values():
+            if self._match_conditions(conditions, section_cfg):
+                r = {}
+                for k in target_keys:
+                    if k not in section_cfg:
+                        break
+                    r[k] = section_cfg[k]
+                if r:
+                    results.append(r)
+        return results
+
+    @staticmethod
+    def _match_conditions(conds, cfg):
+        for cond in conds:
+            key, value = cond
+            if key not in cfg or cfg[key] != value:
+                return False
+        return True
+
+    def run(self, *cmd):
+        """Executes a terminal command on the AP.
+
+        Args:
+            cmd: A tuple of command strings.
+
+        Returns:
+            The terminal output of the command.
+        """
+        return self._client.sys("exec", *cmd)
+
+    def apply_configs(self, ap_config):
+        """Applies configurations to the access point.
+
+        Reads the configuration file, adds wifi interfaces, and sets parameters
+        based on the configuration file.
+
+        Args:
+            ap_config: A dict containing the configurations for the AP.
+        """
+        self.reset()
+        for k, v in ap_config.items():
+            if "radio" in k:
+                self._apply_radio_configs(k, v)
+            if "network" in k:
+                # TODO(angli) Implement this.
+                pass
+        self.apply_wifi_changes()
+
+    def _apply_radio_configs(self, radio_id, radio_config):
+        """Applies conigurations on a radio of the AP.
+
+        Sets the options in the radio config.
+        Adds wifi-ifaces to this radio based on the configurations.
+        """
+        for k, v in radio_config.items():
+            if k == "settings":
+                self._set_options('wireless', radio_id, v,
+                                  self.RADIO_DEFAULTS)
+            if k == "wifi-iface":
+                for cfg in v:
+                    cfg["device"] = radio_id
+                self._add_ifaces(v)
+
+    def reset(self):
+        """Resets the AP to a clean state.
+        
+        Deletes all wifi-ifaces.
+        Enable all the radios.
+        """
+        sections = self._client.get_all("wireless")
+        to_be_deleted = []
+        for section_id in sections.keys():
+            if section_id not in self.RADIO_NAMES:
+                to_be_deleted.append(section_id)
+        self.delete_ifaces_by_ids(to_be_deleted)
+        for r in self.RADIO_NAMES:
+            self.toggle_radio_state(r, True)
+
+    def toggle_radio_state(self, radio_name, state=None):
+        """Toggles the state of a radio.
+
+        If input state is None, toggle the state of the radio.
+        Otherwise, set the radio's state to input state.
+        State True is equivalent to 'disabled':'0'
+
+        Args:
+            radio_name: Name of the radio to change state.
+            state: State to set to, default is None.
+
+        Raises:
+            ClientError: If the radio specified does not exist on the AP.
+        """
+        if radio_name not in self.RADIO_NAMES:
+            raise ClientError("Trying to change none-existent radio's state")
+        cur_state = self._client.get("wireless", radio_name, "disabled")
+        cur_state = True if cur_state=='0' else False
+        if state == cur_state:
+            return
+        new_state = '1' if cur_state else '0'
+        self._set_option("wireless", radio_name, "disabled", new_state)
+        self.apply_wifi_changes()
+        return
+
+    def set_ssid_state(self, ssid, state):
+        """Sets the state of ssid (turns on/off).
+
+        Args:
+            ssid: The ssid whose state is being changed.
+            state: State to set the ssid to. Enable the ssid if True, disable
+                otherwise.
+        """
+        new_state = '0' if state else '1'
+        section_ids = self.section_id_lookup("wireless", "ssid", ssid)
+        for s_id in section_ids:
+            self._set_option("wireless", s_id, "disabled", new_state)
+
+    def get_ssids(self, conds):
+        """Gets all the ssids that match the conditions.
+
+        Params:
+            conds: An iterable of tuples, each representing a key:value pair
+                an ssid must have to be included.
+
+        Returns:
+            A list of ssids that contain all the specified key:value pairs.
+        """
+        results = []
+        for s in self._section_option_lookup("wireless", conds, "ssid"):
+            results.append(s["ssid"])
+        return results
+
+    def get_active_ssids(self):
+        """Gets the ssids that are currently not disabled.
+
+        Returns:
+            A list of ssids that are currently active.
+        """
+        conds = (("disabled", "0"),)
+        return self.get_ssids(conds)
+
+    def get_active_ssids_info(self, *keys):
+        """Gets the specified info of currently active ssids
+
+        If frequency is requested, it'll be retrieved from the radio section
+        associated with this ssid.
+
+        Params:
+            keys: Names of the fields to include in the returned info.
+                e.g. "frequency".
+
+        Returns:
+            Values of the requested info.
+        """
+        conds = (("disabled", "0"),)
+        keys = [w.replace("frequency","device") for w in keys]
+        if "device" not in keys:
+            keys.append("device")
+        info = self._section_option_lookup("wireless", conds, "ssid", *keys)
+        results = []
+        for i in info:
+            radio = i["device"]
+            # Skip this info the radio its ssid is on is disabled.
+            disabled = self._client.get("wireless", radio, "disabled")
+            if disabled != '0':
+                continue
+            c = int(self._client.get("wireless", radio, "channel"))
+            if radio == "radio0":
+                i["frequency"] = WifiEnums.channel_2G_to_freq[c]
+            elif radio == "radio1":
+                i["frequency"] = WifiEnums.channel_5G_to_freq[c]
+            results.append(i)
+        return results
+
+    def get_radio_option(self, key, idx=0):
+        """Gets an option from the configured settings of a radio.
+
+        Params:
+            key: Name of the option to retrieve.
+            idx: Index of the radio to retrieve the option from. Default is 0.
+
+        Returns:
+            The value of the specified option and radio.
+        """
+        r = None
+        if idx == 0:
+            r = "radio0"
+        elif idx == 1:
+            r = "radio1"
+        return self._client.get("wireless", r, key)
+
+    def apply_wifi_changes(self):
+        """Applies committed wifi changes by restarting wifi.
+
+        Raises:
+            ServerError: Something funny happened restarting wifi on the AP.
+        """
+        s = self._client.commit('wireless')
+        resp = self.run('wifi')
+        return resp
+        # if resp != '' or not s:
+        #     raise ServerError(("Exception in refreshing wifi changes, commit"
+        #                        " status: ") + str(s) + ", wifi restart response: "
+        #                        + str(resp))
+
+    def set_wifi_channel(self, channel, device='radio0'):
+        self.set('wireless', device, 'channel', channel)
+
+    def _add_ifaces(self, configs):
+        """Adds wifi-ifaces in the AP's wireless config based on a list of
+        configuration dict.
+
+        Args:
+            configs: A list of dicts each representing a wifi-iface config.
+        """
+        for config in configs:
+            self._add_cfg_section('wireless', 'wifi-iface',
+                              config, self.IFACE_DEFAULTS)
+
+    def _add_cfg_section(self, cfg_name, section, options, defaults=None):
+        """Adds a section in a configuration file.
+
+        Args:
+            cfg_name: Name of the config file to add a section to.
+                e.g. 'wireless'.
+            section: Type of the secion to add. e.g. 'wifi-iface'.
+            options: A dict containing all key:value pairs of the options.
+                e.g. {'ssid': 'test', 'mode': 'ap'}
+
+        Raises:
+            ServerError: Uci add call returned False.
+        """
+        section_id = self._client.add(cfg_name, section)
+        if not section_id:
+            raise ServerError(' '.join(("Failed adding", section, "in",
+                              cfg_name)))
+        self._set_options(cfg_name, section_id, options, defaults)
+
+    def _set_options(self, cfg_name, section_id, options, defaults):
+        """Sets options in a section.
+
+        Args:
+            cfg_name: Name of the config file to add a section to.
+                e.g. 'wireless'.
+            section_id: ID of the secion to add options to. e.g. 'cfg000864'.
+            options: A dict containing all key:value pairs of the options.
+                e.g. {'ssid': 'test', 'mode': 'ap'}
+
+        Raises:
+            ServerError: Uci set call returned False.
+        """
+        # Fill the fields not defined in config with default values.
+        if defaults:
+            for k, v in defaults.items():
+                if k not in options:
+                    options[k] = v
+        # Set value pairs defined in config.
+        for k, v in options.items():
+            self._set_option(cfg_name, section_id, k, v)
+
+    def _set_option(self, cfg_name, section_id, k, v):
+        """Sets an option in a config section.
+
+        Args:
+            cfg_name: Name of the config file the section is in.
+                e.g. 'wireless'.
+            section_id: ID of the secion to set option in. e.g. 'cfg000864'.
+            k: Name of the option.
+            v: Value to set the option to.
+
+        Raises:
+            ServerError: If the rpc called returned False.
+        """
+        status = self._client.set(cfg_name, section_id, k, v)
+        if not status:
+            # Delete whatever was added.
+                raise ServerError(' '.join(("Failed adding option", str(k),
+                                  ':', str(d), "to", str(section_id))))
+
+    def delete_ifaces_by_ids(self, ids):
+        """Delete wifi-ifaces that are specified by the ids from the AP's
+        wireless config.
+
+        Args:
+            ids: A list of ids whose wifi-iface sections to be deleted.
+        """
+        for i in ids:
+            self._delete_cfg_section_by_id('wireless', i)
+
+    def delete_ifaces(self, key, value):
+        """Delete wifi-ifaces that contain the specified key:value pair.
+
+        Args:
+            key: Key of the pair.
+            value: Value of the pair.
+        """
+        self._delete_cfg_sections('wireless', key, value)
+
+    def _delete_cfg_sections(self, cfg_name, key, value):
+        """Deletes config sections that have the specified key:value pair.
+
+        Finds the ids of sections that match a key:value pair in the specified
+        config file and delete the section.
+
+        Args:
+            cfg_name: Name of the config file to delete sections from.
+                e.g. 'wireless'.
+            key: Name of the option to be matched.
+            value: Value of the option to be matched.
+
+        Raises:
+            ClientError: Could not find any section that has the key:value
+                pair.
+        """
+        section_ids = self.section_id_lookup(cfg_name, key, value)
+        if not section_ids:
+            raise ClientError(' '.join(("Could not find any section that has ",
+                              key, ":", value)))
+        for section_id in section_ids:
+            self._delete_cfg_section_by_id(cfg_name, section_id)
+
+    def _delete_cfg_section_by_id(self, cfg_name, section_id):
+        """Deletes the config section with specified id.
+
+        Args:
+            cfg_name: Name of the config file to the delete a section from.
+                e.g. 'wireless'.
+            section_id: ID of the section to be deleted. e.g. 'cfg0d3777'.
+
+        Raises:
+            ServerError: Uci delete call returned False.
+        """
+        self._client.delete(cfg_name, section_id)
+
+    def _get_iw_info(self):
+        results = []
+        text = self.run("iw dev").replace('\t', '')
+        interfaces = text.split("Interface")
+        for intf in interfaces:
+            if len(intf.strip()) < 6:
+                # This is a PHY mark.
+                continue
+            # This is an interface line.
+            intf = intf.replace(', ', '\n')
+            lines = intf.split('\n')
+            r = {}
+            for l in lines:
+                if ' ' in l:
+                    # Only the lines with space are processed.
+                    k, v = l.split(' ', 1)
+                    if k == "addr":
+                        k = "bssid"
+                    if "wlan" in v:
+                        k = "interface"
+                    if k == "channel":
+                        vs = v.split(' ', 1)
+                        v = int(vs[0])
+                        r["frequency"] = int(vs[1].split(' ', 1)[0][1:5])
+                    if k[-1] == ':':
+                        k = k[:-1]
+                    r[k] = v
+            results.append(r)
+        return results
+
+    def get_active_bssids_info(self, radio, *args):
+        wlan = None
+        if radio == "radio0":
+            wlan = "wlan0"
+        if radio == "radio1":
+            wlan = "wlan1"
+        infos = self._get_iw_info()
+        bssids = []
+        for i in infos:
+            if wlan in i["interface"]:
+                r = {}
+                for k,v in i.items():
+                    if k in args:
+                        r[k] = v
+                r["bssid"] = i["bssid"].upper()
+                bssids.append(r)
+        return bssids
+
+    def toggle_bssid_state(self, bssid):
+        if bssid == self.get_bssid("radio0"):
+            self.toggle_radio_state("radio0")
+            return True
+        elif bssid == self.get_bssid("radio1"):
+            self.toggle_radio_state("radio1")
+            return True
+        return False
+
+    def __getattr__(self, name):
+        return _LibCaller(self._client, name)
+
+class _LibCaller:
+    def __init__(self, client, *args):
+        self._client = client
+        self._args = args
+
+    def __getattr__(self, name):
+        return _LibCaller(self._client, *self._args+(name,))
+
+    def __call__(self, *args):
+        return self._client.call("/".join(self._args[:-1]),
+                                 self._args[-1],
+                                 *args)
diff --git a/acts/framework/acts/controllers/adb.py b/acts/framework/acts/controllers/adb.py
new file mode 100644
index 0000000..363ce19
--- /dev/null
+++ b/acts/framework/acts/controllers/adb.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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.utils import exe_cmd
+
+class AdbError(Exception):
+    """Raised when there is an error in adb operations."""
+
+SL4A_LAUNCH_CMD=("am start -a com.googlecode.android_scripting.action.LAUNCH_SERVER "
+    "-n com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher "
+    "--ei com.googlecode.android_scripting.extra.USE_SERVICE_PORT {}")
+
+def get_available_host_ports(num):
+    """Gets available host port numbers for adb forward.
+
+    Starting from 9999 and counting down, check if the port is already used by
+    adb forward. If so, continue to the next number.
+
+    Args:
+        num: Number of port numbers needed.
+
+    Returns:
+        A list of integers each representing a port number available for adb
+        forward.
+    """
+    used_ports = list_occupied_ports()
+    results = []
+    port = 9999
+    cnt = 0
+    while cnt < num and port > 1024:
+        if port not in used_ports:
+            results.append(port)
+            cnt += 1
+        port -= 1
+    return results
+
+def list_occupied_ports():
+    """Lists all the host ports occupied by adb forward.
+
+    Returns:
+        A list of integers representing occupied host ports.
+    """
+    out = AdbProxy().forward("--list")
+    clean_lines = str(out, 'utf-8').strip().split('\n')
+    used_ports = []
+    for line in clean_lines:
+        tokens = line.split(" tcp:")
+        if len(tokens) != 3:
+            continue
+        used_ports.append(int(tokens[1]))
+    return used_ports
+
+def is_port_availble(port):
+    """Checks if a port number on the host is available.
+
+    Args:
+        port: The host port number to check.
+
+    Returns:
+        True is this port is available for adb forward.
+    """
+    return port not in list_occupied_ports()
+
+class AdbProxy():
+    """Proxy class for ADB.
+
+    For syntactic reasons, the '-' in adb commands need to be replaced with
+    '_'. Can directly execute adb commands on an object:
+    >> adb = AdbProxy(<serial>)
+    >> adb.start_server()
+    >> adb.devices() # will return the console output of "adb devices".
+    """
+    def __init__(self, serial=""):
+        self.serial = serial
+        if serial:
+            self.adb_str = "adb -s {}".format(serial)
+        else:
+            self.adb_str = "adb"
+
+    def _exec_adb_cmd(self, name, arg_str):
+        return exe_cmd(' '.join((self.adb_str, name, arg_str)))
+
+    def tcp_forward(self, host_port, device_port):
+        """Starts tcp forwarding.
+
+        Args:
+            host_port: Port number to use on the computer.
+            device_port: Port number to use on the android device.
+        """
+        self.forward("tcp:{} tcp:{}".format(host_port, device_port))
+
+    def start_sl4a(self, port=8080):
+        """Starts sl4a server on the android device.
+
+        Args:
+            port: Port number to use on the android device.
+        """
+        self.shell(SL4A_LAUNCH_CMD.format(port))
+        # TODO(angli): Make is_sl4a_running reliable so we don't have to do a
+        # dumb wait.
+        time.sleep(3)
+        if not self.is_sl4a_running():
+            raise AdbError(
+              "com.googlecode.android_scripting process never started.")
+
+    def is_sl4a_running(self):
+        """Checks if the sl4a app is running on an android device.
+
+        Returns:
+            True if the sl4a app is running, False otherwise.
+        """
+        #Grep for process with a preceding S which means it is truly started.
+        out = self.shell('ps | grep "S com.googlecode.android_scripting"')
+        if len(out)==0:
+          return False
+        return True
+
+    def __getattr__(self, name):
+        def adb_call(*args):
+            clean_name = name.replace('_', '-')
+            arg_str = ' '.join(str(elem) for elem in args)
+            return self._exec_adb_cmd(clean_name, arg_str)
+        return adb_call
diff --git a/acts/framework/acts/controllers/android.py b/acts/framework/acts/controllers/android.py
new file mode 100644
index 0000000..bd67d1f
--- /dev/null
+++ b/acts/framework/acts/controllers/android.py
@@ -0,0 +1,107 @@
+# python3.4
+# Copyright (C) 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""
+JSON RPC interface to android scripting engine.
+"""
+
+import json
+import os
+import socket
+import threading
+
+HOST = os.environ.get('AP_HOST', None)
+PORT = os.environ.get('AP_PORT', 9999)
+
+class SL4AException(Exception):
+    pass
+
+class SL4AAPIError(SL4AException):
+    """Raised when remote API reports an error."""
+
+class SL4AProtocolError(SL4AException):
+    """Raised when there is some error in exchanging data with server on device."""
+    NO_RESPONSE_FROM_HANDSHAKE = "No response from handshake."
+    NO_RESPONSE_FROM_SERVER = "No response from server."
+    MISMATCHED_API_ID = "Mismatched API id."
+
+def IDCounter():
+    i = 0
+    while True:
+        yield i
+        i += 1
+
+class Android(object):
+    COUNTER = IDCounter()
+
+    def __init__(self, cmd='initiate', uid=-1, port=PORT, addr=HOST, timeout=None):
+        self.lock = threading.RLock()
+        self.client = None  # prevent close errors on connect failure
+        self.uid = None
+        try:
+            self.conn = socket.create_connection((addr, port), 60)
+            self.conn.settimeout(timeout)
+        except (TimeoutError, socket.timeout):
+            print("Failed to create socket connection!")
+            raise
+        self.client = self.conn.makefile(mode="brw")
+
+        resp = self._cmd(cmd, uid)
+        if not resp:
+            raise SL4AProtocolError(SL4AProtocolError.NO_RESPONSE_FROM_HANDSHAKE)
+        result = json.loads(str(resp, encoding="utf8"))
+        if result['status']:
+            self.uid = result['uid']
+        else:
+            self.uid = -1
+
+    def close(self):
+        if self.conn is not None:
+            self.conn.close()
+            self.conn = None
+
+    def _cmd(self, command, uid=None):
+        if not uid:
+            uid = self.uid
+        self.client.write(
+            json.dumps({'cmd': command, 'uid': uid})
+                .encode("utf8")+b'\n')
+        self.client.flush()
+        return self.client.readline()
+
+    def _rpc(self, method, *args):
+        self.lock.acquire()
+        apiid = next(Android.COUNTER)
+        self.lock.release()
+        data = {'id': apiid,
+                'method': method,
+                'params': args}
+        request = json.dumps(data)
+        self.client.write(request.encode("utf8")+b'\n')
+        self.client.flush()
+        response = self.client.readline()
+        if not response:
+            raise SL4AProtocolError(SL4AProtocolError.NO_RESPONSE_FROM_SERVER)
+        result = json.loads(str(response, encoding="utf8"))
+        if result['error']:
+            raise SL4AAPIError(result['error'])
+        if result['id'] != apiid:
+            raise SL4AProtocolError(SL4AProtocolError.MISMATCHED_API_ID)
+        return result['result']
+
+    def __getattr__(self, name):
+        def rpc_call(*args):
+            return self._rpc(name, *args)
+        return rpc_call
diff --git a/acts/framework/acts/controllers/android_device.py b/acts/framework/acts/controllers/android_device.py
new file mode 100644
index 0000000..4081c0e
--- /dev/null
+++ b/acts/framework/acts/controllers/android_device.py
@@ -0,0 +1,561 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import time
+import traceback
+
+import acts.controllers.android as android
+
+from acts.controllers.adb import AdbProxy
+from acts.controllers.adb import is_port_availble
+from acts.controllers.adb import get_available_host_ports
+from acts.controllers.fastboot import FastbootProxy
+from acts.event_dispatcher import EventDispatcher
+from acts.logger import LoggerProxy
+from acts.signals import ControllerError
+from acts.utils import exe_cmd
+
+def create(configs, logger):
+    if not configs:
+        ads = get_all_instances()
+    elif isinstance(configs[0], str):
+        # Configs is a list of serials.
+        ads = get_instances(configs, logger)
+    else:
+        # Configs is a list of dicts.
+        ads = get_instances_with_configs(configs, logger)
+    for ad in ads:
+        try:
+            ad.get_droid()
+            ad.ed.start()
+        except:
+            raise ControllerError("Failed to start sl4a on %s" % ad.serial)
+    return ads
+
+def destroy(ads):
+    for ad in ads:
+        try:
+            ad.terminate_all_sessions()
+        except:
+            pass
+
+class AndroidDeviceError(Exception):
+    pass
+
+class DoesNotExistError(AndroidDeviceError):
+    """Raised when something that does not exist is referenced.
+    """
+
+def _parse_device_list(device_list_str, key):
+    """Parses a byte string representing a list of devices. The string is
+    generated by calling either adb or fastboot.
+
+    Args:
+        device_list_str: Output of adb or fastboot.
+        key: The token that signifies a device in device_list_str.
+
+    Returns:
+        A list of android device serial numbers.
+    """
+    clean_lines = str(device_list_str, 'utf-8').strip().split('\n')
+    results = []
+    for line in clean_lines:
+        tokens = line.strip().split('\t')
+        if len(tokens) == 2 and tokens[1] == key:
+            results.append(tokens[0])
+    return results
+
+def list_adb_devices():
+    """List all android devices connected to the computer that are detected by
+    adb.
+
+    Returns:
+        A list of android device serials. Empty if there's none.
+    """
+    out = AdbProxy().devices()
+    return _parse_device_list(out, "device")
+
+def list_fastboot_devices():
+    """List all android devices connected to the computer that are in in
+    fastboot mode. These are detected by fastboot.
+
+    Returns:
+        A list of android device serials. Empty if there's none.
+    """
+    out = FastbootProxy().devices()
+    return _parse_device_list(out, "fastboot")
+
+def get_instances(serials, logger=None):
+    """Create AndroidDevice instances from a list of serials.
+
+    Args:
+        serials: A list of android device serials.
+        logger: A logger to be passed to each instance.
+
+    Returns:
+        A list of AndroidDevice objects.
+    """
+    results = []
+    for s in serials:
+        results.append(AndroidDevice(s, logger=logger))
+    return results
+
+def get_instances_with_configs(configs, logger=None):
+    """Create AndroidDevice instances from a list of json configs.
+
+    Each config should have the required key-value pair "serial".
+
+    Args:
+        configs: A list of dicts each representing the configuration of one
+            android device.
+        logger: A logger to be passed to each instance.
+
+    Returns:
+        A list of AndroidDevice objects.
+    """
+    results = []
+    for c in configs:
+        try:
+            serial = c.pop("serial")
+        except KeyError:
+            raise ControllerError(('Requried value "serial" is missing in '
+                'AndroidDevice config %s.') % c)
+        ad = AndroidDevice(serial, logger=logger)
+        ad.load_config(c)
+        results.append(ad)
+    return results
+
+def get_all_instances(include_fastboot=False, logger=None):
+    """Create AndroidDevice instances for all attached android devices.
+
+    Args:
+        include_fastboot: Whether to include devices in bootloader mode or not.
+        logger: A logger to be passed to each instance.
+
+    Returns:
+        A list of AndroidDevice objects each representing an android device
+        attached to the computer.
+    """
+    if include_fastboot:
+        serial_list = list_adb_devices() + list_fastboot_devices()
+        return get_instances(serial_list, logger=logger)
+    return get_instances(list_adb_devices(), logger=logger)
+
+def filter_devices(ads, func):
+    """Finds the AndroidDevice instances from a list that match certain
+    conditions.
+
+    Args:
+        ads: A list of AndroidDevice instances.
+        func: A function that takes an AndroidDevice object and returns True
+            if the device satisfies the filter condition.
+
+    Returns:
+        A list of AndroidDevice instances that satisfy the filter condition.
+    """
+    results = []
+    for ad in ads:
+        if func(ad):
+            results.append(ad)
+    return results
+
+def get_device(ads, **kwargs):
+    """Finds a unique AndroidDevice instance from a list that has specific
+    attributes of certain values.
+
+    Example:
+        get_device(android_devices, label="foo", phone_number="1234567890")
+        get_device(android_devices, model="angler")
+
+    Args:
+        ads: A list of AndroidDevice instances.
+        kwargs: keyword arguments used to filter AndroidDevice instances.
+
+    Returns:
+        The target AndroidDevice instance.
+
+    Raises:
+        AndroidDeviceError is raised if none or more than one device is
+        matched.
+    """
+    def _get_device_filter(ad):
+        for k, v in kwargs.items():
+            if not hasattr(ad, k):
+                return False
+            elif getattr(ad, k) != v:
+                return False
+        return True
+    filtered = filter_devices(ads, _get_device_filter)  
+    if not filtered:
+        raise AndroidDeviceError("No device matched")
+    elif len(filtered) == 1:
+        return filtered[0]
+    else:
+        serials = [ad.serial for ad in filtered]
+        raise AndroidDeviceError("More than one device matched: " % serials)
+
+class AndroidDevice:
+    """Class representing an android device.
+
+    Each object of this class represents one Android device in ACTS, including
+    handles to adb, fastboot, and sl4a clients. In addition to direct adb
+    commands, this object also uses adb port forwarding to talk to the Android
+    device.
+
+    Attributes:
+        serial: A string that's the serial number of the Androi device.
+        h_port: An integer that's the port number for adb port forwarding used
+            on the computer the Android device is connected
+        d_port: An integer  that's the port number used on the Android device
+            for adb port forwarding.
+        log: A LoggerProxy object used for the class's internal logging.
+        adb: An AdbProxy object used for interacting with the device via adb.
+        fastboot: A FastbootProxy object used for interacting with the device
+            via fastboot.
+    """
+
+    def __init__(self, serial="", host_port=None, device_port=8080,
+                 logger=None):
+        self.serial = serial
+        self.h_port = host_port
+        self.d_port = device_port
+        self.log = LoggerProxy(logger)
+        self._droid_sessions = {}
+        self._event_dispatchers = {}
+        self.adb = AdbProxy(serial)
+        self.fastboot = FastbootProxy(serial)
+        if not self.is_bootloader:
+            self.root_adb()
+
+    def __del__(self):
+        if self.h_port:
+            self.adb.forward("--remove tcp:%d" % self.h_port)
+
+    @property
+    def is_bootloader(self):
+        """True if the device is in bootloader mode.
+        """
+        return self.serial in list_fastboot_devices()
+
+    @property
+    def is_adb_root(self):
+        """True if adb is running as root for this device.
+        """
+        return "root" in self.adb.shell("id -u").decode("utf-8")
+
+    @property
+    def model(self):
+        """The Android code name for the device.
+        """
+        # If device is in bootloader mode, get mode name from fastboot.
+        if self.is_bootloader:
+            out = self.fastboot.getvar("product").strip()
+            # "out" is never empty because of the "total time" message fastboot
+            # writes to stderr.
+            lines = out.decode("utf-8").split('\n', 1)
+            if lines:
+                tokens = lines[0].split(' ')
+                if len(tokens) > 1:
+                    return tokens[1].lower()
+            return None
+        out = self.adb.shell('getprop | grep ro.build.product')
+        model = out.decode("utf-8").strip().split('[')[-1][:-1].lower()
+        if model == "sprout":
+            return model
+        else:
+            out = self.adb.shell('getprop | grep ro.product.name')
+            model = out.decode("utf-8").strip().split('[')[-1][:-1].lower()
+            return model
+
+    @property
+    def droid(self):
+        """The first sl4a session initiated on this device. None if there isn't
+        one.
+        """
+        try:
+            session_id = sorted(self._droid_sessions)[0]
+            return self._droid_sessions[session_id][0]
+        except IndexError:
+            return None
+
+    @property
+    def ed(self):
+        """The first event_dispatcher instance created on this device. None if
+        there isn't one.
+        """
+        try:
+            session_id = sorted(self._event_dispatchers)[0]
+            return self._event_dispatchers[session_id]
+        except IndexError:
+            return None
+
+    @property
+    def droids(self):
+        """A list of the active sl4a sessions on this device.
+
+        If multiple connections exist for the same session, only one connection
+        is listed.
+        """
+        keys = sorted(self._droid_sessions)
+        results = []
+        for k in keys:
+            results.append(self._droid_sessions[k][0])
+        return results
+
+    @property
+    def eds(self):
+        """A list of the event_dispatcher objects on this device.
+
+        The indexing of the list matches that of the droids property.
+        """
+        keys = sorted(self._event_dispatchers)
+        results = []
+        for k in keys:
+            results.append(self._event_dispatchers[k])
+        return results
+
+    def load_config(self, config):
+        """Add attributes to the AndroidDevice object based on json config.
+
+        Args:
+            config: A dictionary representing the configs.
+
+        Raises:
+            ControllerError is raised if the config is trying to overwrite an
+            existing attribute.
+        """
+        for k, v in config.items():
+            if hasattr(self, k):
+                raise ControllerError(("Attempting to set existing attribute "
+                    "%s on %s") % (k, self.serial))
+            setattr(self, k, v)
+
+    def root_adb(self):
+        """Change adb to root mode for this device.
+        """
+        if not self.is_adb_root:
+            self.adb.root()
+            self.adb.wait_for_device()
+            self.adb.remount()
+            self.adb.wait_for_device()
+
+    def get_droid(self, handle_event=True):
+        """Create an sl4a connection to the device.
+
+        Return the connection handler 'droid'. By default, another connection
+        on the same session is made for EventDispatcher, and the dispatcher is
+        returned to the caller as well.
+        If sl4a server is not started on the device, try to start it.
+
+        Args:
+            handle_event: True if this droid session will need to handle
+                events.
+
+        Returns:
+            droid: Android object used to communicate with sl4a on the android
+                device.
+            ed: An optional EventDispatcher to organize events for this droid.
+
+        Examples:
+            Don't need event handling:
+            >>> ad = AndroidDevice()
+            >>> droid = ad.get_droid(False)
+
+            Need event handling:
+            >>> ad = AndroidDevice()
+            >>> droid, ed = ad.get_droid()
+        """
+        if not self.h_port or not is_port_availble(self.h_port):
+            self.h_port = get_available_host_ports(1)[0]
+        self.adb.tcp_forward(self.h_port, self.d_port)
+        try:
+            droid = self.start_new_session()
+        except:
+            self.adb.start_sl4a()
+            droid = self.start_new_session()
+        if handle_event:
+            ed = self.get_dispatcher(droid)
+            return droid, ed
+        return droid
+
+    def get_dispatcher(self, droid):
+        """Return an EventDispatcher for an sl4a session
+
+        Args:
+            droid: Session to create EventDispatcher for.
+
+        Returns:
+            ed: An EventDispatcher for specified session.
+        """
+        ed_key = self.serial + str(droid.uid)
+        if ed_key in self._event_dispatchers:
+            if self._event_dispatchers[ed_key] is None:
+                raise AndroidDeviceError("EventDispatcher Key Empty")
+            self.log.debug(("Returning existing key %s for event dispatcher!"
+                ) % ed_key)
+            return self._event_dispatchers[ed_key]
+        event_droid = self.add_new_connection_to_session(droid.uid)
+        ed = EventDispatcher(event_droid)
+        self._event_dispatchers[ed_key] = ed
+        return ed
+
+    def start_new_session(self):
+        """Start a new session in sl4a.
+
+        Also caches the droid in a dict with its uid being the key.
+
+        Returns:
+            An Android object used to communicate with sl4a on the android
+                device.
+
+        Raises:
+            SL4AException: Something is wrong with sl4a and it returned an
+            existing uid to a new session.
+        """
+        droid = android.Android(port=self.h_port)
+        if droid.uid in self._droid_sessions:
+            raise android.SL4AException(("SL4A returned an existing uid for a "
+                "new session. Abort."))
+        self._droid_sessions[droid.uid] = [droid]
+        return droid
+
+    def add_new_connection_to_session(self, session_id):
+        """Create a new connection to an existing sl4a session.
+
+        Args:
+            session_id: UID of the sl4a session to add connection to.
+
+        Returns:
+            An Android object used to communicate with sl4a on the android
+                device.
+
+        Raises:
+            DoesNotExistError: Raised if the session it's trying to connect to
+            does not exist.
+        """
+        if session_id not in self._droid_sessions:
+            raise DoesNotExistError("Session %d doesn't exist." % session_id)
+        droid = android.Android(cmd='continue', uid=session_id,
+            port=self.h_port)
+        return droid
+
+    def terminate_session(self, session_id):
+        """Terminate a session in sl4a.
+
+        Send terminate signal to sl4a server; stop dispatcher associated with
+        the session. Clear corresponding droids and dispatchers from cache.
+
+        Args:
+            session_id: UID of the sl4a session to terminate.
+        """
+        if self._droid_sessions and (session_id in self._droid_sessions):
+            for droid in self._droid_sessions[session_id]:
+                droid.closeSl4aSession()
+                droid.close()
+            del self._droid_sessions[session_id]
+        ed_key = self.serial + str(session_id)
+        if ed_key in self._event_dispatchers:
+            self._event_dispatchers[ed_key].clean_up()
+            del self._event_dispatchers[ed_key]
+
+    def terminate_all_sessions(self):
+        """Terminate all sl4a sessions on the AndroidDevice instance.
+
+        Terminate all sessions and clear caches.
+        """
+        if self._droid_sessions:
+            session_ids = list(self._droid_sessions.keys())
+            for session_id in session_ids:
+                try:
+                    self.terminate_session(session_id)
+                except:
+                    msg = "Failed to terminate session %d." % session_id
+                    self.log.exception(msg)
+                    self.log.error(traceback.format_exc())
+            if self.h_port:
+                self.adb.forward("--remove tcp:%d" % self.h_port)
+                self.h_port = None
+
+    def run_iperf_client(self, server_host, extra_args=""):
+        """Start iperf client on the device.
+
+        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".
+
+        Returns:
+            status: true if iperf client start successfully.
+            results: results have data flow information
+        """
+        out = self.adb.shell("iperf3 -c {} {}".format(server_host, extra_args))
+        clean_out = str(out,'utf-8').strip().split('\n')
+        if "error" in clean_out[0].lower():
+            return False, clean_out
+        return True, clean_out
+
+    def wait_for_boot_completion(self):
+        """Waits for the device to boot up.
+
+        Returns:
+            True if the device successfully finished booting, False otherwise.
+        """
+        timeout = time.time() + 60*15 # wait for 15 minutes
+        while True:
+            out = self.adb.shell("getprop sys.boot_completed")
+            completed = out.decode('utf-8').strip()
+            if completed == '1':
+                return True
+            if time.time() > timeout:
+                return False
+            time.sleep(5)
+
+    def reboot(self):
+        """Reboots the device.
+
+        Terminate all sl4a sessions, reboot the device, wait for device to
+        complete booting, and restart an sl4a session.
+
+        This is a blocking method.
+
+        This is probably going to print some error messages in console. Only
+        use if there's no other option.
+
+        Example:
+            droid, ed = ad.reboot()
+
+        Returns:
+            An sl4a session with an event_dispatcher.
+
+        Raises:
+            AndroidDeviceError is raised if waiting for completion timed
+            out.
+        """
+        if self.is_bootloader:
+            self.fastboot.reboot()
+            return
+        self.terminate_all_sessions()
+        self.adb.reboot()
+        time.sleep(5)
+        if not self.wait_for_boot_completion():
+            raise AndroidDeviceError("Reboot timed out on %s." % self.serial)
+        self.root_adb()
+        droid, ed = self.get_droid()
+        ed.start()
+        return droid, ed
diff --git a/acts/framework/acts/controllers/attenuator.py b/acts/framework/acts/controllers/attenuator.py
new file mode 100644
index 0000000..7eede0b
--- /dev/null
+++ b/acts/framework/acts/controllers/attenuator.py
@@ -0,0 +1,373 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2015 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 importlib
+
+from acts.keys import Config
+
+def create(configs, logger):
+    objs = []
+    for c in configs:
+        attn_model = c["Model"]
+        # Default to telnet.
+        protocol = "telnet"
+        if "Protocol" in c:
+            protocol = c["Protocol"]
+        module_name = "acts.controllers.attenuator_lib.%s.%s" % (attn_model,
+            protocol)
+        module = importlib.import_module(module_name)
+        inst_cnt = c["InstrumentCount"]
+        attn_inst = module.AttenuatorInstrument(inst_cnt)
+        attn_inst.model = attn_model
+        insts = attn_inst.open(c[Config.key_address.value],
+            c[Config.key_port.value])
+        for i in range(inst_cnt):
+            attn = Attenuator(attn_inst, idx=i)
+            if "Paths" in c:
+                try:
+                    setattr(attn, "path", c["Paths"][i])
+                except IndexError:
+                    logger.error("No path specified for attenuator %d." % i)
+                    raise
+            objs.append(attn)
+    return objs
+
+def destroy(objs):
+    return
+
+r"""
+Base classes which define how attenuators should be accessed, managed, and manipulated.
+
+Users will instantiate a specific child class, but almost all operation should be performed
+on the methods and data members defined here in the base classes or the wrapper classes.
+"""
+
+
+class AttenuatorError(Exception):
+    r"""This is the Exception class defined for all errors generated by Attenuator-related modules.
+    """
+    pass
+
+
+class InvalidDataError(AttenuatorError):
+    r"""This exception is  thrown when an unexpected result is seen on the transport layer below
+    the module.
+
+    When this exception is seen, closing an re-opening the link to the attenuator instrument is
+    probably necessary. Something has gone wrong in the transport.
+    """
+    pass
+
+
+class InvalidOperationError(AttenuatorError):
+    r"""Certain methods may only be accessed when the instance upon which they are invoked is in
+    a certain state. This indicates that the object is not in the correct state for a method to be
+    called.
+    """
+    pass
+
+
+class AttenuatorInstrument():
+    r"""This is a base class that defines the primitive behavior of all attenuator
+    instruments.
+
+    The AttenuatorInstrument class is designed to provide a simple low-level interface for
+    accessing any step attenuator instrument comprised of one or more attenuators and a
+    controller. All AttenuatorInstruments should override all the methods below and call
+    AttenuatorInstrument.__init__ in their constructors. Outside of setup/teardown,
+    devices should be accessed via this generic "interface".
+    """
+    model = None
+    INVALID_MAX_ATTEN = 999.9
+
+    def __init__(self, num_atten=0):
+        r"""This is the Constructor for Attenuator Instrument.
+
+        Parameters
+        ----------
+        num_atten : This optional parameter is the number of attenuators contained within the
+        instrument. In some instances setting this number to zero will allow the driver to
+        auto-determine, the number of attenuators; however, this behavior is not guaranteed.
+
+        Raises
+        ------
+        NotImplementedError
+            This constructor should never be called directly. It may only be called by a child.
+
+        Returns
+        -------
+        self
+            Returns a newly constructed AttenuatorInstrument
+        """
+
+        if type(self) is AttenuatorInstrument:
+            raise NotImplementedError("Base class should not be instantiated directly!")
+
+        self.num_atten = num_atten
+        self.max_atten = AttenuatorInstrument.INVALID_MAX_ATTEN
+        self.properties = None
+
+    def set_atten(self, idx, value):
+        r"""This function sets the attenuation of an attenuator given its index in the instrument.
+
+        Parameters
+        ----------
+        idx : This zero-based index is the identifier for a particular attenuator in an
+        instrument.
+        value : This is a floating point value for nominal attenuation to be set.
+
+        Raises
+        ------
+        NotImplementedError
+            This constructor should never be called directly. It may only be called by a child.
+        """
+        raise NotImplementedError("Base class should not be called directly!")
+
+    def get_atten(self, idx):
+        r"""This function returns the current attenuation from an attenuator at a given index in
+        the instrument.
+
+        Parameters
+        ----------
+        idx : This zero-based index is the identifier for a particular attenuator in an instrument.
+
+        Raises
+        ------
+        NotImplementedError
+            This constructor should never be called directly. It may only be called by a child.
+
+        Returns
+        -------
+        float
+            Returns a the current attenuation value
+        """
+        raise NotImplementedError("Base class should not be called directly!")
+
+
+class Attenuator():
+    r"""This class defines an object representing a single attenuator in a remote instrument.
+
+    A user wishing to abstract the mapping of attenuators to physical instruments should use this
+    class, which provides an object that obscures the physical implementation an allows the user
+    to think only of attenuators regardless of their location.
+    """
+
+    def __init__(self, instrument, idx=0, offset=0):
+        r"""This is the constructor for Attenuator
+
+        Parameters
+        ----------
+        instrument : Reference to an AttenuatorInstrument on which the Attenuator resides
+        idx : This zero-based index is the identifier for a particular attenuator in an instrument.
+        offset : A power offset value for the attenuator to be used when performing future
+        operations. This could be used for either calibration or to allow group operations with
+        offsets between various attenuators.
+
+        Raises
+        ------
+        TypeError
+            Requires a valid AttenuatorInstrument to be passed in.
+        IndexError
+            The index of the attenuator in the AttenuatorInstrument must be within the valid range.
+
+        Returns
+        -------
+        self
+            Returns a newly constructed Attenuator
+        """
+        if not isinstance(instrument, AttenuatorInstrument):
+            raise TypeError("Must provide an Attenuator Instrument Ref")
+        self.model = instrument.model
+        self.instrument = instrument
+        self.idx = idx
+        self.offset = offset
+
+        if(self.idx >= instrument.num_atten):
+            raise IndexError("Attenuator index out of range for attenuator instrument")
+
+    def set_atten(self, value):
+        r"""This function sets the attenuation of Attenuator.
+
+        Parameters
+        ----------
+        value : This is a floating point value for nominal attenuation to be set.
+
+        Raises
+        ------
+        ValueError
+            The requested set value+offset must be less than the maximum value.
+        """
+
+        if value+self.offset > self.instrument.max_atten:
+            raise ValueError("Attenuator Value+Offset greater than Max Attenuation!")
+
+        self.instrument.set_atten(self.idx, value+self.offset)
+
+    def get_atten(self):
+        r"""This function returns the current attenuation setting of Attenuator, normalized by
+        the set offset.
+
+        Returns
+        -------
+        float
+            Returns a the current attenuation value
+        """
+
+        return self.instrument.get_atten(self.idx) - self.offset
+
+    def get_max_atten(self):
+        r"""This function returns the max attenuation setting of Attenuator, normalized by
+        the set offset.
+
+        Returns
+        -------
+        float
+            Returns a the max attenuation value
+        """
+        if (self.instrument.max_atten == AttenuatorInstrument.INVALID_MAX_ATTEN):
+            raise ValueError("Invalid Max Attenuator Value")
+
+        return self.instrument.max_atten - self.offset
+
+
+class AttenuatorGroup(object):
+    r"""This is a handy abstraction for groups of attenuators that will share behavior.
+
+    Attenuator groups are intended to further facilitate abstraction of testing functions from
+    the physical objects underlying them. By adding attenuators to a group, it is possible to
+    operate on functional groups that can be thought of in a common manner in the test. This
+    class is intended to provide convenience to the user and avoid re-implementation of helper
+    functions and small loops scattered throughout user code.
+
+    """
+
+    def __init__(self, name=""):
+        r"""This is the constructor for AttenuatorGroup
+
+        Parameters
+        ----------
+        name : The name is an optional parameter intended to further facilitate the passing of
+        easily tracked groups of attenuators throughout code. It is left to the user to use the
+        name in a way that meets their needs.
+
+        Returns
+        -------
+        self
+            Returns a newly constructed AttenuatorGroup
+        """
+        self.name = name
+        self.attens = []
+        self._value = 0
+
+    def add_from_instrument(self, instrument, indices):
+        r"""This function provides a way to create groups directly from the Attenuator Instrument.
+
+        This function will create Attenuator objects for all of the indices passed in and add
+        them to the group.
+
+        Parameters
+        ----------
+        instrument : A ref to the instrument from which attenuators will be added
+        indices : You pay pass in the indices either as a range, a list, or a single integer.
+
+        Raises
+        ------
+        TypeError
+            Requires a valid AttenuatorInstrument to be passed in.
+        """
+
+        if not instrument or not isinstance(instrument, AttenuatorInstrument):
+            raise TypeError("Must provide an Attenuator Instrument Ref")
+
+        if type(indices) is range or type(indices) is list:
+            for i in indices:
+                self.attens.append(Attenuator(instrument, i))
+        elif type(indices) is int:
+            self.attens.append(Attenuator(instrument, indices))
+
+    def add(self, attenuator):
+        r"""This function adds an already constructed Attenuator object to the AttenuatorGroup.
+
+        Parameters
+        ----------
+        attenuator : An Attenuator object.
+
+        Raises
+        ------
+        TypeError
+            Requires a valid Attenuator to be passed in.
+        """
+
+        if not isinstance(attenuator, Attenuator):
+            raise TypeError("Must provide an Attenuator")
+
+        self.attens.append(attenuator)
+
+    def synchronize(self):
+        r"""This function can be called to ensure all Attenuators within a group are set
+        appropriately.
+        """
+
+        self.set_atten(self._value)
+
+    def is_synchronized(self):
+        r"""This function queries all the Attenuators in the group to determine whether or not
+        they are synchronized.
+
+        Returns
+        -------
+        bool
+            True if the attenuators are synchronized.
+        """
+
+        for att in self.attens:
+            if att.get_atten() != self._value:
+                return False
+        return True
+
+    def set_atten(self, value):
+        r"""This function sets the attenuation value of all attenuators in the group.
+
+        Parameters
+        ----------
+        value : This is a floating point value for nominal attenuation to be set.
+
+        Returns
+        -------
+        bool
+            True if the attenuators are synchronized.
+        """
+
+        value = float(value)
+        for att in self.attens:
+            att.set_atten(value)
+        self._value = value
+
+    def get_atten(self):
+        r"""This function returns the current attenuation setting of AttenuatorGroup.
+
+        This returns a cached value that assumes the attenuators are synchronized. It avoids a
+        relatively expensive call for a common operation, and trusts the user to ensure
+        synchronization.
+
+        Returns
+        -------
+        float
+            Returns a the current attenuation value for the group, which is independent of any
+            individual attenuator offsets.
+        """
+
+        return float(self._value)
diff --git a/acts/framework/acts/controllers/attenuator_lib/__init__.py b/acts/framework/acts/controllers/attenuator_lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/attenuator_lib/__init__.py
diff --git a/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py b/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py
new file mode 100644
index 0000000..fdb54e6
--- /dev/null
+++ b/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python3.4
+
+#   Copyright 2014- The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""
+Helper module for common telnet capability to communicate with AttenuatorInstrument(s).
+
+User code shouldn't need to directly access this class.
+"""
+
+
+import telnetlib
+from acts.controllers import attenuator
+
+
+def _ascii_string(uc_string):
+    return str(uc_string).encode('ASCII')
+
+
+class _TNHelper():
+    #This is an internal helper class for Telnet+SCPI command-based instruments.
+    #It should only be used by those implemention control libraries and not by any user code
+    # directly
+
+    def __init__(self, tx_cmd_separator="\n", rx_cmd_separator="\n", prompt=""):
+        self._tn = None
+
+        self.tx_cmd_separator = tx_cmd_separator
+        self.rx_cmd_separator = rx_cmd_separator
+        self.prompt = prompt
+
+    def open(self, host, port=23):
+        if self._tn:
+            self._tn.close()
+
+        self._tn = telnetlib.Telnet()
+        self._tn.open(host, port, 10)
+
+    def is_open(self):
+        return bool(self._tn)
+
+    def close(self):
+        if self._tn:
+            self._tn.close()
+            self._tn = None
+
+    def cmd(self, cmd_str, wait_ret=True):
+        if not isinstance(cmd_str, str):
+            raise TypeError("Invalid command string", cmd_str)
+
+        if not self.is_open():
+            raise attenuator.InvalidOperationError("Telnet connection not open for commands")
+
+        cmd_str.strip(self.tx_cmd_separator)
+        self._tn.read_until(_ascii_string(self.prompt), 2)
+        self._tn.write(_ascii_string(cmd_str+self.tx_cmd_separator))
+
+        if wait_ret is False:
+            return None
+
+        match_idx, match_val, ret_text = \
+            self._tn.expect([_ascii_string("\S+"+self.rx_cmd_separator)], 1)
+
+        if match_idx == -1:
+            raise attenuator.InvalidDataError("Telnet command failed to return valid data")
+
+        ret_text = ret_text.decode()
+        ret_text = ret_text.strip(self.tx_cmd_separator + self.rx_cmd_separator + self.prompt)
+
+        return ret_text
diff --git a/acts/framework/acts/controllers/attenuator_lib/aeroflex/__init__.py b/acts/framework/acts/controllers/attenuator_lib/aeroflex/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/attenuator_lib/aeroflex/__init__.py
diff --git a/acts/framework/acts/controllers/attenuator_lib/aeroflex/telnet.py b/acts/framework/acts/controllers/attenuator_lib/aeroflex/telnet.py
new file mode 100644
index 0000000..5a68f63
--- /dev/null
+++ b/acts/framework/acts/controllers/attenuator_lib/aeroflex/telnet.py
@@ -0,0 +1,150 @@
+#!/usr/bin/python3.4
+
+#   Copyright 2014- The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""
+Class for Telnet control of Aeroflex 832X and 833X Series Attenuator Modules
+
+This class provides a wrapper to the Aeroflex attenuator modules for purposes
+of simplifying and abstracting control down to the basic necessities. It is
+not the intention of the module to expose all functionality, but to allow
+interchangeable HW to be used.
+
+See http://www.aeroflex.com/ams/weinschel/PDFILES/IM-608-Models-8320-&-8321-preliminary.pdf
+"""
+
+
+from acts.controllers import attenuator
+from acts.controllers.attenuator_lib import _tnhelper
+
+
+class AttenuatorInstrument(attenuator.AttenuatorInstrument):
+
+    def __init__(self, num_atten=0):
+        super().__init__(num_atten)
+
+        self._tnhelper = _tnhelper._TNHelper(tx_cmd_separator="\r\n",
+                                             rx_cmd_separator="\r\n",
+                                             prompt=">")
+        self.properties = None
+
+    def open(self, host, port=23):
+        r"""Opens a telnet connection to the desired AttenuatorInstrument and queries basic
+        information.
+
+        Parameters
+        ----------
+        host : A valid hostname (IP address or DNS-resolvable name) to an MC-DAT attenuator
+        instrument.
+        port : An optional port number (defaults to telnet default 23)
+        """
+        self._tnhelper.open(host, port)
+
+        # work around a bug in IO, but this is a good thing to do anyway
+        self._tnhelper.cmd("*CLS", False)
+
+        if self.num_atten == 0:
+            self.num_atten = int(self._tnhelper.cmd("RFCONFIG? CHAN"))
+
+        configstr = self._tnhelper.cmd("RFCONFIG? ATTN 1")
+
+        self.properties = dict(zip(['model', 'max_atten', 'min_step',
+                                    'unknown', 'unknown2', 'cfg_str'],
+                                   configstr.split(", ", 5)))
+
+        self.max_atten = float(self.properties['max_atten'])
+
+    def is_open(self):
+        r"""This function returns the state of the telnet connection to the underlying
+        AttenuatorInstrument.
+
+        Returns
+        -------
+        Bool
+            True if there is a successfully open connection to the AttenuatorInstrument
+        """
+
+        return bool(self._tnhelper.is_open())
+
+    def close(self):
+        r"""Closes a telnet connection to the desired AttenuatorInstrument.
+
+        This should be called as part of any teardown procedure prior to the attenuator
+        instrument leaving scope.
+        """
+
+        self._tnhelper.close()
+
+    def set_atten(self, idx, value):
+        r"""This function sets the attenuation of an attenuator given its index in the instrument.
+
+        Parameters
+        ----------
+        idx : This zero-based index is the identifier for a particular attenuator in an
+        instrument.
+        value : This is a floating point value for nominal attenuation to be set.
+
+        Raises
+        ------
+        InvalidOperationError
+            This error occurs if the underlying telnet connection to the instrument is not open.
+        IndexError
+            If the index of the attenuator is greater than the maximum index of the underlying
+            instrument, this error will be thrown. Do not count on this check programmatically.
+        ValueError
+            If the requested set value is greater than the maximum attenuation value, this error
+            will be thrown. Do not count on this check programmatically.
+        """
+
+
+        if not self.is_open():
+            raise attenuator.InvalidOperationError("Connection not open!")
+
+        if idx >= self.num_atten:
+            raise IndexError("Attenuator index out of range!", self.num_atten, idx)
+
+        if value > self.max_atten:
+            raise ValueError("Attenuator value out of range!", self.max_atten, value)
+
+        self._tnhelper.cmd("ATTN " + str(idx+1) + " " + str(value), False)
+
+    def get_atten(self, idx):
+        r"""This function returns the current attenuation from an attenuator at a given index in
+        the instrument.
+
+        Parameters
+        ----------
+        idx : This zero-based index is the identifier for a particular attenuator in an instrument.
+
+        Raises
+        ------
+        InvalidOperationError
+            This error occurs if the underlying telnet connection to the instrument is not open.
+
+        Returns
+        -------
+        float
+            Returns a the current attenuation value
+        """
+        if not self.is_open():
+            raise attenuator.InvalidOperationError("Connection not open!")
+
+#       Potentially redundant safety check removed for the moment
+#       if idx >= self.num_atten:
+#           raise IndexError("Attenuator index out of range!", self.num_atten, idx)
+
+        atten_val = self._tnhelper.cmd("ATTN? " + str(idx+1))
+
+        return float(atten_val)
diff --git a/acts/framework/acts/controllers/attenuator_lib/minicircuits/__init__.py b/acts/framework/acts/controllers/attenuator_lib/minicircuits/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/attenuator_lib/minicircuits/__init__.py
diff --git a/acts/framework/acts/controllers/attenuator_lib/minicircuits/telnet.py b/acts/framework/acts/controllers/attenuator_lib/minicircuits/telnet.py
new file mode 100644
index 0000000..25bd347
--- /dev/null
+++ b/acts/framework/acts/controllers/attenuator_lib/minicircuits/telnet.py
@@ -0,0 +1,157 @@
+#!/usr/bin/python3.4
+
+#   Copyright 2014- The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""
+Class for Telnet control of Mini-Circuits RCDAT series attenuators
+
+This class provides a wrapper to the MC-RCDAT attenuator modules for purposes
+of simplifying and abstracting control down to the basic necessities. It is
+not the intention of the module to expose all functionality, but to allow
+interchangeable HW to be used.
+
+See http://www.minicircuits.com/softwaredownload/Prog_Manual-6-Programmable_Attenuator.pdf
+"""
+
+
+from acts.controllers import attenuator
+from acts.controllers.attenuator_lib import _tnhelper
+
+
+class AttenuatorInstrument(attenuator.AttenuatorInstrument):
+    r"""This provides a specific telnet-controlled implementation of AttenuatorInstrument for
+    Mini-Circuits RC-DAT attenuators.
+
+    With the exception of telnet-specific commands, all functionality is defined by the
+    AttenuatorInstrument class. Because telnet is a stateful protocol, the functionality of
+    AttenuatorInstrument is contingent upon a telnet connection being established.
+    """
+
+    def __init__(self, num_atten=0):
+        super().__init__(num_atten)
+        self._tnhelper = _tnhelper._TNHelper(tx_cmd_separator="\r\n",
+                                             rx_cmd_separator="\n\r",
+                                             prompt="")
+
+    def __del__(self):
+        if self.is_open():
+            self.close()
+
+    def open(self, host, port=23):
+        r"""Opens a telnet connection to the desired AttenuatorInstrument and queries basic
+        information.
+
+        Parameters
+        ----------
+        host : A valid hostname (IP address or DNS-resolvable name) to an MC-DAT attenuator
+        instrument.
+        port : An optional port number (defaults to telnet default 23)
+        """
+
+        self._tnhelper.open(host, port)
+
+        if self.num_atten == 0:
+            self.num_atten = 1
+
+        config_str = self._tnhelper.cmd("MN?")
+
+        if config_str.startswith("MN="):
+            config_str = config_str[len("MN="):]
+
+        self.properties = dict(zip(['model', 'max_freq', 'max_atten'], config_str.split("-", 2)))
+        self.max_atten = float(self.properties['max_atten'])
+
+    def is_open(self):
+        r"""This function returns the state of the telnet connection to the underlying
+        AttenuatorInstrument.
+
+        Returns
+        -------
+        Bool
+            True if there is a successfully open connection to the AttenuatorInstrument
+        """
+
+        return bool(self._tnhelper.is_open())
+
+    def close(self):
+        r"""Closes a telnet connection to the desired AttenuatorInstrument.
+
+        This should be called as part of any teardown procedure prior to the attenuator
+        instrument leaving scope.
+        """
+
+        self._tnhelper.close()
+
+    def set_atten(self, idx, value):
+        r"""This function sets the attenuation of an attenuator given its index in the instrument.
+
+        Parameters
+        ----------
+        idx : This zero-based index is the identifier for a particular attenuator in an
+        instrument.
+        value : This is a floating point value for nominal attenuation to be set.
+
+        Raises
+        ------
+        InvalidOperationError
+            This error occurs if the underlying telnet connection to the instrument is not open.
+        IndexError
+            If the index of the attenuator is greater than the maximum index of the underlying
+            instrument, this error will be thrown. Do not count on this check programmatically.
+        ValueError
+            If the requested set value is greater than the maximum attenuation value, this error
+            will be thrown. Do not count on this check programmatically.
+        """
+
+        if not self.is_open():
+            raise attenuator.InvalidOperationError("Connection not open!")
+
+        if idx >= self.num_atten:
+            raise IndexError("Attenuator index out of range!", self.num_atten, idx)
+
+        if value > self.max_atten:
+            raise ValueError("Attenuator value out of range!", self.max_atten, value)
+
+        self._tnhelper.cmd("SETATT=" + str(value))
+
+    def get_atten(self, idx):
+        r"""This function returns the current attenuation from an attenuator at a given index in
+        the instrument.
+
+        Parameters
+        ----------
+        idx : This zero-based index is the identifier for a particular attenuator in an instrument.
+
+        Raises
+        ------
+        InvalidOperationError
+            This error occurs if the underlying telnet connection to the instrument is not open.
+
+        Returns
+        -------
+        float
+            Returns a the current attenuation value
+        """
+
+        if not self.is_open():
+            raise attenuator.InvalidOperationError("Connection not open!")
+
+#       Potentially redundant safety check removed for the moment
+#       if idx >= self.num_atten:
+#           raise IndexError("Attenuator index out of range!", self.num_atten, idx)
+
+        atten_val = self._tnhelper.cmd("ATT?")
+
+        return float(atten_val)
diff --git a/acts/framework/acts/controllers/fastboot.py b/acts/framework/acts/controllers/fastboot.py
new file mode 100644
index 0000000..a54301e
--- /dev/null
+++ b/acts/framework/acts/controllers/fastboot.py
@@ -0,0 +1,70 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 subprocess import Popen, PIPE
+
+def exe_cmd(*cmds):
+    """Executes commands in a new shell. Directing stderr to PIPE.
+
+    This is fastboot's own exe_cmd because of its peculiar way of writing
+    non-error info to stderr.
+
+    Args:
+        cmds: A sequence of commands and arguments.
+
+    Returns:
+        The output of the command run.
+
+    Raises:
+        Exception is raised if an error occurred during the command execution.
+    """
+    cmd = ' '.join(cmds)
+    proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True)
+    (out, err) = proc.communicate()
+    if not err:
+        return out
+    return err
+
+class FastbootError(Exception):
+    """Raised when there is an error in fastboot operations."""
+
+class FastbootProxy():
+    """Proxy class for fastboot.
+
+    For syntactic reasons, the '-' in fastboot commands need to be replaced
+    with '_'. Can directly execute fastboot commands on an object:
+    >> fb = FastbootProxy(<serial>)
+    >> fb.devices() # will return the console output of "fastboot devices".
+    """
+    def __init__(self, serial=""):
+        self.serial = serial
+        if serial:
+            self.fastboot_str = "fastboot -s {}".format(serial)
+        else:
+            self.fastboot_str = "fastboot"
+
+    def _exec_fastboot_cmd(self, name, arg_str):
+        return exe_cmd(' '.join((self.fastboot_str, name, arg_str)))
+
+    def args(self, *args):
+        return exe_cmd(' '.join((self.fastboot_str,) + args))
+
+    def __getattr__(self, name):
+        def fastboot_call(*args):
+            clean_name = name.replace('_', '-')
+            arg_str = ' '.join(str(elem) for elem in args)
+            return self._exec_fastboot_cmd(clean_name, arg_str)
+        return fastboot_call
diff --git a/acts/framework/acts/controllers/iperf_server.py b/acts/framework/acts/controllers/iperf_server.py
new file mode 100644
index 0000000..97afd88
--- /dev/null
+++ b/acts/framework/acts/controllers/iperf_server.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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
+from signal import SIGTERM
+import subprocess
+
+from acts.utils import create_dir
+from acts.utils import start_standing_subprocess
+from acts.utils import stop_standing_subprocess
+
+def create(configs, logger):
+    log_path = os.path.dirname(logger.handlers[1].baseFilename)
+    results = []
+    for c in configs:
+        try:
+            results.append(IPerfServer(c, log_path))
+        except:
+            pass
+    return results
+
+def destroy(objs):
+    for ipf in objs:
+        try:
+            ipf.stop()
+        except:
+            pass
+
+class IPerfServer():
+    """Class that handles iperf3 operations.
+    """
+    def __init__(self, port, log_path):
+        self.port = port
+        self.log_path = os.path.join(os.path.expanduser(log_path), "iPerf")
+        self.iperf_str = "iperf3 -s -p {}".format(port)
+        self.iperf_process = None
+        self.exec_count = 0
+        self.started = False
+
+    def start(self, extra_args="", tag=""):
+        """Starts iperf server on specified port.
+
+        Args:
+            extra_args: A string representing extra arguments to start iperf
+                server with.
+            tag: Appended to log file name to identify logs from different
+                iperf runs.
+        """
+        if self.started:
+            return
+        create_dir(self.log_path)
+        self.exec_count += 1
+        if tag:
+            tag = tag + ','
+        out_file_name = "IPerfServer,{},{}{}.log".format(self.port, tag,
+            self.exec_count)
+        full_out_path = os.path.join(self.log_path, out_file_name)
+        cmd = "{} {} > {}".format(self.iperf_str, extra_args, full_out_path)
+        self.iperf_process = start_standing_subprocess(cmd)
+        self.started = True
+
+    def stop(self):
+        if self.started:
+            stop_standing_subprocess(self.iperf_process)
+            self.started = False
diff --git a/acts/framework/acts/controllers/monsoon.py b/acts/framework/acts/controllers/monsoon.py
new file mode 100644
index 0000000..617556f
--- /dev/null
+++ b/acts/framework/acts/controllers/monsoon.py
@@ -0,0 +1,815 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2015 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""Interface for a USB-connected Monsoon power meter
+(http://msoon.com/LabEquipment/PowerMonitor/).
+"""
+
+_new_author_ = 'angli@google.com (Ang Li)'
+_author_ = 'kens@google.com (Ken Shirriff)'
+
+import fcntl
+import os
+import select
+import struct
+import sys
+import time
+import traceback
+import collections
+
+# http://pyserial.sourceforge.net/
+# On ubuntu, apt-get install python3-pyserial
+import serial
+
+from acts.logger import LoggerProxy
+from acts.controllers.android_device import list_adb_devices
+from acts.utils import timeout
+
+def create(configs, logger):
+    objs = []
+    for c in configs:
+        objs.append(Monsoon(serial=c, logger=logger))
+    return objs
+
+def destroy(objs):
+    return
+
+class MonsoonError(Exception):
+    """Raised for exceptions encountered in monsoon lib."""
+
+class MonsoonProxy:
+    """Class that directly talks to monsoon over serial.
+
+    Provides a simple class to use the power meter, e.g.
+    mon = monsoon.Monsoon()
+    mon.SetVoltage(3.7)
+    mon.StartDataCollection()
+    mydata = []
+    while len(mydata) < 1000:
+        mydata.extend(mon.CollectData())
+    mon.StopDataCollection()
+
+    See http://wiki/Main/MonsoonProtocol for information on the protocol.
+    """
+
+    def __init__(self, device=None, serialno=None, wait=1):
+        """Establish a connection to a Monsoon.
+
+        By default, opens the first available port, waiting if none are ready.
+        A particular port can be specified with "device", or a particular
+        Monsoon can be specified with "serialno" (using the number printed on
+        its back). With wait=0, IOError is thrown if a device is not
+        immediately available.
+        """
+        self._coarse_ref = self._fine_ref = self._coarse_zero = 0
+        self._fine_zero = self._coarse_scale = self._fine_scale = 0
+        self._last_seq = 0
+        self.start_voltage = 0
+
+        if device:
+            self.ser = serial.Serial(device, timeout=1)
+            return
+        # Try all devices connected through USB virtual serial ports until we
+        # find one we can use.
+        while True:
+            for dev in os.listdir("/dev"):
+                prefix = "ttyACM"
+                # Prefix is different on Mac OS X.
+                if sys.platform == "darwin":
+                    prefix = "tty.usbmodem"
+                if not dev.startswith(prefix):
+                    continue
+                tmpname = "/tmp/monsoon.%s.%s" % (os.uname()[0], dev)
+                self._tempfile = open(tmpname, "w")
+                try:
+                    os.chmod(tmpname, 0o666)
+                except OSError as e:
+                    pass
+
+                try:  # use a lockfile to ensure exclusive access
+                    fcntl.lockf(self._tempfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
+                except IOError as e:
+                    # TODO(angli): get rid of all print statements.
+                    print("device %s is in use" % dev, file=sys.stderr)
+                    continue
+
+                try:  # try to open the device
+                    self.ser = serial.Serial("/dev/%s" % dev, timeout=1)
+                    self.StopDataCollection()  # just in case
+                    self._FlushInput()  # discard stale input
+                    status = self.GetStatus()
+                except Exception as e:
+                    print("error opening device %s: %s" % (dev, e),
+                        file=sys.stderr)
+                    print(traceback.format_exc())
+                    continue
+
+                if not status:
+                    print("no response from device %s" % dev, file=sys.stderr)
+                elif serialno and status["serialNumber"] != serialno:
+                    print(("Note: another device serial #%d seen on %s" %
+                        (status["serialNumber"], dev)), file=sys.stderr)
+                else:
+                    self.start_voltage = status["voltage1"]
+                    return
+
+            self._tempfile = None
+            if not wait: raise IOError("No device found")
+            print("Waiting for device...", file=sys.stderr)
+            time.sleep(1)
+
+    def GetStatus(self):
+        """Requests and waits for status.
+
+        Returns:
+            status dictionary.
+        """
+        # status packet format
+        STATUS_FORMAT = ">BBBhhhHhhhHBBBxBbHBHHHHBbbHHBBBbbbbbbbbbBH"
+        STATUS_FIELDS = [
+                "packetType", "firmwareVersion", "protocolVersion",
+                "mainFineCurrent", "usbFineCurrent", "auxFineCurrent",
+                "voltage1", "mainCoarseCurrent", "usbCoarseCurrent",
+                "auxCoarseCurrent", "voltage2", "outputVoltageSetting",
+                "temperature", "status", "leds", "mainFineResistor",
+                "serialNumber", "sampleRate", "dacCalLow", "dacCalHigh",
+                "powerUpCurrentLimit", "runTimeCurrentLimit", "powerUpTime",
+                "usbFineResistor", "auxFineResistor",
+                "initialUsbVoltage", "initialAuxVoltage",
+                "hardwareRevision", "temperatureLimit", "usbPassthroughMode",
+                "mainCoarseResistor", "usbCoarseResistor", "auxCoarseResistor",
+                "defMainFineResistor", "defUsbFineResistor",
+                "defAuxFineResistor", "defMainCoarseResistor",
+                "defUsbCoarseResistor", "defAuxCoarseResistor", "eventCode",
+                "eventData", ]
+
+        self._SendStruct("BBB", 0x01, 0x00, 0x00)
+        while 1:  # Keep reading, discarding non-status packets
+            read_bytes = self._ReadPacket()
+            if not read_bytes:
+                return None
+            calsize = struct.calcsize(STATUS_FORMAT)
+            if len(read_bytes) != calsize or read_bytes[0] != 0x10:
+                print("Wanted status, dropped type=0x%02x, len=%d" % (
+                    read_bytes[0], len(read_bytes)), file=sys.stderr)
+                continue
+            status = dict(zip(STATUS_FIELDS, struct.unpack(STATUS_FORMAT,
+                read_bytes)))
+            assert status["packetType"] == 0x10
+            for k in status.keys():
+                if k.endswith("VoltageSetting"):
+                    status[k] = 2.0 + status[k] * 0.01
+                elif k.endswith("FineCurrent"):
+                    pass # needs calibration data
+                elif k.endswith("CoarseCurrent"):
+                    pass # needs calibration data
+                elif k.startswith("voltage") or k.endswith("Voltage"):
+                    status[k] = status[k] * 0.000125
+                elif k.endswith("Resistor"):
+                    status[k] = 0.05 + status[k] * 0.0001
+                    if k.startswith("aux") or k.startswith("defAux"):
+                        status[k] += 0.05
+                elif k.endswith("CurrentLimit"):
+                    status[k] = 8 * (1023 - status[k]) / 1023.0
+            return status
+
+    def RampVoltage(self, start, end):
+        v = start
+        if v < 3.0: v = 3.0 # protocol doesn't support lower than this
+        while (v < end):
+            self.SetVoltage(v)
+            v += .1
+            time.sleep(.1)
+        self.SetVoltage(end)
+
+    def SetVoltage(self, v):
+        """Set the output voltage, 0 to disable.
+        """
+        if v == 0:
+            self._SendStruct("BBB", 0x01, 0x01, 0x00)
+        else:
+            self._SendStruct("BBB", 0x01, 0x01, int((v - 2.0) * 100))
+
+    def GetVoltage(self):
+        """Get the output voltage.
+
+        Returns:
+            Current Output Voltage (in unit of v).
+        """
+        return self.GetStatus()["outputVoltageSetting"]
+
+    def SetMaxCurrent(self, i):
+        """Set the max output current.
+        """
+        assert i >= 0 and i <= 8
+        val = 1023 - int((i/8)*1023)
+        self._SendStruct("BBB", 0x01, 0x0a, val & 0xff)
+        self._SendStruct("BBB", 0x01, 0x0b, val >> 8)
+
+    def SetMaxPowerUpCurrent(self, i):
+        """Set the max power up current.
+        """
+        assert i >= 0 and i <= 8
+        val = 1023 - int((i/8)*1023)
+        self._SendStruct("BBB", 0x01, 0x08, val & 0xff)
+        self._SendStruct("BBB", 0x01, 0x09, val >> 8)
+
+    def SetUsbPassthrough(self, val):
+        """Set the USB passthrough mode: 0 = off, 1 = on,  2 = auto.
+        """
+        self._SendStruct("BBB", 0x01, 0x10, val)
+
+    def GetUsbPassthrough(self):
+        """Get the USB passthrough mode: 0 = off, 1 = on,  2 = auto.
+
+        Returns:
+            Current USB passthrough mode.
+        """
+        return self.GetStatus()["usbPassthroughMode"]
+
+    def StartDataCollection(self):
+        """Tell the device to start collecting and sending measurement data.
+        """
+        self._SendStruct("BBB", 0x01, 0x1b, 0x01) # Mystery command
+        self._SendStruct("BBBBBBB", 0x02, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8)
+
+    def StopDataCollection(self):
+        """Tell the device to stop collecting measurement data.
+        """
+        self._SendStruct("BB", 0x03, 0x00) # stop
+
+    def CollectData(self):
+        """Return some current samples. Call StartDataCollection() first.
+        """
+        while 1:  # loop until we get data or a timeout
+            _bytes = self._ReadPacket()
+            if not _bytes:
+                return None
+            if len(_bytes) < 4 + 8 + 1 or _bytes[0] < 0x20 or _bytes[0] > 0x2F:
+                print("Wanted data, dropped type=0x%02x, len=%d" % (
+                    _bytes[0], len(_bytes)), file=sys.stderr)
+                continue
+
+            seq, _type, x, y = struct.unpack("BBBB", _bytes[:4])
+            data = [struct.unpack(">hhhh", _bytes[x:x+8])
+                            for x in range(4, len(_bytes) - 8, 8)]
+
+            if self._last_seq and seq & 0xF != (self._last_seq + 1) & 0xF:
+                print("Data sequence skipped, lost packet?", file=sys.stderr)
+            self._last_seq = seq
+
+            if _type == 0:
+                if not self._coarse_scale or not self._fine_scale:
+                    print("Waiting for calibration, dropped data packet.",
+                        file=sys.stderr)
+                    continue
+                out = []
+                for main, usb, aux, voltage in data:
+                    if main & 1:
+                        coarse = ((main & ~1) - self._coarse_zero)
+                        out.append(coarse * self._coarse_scale)
+                    else:
+                        out.append((main - self._fine_zero) * self._fine_scale)
+                return out
+            elif _type == 1:
+                self._fine_zero = data[0][0]
+                self._coarse_zero = data[1][0]
+            elif _type == 2:
+                self._fine_ref = data[0][0]
+                self._coarse_ref = data[1][0]
+            else:
+                print("Discarding data packet type=0x%02x" % _type,
+                    file=sys.stderr)
+                continue
+
+            # See http://wiki/Main/MonsoonProtocol for details on these values.
+            if self._coarse_ref != self._coarse_zero:
+                self._coarse_scale = 2.88 / (self._coarse_ref - self._coarse_zero)
+            if self._fine_ref != self._fine_zero:
+                self._fine_scale = 0.0332 / (self._fine_ref - self._fine_zero)
+
+    def _SendStruct(self, fmt, *args):
+        """Pack a struct (without length or checksum) and send it.
+        """
+        data = struct.pack(fmt, *args)
+        data_len = len(data) + 1
+        checksum = (data_len + sum(data)) % 256
+        out = bytes([data_len]) + data + bytes([checksum])
+        self.ser.write(out)
+
+    def _ReadPacket(self):
+        """Read a single data record as a string (without length or checksum).
+        """
+        len_char = self.ser.read(1)
+        if not len_char:
+            print("Reading from serial port timed out.", file=sys.stderr)
+            return None
+
+        data_len = ord(len_char)
+        if not data_len:
+            return ""
+        result = self.ser.read(int(data_len))
+        if len(result) != data_len:
+            print("Length mismatch, expected %d bytes, got %d bytes." % data_len,
+                len(result))
+            return None
+        body = result[:-1]
+        checksum = (sum(result[:-1]) + data_len) % 256
+        if result[-1] != checksum:
+            print("Invalid checksum from serial port!", file=sys.stderr)
+            print("Expected {}, got {}".format(hex(checksum), hex(result[-1])))
+            return None
+        return result[:-1]
+
+    def _FlushInput(self):
+        """ Flush all read data until no more available. """
+        self.ser.flush()
+        flushed = 0
+        while True:
+            ready_r, ready_w, ready_x = select.select([self.ser], [],
+                [self.ser], 0)
+            if len(ready_x) > 0:
+                print("exception from serial port", file=sys.stderr)
+                return None
+            elif len(ready_r) > 0:
+                flushed += 1
+                self.ser.read(1)  # This may cause underlying buffering.
+                self.ser.flush()  # Flush the underlying buffer too.
+            else:
+                break
+        # if flushed > 0:
+        #     print("dropped >%d bytes" % flushed, file=sys.stderr)
+
+class MonsoonData:
+    """A class for reporting power measurement data from monsoon.
+
+    Data means the measured current value in Amps.
+    """
+    # Number of digits for long rounding.
+    lr = 8
+    # Number of digits for short rounding
+    sr = 6
+    # Delimiter for writing multiple MonsoonData objects to text file.
+    delimiter = "\n\n==========\n\n"
+
+    def __init__(self, data_points, timestamps, hz, voltage, offset=0):
+        """Instantiates a MonsoonData object.
+
+        Args:
+            data_points: A list of current values in Amp (float).
+            timestamps: A list of epoch timestamps (int).
+            hz: The hertz at which the data points are measured.
+            voltage: The voltage at which the data points are measured.
+            offset: The number of initial data points to discard
+                in calculations.
+        """
+        self._data_points = data_points
+        self._timestamps = timestamps
+        self.offset = offset
+        msg = "offset number must be smaller than the number of data points."
+        assert self.offset <= len(self._data_points), msg
+        self.data_points = self._data_points[self.offset:]
+        self.timestamps = self._timestamps[self.offset:]
+        self.hz = hz
+        self.voltage = voltage
+        self.tag = None
+        self._validate_data()
+
+    @property
+    def average_current(self):
+        """Average current in the unit of mA.
+        """
+        len_data_pt = len(self.data_points)
+        if len_data_pt == 0:
+            return 0
+        cur = sum(self.data_points) * 1000 / len_data_pt
+        return round(cur, self.sr)
+
+    @property
+    def total_charge(self):
+        """Total charged used in the unit of mAh.
+        """
+        charge = (sum(self.data_points) / self.hz) * 1000 / 3600
+        return round(charge, self.sr)
+
+    @property
+    def total_power(self):
+        """Total power used.
+        """
+        power = self.average_current * self.voltage
+        return round(power, self.sr)
+
+    @staticmethod
+    def from_string(data_str):
+        """Creates a MonsoonData object from a string representation generated
+        by __str__.
+
+        Args:
+            str: The string representation of a MonsoonData.
+
+        Returns:
+            A MonsoonData object.
+        """
+        lines = data_str.strip().split('\n')
+        msg = "Invalid format. Is this string generated by MonsoonData class?"
+        assert len(lines) > 4, msg
+        assert "Average Current:" in lines[1], msg
+        assert "Voltage: " in lines[2], msg
+        assert "Total Power: " in lines[3], msg
+        assert "Taken at " in lines[4]
+        assert lines[5] == "Time" + ' ' * 7 + "Amp", msg
+        hz_str = lines[4].split()[2]
+        hz = int(hz_str[:-2])
+        voltage_str = lines[2].split()[1]
+        voltage = int(voltage[:-1])
+        lines = lines[6:]
+        t = []
+        v = []
+        for l in lines:
+            try:
+                timestamp, value = l.split(' ')
+                t.append(int(timestamp))
+                v.append(float(value))
+            except ValueError:
+                raise AssertionError(msg)
+        return MonsoonData(v, t, hz, voltage)
+
+    @staticmethod
+    def save_to_text_file(monsoon_data, file_path):
+        """Save multiple MonsoonData objects to a text file.
+
+        Args:
+            monsoon_data: A list of MonsoonData objects to write to a text
+                file.
+            file_path: The full path of the file to save to, including the file
+                name.
+        """
+        with open(file_path, 'w') as f:
+            for md in monsoon_data:
+                f.write(str(md))
+                f.write(MonsoonData.delimiter)
+
+    @staticmethod
+    def from_text_file(file_path):
+        """Load MonsoonData objects from a text file generated by
+        MonsoonData.save_to_text_file.
+
+        Args:
+            file_path: The full path of the file load from, including the file
+                name.
+
+        Returns:
+            A list of MonsoonData objects.
+        """
+        results = []
+        with open(file_path, 'r') as f:
+            data_strs = f.read().split(MonsoonData.delimiter)
+            for data_str in data_strs:
+                results.append(MonsoonData.from_string(data_str))
+        return results
+
+    def _validate_data(self):
+        """Verifies that the data points contained in the class are valid.
+        """
+        msg = "Error! Expected {} timestamps, found {}.".format(
+            len(self._data_points), len(self._timestamps))
+        assert len(self._data_points) == len(self._timestamps), msg
+
+    def update_offset(self, new_offset):
+        """Updates how many data points to skip in caculations.
+
+        Always use this function to update offset instead of directly setting
+        self.offset.
+
+        Args:
+            new_offset: The new offset.
+        """
+        self.offset = new_offset
+        self.data_points = self._data_points[self.offset:]
+        self.timestamps = self._timestamps[self.offset:]
+
+    def get_data_with_timestamps(self):
+        """Returns the data points with timestamps.
+
+        Returns:
+            A list of tuples in the format of (timestamp, data)
+        """
+        result = []
+        for t, d in zip(self.timestamps, self.data_points):
+            result.append(t, round(d, self.lr))
+        return result
+
+    def get_average_record(self, n):
+        """Returns a list of average current numbers, each representing the
+        average over the last n data points.
+
+        Args:
+            n: Number of data points to average over.
+
+        Returns:
+            A list of average current values.
+        """
+        history_deque = collections.deque()
+        averages = []
+        for d in self.data_points:
+            history_deque.appendleft(d)
+            if len(history_deque) > n:
+                history_deque.pop()
+            avg = sum(history_deque) / len(history_deque)
+            averages.append(round(avg, self.lr))
+        return averages
+
+    def _header(self):
+        strs = [""]
+        if self.tag:
+            strs.append(self.tag)
+        else:
+            strs.append("Monsoon Measurement Data")
+        strs.append("Average Current: {}mA.".format(self.average_current))
+        strs.append("Voltage: {}V.".format(self.voltage))
+        strs.append("Total Power: {}mW.".format(self.total_power))
+        strs.append("Taken at {}Hz with an offset of {} samples.".format(
+            self.hz, self.offset))
+        return "\n".join(strs)
+
+    def __len__(self):
+        return len(self.data_points)
+
+    def __str__(self):
+        strs = []
+        strs.append(self._header())
+        strs.append("Time" + ' ' * 7 + "Amp")
+        for t, d in zip(self.timestamps, self.data_points):
+            strs.append("{} {}".format(t, round(d, self.sr)))
+        return "\n".join(strs)
+
+    def __repr__(self):
+        return self._header()
+
+class Monsoon:
+    """The wrapper class for test scripts to interact with monsoon.
+    """
+    def __init__(self, *args, **kwargs):
+        serial = kwargs["serial"]
+        device = None
+        if "logger" in kwargs:
+            self.log = LoggerProxy(kwargs["logger"])
+        else:
+            self.log = LoggerProxy()
+        if "device" in kwargs:
+            device = kwargs["device"]
+        self.mon = MonsoonProxy(serialno=serial, device=device)
+
+    def set_voltage(self, volt, ramp=False):
+        """Sets the output voltage of monsoon.
+
+        Args:
+            volt: Voltage to set the output to.
+            ramp: If true, the output voltage will be increased gradually to
+                prevent tripping Monsoon overvoltage.
+        """
+        if ramp:
+            self.mon.RampVoltage(mon.start_voltage, volt)
+        else:
+            self.mon.SetVoltage(volt)
+
+    def set_max_current(self, cur):
+        """Sets monsoon's max output current.
+
+        Args:
+            cur: The max current in A.
+        """
+        self.mon.SetMaxCurrent(cur)
+
+    def set_max_init_current(self, cur):
+        """Sets the max power-up/inital current.
+
+        Args:
+            cur: The max initial current allowed in mA.
+        """
+        self.mon.SetMaxPowerUpCurrent(cur)
+
+    @property
+    def status(self):
+        """Gets the status params of monsoon.
+
+        Returns:
+            A dictionary where each key-value pair represents a monsoon status
+            param.
+        """
+        return self.mon.GetStatus()
+
+    def take_samples(self, sample_hz, sample_num, sample_offset=0, live=False):
+        """Take samples of the current value supplied by monsoon.
+
+        This is the actual measurement for power consumption. This function
+        blocks until the number of samples requested has been fulfilled.
+
+        Args:
+            hz: Number of points to take for every second.
+            sample_num: Number of samples to take.
+            offset: The number of initial data points to discard in MonsoonData
+                calculations. sample_num is extended by offset to compensate.
+            live: Print each sample in console as measurement goes on.
+
+        Returns:
+            A MonsoonData object representing the data obtained in this
+            sampling. None if sampling is unsuccessful.
+        """
+        sys.stdout.flush()
+        voltage = self.mon.GetVoltage()
+        self.log.info("Taking samples at %dhz for %ds, voltage %fv." % (
+            sample_hz, sample_num/sample_hz, voltage))
+        sample_num += sample_offset
+        # Make sure state is normal
+        self.mon.StopDataCollection()
+        status = self.mon.GetStatus()
+        native_hz = status["sampleRate"] * 1000
+
+        # Collect and average samples as specified
+        self.mon.StartDataCollection()
+
+        # In case sample_hz doesn't divide native_hz exactly, use this
+        # invariant: 'offset' = (consumed samples) * sample_hz -
+        # (emitted samples) * native_hz
+        # This is the error accumulator in a variation of Bresenham's
+        # algorithm.
+        emitted = offset = 0
+        collected = []
+        # past n samples for rolling average
+        history_deque = collections.deque()
+        current_values = []
+        timestamps = []
+
+        try:
+            last_flush = time.time()
+            while emitted < sample_num or sample_num == -1:
+                # The number of raw samples to consume before emitting the next
+                # output
+                need = int((native_hz - offset + sample_hz - 1) / sample_hz)
+                if need > len(collected):     # still need more input samples
+                    samples = self.mon.CollectData()
+                    if not samples:
+                        break
+                    collected.extend(samples)
+                else:
+                    # Have enough data, generate output samples.
+                    # Adjust for consuming 'need' input samples.
+                    offset += need * sample_hz
+                    # maybe multiple, if sample_hz > native_hz
+                    while offset >= native_hz:
+                        # TODO(angli): Optimize "collected" operations.
+                        this_sample = sum(collected[:need]) / need
+                        this_time = int(time.time())
+                        timestamps.append(this_time)
+                        if live:
+                            self.log.info("%s %s" % (this_time, this_sample))
+                        current_values.append(this_sample)
+                        sys.stdout.flush()
+                        offset -= native_hz
+                        emitted += 1 # adjust for emitting 1 output sample
+                    collected = collected[need:]
+                    now = time.time()
+                    if now - last_flush >= 0.99: # flush every second
+                        sys.stdout.flush()
+                        last_flush = now
+        except Exception as e:
+            pass
+        self.mon.StopDataCollection()
+        try:
+            return MonsoonData(current_values, timestamps, sample_hz,
+                voltage, offset=sample_offset)
+        except:
+            return None
+
+    @timeout(60)
+    def usb(self, state):
+        """Sets the monsoon's USB passthrough mode. This is specific to the
+        USB port in front of the monsoon box which connects to the powered
+        device, NOT the USB that is used to talk to the monsoon itself.
+
+        "Off" means USB always off.
+        "On" means USB always on.
+        "Auto" means USB is automatically turned off when sampling is going on,
+        and turned back on when sampling finishes.
+
+        Args:
+            stats: The state to set the USB passthrough to.
+
+        Returns:
+            True if the state is legal and set. False otherwise.
+        """
+        state_lookup = {
+            "off": 0,
+            "on": 1,
+            "auto": 2
+        }
+        state = state.lower()
+        if state in state_lookup:
+            current_state = self.mon.GetUsbPassthrough()
+            while(current_state != state_lookup[state]):
+                self.mon.SetUsbPassthrough(state_lookup[state])
+                time.sleep(1)
+                current_state = self.mon.GetUsbPassthrough()
+            return True
+        return False
+
+    @timeout(15)
+    def _wait_for_device(self, ad):
+        while ad.serial not in list_adb_devices():
+            pass
+        ad.adb.wait_for_device()
+
+    def execute_sequence_and_measure(self, hz, duration, step_funcs, ad, offset_sec=20, *args, **kwargs):
+        """Executes a sequence of steps and take samples in-between.
+
+        For each step function, the following steps are followed:
+        1. The function is executed to put the android device in a state.
+        2. If the function returns False, skip to next step function.
+        3. If the function returns True, sl4a session is disconnected.
+        4. Monsoon takes samples.
+        5. Sl4a is reconnected.
+
+        Because it takes some time for the device to calm down after the usb
+        connection is cut, an offset is set for each measurement. The default
+        is 20s.
+
+        Args:
+            hz: Number of samples to take per second.
+            durations: Number(s) of minutes to take samples for in each step.
+                If this is an integer, all the steps will sample for the same
+                amount of time. If this is an iterable of the same length as
+                step_funcs, then each number represents the number of minutes
+                to take samples for after each step function.
+                e.g. If durations[0] is 10, we'll sample for 10 minutes after
+                step_funcs[0] is executed.
+            step_funcs: A list of funtions, whose first param is an android
+                device object. If a step function returns True, samples are
+                taken after this step, otherwise we move on to the next step
+                function.
+            ad: The android device object connected to this monsoon.
+            offset_sec: The number of seconds of initial data to discard.
+            *args, **kwargs: Extra args to be passed into each step functions.
+
+        Returns:
+            The MonsoonData objects from samplings.
+        """
+        sample_nums = []
+        try:
+            if len(duration) != len(step_funcs):
+                raise MonsoonError(("The number of durations need to be the "
+                    "same as the number of step functions."))
+            for d in duration:
+                sample_nums.append(d * 60 * hz)
+        except TypeError:
+            num = duration * 60 * hz
+            sample_nums = [num] * len(step_funcs)
+        results = []
+        oset = offset_sec * hz
+        for func, num in zip(step_funcs, sample_nums):
+            try:
+                self.usb("auto")
+                step_name = func.__name__
+                self.log.info("Executing step function %s." % step_name)
+                take_sample = func(ad, *args, **kwargs)
+                if not take_sample:
+                    self.log.info("Skip taking samples for %s" % step_name)
+                    continue
+                time.sleep(1)
+                ad.terminate_all_sessions()
+                time.sleep(1)
+                self.log.info("Taking samples for %s." % step_name)
+                data = self.take_samples(hz, num, sample_offset=oset)
+                assert data, "Sampling for %s failed." % step_name
+                self.log.info("Sample summary: %s" % repr(data))
+                # self.log.debug(str(data))
+                data.tag = step_name
+                results.append(data)
+            except Exception:
+                msg = "Exception happened during step %s, abort!" % func.__name__
+                self.log.exception(msg)
+                return results
+            finally:
+                self.mon.StopDataCollection()
+                self.usb("on")
+                self._wait_for_device(ad)
+                # Wait for device to come back online.
+                time.sleep(10)
+                droid, ed = ad.get_droid(True)
+                ed.start()
+                # Release wake lock to put device into sleep.
+                droid.goToSleepNow()
+        return results
\ No newline at end of file
diff --git a/acts/framework/acts/controllers/tel/__init__.py b/acts/framework/acts/controllers/tel/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/tel/__init__.py
diff --git a/acts/framework/acts/controllers/tel/_anritsu_utils.py b/acts/framework/acts/controllers/tel/_anritsu_utils.py
new file mode 100644
index 0000000..21f3a2f
--- /dev/null
+++ b/acts/framework/acts/controllers/tel/_anritsu_utils.py
@@ -0,0 +1,232 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+#   Copyright 2014- The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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.
+"""
+
+OPERATION_COMPLETE = 1
+NO_ERROR = 0
+
+ANRITSU_ERROR_CODES = {
+    0: 'No errors occurred',
+    2: 'The specified file does not exist',
+    14: 'The buffer size is insufficient',
+    29: 'The save destination is a write-protected file.',
+    80: 'A file with the same name already exists.'
+        ' (If Overwrite is specified to 0.)',
+    87: 'The specified value is wrong.',
+    112: 'The disk space is insufficient.',
+    183: 'SmartStudio is already running.',
+    1060: 'The control software has not been started or has already terminated',
+    1067: 'SmartStudio, control software or SMS Centre could not start due to'
+          'a problem or problems resulting from OS or the MD8475A system.',
+    1229: 'Connecting to the server failed.',
+    1235: 'A request is suspended.',
+    1460: 'The operation is terminated due to the expiration of the'
+          ' timeout period.',
+    9999: 'A GPIB command error occurred.',
+    536870912: 'The license could not be confirmed.',
+    536870913: 'The specified file cannot be loaded by the SmartStudio.',
+    536870914: 'The specified process ID does not exist.',
+    536870915: 'The received data does not exist.',
+    536870916: 'Simulation is not running.',
+    536870917: 'Simulation is running.',
+    536870918: 'Test Case has never been executed.',
+    536870919: 'The resource cannot be obtained.',
+    536870920: 'A resource protocol error, such as download error or'
+               ' license error, occurred.',
+    536870921: 'The function call has been in invalid status.',
+    536870922: 'The current Simulation Model does not allow the operation.',
+    536870923: 'The Cell name to be set does not exist.',
+    536870924: 'The test is being executed.',
+    536870925: 'The current UE status does not correspond to the'
+               ' test parameters.',
+    536870926: 'There is no LOG information because the simulation'
+               ' has not been executed.',
+    536870927: 'Measure Export has already been executed.',
+    536870928: 'SmartStudio is not connected to the SMS Centre.',
+    536870929: 'SmartStudio failed to send an SMS message to the SMS Centre.',
+    536870930: 'SmartStudio has successfully sent an SMS message'
+               ' to the SMS Centre,but the SMS Centre judges it as an error.',
+    536870931: 'The processing that is unavailable with the current system'
+               ' status has been executed.',
+    536870932: 'The option could not be confirmed.',
+    536870933: 'Measure Export has been stopped.',
+    536870934: 'SmartStudio cannot load the specified file because the'
+               ' version is old.',
+    536870935: 'The data with the specified PDN number does not exist.',
+    536870936: 'The data with the specified Dedicated number does not exist.',
+    536870937: 'The PDN data cannot be added because the upper limit of the'
+               ' number of PDN data has been reached.',
+    536870938: 'The number of antennas, which cannot be set to the current'
+               ' Simulation Model,has been specified.',
+    536870939: 'Calibration of path loss failed.',
+    536870940: 'There is a parameter conflict.',
+    536870941: 'The DL Ref Power setting is out of the setting range'
+               ' at W-CDMA (Evolution).',
+    536870942: 'DC-HSDPA is not available for the current channel setting.',
+    536870943: 'The specified Packet Rate cannot be used by the current'
+               ' Simulation Model.',
+    536870944: 'The W-CDMA Cell parameter F-DPCH is set to Enable.',
+    536870945: 'Target is invalid.',
+    536870946: 'The PWS Centre detects an error.',
+    536870947: 'The Ec/Ior setting is invalid.',
+    536870948: 'The combination of Attach Type and TA Update Type is invalid.',
+    536870949: 'The license of the option has expired.',
+    536870950: 'The Ping command is being executed.',
+    536870951: 'The Ping command is not being executed.',
+    536870952: 'The current Test Case parameter setting is wrong.',
+    536870953: 'The specified IP address is the same as that of Default Gateway'
+               'specified by Simulation parameter.',
+    536870954: 'TFT IE conversion failed.',
+    536875008: 'An error exists in the parameter configuration.'
+               '(This error applies only to the current version.)',
+    536936448: 'License verification failed.',
+    536936449: 'The IMS Services cannot load the specified file.',
+    536936462: 'Simulation is not performed and no log information exists.',
+    536936467: 'The executed process is inoperable in the current status'
+               ' of Visual User Agent.',
+    536936707: 'The specified Virtual Network is not running.',
+    536936709: 'The specified Virtual Network is running. '
+               'Any one of the Virtual Networks is running.',
+    536936727: 'The specified Virtual Network does not exist.',
+    536936729: 'When the Virtual Network already exists.',
+    554762241: 'The RF Measurement launcher cannot be accessed.',
+    554762242: 'License check of the RF Measurement failed.',
+    554762243: 'Function is called when RF Measurement cannot be set.',
+    554762244: 'RF Measurement has been already started.',
+    554762245: 'RF Measurement failed to start due to a problem resulting'
+               ' from OS or the MD8475A system.',
+    554762246: 'RF Measurement is not started or is already terminated.',
+    554762247: 'There is a version mismatch between RF Measurement and CAL.',
+    554827777: 'The specified value for RF Measurement is abnormal.',
+    554827778: 'GPIB command error has occurred in RF Measurement.',
+    554827779: 'Invalid file path was specified to RF Measurement.',
+    554827780: 'RF Measurement argument is NULL pointer.',
+    555810817: 'RF Measurement is now performing the measurement.',
+    555810818: 'RF Measurement is now not performing the measurement.',
+    555810819: 'RF Measurement is not measured yet. (There is no result '
+               'information since measurement is not performed.)',
+    555810820: 'An error has occurred when RF Measurement'
+               ' starts the measurement.',
+    555810821: 'Simulation has stopped when RF Measurement is '
+               'performing the measurement.',
+    555810822: 'An error has been retrieved from the Platform when '
+               'RF Measurement is performing the measurement.',
+    555810823: 'Measurement has been started in the system state where RF '
+               'Measurement is invalid.',
+    556859393: 'RF Measurement is now saving a file.',
+    556859394: 'There is insufficient disk space when saving'
+               'a Measure Result file of RF Measurement.',
+    556859395: 'An internal error has occurred or USB cable has been'
+               ' disconnected when saving a Measure Result'
+               ' file of RF Measurement.',
+    556859396: 'A write-protected file was specified as the save destination'
+               ' when saving a Measure Result file of RF Measurement.',
+    568328193: 'An internal error has occurred in RF Measurement.',
+    687865857: 'Calibration Measure DSP is now being measured.',
+    687865858: 'Calibration measurement failed.',
+    687865859: 'Calibration slot is empty or its system does not apply.',
+    687865860: 'Unexpected command is received from Calibration HWC.',
+    687865861: 'Failed to receive the Calibration measurement result.',
+    687865862: 'Failed to open the correction value file on the'
+               ' Calibration HDD.',
+    687865863: 'Failed to move the pointer on the Calibration correction'
+               ' value table.',
+    687865864: 'Failed to write the correction value to the Calibration'
+               ' correction value file on the Calibration HDD.',
+    687865865: 'Failed to load the correction value from the Calibration HDD.',
+    687865866: 'Failed to create a directory to which the correction value '
+               'file on the Calibration HDD is saved.',
+    687865867: 'Correction data has not been written in the'
+               ' Calibration-specified correction table.',
+    687865868: 'Data received from Calibration HWC does not exist.',
+    687865869: 'Data has not been written to the Flash ROM'
+               ' of Calibration BASE UNIT.',
+    687865870: 'Correction data has not been written to the'
+               ' Calibration-specified sector.',
+    687866111: 'An calibration error other than described above occurred.',
+}
+
+
+def _error_code_tostring(error_code):
+    ''' returns the description of the error from the error code
+    returned by anritsu MD8475A '''
+    try:
+        error_string = ANRITSU_ERROR_CODES[error_code]
+    except KeyError:
+        error_string = "Error : {} ".format(error_code)
+
+    return error_string
+
+
+class AnritsuUtils():
+    def gsm_encode(text):
+        '''To encode text string with GSM 7-bit alphabet for common symbols'''
+        table = {' ': '%20', '!': '%21', '\"': '%22', '#': '%23', '$': '%24',
+                 '/': '%2F', '%': '%25', '&': '%26', '\'': '%27', '(': '%28',
+                 ')': '%29', '*': '%2A', '+': '%2B', ',': '%2C', ':': '%3A',
+                 ';': '%3B', '<': '%3C', '=': '%3D', '>': '%3E', '?': '%3F',
+                 '@': '%40', '[': '%5B', ']': '%5D', '_': '%5F', 'é': '%C3%A9'}
+        coded_str = ""
+        for char in text:
+            if char in table:
+                coded_str += table[char]
+            else:
+                coded_str += char
+        return coded_str
+
+    def gsm_decode(text):
+        '''To decode text string with GSM 7-bit alphabet for common symbols'''
+        table = {'%20': ' ', '%21': '!', '%22': '\"', '%23': '#', '%24': '$',
+                 '%2F': '/', '%25': '%', '%26': '&', '%27': '\'', '%28': '(',
+                 '%29': ')', '%2A': '*', '%2B': '+', '%2C': ',', '%3A': ':',
+                 '%3B': ';', '%3C': '<', '%3D': '=', '%3E': '>', '%3F': '?',
+                 '%40': '@', '%5B': '[', '%5D': ']', '%5F': '_', '%C3%A9': 'é'}
+        coded_str = text
+        for char in table:
+            if char in text:
+                coded_str = coded_str.replace(char, table[char])
+        return coded_str
+
+    def cdma_encode(text):
+        '''To encode text string with GSM 7-bit alphabet for common symbols'''
+        table = {' ': '%20', '!': '%21', '\"': '%22', '#': '%23', '$': '%24',
+                 '/': '%2F', '%': '%25', '&': '%26', '\'': '%27', '(': '%28',
+                 ')': '%29', '*': '%2A', '+': '%2B', ',': '%2C', ':': '%3A',
+                 ';': '%3B', '<': '%3C', '=': '%3D', '>': '%3E', '?': '%3F',
+                 '@': '%40', '[': '%5B', ']': '%5D', '_': '%5F'}
+        coded_str = ""
+        for char in text:
+            if char in table:
+                coded_str += table[char]
+            else:
+                coded_str += char
+        return coded_str
+
+class AnritsuError(Exception):
+    '''Exception for errors related to Anritsu.'''
+    def __init__(self, error, command=None):
+        self._error_code = error
+        self._error_message = _error_code_tostring(self._error_code)
+        if command is not None:
+            self._error_message = "Command {} returned the error: '{}'".format(
+                                  command, self._error_message)
+
+    def __str__(self):
+        return self._error_message
diff --git a/acts/framework/acts/controllers/tel/md8475a.py b/acts/framework/acts/controllers/tel/md8475a.py
new file mode 100644
index 0000000..42f125d
--- /dev/null
+++ b/acts/framework/acts/controllers/tel/md8475a.py
@@ -0,0 +1,3183 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+#   Copyright 2014- The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""
+Controller interface for Anritsu Signalling Tester MD8475A.
+"""
+
+import time
+import socket
+from enum import Enum
+from enum import IntEnum
+
+from . _anritsu_utils import *
+
+TERMINATOR = "\0"
+SMARTSTUDIO_LAUNCH_WAIT_TIME = 90
+SMARTSTUDIO_SIMULATION_START_WAIT_TIME = 120
+REGISTRATION_STATE_WAIT_TIME = 240
+COMMUNICATION_STATE_WAIT_TIME = 240
+ANRITSU_SOCKET_BUFFER_SIZE = 8192
+COMMAND_COMPLETE_WAIT_TIME = 90
+SETTLING_TIME = 1
+WAIT_TIME_IDENTITY_RESPONSE = 5
+
+IMSI_READ_USERDATA_WCDMA = "081501"
+IMEI_READ_USERDATA_WCDMA = "081502"
+IMEISV_READ_USERDATA_WCDMA = "081503"
+IMSI_READ_USERDATA_LTE = "075501"
+IMEI_READ_USERDATA_LTE = "075502"
+IMEISV_READ_USERDATA_LTE = "075503"
+IMSI_READ_USERDATA_GSM = "081501"
+IMEI_READ_USERDATA_GSM = "081502"
+IMEISV_READ_USERDATA_GSM = "081503"
+IDENTITY_REQ_DATA_LEN = 24
+SEQ_LOG_MESSAGE_START_INDEX = 60
+
+class ProcessingStatus(Enum):
+    ''' MD8475A processing status for UE,Packet,Voice,Video,SMS,
+        PPP, PWS '''
+    PROCESS_STATUS_NONE = "NONE"
+    PROCESS_STATUS_NOTRUN = "NOTRUN"
+    PROCESS_STATUS_POWEROFF = "POWEROFF"
+    PROCESS_STATUS_REGISTRATION = "REGISTRATION"
+    PROCESS_STATUS_DETACH = "DETACH"
+    PROCESS_STATUS_IDLE = "IDLE"
+    PROCESS_STATUS_ORIGINATION = "ORIGINATION"
+    PROCESS_STATUS_HANDOVER = "HANDOVER"
+    PROCESS_STATUS_UPDATING = "UPDATING"
+    PROCESS_STATUS_TERMINATION = "TERMINATION"
+    PROCESS_STATUS_COMMUNICATION = "COMMUNICATION"
+    PROCESS_STATUS_UERELEASE = "UERELEASE"
+    PROCESS_STATUS_NWRELEASE = "NWRELEASE"
+
+
+class BtsNumber(Enum):
+    '''ID number for MD8475A supported BTS '''
+    BTS1 = "BTS1"
+    BTS2 = "BTS2"
+    BTS3 = "BTS3"
+    BTS4 = "BTS4"
+
+
+class BtsTechnology(Enum):
+    ''' BTS system technology'''
+    LTE = "LTE"
+    WCDMA = "WCDMA"
+    TDSCDMA = "TDSCDMA"
+    GSM = "GSM"
+    CDMA1X = "CDMA1X"
+    EVDO = "EVDO"
+
+
+class BtsBandwidth(Enum):
+    ''' Values for Cell Bandwidth '''
+    LTE_BANDWIDTH_1dot4MHz = "1.4MHz"
+    LTE_BANDWIDTH_3MHz = "3MHz"
+    LTE_BANDWIDTH_5MHz = "5MHz"
+    LTE_BANDWIDTH_10MHz = "10MHz"
+    LTE_BANDWIDTH_15MHz = "15MHz"
+    LTE_BANDWIDTH_20MHz = "20MHz"
+
+
+class BtsPacketRate(Enum):
+    ''' Values for Cell Packet rate '''
+    LTE_MANUAL = "MANUAL"
+    LTE_BESTEFFORT = "BESTEFFORT"
+    WCDMA_DLHSAUTO_REL7_UL384K = "DLHSAUTO_REL7_UL384K"
+    WCDMA_DL18_0M_UL384K = "DL18_0M_UL384K"
+    WCDMA_DL21_6M_UL384K = "DL21_6M_UL384K"
+    WCDMA_DLHSAUTO_REL7_ULHSAUTO = "DLHSAUTO_REL7_ULHSAUTO"
+    WCDMA_DL18_0M_UL1_46M = "DL18_0M_UL1_46M"
+    WCDMA_DL18_0M_UL2_0M = "DL18_0M_UL2_0M"
+    WCDMA_DL18_0M_UL5_76M = "DL18_0M_UL5_76M"
+    WCDMA_DL21_6M_UL1_46M = "DL21_6M_UL1_46M"
+    WCDMA_DL21_6M_UL2_0M = "DL21_6M_UL2_0M"
+    WCDMA_DL21_6M_UL5_76M = "DL21_6M_UL5_76M"
+    WCDMA_DLHSAUTO_REL8_UL384K = "DLHSAUTO_REL8_UL384K"
+    WCDMA_DL23_4M_UL384K = "DL23_4M_UL384K"
+    WCDMA_DL28_0M_UL384K = "DL28_0M_UL384K"
+    WCDMA_DL36_0M_UL384K = "DL36_0M_UL384K"
+    WCDMA_DL43_2M_UL384K = "DL43_2M_UL384K"
+    WCDMA_DLHSAUTO_REL8_ULHSAUTO = "DLHSAUTO_REL8_ULHSAUTO"
+    WCDMA_DL23_4M_UL1_46M = "DL23_4M_UL1_46M"
+    WCDMA_DL23_4M_UL2_0M = "DL23_4M_UL2_0M"
+    WCDMA_DL23_4M_UL5_76M = "DL23_4M_UL5_76M"
+    WCDMA_DL28_0M_UL1_46M = "DL28_0M_UL1_46M"
+    WCDMA_DL28_0M_UL2_0M = "DL28_0M_UL2_0M"
+    WCDMA_DL28_0M_UL5_76M = "L28_0M_UL5_76M"
+    WCDMA_DL36_0M_UL1_46M = "DL36_0M_UL1_46M"
+    WCDMA_DL36_0M_UL2_0M = "DL36_0M_UL2_0M"
+    WCDMA_DL36_0M_UL5_76M = "DL36_0M_UL5_76M"
+    WCDMA_DL43_2M_UL1_46M = "DL43_2M_UL1_46M"
+    WCDMA_DL43_2M_UL2_0M = "DL43_2M_UL2_0M"
+    WCDMA_DL43_2M_UL5_76M = "L43_2M_UL5_76M"
+
+
+class BtsPacketWindowSize(Enum):
+    ''' Values for Cell Packet window size '''
+    WINDOW_SIZE_1 = 1
+    WINDOW_SIZE_8 = 8
+    WINDOW_SIZE_16 = 16
+    WINDOW_SIZE_32 = 32
+    WINDOW_SIZE_64 = 64
+    WINDOW_SIZE_128 = 128
+    WINDOW_SIZE_256 = 256
+    WINDOW_SIZE_512 = 512
+    WINDOW_SIZE_768 = 768
+    WINDOW_SIZE_1024 = 1024
+    WINDOW_SIZE_1536 = 1536
+    WINDOW_SIZE_2047 = 2047
+
+
+class BtsServiceState(Enum):
+    ''' Values for BTS service state '''
+    SERVICE_STATE_IN = "IN"
+    SERVICE_STATE_OUT = "OUT"
+
+
+class BtsCellBarred(Enum):
+    ''' Values for Cell barred parameter '''
+    NOTBARRED = "NOTBARRED"
+    BARRED = "BARRED"
+
+
+class BtsAccessClassBarred(Enum):
+    ''' Values for Access class barred parameter '''
+    NOTBARRED = "NOTBARRED"
+    EMERGENCY = "EMERGENCY"
+    BARRED = "BARRED"
+    USERSPECIFIC = "USERSPECIFIC"
+
+
+class BtsLteEmergencyAccessClassBarred(Enum):
+    ''' Values for Lte emergency access class barred parameter '''
+    NOTBARRED = "NOTBARRED"
+    BARRED = "BARRED"
+
+
+class BtsNwNameEnable(Enum):
+    ''' Values for BT network name enable parameter '''
+    NAME_ENABLE = "ON"
+    NAME_DISABLE = "OFF"
+
+
+class IPAddressType(Enum):
+    ''' Values for IP address type '''
+    IPV4 = "IPV4"
+    IPV6 = "IPV6"
+    IPV4V6 = "IPV4V6"
+
+
+class TriggerMessageIDs(Enum):
+    ''' ID for Trigger messages  '''
+    RRC_CONNECTION_REQ = 111101
+    RRC_CONN_REESTABLISH_REQ = 111100
+    ATTACH_REQ = 141141
+    DETACH_REQ = 141145
+    MM_LOC_UPDATE_REQ = 221108
+    GMM_ATTACH_REQ = 241101
+    GMM_RA_UPDATE_REQ = 241108
+    IDENTITY_REQUEST_LTE = 141155
+    IDENTITY_REQUEST_WCDMA = 241115
+    IDENTITY_REQUEST_GSM = 641115
+
+
+class TriggerMessageReply(Enum):
+    ''' Values for Trigger message reply parameter '''
+    ACCEPT = "ACCEPT"
+    REJECT = "REJECT"
+    IGNORE = "IGNORE"
+    NONE = "NONE"
+    ILLEGAL = "ILLEGAL"
+
+
+class TestProcedure(Enum):
+    ''' Values for different Test procedures in MD8475A '''
+    PROCEDURE_BL = "BL"
+    PROCEDURE_SELECTION = "SELECTION"
+    PROCEDURE_RESELECTION = "RESELECTION"
+    PROCEDURE_REDIRECTION = "REDIRECTION"
+    PROCEDURE_HO = "HO"
+    PROCEDURE_HHO = "HHO"
+    PROCEDURE_SHO = "SHO"
+    PROCEDURE_MEASUREMENT = "MEASUREMENT"
+    PROCEDURE_CELLCHANGE = "CELLCHANGE"
+    PROCEDURE_MULTICELL = "MULTICELL"
+
+
+class TestPowerControl(Enum):
+    ''' Values for power control in test procedure '''
+    POWER_CONTROL_ENABLE = "ENABLE"
+    POWER_CONTROL_DISABLE = "DISABLE"
+
+
+class TestMeasurement(Enum):
+    ''' Values for mesaurement in test procedure '''
+    MEASUREMENT_ENABLE = "ENABLE"
+    MEASUREMENT_DISABLE = "DISABLE"
+
+'''MD8475A processing states'''
+_PROCESS_STATES = {
+    "NONE": ProcessingStatus.PROCESS_STATUS_NONE,
+    "NOTRUN": ProcessingStatus.PROCESS_STATUS_NOTRUN,
+    "POWEROFF": ProcessingStatus.PROCESS_STATUS_POWEROFF,
+    "REGISTRATION": ProcessingStatus.PROCESS_STATUS_REGISTRATION,
+    "DETACH": ProcessingStatus.PROCESS_STATUS_DETACH,
+    "IDLE": ProcessingStatus.PROCESS_STATUS_IDLE,
+    "ORIGINATION": ProcessingStatus.PROCESS_STATUS_ORIGINATION,
+    "HANDOVER": ProcessingStatus.PROCESS_STATUS_HANDOVER,
+    "UPDATING": ProcessingStatus.PROCESS_STATUS_UPDATING,
+    "TERMINATION": ProcessingStatus.PROCESS_STATUS_TERMINATION,
+    "COMMUNICATION": ProcessingStatus.PROCESS_STATUS_COMMUNICATION,
+    "UERELEASE": ProcessingStatus.PROCESS_STATUS_UERELEASE,
+    "NWRELEASE": ProcessingStatus.PROCESS_STATUS_NWRELEASE,
+}
+
+
+class VirtualPhoneStatus(IntEnum):
+    ''' MD8475A virtual phone status for UE voice and UE video
+        PPP, PWS '''
+    STATUS_IDLE = 0
+    STATUS_VOICECALL_ORIGINATION = 1
+    STATUS_VOICECALL_INCOMING = 2
+    STATUS_VOICECALL_INPROGRESS = 3
+    STATUS_VOICECALL_DISCONNECTING = 4
+    STATUS_VOICECALL_DISCONNECTED = 5
+    STATUS_VIDEOCALL_ORIGINATION = 6
+    STATUS_VIDEOCALL_INCOMING = 7
+    STATUS_VIDEOCALL_INPROGRESS = 8
+    STATUS_VIDEOCALL_DISCONNECTING = 9
+    STATUS_VIDEOCALL_DISCONNECTED = 10
+
+
+'''Virtual Phone Status '''
+_VP_STATUS = {
+    "0": VirtualPhoneStatus.STATUS_IDLE,
+    "1": VirtualPhoneStatus.STATUS_VOICECALL_ORIGINATION,
+    "2": VirtualPhoneStatus.STATUS_VOICECALL_INCOMING,
+    "3": VirtualPhoneStatus.STATUS_VOICECALL_INPROGRESS,
+    "4": VirtualPhoneStatus.STATUS_VOICECALL_DISCONNECTING,
+    "5": VirtualPhoneStatus.STATUS_VOICECALL_DISCONNECTED,
+    "6": VirtualPhoneStatus.STATUS_VIDEOCALL_ORIGINATION,
+    "7": VirtualPhoneStatus.STATUS_VIDEOCALL_INCOMING,
+    "8": VirtualPhoneStatus.STATUS_VIDEOCALL_INPROGRESS,
+    "9": VirtualPhoneStatus.STATUS_VIDEOCALL_DISCONNECTING,
+    "10": VirtualPhoneStatus.STATUS_VIDEOCALL_DISCONNECTED,
+}
+
+class VirtualPhoneAutoAnswer(Enum):
+    ''' Virtual phone auto answer enable values'''
+    ON = "ON"
+    OFF = "OFF"
+
+class CsfbType(Enum):
+    ''' CSFB Type values'''
+    CSFB_TYPE_REDIRECTION = "REDIRECTION"
+    CSFB_TYPE_HANDOVER = "HO"
+
+
+class ReturnToEUTRAN(Enum):
+    '''Return to EUTRAN setting values '''
+    RETEUTRAN_ENABLE = "ENABLE"
+    RETEUTRAN_DISABLE = "DISABLE"
+
+
+class CTCHSetup(Enum):
+    '''CTCH setting values '''
+    CTCH_ENABLE = "ENABLE"
+    CTCH_DISABLE = "DISABLE"
+
+class UEIdentityType(Enum):
+    '''UE Identity type values '''
+    IMSI = "IMSI"
+    IMEI = "IMEI"
+    IMEISV = "IMEISV"
+
+class CBCHSetup(Enum):
+    '''CBCH setting values '''
+    CBCH_ENABLE = "ENABLE"
+    CBCH_DISABLE = "DISABLE"
+
+class MD8475A():
+    """Class to communicate with Anritsu MD8475A Signalling Tester.
+       This uses GPIB command to interface with Anritsu MD8475A """
+
+    def __init__(self, ip_address, log_handle):
+        self._error_reporting = True
+        self._ipaddr = ip_address
+        self.log = log_handle
+
+        # Open socket connection to Signaling Tester
+        self.log.info("Opening Socket Connection with "
+              "Signaling Tester ({}) ".format(self._ipaddr))
+        try:
+            self._sock = socket.create_connection((self._ipaddr, 28002),
+                                                  timeout=30)
+            self.send_query("*IDN?", 60)
+            self.log.info("Communication with Signaling Tester OK.")
+            self.log.info("Opened Socket connection to ({})"
+                  "with handle ({})".format(self._ipaddr, self._sock))
+            # launching Smart Studio Application needed for the simulation
+            ret = self.launch_smartstudio()
+        except socket.timeout:
+            raise AnritsuError("Timeout happened while conencting to"
+                               " Anritsu MD8475A")
+        except socket.error:
+            raise AnritsuError("Socket creation error")
+
+    def get_BTS(self, btsnumber):
+        """ Returns the BTS object based on the BTS number provided
+
+        Args:
+            btsnumber: BTS number (BTS1, BTS2)
+
+        Returns:
+            BTS object
+        """
+        return _BaseTransceiverStation(self, btsnumber)
+
+    def get_AnritsuTestCases(self):
+        """ Returns the Anritsu Test Case Module Object
+
+        Args:
+            None
+
+        Returns:
+            Anritsu Test Case Module Object
+        """
+        return _AnritsuTestCases(self)
+
+    def get_VirtualPhone(self):
+        """ Returns the Anritsu Virtual Phone Module Object
+
+        Args:
+            None
+
+        Returns:
+            Anritsu Virtual Phone Module Object
+        """
+        return _VirtualPhone(self)
+
+    def get_PDN(self, pdn_number):
+        """ Returns the PDN Module Object
+
+        Args:
+            None
+
+        Returns:
+            Anritsu PDN Module Object
+        """
+        return _PacketDataNetwork(self, pdn_number)
+
+    def get_TriggerMessage(self):
+        """ Returns the Anritsu Trigger Message Module Object
+
+        Args:
+            None
+
+        Returns:
+            Anritsu Trigger Message Module Object
+        """
+        return _TriggerMessage(self)
+
+    def send_query(self, query, sock_timeout=10):
+        """ Sends a Query message to Anritsu and return response
+
+        Args:
+            query - Query string
+
+        Returns:
+            query response
+        """
+        self.log.info("--> {}".format(query))
+        querytoSend = (query + TERMINATOR).encode('utf-8')
+        self._sock.settimeout(sock_timeout)
+        try:
+            self._sock.send(querytoSend)
+            result = self._sock.recv(ANRITSU_SOCKET_BUFFER_SIZE).rstrip(TERMINATOR.encode('utf-8'))
+            response = result.decode('utf-8')
+            self.log.info('<-- {}'.format(response))
+            return response
+        except socket.timeout:
+            raise AnritsuError("Timeout: Response from Anritsu")
+        except socket.error:
+            raise AnritsuError("Socket Error")
+
+    def send_command(self, command, sock_timeout=20):
+        """ Sends a Command message to Anritsu
+
+        Args:
+            command - command string
+
+        Returns:
+            None
+        """
+        self.log.info("--> {}".format(command))
+        if self._error_reporting:
+            cmdToSend = (command + ";ERROR?" + TERMINATOR).encode('utf-8')
+            self._sock.settimeout(sock_timeout)
+            try:
+                self._sock.send(cmdToSend)
+                err = self._sock.recv(ANRITSU_SOCKET_BUFFER_SIZE).rstrip(TERMINATOR.encode('utf-8'))
+                error = int(err.decode('utf-8'))
+                if error != NO_ERROR:
+                    raise AnritsuError(error,  command)
+                else:
+                    # check operation status
+                    status = self.send_query("*OPC?")
+                    if int(status) != OPERATION_COMPLETE:
+                        raise AnritsuError("Operation not completed")
+            except socket.timeout:
+                raise AnritsuError("Timeout for Command Response from Anritsu")
+            except socket.error:
+                raise AnritsuError("Socket Error for Anritsu command")
+            except Exception as e:
+                raise AnritsuError(e,  command)
+        else:
+            cmdToSend = (command + TERMINATOR).encode('utf-8')
+            try:
+                self._sock.send(cmdToSend)
+            except socket.error:
+                raise AnritsuError("Socket Error", command)
+            return
+
+    def launch_smartstudio(self):
+        """ launch the Smart studio application
+            This should be done before stating simulation
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        # check the Smart Studio status . If Smart Studio doesn't exist ,
+        # start it.if it is running, stop it. Smart Studio should be in
+        # NOTRUN (Simulation Stopped) state to start new simulation
+        stat = self.send_query("STAT?", 30)
+        if stat == "NOTEXIST":
+            self.log.info("Launching Smart Studio Application,"
+                  "it takes about a minute.")
+            time_to_wait = SMARTSTUDIO_LAUNCH_WAIT_TIME
+            sleep_interval = 15
+            waiting_time = 0
+
+            err = self.send_command("RUN", 120)
+            stat = self.send_query("STAT?")
+            while stat != "NOTRUN":
+                time.sleep(sleep_interval)
+                waiting_time = waiting_time + sleep_interval
+                if waiting_time <= time_to_wait:
+                    stat = self.send_query("STAT?")
+                else:
+                    raise AnritsuError("Timeout: Smart Studio launch")
+        elif stat == "RUNNING":
+            # Stop simulation if necessary
+            self.send_command("STOP", 60)
+            stat = self.send_query("STAT?")
+
+        # The state of the Smart Studio should be NOTRUN at this point
+        # after the one of the steps from above
+        if stat != "NOTRUN":
+            self.log.info("Can not launch Smart Studio, "
+                  "please shut down all the Smart Studio SW components")
+            raise AnritsuError("Could not run SmartStudio")
+
+    def close_smartstudio(self):
+        """ Closes the Smart studio application
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        self.stop_simulation()
+        self.send_command("EXIT", 60)
+
+    def get_smartstudio_status(self):
+        """ Gets the Smart studio status
+
+        Args:
+            None
+
+        Returns:
+            Smart studio status
+        """
+        return self.send_query("STAT?")
+
+    def start_simulation(self):
+        """ Starting the simulation of the network model.
+            simulation model or simulation parameter file
+            should be set before starting the simulation
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        time_to_wait = SMARTSTUDIO_SIMULATION_START_WAIT_TIME
+        sleep_interval = 2
+        waiting_time = 0
+
+        self.send_command("START", 120)
+
+        #FIXME. Doing this since Anritsu has some issue with CALLSTAT? in CDMA1X
+        simmodel = self.get_simulation_model();
+        list = simmodel.split(',')
+        if list[0] == 'CDMA1X':
+            time.sleep(15)
+            self.log.info("CDMA1X: Not waiting for POWEROFF state, returning")
+            return
+
+        callstat = self.send_query("CALLSTAT?").split(",")
+        while callstat[0] != "POWEROFF":
+            time.sleep(sleep_interval)
+            waiting_time = waiting_time + sleep_interval
+            if waiting_time <= time_to_wait:
+                callstat = self.send_query("CALLSTAT?").split(",")
+            else:
+                raise AnritsuError("Timeout: Starting simulation")
+
+    def stop_simulation(self):
+        """ Stop simulation operation
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        stat = self.send_query("STAT?")
+        # Stop simulation if its is RUNNING
+        if stat == "RUNNING":
+            self.send_command("STOP", 60)
+            stat = self.send_query("STAT?")
+            if stat != "NOTRUN":
+                self.log.info("Failed to stop simulation")
+                raise AnritsuError("Failed to stop simulation")
+
+    def reset(self):
+        """ reset simulation parameters
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        self.send_command("*RST", COMMAND_COMPLETE_WAIT_TIME)
+
+    def load_simulation_paramfile(self, filepath):
+        """ loads simulation model parameter file
+        Args:
+          filepath : simulation model parameter file path
+
+        Returns:
+            None
+        """
+        self.stop_simulation()
+        cmd = "LOADSIMPARAM \"" + filepath + '\";ERROR?'
+        self.send_query(cmd)
+
+    def load_cell_paramfile(self, filepath):
+        """ loads cell model parameter file
+
+        Args:
+          filepath : cell model parameter file path
+
+        Returns:
+            None
+        """
+        self.stop_simulation()
+        cmd = "LOADCELLPARAM \"" + filepath + '\";ERROR?'
+        status = int(self.send_query(cmd))
+        if status != NO_ERROR :
+            raise AnritsuError(status, cmd)
+
+    def set_simulation_model(self, bts1, bts2=None, bts3=None, bts4=None):
+        """ Sets the simulation model
+
+        Args:
+            bts1 - BTS1 RAT
+            bts1 - BTS2 RAT
+            bts3 - Not used now
+            bts4 - Not used now
+
+        Returns:
+            None
+        """
+        self.stop_simulation()
+        simmodel = bts1.value
+        if bts2 is not None:
+            simmodel = simmodel + "," + bts2.value
+        cmd = "SIMMODEL " + simmodel
+        self.send_command(cmd, COMMAND_COMPLETE_WAIT_TIME)
+
+    def get_simulation_model(self):
+        """ Gets the simulation model
+
+        Args:
+            None
+
+        Returns:
+            Current simulation model
+        """
+        cmd = "SIMMODEL?"
+        return self.send_query(cmd)
+
+    def set_simulation_state_to_poweroff(self):
+        """ Sets the simulation state to POWER OFF
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        self.send_command("RESETSIMULATION POWEROFF")
+        time_to_wait = 30
+        sleep_interval = 2
+        waiting_time = 0
+
+        callstat = self.send_query("CALLSTAT?").split(",")
+        while callstat[0] != "POWEROFF":
+            time.sleep(sleep_interval)
+            waiting_time = waiting_time + sleep_interval
+            if waiting_time <= time_to_wait:
+                callstat = self.send_query("CALLSTAT?").split(",")
+            else:
+                break
+
+    def set_simulation_state_to_idle(self, btsnumber):
+        """ Sets the simulation state to IDLE
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        if not isinstance(btsnumber, BtsNumber):
+            raise ValueError(' The parameter should be of type "BtsNumber" ')
+        cmd = "RESETSIMULATION IDLE," + btsnumber.value
+        self.send_command(cmd)
+        time_to_wait = 30
+        sleep_interval = 2
+        waiting_time = 0
+
+        callstat = self.send_query("CALLSTAT?").split(",")
+        while callstat[0] != "IDLE":
+            time.sleep(sleep_interval)
+            waiting_time = waiting_time + sleep_interval
+            if waiting_time <= time_to_wait:
+                callstat = self.send_query("CALLSTAT?").split(",")
+            else:
+                break
+
+    def wait_for_registration_state(self):
+        """ Waits for UE registration state on Anritsu
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        self.log.info("wait for IDLE/COMMUNICATION state on anritsu.")
+        time_to_wait = REGISTRATION_STATE_WAIT_TIME
+        sleep_interval = 1
+        waiting_time = 0
+
+        #FIXME. Doing this since Anritsu has some issue with CALLSTAT? in CDMA1X
+        simmodel = self.get_simulation_model();
+        list = simmodel.split(',')
+        if list[0] == 'CDMA1X':
+            time.sleep(15)
+            self.log.info("CDMA1X: Not waiting for IDLE/COMMUNICATION state,"
+                          " returning")
+            return
+
+        callstat = self.send_query("CALLSTAT?").split(",")
+        while callstat[0] != "IDLE" and callstat[1] != "COMMUNICATION":
+            time.sleep(sleep_interval)
+            waiting_time = waiting_time + sleep_interval
+            if waiting_time <= time_to_wait:
+                callstat = self.send_query("CALLSTAT?").split(",")
+            else:
+                raise AnritsuError("UE failed to register on network")
+
+    def wait_for_communication_state(self):
+        """ Waits for UE communication state on Anritsu
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        self.log.info("wait for COMMUNICATION state on anritsu")
+        time_to_wait = COMMUNICATION_STATE_WAIT_TIME
+        sleep_interval = 1
+        waiting_time = 0
+
+        #FIXME. Doing this since Anritsu has some issue with CALLSTAT? in CDMA1X
+        simmodel = self.get_simulation_model();
+        list = simmodel.split(',')
+        if list[0] == 'CDMA1X':
+            time.sleep(15)
+            self.log.info("CDMA1X: Not waiting for COMMUNICATION state,"
+                          " returning")
+            return
+
+        callstat = self.send_query("CALLSTAT?").split(",")
+        while callstat[1] != "COMMUNICATION":
+            time.sleep(sleep_interval)
+            waiting_time = waiting_time + sleep_interval
+            if waiting_time <= time_to_wait:
+                callstat = self.send_query("CALLSTAT?").split(",")
+            else:
+                raise AnritsuError("UE failed to register on network")
+
+    def get_camping_cell(self):
+        """ Gets the current camping cell information
+
+        Args:
+          None
+
+        Returns:
+            returns a tuple (BTS number, RAT Technology) '
+        """
+        bts_number, rat_info = self.send_query("CAMPINGCELL?").split(",")
+        return bts_number, rat_info
+
+    def start_testcase(self):
+        """ Starts a test case on Anritsu
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        self.send_command("STARTTEST")
+
+    def get_testcase_status(self):
+        """ Gets the current test case status on Anritsu
+
+        Args:
+          None
+
+        Returns:
+            current test case status
+        """
+        return self.send_query("TESTSTAT?")
+
+    @property
+    def gateway_ipv4addr(self):
+        """ Gets the IPv4 address of the default gateway
+
+        Args:
+          None
+
+        Returns:
+            current UE status
+        """
+        return self.send_query("DGIPV4?")
+
+    @gateway_ipv4addr.setter
+    def gateway_ipv4addr(self, ipv4_addr):
+        """ sets the IPv4 address of the default gateway
+        Args:
+            ipv4_addr: IPv4 address of the default gateway
+
+        Returns:
+            None
+        """
+        cmd = "DGIPV4 " + ipv4_addr
+        self.send_command(cmd)
+
+    def get_ue_status(self):
+        """ Gets the current UE status on Anritsu
+
+        Args:
+          None
+
+        Returns:
+            current UE status
+        """
+        UE_STATUS_INDEX = 0
+        ue_status = self.send_query("CALLSTAT?").split(",")[UE_STATUS_INDEX]
+        return _PROCESS_STATES[ue_status]
+
+    def get_packet_status(self):
+        """ Gets the current Packet status on Anritsu
+
+        Args:
+          None
+
+        Returns:
+            current Packet status
+        """
+        PACKET_STATUS_INDEX = 1
+        packet_status = self.send_query("CALLSTAT?").split(",")[PACKET_STATUS_INDEX]
+        return _PROCESS_STATES[packet_status]
+
+    def disconnect(self):
+        """ Disconnect the Anritsu box from test PC
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        # exit smart studio application
+        self.close_smartstudio()
+        self._sock.close()
+
+    def machine_reboot(self):
+        """ Reboots the Anritsu Machine
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        self.send_command("REBOOT")
+
+    def save_sequence_log(self, fileName):
+        """ Saves the Anritsu Sequence logs to file
+
+        Args:
+          fileName: log file name
+
+        Returns:
+            None
+        """
+        cmd = 'SAVESEQLOG "{}"'.format(fileName)
+        self.send_command(cmd)
+
+    def clear_sequence_log(self):
+        """ Clears the Anritsu Sequence logs
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        self.send_command("CLEARSEQLOG")
+
+    def save_message_log(self, fileName):
+        """ Saves the Anritsu Message logs to file
+
+        Args:
+          fileName: log file name
+
+        Returns:
+            None
+        """
+        cmd = 'SAVEMSGLOG "{}"'.format(fileName)
+        self.send_command(cmd)
+
+    def clear_message_log(self):
+        """ Clears the Anritsu Message logs
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        self.send_command("CLEARMSGLOG")
+
+    def save_trace_log(self, fileName, fileType, overwrite, start, end):
+        """ Saves the Anritsu Trace logs
+
+        Args:
+          fileName: log file name
+          fileType: file type (BINARY, TEXT, H245,PACKET, CPLABE)
+          overwrite: whether to over write
+          start: starting trace number
+          end: ending trace number
+
+        Returns:
+            None
+        """
+        cmd = 'SAVETRACELOG "{}",{},{},{},{}'.format(fileName, fileType,
+                                                     overwrite, start, end)
+        self.send_command(cmd)
+
+    def send_cmas_lte_wcdma(self, serialNo, messageID, warningMessage):
+        """ Sends a CMAS message
+
+        Args:
+          serialNo: serial number of CMAS message
+          messageID: CMAS message ID
+          warningMessage:  CMAS Warning message
+
+        Returns:
+            None
+        """
+        cmd = ('PWSSENDWM 3GPP,"btsno=1&warningsystem=CMAS&serialno={}'
+               '&MessageID={}&wm={}"').format(serialNo, messageID,
+                                              warningMessage)
+        self.send_command(cmd)
+
+    def send_etws_lte_wcdma(self, serialNo, messageID, warningType, warningMessage,
+                  userAlertenable, popUpEnable):
+        """ Sends a ETWS message
+
+        Args:
+          serialNo: serial number of CMAS message
+          messageID: CMAS message ID
+          warningMessage:  CMAS Warning message
+
+        Returns:
+            None
+        """
+        cmd = ('PWSSENDWM 3GPP,"btsno=1&warningsystem=ETWS&serialno={}&'
+               'primary=ON&PrimaryMessageID={}&Secondary=ON&SecondaryMessageID={}'
+               '&warningtype={}&wm={}&useralert={}&popup={}&languagecode=en"').format(
+               serialNo, messageID, messageID, warningType, warningMessage,
+               userAlertenable, popUpEnable)
+        self.send_command(cmd)
+
+    def send_cmas_etws_cdma1x(self, message_id, service_category, alert_ext,
+            response_type, severity, urgency, certainty):
+        """ Sends a CMAS/ETWS message on CDMA 1X
+
+        Args:
+          serviceCategory: service category of alert
+          messageID: message ID
+          alertText: Warning message
+
+        Returns:
+            None
+        """
+        param = ('\"btsno=1&ServiceCategory={}&MessageID={}&AlertText={}&CharSet=ASCII'
+                '&ResponseType={}&Severity={}&Urgency={}&Certainty={}\"').format(
+                service_category, message_id, alert_ext, response_type, severity,
+                urgency, certainty)
+        cmd = ('PWSSENDWM 3GPP2,{}').format(param)
+        self.send_command(cmd)
+
+    @property
+    def csfb_type(self):
+        """ Gets the current CSFB type
+
+        Args:
+            None
+
+        Returns:
+            current CSFB type
+        """
+        return self.send_query("SIMMODELEX? CSFB")
+
+    @csfb_type.setter
+    def csfb_type(self, csfb_type):
+        """ sets the CSFB type
+        Args:
+            csfb_type: CSFB type
+
+        Returns:
+            None
+        """
+        if not isinstance(csfb_type, CsfbType):
+            raise ValueError('The parameter should be of type "CsfbType" ')
+        cmd = "SIMMODELEX CSFB," + csfb_type.value
+        self.send_command(cmd)
+
+    @property
+    def csfb_return_to_eutran(self):
+        """ Gets the current return to EUTRAN status
+
+        Args:
+            None
+
+        Returns:
+            current return to EUTRAN status
+        """
+        return self.send_query("SIMMODELEX? RETEUTRAN")
+
+    @csfb_return_to_eutran.setter
+    def csfb_return_to_eutran(self, enable):
+        """ sets the return to EUTRAN feature
+        Args:
+            enable: enable/disable return to EUTRAN feature
+
+        Returns:
+            None
+        """
+        if not isinstance(enable, ReturnToEUTRAN):
+            raise ValueError('The parameter should be of type "ReturnToEUTRAN"')
+        cmd = "SIMMODELEX RETEUTRAN," + enable.value
+        self.send_command(cmd)
+
+    def set_packet_preservation(self):
+        """ Set packet state to Preservation
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        cmd = "OPERATEPACKET PRESERVATION"
+        self.send_command(cmd)
+
+    def set_packet_dormant(self):
+        """ Set packet state to Dormant
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        cmd = "OPERATEPACKET DORMANT"
+        self.send_command(cmd)
+
+    def get_ue_identity(self, identity_type):
+        """ Get the UE identity IMSI, IMEI, IMEISV
+
+        Args:
+            identity_type : IMSI/IMEI/IMEISV
+
+        Returns:
+            IMSI/IMEI/IMEISV value
+        """
+        bts, rat = self.get_camping_cell()
+        if rat == BtsTechnology.LTE.value:
+            identity_request = TriggerMessageIDs.IDENTITY_REQUEST_LTE.value
+            if identity_type == UEIdentityType.IMSI:
+                userdata = IMSI_READ_USERDATA_LTE
+            elif identity_type == UEIdentityType.IMEI:
+                userdata = IMEI_READ_USERDATA_LTE
+            elif identity_type == UEIdentityType.IMEISV:
+                userdata = IMEISV_READ_USERDATA_LTE
+            else:
+                return None
+        elif rat == BtsTechnology.WCDMA.value:
+            identity_request = TriggerMessageIDs.IDENTITY_REQUEST_WCDMA.value
+            if identity_type == UEIdentityType.IMSI:
+                userdata = IMSI_READ_USERDATA_WCDMA
+            elif identity_type == UEIdentityType.IMEI:
+                userdata = IMEI_READ_USERDATA_WCDMA
+            elif identity_type == UEIdentityType.IMEISV:
+                userdata = IMEISV_READ_USERDATA_WCDMA
+            else:
+                return None
+        elif rat == BtsTechnology.GSM.value:
+            identity_request = TriggerMessageIDs.IDENTITY_REQUEST_GSM.value
+            if identity_type == UEIdentityType.IMSI:
+                userdata = IMSI_READ_USERDATA_GSM
+            elif identity_type == UEIdentityType.IMEI:
+                userdata = IMEI_READ_USERDATA_GSM
+            elif identity_type == UEIdentityType.IMEISV:
+                userdata = IMEISV_READ_USERDATA_GSM
+            else:
+                return None
+        else:
+            return None
+
+        self.send_command("TMMESSAGEMODE {},USERDATA".format(identity_request))
+        time.sleep(SETTLING_TIME)
+        self.send_command("TMUSERDATA {}, {}, {}".format(identity_request,
+                                              userdata, IDENTITY_REQ_DATA_LEN))
+        time.sleep(SETTLING_TIME)
+        self.send_command("TMSENDUSERMSG {}".format(identity_request))
+        time.sleep(WAIT_TIME_IDENTITY_RESPONSE)
+        # Go through sequence log and find the identity response message
+        target = '"{}"'.format(identity_type.value)
+        seqlog = self.send_query("SEQLOG?").split(",")
+        while (target not in seqlog):
+            index = int(seqlog[0]) - 1
+            if index < SEQ_LOG_MESSAGE_START_INDEX:
+                self.log.error("Can not find "+ target)
+                return None
+            seqlog = self.send_query("SEQLOG? %d" % index).split(",")
+        return (seqlog[-1])
+
+class _AnritsuTestCases:
+    '''Class to interact with the MD8475 supported test procedures '''
+
+    def __init__(self, anritsu):
+        self._anritsu = anritsu
+
+    @property
+    def procedure(self):
+        """ Gets the current Test Procedure type
+
+        Args:
+            None
+
+        Returns:
+            One of TestProcedure type values
+        """
+        return self._anritsu.send_query("TESTPROCEDURE?")
+
+    @procedure.setter
+    def procedure(self, procedure):
+        """ sets the Test Procedure type
+        Args:
+            procedure: One of TestProcedure type values
+
+        Returns:
+            None
+        """
+        if not isinstance(procedure, TestProcedure):
+            raise ValueError('The parameter should be of type "TestProcedure" ')
+        cmd = "TESTPROCEDURE " + procedure.value
+        self._anritsu.send_command(cmd)
+
+    @property
+    def bts_direction(self):
+        """ Gets the current Test direction
+
+         Args:
+            None
+
+        Returns:
+            Current Test direction eg:BTS2,BTS1
+        """
+        return self._anritsu.send_query("TESTBTSDIRECTION?")
+
+    @bts_direction.setter
+    def bts_direction(self, direction):
+        """ sets the Test direction  eg: BTS1 to BTS2 '''
+
+        Args:
+            direction: tuple (from-bts,to_bts) of type BtsNumber
+
+        Returns:
+            None
+        """
+        if not isinstance(direction, tuple) or len(direction) is not 2:
+            raise ValueError("Pass a tuple with two items")
+        from_bts, to_bts = direction
+        if (isinstance(from_bts, BtsNumber) and isinstance(to_bts, BtsNumber)):
+            cmd = "TESTBTSDIRECTION {},{}".format(from_bts.value, to_bts.value)
+            self._anritsu.send_command(cmd)
+        else:
+            raise ValueError(' The parameters should be of type "BtsNumber" ')
+
+    @property
+    def registration_timeout(self):
+        """ Gets the current Test registration timeout
+
+        Args:
+            None
+
+        Returns:
+            Current test registration timeout value
+        """
+        return self._anritsu.send_query("TESTREGISTRATIONTIMEOUT?")
+
+    @registration_timeout.setter
+    def registration_timeout(self, timeout_value):
+        """ sets the Test registration timeout value
+        Args:
+            timeout_value: test registration timeout value
+
+        Returns:
+            None
+        """
+        cmd = "TESTREGISTRATIONTIMEOUT " + str(timeout_value)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def power_control(self):
+        """ Gets the power control enabled/disabled status for test case
+
+        Args:
+            None
+
+        Returns:
+            current power control enabled/disabled status
+        """
+        return self._anritsu.send_query("TESTPOWERCONTROL?")
+
+    @power_control.setter
+    def power_control(self, enable):
+        """ Sets the power control enabled/disabled status for test case
+
+        Args:
+            enable:  enabled/disabled
+
+        Returns:
+            None
+        """
+        if not isinstance(enable, TestPowerControl):
+            raise ValueError(' The parameter should be of type'
+                             ' "TestPowerControl" ')
+        cmd = "TESTPOWERCONTROL " + enable.value
+        self._anritsu.send_command(cmd)
+
+    @property
+    def measurement_LTE(self):
+        """ Checks measurement status for LTE test case
+
+        Args:
+            None
+
+        Returns:
+            Enabled/Disabled
+        """
+        return self._anritsu.send_query("TESTMEASUREMENT? LTE")
+
+    @measurement_LTE.setter
+    def measurement_LTE(self, enable):
+        """ Sets the measurement enabled/disabled status for LTE test case
+
+        Args:
+            enable:  enabled/disabled
+
+        Returns:
+            None
+        """
+        if not isinstance(enable, TestMeasurement):
+            raise ValueError(' The parameter should be of type'
+                             ' "TestMeasurement" ')
+        cmd = "TESTMEASUREMENT LTE," + enable.value
+        self._anritsu.send_command(cmd)
+
+    @property
+    def measurement_WCDMA(self):
+        """ Checks measurement status for WCDMA test case
+
+        Args:
+            None
+
+        Returns:
+            Enabled/Disabled
+        """
+        return self._anritsu.send_query("TESTMEASUREMENT? WCDMA")
+
+    @measurement_WCDMA.setter
+    def measurement_WCDMA(self, enable):
+        """ Sets the measurement enabled/disabled status for WCDMA test case
+
+        Args:
+            enable:  enabled/disabled
+
+        Returns:
+            None
+        """
+        if not isinstance(enable, TestMeasurement):
+            raise ValueError(' The parameter should be of type'
+                             ' "TestMeasurement" ')
+        cmd = "TESTMEASUREMENT WCDMA," + enable.value
+        self._anritsu.send_command(cmd)
+
+    @property
+    def measurement_TDSCDMA(self):
+        """ Checks measurement status for TDSCDMA test case
+
+        Args:
+            None
+
+        Returns:
+            Enabled/Disabled
+        """
+        return self._anritsu.send_query("TESTMEASUREMENT? TDSCDMA")
+
+    @measurement_TDSCDMA.setter
+    def measurement_WCDMA(self, enable):
+        """ Sets the measurement enabled/disabled status for TDSCDMA test case
+
+        Args:
+            enable:  enabled/disabled
+
+        Returns:
+            None
+        """
+        if not isinstance(enable, TestMeasurement):
+            raise ValueError(' The parameter should be of type'
+                             ' "TestMeasurement" ')
+        cmd = "TESTMEASUREMENT TDSCDMA," + enable.value
+        self._anritsu.send_command(cmd)
+
+    def set_pdn_targeteps(self, pdn_order, pdn_number=1):
+        """ Sets PDN to connect as a target when performing the
+           test case for packet handover
+
+        Args:
+            pdn_order:  PRIORITY/USER
+            pdn_number: Target PDN number
+
+        Returns:
+            None
+        """
+        cmd = "TESTPDNTARGETEPS " + pdn_order
+        if pdn_order == "USER":
+            cmd = cmd + "," + str(pdn_number)
+        self._anritsu.send_command(cmd)
+
+
+class _BaseTransceiverStation:
+    '''Class to interact different BTS supported by MD8475 '''
+    def __init__(self, anritsu, btsnumber):
+        if not isinstance(btsnumber, BtsNumber):
+            raise ValueError(' The parameter should be of type "BtsNumber" ')
+        self._bts_number = btsnumber.value
+        self._anritsu = anritsu
+
+    @property
+    def output_level(self):
+        """ Gets the Downlink power of the cell
+
+        Args:
+            None
+
+        Returns:
+            DL Power level
+        """
+        cmd = "OLVL? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @output_level.setter
+    def output_level(self, level):
+        """ Sets the Downlink power of the cell
+
+        Args:
+            level: Power level
+
+        Returns:
+            None
+        """
+        cmd = "OLVL {},{}".format(level, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def input_level(self):
+        """ Gets the reference power of the cell
+
+        Args:
+            None
+
+        Returns:
+            Reference Power level
+        """
+        cmd = "RFLVL? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @input_level.setter
+    def input_level(self, level):
+        """ Sets the reference power of the cell
+
+        Args:
+            level: Power level
+
+        Returns:
+            None
+        """
+        cmd = "RFLVL {},{}".format(level, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def band(self):
+        """ Gets the Band of the cell
+
+        Args:
+            None
+
+        Returns:
+            Cell band
+        """
+        cmd = "BAND? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @band.setter
+    def band(self, band):
+        """ Sets the Band of the cell
+
+        Args:
+            band: Band of the cell
+
+        Returns:
+            None
+        """
+        cmd = "BAND {},{}".format(band, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def bandwidth(self):
+        """ Gets the channel bandwidth of the cell
+
+        Args:
+            None
+
+        Returns:
+            channel bandwidth
+        """
+        cmd = "BANDWIDTH? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @bandwidth.setter
+    def bandwidth(self, bandwidth):
+        """ Sets the channel bandwidth of the cell
+
+        Args:
+            bandwidth: channel bandwidth  of the cell
+
+        Returns:
+            None
+        """
+        if not isinstance(bandwidth, BtsBandwidth):
+            raise ValueError(' The parameter should be of type "BtsBandwidth"')
+        cmd = "BANDWIDTH {},{}".format(bandwidth.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def dl_bandwidth(self):
+        """ Gets the downlink bandwidth of the cell
+
+        Args:
+            None
+
+        Returns:
+            downlink bandwidth
+        """
+        cmd = "DLBANDWIDTH? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @dl_bandwidth.setter
+    def dl_bandwidth(self, bandwidth):
+        """ Sets the downlink bandwidth of the cell
+
+        Args:
+            bandwidth: downlink bandwidth of the cell
+
+        Returns:
+            None
+        """
+        if not isinstance(bandwidth, BtsBandwidth):
+            raise ValueError(' The parameter should be of type "BtsBandwidth"')
+        cmd = "DLBANDWIDTH {},{}".format(bandwidth.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def ul_bandwidth(self):
+        """ Gets the uplink bandwidth of the cell
+
+        Args:
+            None
+
+        Returns:
+            uplink bandwidth
+        """
+        cmd = "ULBANDWIDTH? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @ul_bandwidth.setter
+    def ul_bandwidth(self, bandwidth):
+        """ Sets the uplink bandwidth of the cell
+
+        Args:
+            bandwidth: uplink bandwidth of the cell
+
+        Returns:
+            None
+        """
+        if not isinstance(bandwidth, BtsBandwidth):
+            raise ValueError(' The parameter should be of type "BtsBandwidth" ')
+        cmd = "ULBANDWIDTH {},{}".format(bandwidth.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def packet_rate(self):
+        """ Gets the packet rate of the cell
+
+        Args:
+            None
+
+        Returns:
+            packet rate
+        """
+        cmd = "PACKETRATE? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @packet_rate.setter
+    def packet_rate(self, packetrate):
+        """ Sets the packet rate of the cell
+
+        Args:
+            packetrate: packet rate of the cell
+
+        Returns:
+            None
+        """
+        if not isinstance(packetrate, BtsPacketRate):
+            raise ValueError(' The parameter should be of type'
+                             ' "BtsPacketRate" ')
+        cmd = "PACKETRATE {},{}".format(packetrate.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def ul_windowsize(self):
+        """ Gets the uplink window size of the cell
+
+        Args:
+            None
+
+        Returns:
+            uplink window size
+        """
+        cmd = "ULWINSIZE? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @ul_windowsize.setter
+    def ul_windowsize(self, windowsize):
+        """ Sets the uplink window size of the cell
+
+        Args:
+            windowsize: uplink window size of the cell
+
+        Returns:
+            None
+        """
+        if not isinstance(windowsize, BtsPacketWindowSize):
+            raise ValueError(' The parameter should be of type'
+                             ' "BtsPacketWindowSize" ')
+        cmd = "ULWINSIZE {},{}".format(windowsize.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def dl_windowsize(self):
+        """ Gets the downlink window size of the cell
+
+        Args:
+            None
+
+        Returns:
+            downlink window size
+        """
+        cmd = "DLWINSIZE? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @dl_windowsize.setter
+    def dl_windowsize(self, windowsize):
+        """ Sets the downlink window size of the cell
+
+        Args:
+            windowsize: downlink window size of the cell
+
+        Returns:
+            None
+        """
+        if not isinstance(windowsize, BtsPacketWindowSize):
+            raise ValueError(' The parameter should be of type'
+                             ' "BtsPacketWindowSize" ')
+        cmd = "DLWINSIZE {},{}".format(windowsize.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def service_state(self):
+        """ Gets the service state of BTS
+
+        Args:
+            None
+
+        Returns:
+            service state IN/OUT
+        """
+        cmd = "OUTOFSERVICE? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @service_state.setter
+    def service_state(self, service_state):
+        """ Sets the service state of BTS
+
+        Args:
+            service_state: service state of BTS , IN/OUT
+
+        Returns:
+            None
+        """
+        if not isinstance(service_state, BtsServiceState):
+            raise ValueError(' The parameter should be of type'
+                             ' "BtsServiceState" ')
+        cmd = "OUTOFSERVICE {},{}".format(service_state.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def cell_barred(self):
+        """ Gets the Cell Barred state of the cell
+
+        Args:
+            None
+
+        Returns:
+            one of BtsCellBarred value
+        """
+        cmd = "CELLBARRED?" + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @cell_barred.setter
+    def cell_barred(self, barred_option):
+        """ Sets the Cell Barred state of the cell
+
+        Args:
+            barred_option: Cell Barred state of the cell
+
+        Returns:
+            None
+        """
+        if not isinstance(barred_option, BtsCellBarred):
+            raise ValueError(' The parameter should be of type'
+                             ' "BtsCellBarred" ')
+        cmd = "CELLBARRED {},{}".format(barred_option.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def accessclass_barred(self):
+        """ Gets the Access Class Barred state of the cell
+
+        Args:
+            None
+
+        Returns:
+            one of BtsAccessClassBarred value
+        """
+        cmd = "ACBARRED? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @accessclass_barred.setter
+    def accessclass_barred(self, barred_option):
+        """ Sets the Access Class Barred state of the cell
+
+        Args:
+            barred_option: Access Class Barred state of the cell
+
+        Returns:
+            None
+        """
+        if not isinstance(barred_option, BtsAccessClassBarred):
+            raise ValueError(' The parameter should be of type'
+                             ' "BtsAccessClassBarred" ')
+        cmd = "ACBARRED {},{}".format(barred_option.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def lteemergency_ac_barred(self):
+        """ Gets the LTE emergency Access Class Barred state of the cell
+
+        Args:
+            None
+
+        Returns:
+            one of BtsLteEmergencyAccessClassBarred value
+        """
+        cmd = "LTEEMERGENCYACBARRED? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @lteemergency_ac_barred.setter
+    def lteemergency_ac_barred(self, barred_option):
+        """ Sets the LTE emergency Access Class Barred state of the cell
+
+        Args:
+            barred_option: Access Class Barred state of the cell
+
+        Returns:
+            None
+        """
+        if not isinstance(barred_option, BtsLteEmergencyAccessClassBarred):
+            raise ValueError(' The parameter should be of type'
+                             ' "BtsLteEmergencyAccessClassBarred" ')
+        cmd = "LTEEMERGENCYACBARRED {},{}".format(barred_option.value,
+                                                  self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def mcc(self):
+        """ Gets the MCC of the cell
+
+        Args:
+            None
+
+        Returns:
+            MCC of the cell
+        """
+        cmd = "MCC? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @mcc.setter
+    def mcc(self, mcc_code):
+        """ Sets the MCC of the cell
+
+        Args:
+            mcc_code: MCC of the cell
+
+        Returns:
+            None
+        """
+        cmd = "MCC {},{}".format(mcc_code, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def mnc(self):
+        """ Gets the MNC of the cell
+
+        Args:
+            None
+
+        Returns:
+            MNC of the cell
+        """
+        cmd = "MNC? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @mnc.setter
+    def mnc(self, mnc_code):
+        """ Sets the MNC of the cell
+
+        Args:
+            mnc_code: MNC of the cell
+
+        Returns:
+            None
+        """
+        cmd = "MNC {},{}".format(mnc_code, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def nw_fullname_enable(self):
+        """ Gets the network full name enable status
+
+        Args:
+            None
+
+        Returns:
+            one of BtsNwNameEnable value
+        """
+        cmd = "NWFNAMEON? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @nw_fullname_enable.setter
+    def nw_fullname_enable(self, enable):
+        """ Sets the network full name enable status
+
+        Args:
+            enable: network full name enable status
+
+        Returns:
+            None
+        """
+        if not isinstance(enable, BtsNwNameEnable):
+            raise ValueError(' The parameter should be of type'
+                             ' "BtsNwNameEnable" ')
+        cmd = "NWFNAMEON {},{}".format(enable.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def nw_fullname(self):
+        """ Gets the network full name
+
+        Args:
+            None
+
+        Returns:
+            Network fulll name
+        """
+        cmd = "NWFNAME? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @nw_fullname.setter
+    def nw_fullname(self, fullname):
+        """ Sets the network full name
+
+        Args:
+            fullname: network full name
+
+        Returns:
+            None
+        """
+        cmd = "NWFNAME {},{}".format(fullname, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def nw_shortname_enable(self):
+        """ Gets the network short name enable status
+
+        Args:
+            None
+
+        Returns:
+            one of BtsNwNameEnable value
+        """
+        cmd = "NWSNAMEON? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @nw_shortname_enable.setter
+    def nw_shortname_enable(self, enable):
+        """ Sets the network short name enable status
+
+        Args:
+            enable: network short name enable status
+
+        Returns:
+            None
+        """
+        if not isinstance(enable, BtsNwNameEnable):
+            raise ValueError(' The parameter should be of type'
+                             ' "BtsNwNameEnable" ')
+        cmd = "NWSNAMEON {},{}".format(enable.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def nw_shortname(self):
+        """ Gets the network short name
+
+        Args:
+            None
+
+        Returns:
+            Network short name
+        """
+        cmd = "NWSNAME? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @nw_shortname.setter
+    def nw_shortname(self, shortname):
+        """ Sets the network short name
+
+        Args:
+            shortname: network short name
+
+        Returns:
+            None
+        """
+        cmd = "NWSNAME {},{}".format(shortname, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    def apply_parameter_changes(self):
+        """ apply the parameter changes at run time
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        cmd = "APPLYPARAM"
+        self._anritsu.send_command(cmd)
+
+    @property
+    def wcdma_ctch(self):
+        """ Gets the WCDMA CTCH enable/disable status
+
+        Args:
+            None
+
+        Returns:
+            one of CTCHSetup values
+        """
+        cmd = "CTCHPARAMSETUP? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @wcdma_ctch.setter
+    def wcdma_ctch(self, enable):
+        """ Sets the WCDMA CTCH enable/disable status
+
+        Args:
+            enable: WCDMA CTCH enable/disable status
+
+        Returns:
+            None
+        """
+        cmd = "CTCHPARAMSETUP {},{}".format(enable.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def lac(self):
+        """ Gets the Location Area Code of the cell
+
+        Args:
+            None
+
+        Returns:
+            LAC value
+        """
+        cmd = "LAC? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @lac.setter
+    def lac(self, lac):
+        """ Sets the Location Area Code of the cell
+
+        Args:
+            lac: Location Area Code of the cell
+
+        Returns:
+            None
+        """
+        cmd = "LAC {},{}".format(lac, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def rac(self):
+        """ Gets the Routing Area Code of the cell
+
+        Args:
+            None
+
+        Returns:
+            RAC value
+        """
+        cmd = "RAC? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @rac.setter
+    def rac(self, rac):
+        """ Sets the Routing Area Code of the cell
+
+        Args:
+            rac: Routing Area Code of the cell
+
+        Returns:
+            None
+        """
+        cmd = "RAC {},{}".format(rac, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def dl_channel(self):
+        """ Gets the downlink channel number of the cell
+
+        Args:
+            None
+
+        Returns:
+            RAC value
+        """
+        cmd = "DLCHAN? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @dl_channel.setter
+    def dl_channel(self, channel):
+        """ Sets the downlink channel number of the cell
+
+        Args:
+            channel: downlink channel number of the cell
+
+        Returns:
+            None
+        """
+        cmd = "DLCHAN {},{}".format(channel, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def sector1_mcc(self):
+        """ Gets the sector 1 MCC of the CDMA cell
+
+        Args:
+            None
+
+        Returns:
+            sector 1 mcc
+        """
+        cmd = "S1MCC? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @sector1_mcc.setter
+    def sector1_mcc(self, mcc):
+        """ Sets the sector 1 MCC of the CDMA cell
+
+        Args:
+            mcc: sector 1 MCC of the CDMA cell
+
+        Returns:
+            None
+        """
+        cmd = "S1MCC {},{}".format(mcc, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def sector1_sid(self):
+        """ Gets the sector 1 system ID of the CDMA cell
+
+        Args:
+            None
+
+        Returns:
+            sector 1 system Id
+        """
+        cmd = "S1SID? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @sector1_sid.setter
+    def sector1_sid(self, sid):
+        """ Sets the sector 1 system ID of the CDMA cell
+
+        Args:
+            sid: sector 1 system ID of the CDMA cell
+
+        Returns:
+            None
+        """
+        cmd = "S1SID {},{}".format(sid, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def sector1_nid(self):
+        """ Gets the sector 1 network ID of the CDMA cell
+
+        Args:
+            None
+
+        Returns:
+            sector 1 network Id
+        """
+        cmd = "S1NID? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @sector1_nid.setter
+    def sector1_nid(self, nid):
+        """ Sets the sector 1 network ID of the CDMA cell
+
+        Args:
+            nid: sector 1 network ID of the CDMA cell
+
+        Returns:
+            None
+        """
+        cmd = "S1NID {},{}".format(nid, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def sector1_baseid(self):
+        """ Gets the sector 1 Base ID of the CDMA cell
+
+        Args:
+            None
+
+        Returns:
+            sector 1 Base Id
+        """
+        cmd = "S1BASEID? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @sector1_baseid.setter
+    def sector1_baseid(self, baseid):
+        """ Sets the sector 1 Base ID of the CDMA cell
+
+        Args:
+            baseid: sector 1 Base ID of the CDMA cell
+
+        Returns:
+            None
+        """
+        cmd = "S1BASEID {},{}".format(baseid, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def sector1_latitude(self):
+        """ Gets the sector 1 latitude of the CDMA cell
+
+        Args:
+            None
+
+        Returns:
+            sector 1 latitude
+        """
+        cmd = "S1LATITUDE? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @sector1_latitude.setter
+    def sector1_latitude(self, latitude):
+        """ Sets the sector 1 latitude of the CDMA cell
+
+        Args:
+            latitude: sector 1 latitude of the CDMA cell
+
+        Returns:
+            None
+        """
+        cmd = "S1LATITUDE {},{}".format(latitude, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def sector1_longitude(self):
+        """ Gets the sector 1 longitude of the CDMA cell
+
+        Args:
+            None
+
+        Returns:
+            sector 1 longitude
+        """
+        cmd = "S1LONGITUDE? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @sector1_longitude.setter
+    def sector1_longitude(self, longitude):
+        """ Sets the sector 1 longitude of the CDMA cell
+
+        Args:
+            longitude: sector 1 longitude of the CDMA cell
+
+        Returns:
+            None
+        """
+        cmd = "S1LONGITUDE {},{}".format(longitude, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def cell_id(self):
+        """ Gets the cell identity of the cell
+
+        Args:
+            None
+
+        Returns:
+            cell identity
+        """
+        cmd = "CELLID? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @cell_id.setter
+    def cell_id(self, cell_id):
+        """ Sets the cell identity of the cell
+
+        Args:
+            cell_id: cell identity of the cell
+
+        Returns:
+            None
+        """
+        cmd = "CELLID {},{}".format(cell_id, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def physical_cellid(self):
+        """ Gets the physical cell id of the cell
+
+        Args:
+            None
+
+        Returns:
+            physical cell id
+        """
+        cmd = "PHYCELLID? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @physical_cellid.setter
+    def physical_cellid(self, physical_cellid):
+        """ Sets the physical cell id of the cell
+
+        Args:
+            physical_cellid: physical cell id of the cell
+
+        Returns:
+            None
+        """
+        cmd = "PHYCELLID {},{}".format(physical_cellid, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def gsm_mcs_dl(self):
+        """ Gets the Modulation and Coding scheme (DL) of the GSM cell
+
+        Args:
+            None
+
+        Returns:
+            DL MCS
+        """
+        cmd = "DLMCS? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @gsm_mcs_dl.setter
+    def gsm_mcs_dl(self, mcs_dl):
+        """ Sets the Modulation and Coding scheme (DL) of the GSM cell
+
+        Args:
+            mcs_dl: Modulation and Coding scheme (DL) of the GSM cell
+
+        Returns:
+            None
+        """
+        cmd = "DLMCS {},{}".format(mcs_dl, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def gsm_mcs_ul(self):
+        """ Gets the Modulation and Coding scheme (UL) of the GSM cell
+
+        Args:
+            None
+
+        Returns:
+            UL MCS
+        """
+        cmd = "ULMCS? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @gsm_mcs_ul.setter
+    def gsm_mcs_ul(self, mcs_ul):
+        """ Sets the Modulation and Coding scheme (UL) of the GSM cell
+
+        Args:
+            mcs_ul:Modulation and Coding scheme (UL) of the GSM cell
+
+        Returns:
+            None
+        """
+        cmd = "ULMCS {},{}".format(mcs_ul, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def lte_mcs_dl(self):
+        """ Gets the Modulation and Coding scheme (DL) of the LTE cell
+
+        Args:
+            None
+
+        Returns:
+            DL MCS
+        """
+        cmd = "DLIMCS? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @lte_mcs_dl.setter
+    def lte_mcs_dl(self, mcs_dl):
+        """ Sets the Modulation and Coding scheme (DL) of the LTE cell
+
+        Args:
+            mcs_dl: Modulation and Coding scheme (DL) of the LTE cell
+
+        Returns:
+            None
+        """
+        cmd = "DLIMCS {},{}".format(mcs_dl, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def lte_mcs_ul(self):
+        """ Gets the Modulation and Coding scheme (UL) of the LTE cell
+
+        Args:
+            None
+
+        Returns:
+            UL MCS
+        """
+        cmd = "ULIMCS? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @lte_mcs_ul.setter
+    def lte_mcs_ul(self, mcs_ul):
+        """ Sets the Modulation and Coding scheme (UL) of the LTE cell
+
+        Args:
+            mcs_ul: Modulation and Coding scheme (UL) of the LTE cell
+
+        Returns:
+            None
+        """
+        cmd = "ULIMCS {},{}".format(mcs_ul, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def nrb_dl(self):
+        """ Gets the Downlink N Resource Block of the cell
+
+        Args:
+            None
+
+        Returns:
+            Downlink NRB
+        """
+        cmd = "DLNRB? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @nrb_dl.setter
+    def nrb_dl(self, blocks):
+        """ Sets the Downlink N Resource Block of the cell
+
+        Args:
+            blocks: Downlink N Resource Block of the cell
+
+        Returns:
+            None
+        """
+        cmd = "DLNRB {},{}".format(blocks, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def nrb_ul(self):
+        """ Gets the uplink N Resource Block of the cell
+
+        Args:
+            None
+
+        Returns:
+            uplink NRB
+        """
+        cmd = "ULNRB? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @nrb_ul.setter
+    def nrb_ul(self, blocks):
+        """ Sets the uplink N Resource Block of the cell
+
+        Args:
+            blocks: uplink N Resource Block of the cell
+
+        Returns:
+            None
+        """
+        cmd = "ULNRB {},{}".format(blocks, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def neighbor_cell_mode(self):
+        """ Gets the neighbor cell mode
+
+        Args:
+            None
+
+        Returns:
+            current neighbor cell mode
+        """
+        cmd = "NCLIST? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @neighbor_cell_mode.setter
+    def neighbor_cell_mode(self, mode):
+        """ Sets the neighbor cell mode
+
+        Args:
+            mode: neighbor cell mode , DEFAULT/ USERDATA
+
+        Returns:
+            None
+        """
+        cmd = "NCLIST {},{}".format(mode, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+
+    def get_neighbor_cell_type(self, system, index):
+        """ Gets the neighbor cell type
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell type
+        """
+        cmd = "NCTYPE? {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+
+    def set_neighbor_cell_type(self, system, index, cell_type):
+        """ Sets the neighbor cell type
+
+        Args:
+            system: simulation model of neighbor cell
+                   LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+            cell_type: cell type
+                BTS1, BTS2, BTS3, BTS4,CELLNAME, DISABLE
+
+        Returns:
+            None
+        """
+        cmd = "NCTYPE {},{},{},{}".format(system, index, cell_type, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    def get_neighbor_cell_name(self, system, index):
+        """ Gets the neighbor cell name
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell name
+        """
+        cmd = "NCCELLNAME? {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+
+    def set_neighbor_cell_name(self, system, index, name):
+        """ Sets the neighbor cell name
+
+        Args:
+            system: simulation model of neighbor cell
+                   LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+            name: cell name
+
+        Returns:
+            None
+        """
+        cmd = "NCCELLNAME {},{},{},{}".format(system, index, name, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+
+    def get_neighbor_cell_mcc(self, system, index):
+        """ Gets the neighbor cell mcc
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell mcc
+        """
+        cmd = "NCMCC? {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    def get_neighbor_cell_mnc(self, system, index):
+        """ Gets the neighbor cell mnc
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell mnc
+        """
+        cmd = "NCMNC? {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    def get_neighbor_cell_id(self, system, index):
+        """ Gets the neighbor cell id
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell id
+        """
+        cmd = "NCCELLID? {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    def get_neighbor_cell_tac(self, system, index):
+        """ Gets the neighbor cell tracking area code
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell tracking area code
+        """
+        cmd = "NCTAC? {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    def get_neighbor_cell_dl_channel(self, system, index):
+        """ Gets the neighbor cell downlink channel
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell tracking downlink channel
+        """
+        cmd = "NCDLCHAN? {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    def get_neighbor_cell_dl_bandwidth(self, system, index):
+        """ Gets the neighbor cell downlink bandwidth
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell tracking downlink bandwidth
+        """
+        cmd = "NCDLBANDWIDTH {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    def get_neighbor_cell_pcid(self, system, index):
+        """ Gets the neighbor cell physical cell id
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell physical cell id
+        """
+        cmd = "NCPHYCELLID {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    def get_neighbor_cell_lac(self, system, index):
+        """ Gets the neighbor cell location area code
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell location area code
+        """
+        cmd = "NCLAC {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    def get_neighbor_cell_rac(self, system, index):
+        """ Gets the neighbor cell routing area code
+
+        Args:
+            system: simulation model of neighbor cell
+                    LTE, WCDMA, TDSCDMA, GSM, CDMA1X,EVDO
+            index: Index of neighbor cell
+
+        Returns:
+            neighbor cell routing area code
+        """
+        cmd = "NCRAC {},{},{}".format(system, index, self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    @property
+    def primary_scrambling_code(self):
+        """ Gets the primary scrambling code for WCDMA cell
+
+        Args:
+            None
+
+        Returns:
+            primary scrambling code
+        """
+        cmd = "PRISCRCODE? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @primary_scrambling_code.setter
+    def primary_scrambling_code(self, psc):
+        """ Sets the primary scrambling code for WCDMA cell
+
+        Args:
+            psc: primary scrambling code
+
+        Returns:
+            None
+        """
+        cmd = "PRISCRCODE {},{}".format(psc, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def tac(self):
+        """ Gets the Tracking Area Code of the LTE cell
+
+        Args:
+            None
+
+        Returns:
+            Tracking Area Code of the LTE cell
+        """
+        cmd = "TAC? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @tac.setter
+    def tac(self, tac):
+        """ Sets the Tracking Area Code of the LTE cell
+
+        Args:
+            tac: Tracking Area Code of the LTE cell
+
+        Returns:
+            None
+        """
+        cmd = "TAC {},{}".format(tac, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def cell(self):
+        """ Gets the current cell for BTS
+
+        Args:
+            None
+
+        Returns:
+            current cell for BTS
+        """
+        cmd = "CELLSEL? {}".format(self._bts_number)
+        return self._anritsu.send_query(cmd)
+
+    @cell.setter
+    def cell(self, cell_name):
+        """ sets the  cell for BTS
+        Args:
+            cell_name: cell name
+
+        Returns:
+            None
+        """
+        cmd = "CELLSEL {},{}".format(self._bts_number, cell_name)
+        return self._anritsu.send_command(cmd)
+
+    @property
+    def gsm_cbch(self):
+        """ Gets the GSM CBCH enable/disable status
+
+        Args:
+            None
+
+        Returns:
+            one of CBCHSetup values
+        """
+        cmd = "CBCHPARAMSETUP? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @gsm_cbch.setter
+    def gsm_cbch(self, enable):
+        """ Sets the GSM CBCH enable/disable status
+
+        Args:
+            enable: GSM CBCH enable/disable status
+
+        Returns:
+            None
+        """
+        cmd = "CBCHPARAMSETUP {},{}".format(enable.value, self._bts_number)
+        self._anritsu.send_command(cmd)
+
+class _VirtualPhone:
+    '''Class to interact with virtual phone supported by MD8475 '''
+    def __init__(self, anritsu):
+        self._anritsu = anritsu
+
+    @property
+    def id(self):
+        """ Gets the virtual phone ID
+
+        Args:
+            None
+
+        Returns:
+            virtual phone ID
+        """
+        cmd = "VPID? "
+        return self._anritsu.send_query(cmd)
+
+    @id.setter
+    def id(self, phonenumber):
+        """ Sets the virtual phone ID
+
+        Args:
+            phonenumber: virtual phone ID
+
+        Returns:
+            None
+        """
+        cmd = "VPID {}".format(phonenumber)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def id_c2k(self):
+        """ Gets the virtual phone ID for CDMA 1x
+
+        Args:
+            None
+
+        Returns:
+            virtual phone ID
+        """
+        cmd = "VPIDC2K? "
+        return self._anritsu.send_query(cmd)
+
+    @id_c2k.setter
+    def id_c2k(self, phonenumber):
+        """ Sets the virtual phone ID for CDMA 1x
+
+        Args:
+            phonenumber: virtual phone ID
+
+        Returns:
+            None
+        """
+        cmd = "VPIDC2K {}".format(phonenumber)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def auto_answer(self):
+        """ Gets the auto answer status of virtual phone
+
+        Args:
+            None
+
+        Returns:
+            auto answer status, ON/OFF
+        """
+        cmd = "VPAUTOANSWER? "
+        return self._anritsu.send_query(cmd)
+
+    @auto_answer.setter
+    def auto_answer(self, option):
+        """ Sets the auto answer feature
+
+        Args:
+            option: tuple with two items for turning on Auto Answer
+                    (OFF or (ON, timetowait))
+
+        Returns:
+            None
+        """
+        enable = "OFF"
+        time = 5
+
+        try:
+            enable, time = option
+        except ValueError:
+            if enable != "OFF":
+                raise ValueError("Pass a tuple with two items for"
+                                 " Turning on Auto Answer")
+        cmd = "VPAUTOANSWER {},{}".format(enable.value, time)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def calling_mode(self):
+        """ Gets the calling mode of virtual phone
+
+        Args:
+            None
+
+        Returns:
+            calling mode of virtual phone
+        """
+        cmd = "VPCALLINGMODE? "
+        return self._anritsu.send_query(cmd)
+
+    @calling_mode.setter
+    def calling_mode(self, calling_mode):
+        """ Sets the calling mode of virtual phone
+
+        Args:
+            calling_mode: calling mode of virtual phone
+
+        Returns:
+            None
+        """
+        cmd = "VPCALLINGMODE {}".format(calling_mode)
+        self._anritsu.send_command(cmd)
+
+    def set_voice_off_hook(self):
+        """ Set the virtual phone operating mode to Voice Off Hook
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        cmd = "OPERATEVPHONE 0"
+        return self._anritsu.send_command(cmd)
+
+    def set_voice_on_hook(self):
+        """ Set the virtual phone operating mode to Voice On Hook
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        cmd = "OPERATEVPHONE 1"
+        return self._anritsu.send_command(cmd)
+
+    def set_video_off_hook(self):
+        """ Set the virtual phone operating mode to Video Off Hook
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        cmd = "OPERATEVPHONE 2"
+        return self._anritsu.send_command(cmd)
+
+    def set_video_on_hook(self):
+        """ Set the virtual phone operating mode to Video On Hook
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        cmd = "OPERATEVPHONE 3"
+        return self._anritsu.send_command(cmd)
+
+    def set_call_waiting(self):
+        """ Set the virtual phone operating mode to Call waiting
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        cmd = "OPERATEVPHONE 4"
+        return self._anritsu.send_command(cmd)
+
+    @property
+    def status(self):
+        """ Gets the virtual phone status
+
+        Args:
+            None
+
+        Returns:
+            virtual phone status
+        """
+        cmd = "VPSTAT?"
+        status = self._anritsu.send_query(cmd)
+        return _VP_STATUS[status]
+
+    def sendSms(self, phoneNumber, message):
+        """ Sends the SMS data from Anritsu to UE
+
+        Args:
+            phoneNumber: sender of SMS
+            message: message text
+
+        Returns:
+            None
+        """
+        cmd = ("SENDSMS /?PhoneNumber=001122334455&Sender={}&Text={}"
+               "&DCS=00").format(phoneNumber, AnritsuUtils.gsm_encode(message))
+        return self._anritsu.send_command(cmd)
+
+    def sendSms_c2k(self, phoneNumber, message):
+        """ Sends the SMS data from Anritsu to UE (in CDMA)
+
+        Args:
+            phoneNumber: sender of SMS
+            message: message text
+
+        Returns:
+            None
+        """
+        cmd = ("C2KSENDSMS System=CDMA\&Originating_Address={}\&UserData={}"
+              ).format(phoneNumber, AnritsuUtils.cdma_encode(message))
+        return self._anritsu.send_command(cmd)
+
+    def receiveSms(self):
+        """ Receives SMS messages sent by the UE in an external application
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        return self._anritsu.send_query("RECEIVESMS?")
+
+    def receiveSms_c2k(self):
+        """ Receives SMS messages sent by the UE(in CDMA) in an external application
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        return self._anritsu.send_query("C2KRECEIVESMS?")
+
+    def setSmsStatusReport(self, status):
+        """ Set the Status Report value of the SMS
+
+        Args:
+            status: status code
+
+        Returns:
+            None
+        """
+        cmd = "SMSSTATUSREPORT {}".format(status)
+        return self._anritsu.send_command(cmd)
+
+
+class _PacketDataNetwork:
+    '''Class to configure PDN parameters'''
+    def __init__(self, anritsu, pdnnumber):
+        self._pdn_number = pdnnumber
+        self._anritsu = anritsu
+
+    @property
+    def ue_address_iptype(self):
+        """ Gets IP type of UE for particular PDN
+
+        Args:
+            None
+
+        Returns:
+            IP type of UE for particular PDN
+        """
+        cmd = "PDNIPTYPE? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @ue_address_iptype.setter
+    def ue_address_iptype(self, ip_type):
+        """ Set IP type of UE for particular PDN
+
+        Args:
+            ip_type: IP type of UE
+
+        Returns:
+            None
+        """
+        if not isinstance(ip_type, IPAddressType):
+            raise ValueError(' The parameter should be of type "IPAddressType"')
+        cmd = "PDNIPTYPE {},{}".format(self._pdn_number, ip_type.value)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def ue_address_ipv4(self):
+        """ Gets UE IPv4 address
+
+        Args:
+            None
+
+        Returns:
+            UE IPv4 address
+        """
+        cmd = "PDNIPV4? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @ue_address_ipv4.setter
+    def ue_address_ipv4(self, ip_address):
+        """ Set UE IPv4 address
+
+        Args:
+            ip_address: UE IPv4 address
+
+        Returns:
+            None
+        """
+        cmd = "PDNIPV4 {},{}".format(self._pdn_number, ip_address)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def ue_address_ipv6(self):
+        """ Gets UE IPv6 address
+
+        Args:
+            None
+
+        Returns:
+            UE IPv6 address
+        """
+        cmd = "PDNIPV6? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @ue_address_ipv6.setter
+    def ue_address_ipv6(self, ip_address):
+        """ Set UE IPv6 address
+
+        Args:
+            ip_address: UE IPv6 address
+
+        Returns:
+            None
+        """
+        cmd = "PDNIPV6 {},{}".format(self._pdn_number, ip_address)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def primary_dns_address_ipv4(self):
+        """ Gets Primary DNS server IPv4 address
+
+        Args:
+            None
+
+        Returns:
+            Primary DNS server IPv4 address
+        """
+        cmd = "PDNDNSIPV4PRI? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @primary_dns_address_ipv4.setter
+    def primary_dns_address_ipv4(self, ip_address):
+        """ Set Primary DNS server IPv4 address
+
+        Args:
+            ip_address: Primary DNS server IPv4 address
+
+        Returns:
+            None
+        """
+        cmd = "PDNDNSIPV4PRI {},{}".format(self._pdn_number, ip_address)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def secondary_dns_address_ipv4(self):
+        """ Gets secondary DNS server IPv4 address
+
+        Args:
+            None
+
+        Returns:
+            secondary DNS server IPv4 address
+        """
+        cmd = "PDNDNSIPV4SEC? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @secondary_dns_address_ipv4.setter
+    def secondary_dns_address_ipv4(self, ip_address):
+        """ Set secondary DNS server IPv4 address
+
+        Args:
+            ip_address: secondary DNS server IPv4 address
+
+        Returns:
+            None
+        """
+        cmd = "PDNDNSIPV4SEC {},{}".format(self._pdn_number, ip_address)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def dns_address_ipv6(self):
+        """ Gets DNS server IPv6 address
+
+        Args:
+            None
+
+        Returns:
+            DNS server IPv6 address
+        """
+        cmd = "PDNDNSIPV6? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @dns_address_ipv6.setter
+    def dns_address_ipv6(self, ip_address):
+        """ Set DNS server IPv6 address
+
+        Args:
+            ip_address: DNS server IPv6 address
+
+        Returns:
+            None
+        """
+        cmd = "PDNDNSIPV6 {},{}".format(self._pdn_number, ip_address)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def cscf_address_ipv4(self):
+        """ Gets Secondary P-CSCF IPv4 address
+
+        Args:
+            None
+
+        Returns:
+            Secondary P-CSCF IPv4 address
+        """
+        cmd = "PDNPCSCFIPV4? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @cscf_address_ipv4.setter
+    def cscf_address_ipv4(self, ip_address):
+        """ Set Secondary P-CSCF IPv4 address
+
+        Args:
+            ip_address: Secondary P-CSCF IPv4 address
+
+        Returns:
+            None
+        """
+        cmd = "PDNPCSCFIPV4 {},{}".format(self._pdn_number, ip_address)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def cscf_address_ipv6(self):
+        """ Gets P-CSCF IPv6 address
+
+        Args:
+            None
+
+        Returns:
+            P-CSCF IPv6 address
+        """
+        cmd = "PDNPCSCFIPV6? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @cscf_address_ipv6.setter
+    def cscf_address_ipv6(self, ip_address):
+        """ Set P-CSCF IPv6 address
+
+        Args:
+            ip_address: P-CSCF IPv6 address
+
+        Returns:
+            None
+        """
+        cmd = "PDNPCSCFIPV6 {},{}".format(self._pdn_number, ip_address)
+        self._anritsu.send_command(cmd)
+
+
+class _TriggerMessage:
+    '''Class to interact with trigger message handling supported by MD8475 '''
+    def __init__(self, anritsu):
+        self._anritsu = anritsu
+
+    def set_reply_type(self, message_id, reply_type):
+        """ Sets the reply type of the trigger information
+
+        Args:
+            message_id: trigger information message Id
+            reply_type: reply type of the trigger information
+
+        Returns:
+            None
+        """
+        if not isinstance(message_id, TriggerMessageIDs):
+            raise ValueError(' The parameter should be of type'
+                             ' "TriggerMessageIDs"')
+        if not isinstance(reply_type, TriggerMessageReply):
+            raise ValueError(' The parameter should be of type'
+                             ' "TriggerMessageReply"')
+
+        cmd = "REJECTTYPE {},{}".format(message_id.value, reply_type.value)
+        self._anritsu.send_command(cmd)
+
+    def set_reject_cause(self, message_id, cause):
+        """ Sets the reject cause of the trigger information
+
+        Args:
+            message_id: trigger information message Id
+            cause: cause for reject
+
+        Returns:
+            None
+        """
+        if not isinstance(message_id, TriggerMessageIDs):
+            raise ValueError(' The parameter should be of type'
+                             ' "TriggerMessageIDs"')
+
+        cmd = "REJECTCAUSE {},{}".format(message_id.value, cause)
+        self._anritsu.send_command(cmd)
diff --git a/acts/framework/acts/controllers/tel/mg3710a.py b/acts/framework/acts/controllers/tel/mg3710a.py
new file mode 100644
index 0000000..31383b2
--- /dev/null
+++ b/acts/framework/acts/controllers/tel/mg3710a.py
@@ -0,0 +1,699 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+#   Copyright 2014- The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""
+Controller interface for Anritsu Signal Generator MG3710A.
+"""
+
+import time
+import socket
+from enum import Enum
+from enum import IntEnum
+
+from . _anritsu_utils import *
+
+TERMINATOR = "\n"
+
+class MG3710A():
+    """Class to communicate with Anritsu Signal Generator MG3710A.
+       This uses GPIB command to interface with Anritsu MG3710A """
+
+    def __init__(self, ip_address, log_handle):
+        self._ipaddr = ip_address
+        self.log = log_handle
+
+        # Open socket connection to Signaling Tester
+        self.log.info("Opening Socket Connection with "
+              "Signal Generator MG3710A ({}) ".format(self._ipaddr))
+        try:
+            self._sock = socket.create_connection((self._ipaddr, 49158),
+                                                  timeout=30)
+            self.send_query("*IDN?", 60)
+            self.log.info("Communication Signal Generator MG3710A OK.")
+            self.log.info("Opened Socket connection to ({})"
+                  "with handle ({})".format(self._ipaddr, self._sock))
+        except socket.timeout:
+            raise AnritsuError("Timeout happened while conencting to"
+                               " Anritsu MG3710A")
+        except socket.error:
+            raise AnritsuError("Socket creation error")
+
+    def disconnect(self):
+        """ Disconnect Signal Generator MG3710A
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        self._sock.close()
+
+    def send_query(self, query, sock_timeout=10):
+        """ Sends a Query message to Anritsu MG3710A and return response
+
+        Args:
+            query - Query string
+
+        Returns:
+            query response
+        """
+        self.log.info("--> {}".format(query))
+        querytoSend = (query + TERMINATOR).encode('utf-8')
+        self._sock.settimeout(sock_timeout)
+        try:
+            self._sock.send(querytoSend)
+            result = self._sock.recv(256).rstrip(TERMINATOR.encode('utf-8'))
+            response = result.decode('utf-8')
+            self.log.info('<-- {}'.format(response))
+            return response
+        except socket.timeout:
+            raise AnritsuError("Timeout: Response from Anritsu")
+        except socket.error:
+            raise AnritsuError("Socket Error")
+
+    def send_command(self, command, sock_timeout=30):
+        """ Sends a Command message to Anritsu MG3710A
+
+        Args:
+            command - command string
+
+        Returns:
+            None
+        """
+        self.log.info("--> {}".format(command))
+        cmdToSend = (command + TERMINATOR).encode('utf-8')
+        self._sock.settimeout(sock_timeout)
+        try:
+            self._sock.send(cmdToSend)
+            # check operation status
+            status = self.send_query("*OPC?")
+            if int(status) != OPERATION_COMPLETE:
+                raise AnritsuError("Operation not completed")
+        except socket.timeout:
+            raise AnritsuError("Timeout for Command Response from Anritsu")
+        except socket.error:
+            raise AnritsuError("Socket Error for Anritsu command")
+        return
+
+    @property
+    def sg(self):
+        """ Gets current selected signal generator(SG)
+
+        Args:
+            None
+
+        Returns:
+            selected signal generatr number
+        """
+        return self.send_query("PORT?")
+
+    @sg.setter
+    def sg(self, sg_number):
+        """ Selects the signal generator to be controlled
+
+        Args:
+            sg_number: sg number 1 | 2
+
+        Returns:
+            None
+        """
+        cmd = "PORT {}".format(sg_number)
+        self.send_command(cmd)
+
+    def get_modulation_state(self, sg=1):
+        """ Gets the RF signal modulation state (ON/OFF) of signal generator
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            modulation state . 0 (OFF) | 1(ON)
+        """
+        return self.send_query("OUTP{}:MOD?".format(sg))
+
+    def set_modulation_state(self, state, sg=1):
+        """ Sets the RF signal modulation state
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            state : ON/OFF
+
+        Returns:
+            None
+        """
+        cmd = "OUTP{}:MOD {}".format(sg, state)
+        self.send_command(cmd)
+
+    def get_rf_output_state(self, sg=1):
+        """ Gets RF signal output state (ON/OFF) of signal generator
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            RF signal output state . 0 (OFF) | 1(ON)
+        """
+        return self.send_query("OUTP{}?".format(sg))
+
+    def set_rf_output_state(self, state, sg=1):
+        """ Sets the RF signal output state
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            state : ON/OFF
+
+        Returns:
+            None
+        """
+        cmd = "OUTP{} {}".format(sg, state)
+        self.send_command(cmd)
+
+    def get_frequency(self, sg=1):
+        """ Gets the selected frequency of signal generator
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            selected frequency
+        """
+        return self.send_query("SOUR{}:FREQ?".format(sg))
+
+    def set_frequency(self, freq, sg=1):
+        """ Sets the frequency of signal generator
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            freq : frequency
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:FREQ {}".format(sg, freq)
+        self.send_command(cmd)
+
+    def get_frequency_offset_state(self, sg=1):
+        """ Gets the Frequency Offset enable state (ON/OFF) of signal generator
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            Frequency Offset enable state . 0 (OFF) | 1(ON)
+        """
+        return self.send_query("SOUR{}:FREQ:OFFS:STAT?".format(sg))
+
+    def set_frequency_offset_state(self, state, sg=1):
+        """ Sets the Frequency Offset enable state
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            state : enable state, ON/OFF
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:FREQ:OFFS:STAT {}".format(sg, state)
+        self.send_command(cmd)
+
+    def get_frequency_offset(self, sg=1):
+        """ Gets the current frequency offset value
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            current frequency offset value
+        """
+        return self.send_query("SOUR{}:FREQ:OFFS?".format(sg))
+
+    def set_frequency_offset(self, offset, sg=1):
+        """ Sets the frequency offset value
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            offset : frequency offset value
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:FREQ:OFFS {}".format(sg, offset)
+        self.send_command(cmd)
+
+    def get_frequency_offset_multiplier_state(self, sg=1):
+        """ Gets the Frequency Offset multiplier enable state (ON/OFF) of
+            signal generator
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            Frequency Offset  multiplier enable state . 0 (OFF) | 1(ON)
+        """
+        return self.send_query("SOUR{}:FREQ:MULT:STAT?".format(sg))
+
+    def set_frequency_offset_multiplier_state(self, state, sg=1):
+        """ Sets the  Frequency Offset multiplier enable state
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            state : enable state, ON/OFF
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:FREQ:MULT:STAT {}".format(sg, state)
+        self.send_command(cmd)
+
+    def get_frequency_offset_multiplier(self, sg=1):
+        """ Gets the current frequency offset multiplier value
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            frequency offset multiplier value
+        """
+        return self.send_query("SOUR{}:FREQ:MULT?".format(sg))
+
+    def set_frequency_offset_multiplier(self, multiplier, sg=1):
+        """ Sets the frequency offset multiplier value
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            multiplier : frequency offset multiplier value
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:FREQ:MULT {}".format(sg, multiplier)
+        self.send_command(cmd)
+
+    def get_channel(self, sg=1):
+        """ Gets the current channel number
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            current channel number
+        """
+        return self.send_query("SOUR{}:FREQ:CHAN:NUMB?".format(sg))
+
+    def set_channel(self, channel, sg=1):
+        """ Sets the channel number
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            channel : channel number
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:FREQ:CHAN:NUMB {}".format(sg, channel)
+        self.send_command(cmd)
+
+    def get_channel_group(self, sg=1):
+        """ Gets the current channel group number
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            current channel group number
+        """
+        return self.send_query("SOUR{}:FREQ:CHAN:GRO?".format(sg))
+
+    def set_channel_group(self, group, sg=1):
+        """ Sets the channel group number
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            group : channel group number
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:FREQ:CHAN:GRO {}".format(sg, group)
+        self.send_command(cmd)
+
+    def get_rf_output_level(self, sg=1):
+        """ Gets the current RF output level
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            current RF output level
+        """
+        return self.send_query("SOUR{}:POW:CURR?".format(sg))
+
+    def get_output_level_unit(self, sg=1):
+        """ Gets the current RF output level unit
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            current RF output level unit
+        """
+        return self.send_query("UNIT{}:POW?".format(sg))
+
+    def set_output_level_unit(self, unit, sg=1):
+        """ Sets the RF output level unit
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            unit : Output level unit
+
+        Returns:
+            None
+        """
+        cmd = "UNIT{}:POW {}".format(sg, unit)
+        self.send_command(cmd)
+
+    def get_output_level(self, sg=1):
+        """ Gets the Output level
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            Output level
+        """
+        return self.send_query("SOUR{}:POW?".format(sg))
+
+    def set_output_level(self, level, sg=1):
+        """ Sets the Output level
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            level : Output level
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:POW {}".format(sg, level)
+        self.send_command(cmd)
+
+    def get_arb_state(self, sg=1):
+        """ Gets the ARB function state
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            ARB function state . 0 (OFF) | 1(ON)
+        """
+        return self.send_query("SOUR{}:RAD:ARB?".format(sg))
+
+    def set_arb_state(self, state, sg=1):
+        """ Sets the ARB function state
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            state : enable state (ON/OFF)
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:RAD:ARB {}".format(sg, state)
+        self.send_command(cmd)
+
+    def restart_arb_waveform_pattern(self, sg=1):
+        """ playback the waveform pattern from the beginning.
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:RAD:ARB:WAV:REST".format(sg)
+        self.send_command(cmd)
+
+    def load_waveform(self, package_name, pattern_name, memory, sg=1):
+        """ loads the waveform from HDD to specified memory
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            package_name : Package name of signal
+            pattern_name : Pattern name of signal
+            memory: memory for the signal - "A" or "B"
+
+        Returns:
+            None
+        """
+        cmd = "MMEM{}:LOAD:WAV:WM{} '{}','{}'".format(sg, memory, package_name,
+                                                      pattern_name )
+        self.send_command(cmd)
+
+    def select_waveform(self, package_name, pattern_name, memory, sg=1):
+        """ Selects the waveform to output on specified memory
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            package_name : Package name of signal
+            pattern_name : Pattern name of signal
+            memory: memory for the signal - "A" or "B"
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:RAD:ARB:WM{}:WAV '{}','{}'".format(sg, memory, package_name,
+                                                         pattern_name )
+        self.send_command(cmd)
+
+    def get_freq_relative_display_status(self, sg=1):
+        """ Gets the frequency relative display status
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            frequency relative display status.   0 (OFF) | 1(ON)
+        """
+        return self.send_query("SOUR{}:FREQ:REF:STAT?".format(sg))
+
+    def set_freq_relative_display_status(self, enable, sg=1):
+        """ Sets frequency relative display status
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            enable : enable type (ON/OFF)
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:FREQ:REF:STAT {}".format(sg, enable)
+        self.send_command(cmd)
+
+    def get_freq_channel_display_type(self, sg=1):
+        """ Gets the selected type(frequency/channel) for input display
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            selected type(frequecy/channel) for input display
+        """
+        return self.send_query("SOUR{}:FREQ:TYPE?".format(sg))
+
+    def set_freq_channel_display_type(self, freq_channel, sg=1):
+        """ Sets thes type(frequency/channel) for input display
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            freq_channel : display type (frequency/channel)
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:FREQ:TYPE {}".format(sg, freq_channel)
+        self.send_command(cmd)
+
+    def get_arb_combination_mode(self, sg=1):
+        """ Gets the current mode to generate the pattern
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            current mode to generate the pattern
+        """
+        return self.send_query("SOUR{}:RAD:ARB:PCOM?".format(sg))
+
+    def set_arb_combination_mode(self, mode, sg=1):
+        """ Sets the mode to generate the pattern
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            mode : pattern generation mode
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:RAD:ARB:PCOM {}".format(sg, mode)
+        self.send_command(cmd)
+
+    def get_arb_pattern_aorb_state(self, a_or_b, sg=1):
+        """ Gets the Pattern A/B output state
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            a_or_b : Patten A or Pattern B( "A" or "B")
+
+        Returns:
+            Pattern A/B output state . 0(OFF) | 1(ON)
+        """
+        return self.send_query("SOUR{}:RAD:ARB:WM{}:OUTP?".format(a_or_b, sg))
+
+    def set_arb_pattern_aorb_state(self, a_or_b, state, sg=1):
+        """ Sets the Pattern A/B output state
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            a_or_b : Patten A or Pattern B( "A" or "B")
+            state : output state
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:RAD:ARB:WM{}:OUTP {}".format(sg, a_or_b, state)
+        self.send_command(cmd)
+
+    def get_arb_level_aorb(self, a_or_b, sg=1):
+        """ Gets the Pattern A/B output level
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            a_or_b : Patten A or Pattern B( "A" or "B")
+
+        Returns:
+             Pattern A/B output level
+        """
+        return self.send_query("SOUR{}:RAD:ARB:WM{}:POW?".format(sg, a_or_b))
+
+    def set_arb_level_aorb(self, a_or_b, level, sg=1):
+        """ Sets the Pattern A/B output level
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            a_or_b : Patten A or Pattern B( "A" or "B")
+            level : output level
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:RAD:ARB:WM{}:POW {}".format(sg, a_or_b, level)
+        self.send_command(cmd)
+
+    def get_arb_freq_offset(self, sg=1):
+        """ Gets the frequency offset between Pattern A and Patten B
+            when CenterSignal is A or B.
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            frequency offset between Pattern A and Patten B
+        """
+        return self.send_query("SOUR{}:RAD:ARB:FREQ:OFFS?".format(sg))
+
+    def set_arb_freq_offset(self,  offset, sg=1):
+        """ Sets the frequency offset between Pattern A and Patten B when
+            CenterSignal is A or B.
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            offset : frequency offset
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:RAD:ARB:FREQ:OFFS {}".format(sg, offset)
+        self.send_command(cmd)
+
+    def get_arb_freq_offset_aorb(self, sg=1):
+        """ Gets the frequency offset of Pattern A/Pattern B based on Baseband
+            center frequency
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+
+        Returns:
+            frequency offset
+        """
+        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
+            center frequency
+
+        Args:
+            sg: signal generator number.
+                Default is 1
+            a_or_b : Patten A or Pattern B( "A" or "B")
+            offset : frequency offset
+
+        Returns:
+            None
+        """
+        cmd = "SOUR{}:RAD:ARB:WM{}:FREQ:OFFS {}".format(sg, a_or_b, offset)
+        self.send_command(cmd)
diff --git a/acts/framework/acts/event_dispatcher.py b/acts/framework/acts/event_dispatcher.py
new file mode 100644
index 0000000..ca9f8b4
--- /dev/null
+++ b/acts/framework/acts/event_dispatcher.py
@@ -0,0 +1,423 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014- The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 concurrent.futures import ThreadPoolExecutor
+import queue
+import re
+import socket
+import threading
+import time
+import traceback
+
+class EventDispatcherError(Exception):
+    pass
+
+class IllegalStateError(EventDispatcherError):
+    """Raise when user tries to put event_dispatcher into an illegal state.
+    """
+
+class DuplicateError(EventDispatcherError):
+    """Raise when a duplicate is being created and it shouldn't.
+    """
+
+class EventDispatcher:
+    """Class managing events for an sl4a connection.
+    """
+
+    DEFAULT_TIMEOUT = 60
+
+    def __init__(self, droid):
+        self.droid = droid
+        self.started = False
+        self.executor = None
+        self.poller = None
+        self.event_dict = {}
+        self.handlers = {}
+        self.lock = threading.RLock()   
+
+    def poll_events(self):
+        """Continuously polls all types of events from sl4a.
+
+        Events are sorted by name and store in separate queues.
+        If there are registered handlers, the handlers will be called with
+        corresponding event immediately upon event discovery, and the event
+        won't be stored. If exceptions occur, stop the dispatcher and return
+        """
+        while self.started:
+            event_obj = None
+            event_name = None
+            try:
+                event_obj = self.droid.eventWait(60000)
+            except:
+                if self.started:
+                    print("Exception happened during polling.")
+                    print(traceback.format_exc())
+                    raise
+            if not event_obj:
+                continue
+            elif 'name' not in event_obj:
+                print("Received Malformed event {}".format(event_obj))
+                continue
+            else:
+                event_name = event_obj['name']
+            # if handler registered, process event
+            if event_name in self.handlers:
+                self.handle_subscribed_event(event_obj, event_name)
+            if event_name == "EventDispatcherShutdown":
+                self.droid.closeSl4aSession()
+                break
+            else:
+                self.lock.acquire()
+                if event_name in self.event_dict:  # otherwise, cache event
+                    self.event_dict[event_name].put(event_obj)
+                else:
+                    q = queue.Queue()
+                    q.put(event_obj)
+                    self.event_dict[event_name] = q
+                self.lock.release()
+
+    def register_handler(self, handler, event_name, args):
+        """Registers an event handler.
+
+        One type of event can only have one event handler associated with it.
+
+        Args:
+            handler: The event handler function to be registered.
+            event_name: Name of the event the handler is for.
+            args: User arguments to be passed to the handler when it's called.
+
+        Raises:
+            IllegalStateError: Raised if attempts to register a handler after
+                the dispatcher starts running.
+            DuplicateError: Raised if attempts to register more than one
+                handler for one type of event.
+        """
+        if self.started:
+            raise IllegalStateError(("Can't register service after polling is"
+                " started"))
+        self.lock.acquire()
+        try:
+            if event_name in self.handlers:
+                raise DuplicateError(
+                    'A handler for {} already exists'.format(event_name))
+            self.handlers[event_name] = (handler, args)
+        finally:
+            self.lock.release()
+
+    def start(self):
+        """Starts the event dispatcher.
+
+        Initiates executor and start polling events.
+
+        Raises:
+            IllegalStateError: Can't start a dispatcher again when it's already
+                running.
+        """
+        if not self.started:
+            self.started = True
+            self.executor = ThreadPoolExecutor(max_workers=32)
+            self.poller = self.executor.submit(self.poll_events)
+        else:
+            raise IllegalStateError("Dispatcher is already started.")
+
+    def clean_up(self):
+        """Clean up and release resources after the event dispatcher polling
+        loop has been broken.
+
+        The following things happen:
+        1. Clear all events and flags.
+        2. Close the sl4a client the event_dispatcher object holds.
+        3. Shut down executor without waiting.
+        """
+        uid = self.droid.uid
+        if not self.started:
+            return
+        self.started = False
+        self.clear_all_events()
+        self.droid.close()
+        self.poller.set_result("Done")
+        # The polling thread is guaranteed to finish after a max of 60 seconds,
+        # so we don't wait here.
+        self.executor.shutdown(wait=False)
+
+    def pop_event(self, event_name, timeout=DEFAULT_TIMEOUT):
+        """Pop an event from its queue.
+
+        Return and remove the oldest entry of an event.
+        Block until an event of specified name is available or
+        times out if timeout is set.
+
+        Args:
+            event_name: Name of the event to be popped.
+            timeout: Number of seconds to wait when event is not present.
+                Never times out if None.
+
+        Returns:
+            event: The oldest entry of the specified event. None if timed out.
+
+        Raises:
+            IllegalStateError: Raised if pop is called before the dispatcher
+                starts polling.
+        """
+        if not self.started:
+            raise IllegalStateError(
+                "Dispatcher needs to be started before popping.")
+
+        e_queue = self.get_event_q(event_name)
+
+        if not e_queue:
+            raise TypeError(
+                "Failed to get an event queue for {}".format(event_name))
+
+        try:
+            # Block for timeout
+            if timeout:
+                return e_queue.get(True, timeout)
+            # Non-blocking poll for event
+            elif timeout == 0:
+                return e_queue.get(False)
+            else:
+            # Block forever on event wait
+                return e_queue.get(True)
+        except queue.Empty:
+            raise queue.Empty(
+                'Timeout after {}s waiting for event: {}'.format(
+                    timeout, event_name))
+
+    def wait_for_event(self, event_name, predicate,
+                       timeout=DEFAULT_TIMEOUT, *args, **kwargs):
+        """Wait for an event that satisfies a predicate to appear.
+
+        Continuously pop events of a particular name and check against the
+        predicate until an event that satisfies the predicate is popped or
+        timed out. Note this will remove all the events of the same name that
+        do not satisfy the predicate in the process.
+
+        Args:
+            event_name: Name of the event to be popped.
+            predicate: A function that takes an event and returns True if the
+                predicate is satisfied, False otherwise.
+            timeout: Number of seconds to wait.
+            *args: Optional positional args passed to predicate().
+            **kwargs: Optional keyword args passed to predicate().
+
+        Returns:
+            The event that satisfies the predicate.
+
+        Raises:
+            queue.Empty: Raised if no event that satisfies the predicate was
+                found before time out.
+        """
+        deadline = time.time() + timeout
+
+        while True:
+            event = None
+            try:
+                event = self.pop_event(event_name, 1)
+            except queue.Empty:
+                pass
+
+            if event and predicate(event, *args, **kwargs):
+                return event
+
+            if time.time() > deadline:
+                raise queue.Empty(
+                    'Timeout after {}s waiting for event: {}'.format(
+                        timeout, event_name))
+
+    def pop_events(self, regex_pattern, timeout):
+        """Pop events whose names match a regex pattern.
+
+        If such event(s) exist, pop one event from each event queue that
+        satisfies the condition. Otherwise, wait for an event that satisfies
+        the condition to occur, with timeout.
+
+        Results are sorted by timestamp in ascending order.
+
+        Args:
+            regex_pattern: The regular expression pattern that an event name
+                should match in order to be popped.
+            timeout: Number of seconds to wait for events in case no event
+                matching the condition exits when the function is called.
+
+        Returns:
+            results: Pop events whose names match a regex pattern.
+                Empty if none exist and the wait timed out.
+
+        Raises:
+            IllegalStateError: Raised if pop is called before the dispatcher
+                starts polling.
+            queue.Empty: Raised if no event was found before time out.
+        """
+        if not self.started:
+            raise IllegalStateError(
+                "Dispatcher needs to be started before popping.")
+        deadline = time.time() + timeout
+        while True:
+            #TODO: fix the sleep loop
+            results = self._match_and_pop(regex_pattern)
+            if len(results) != 0 or time.time() > deadline:
+                break
+            time.sleep(1)
+        if len(results) == 0:
+            raise queue.Empty(
+                'Timeout after {}s waiting for event: {}'.format(
+                    timeout, regex_pattern))
+
+        return sorted(results, key=lambda event : event['time'])
+
+    def _match_and_pop(self, regex_pattern):
+        """Pop one event from each of the event queues whose names
+        match (in a sense of regular expression) regex_pattern.
+        """
+        results = []
+        self.lock.acquire()
+        for name in self.event_dict.keys():
+            if re.match(regex_pattern, name):
+                q = self.event_dict[name]
+                if q:
+                    try:
+                        results.append(q.get(False))
+                    except:
+                        pass
+        self.lock.release()
+        return results
+
+    def get_event_q(self, event_name):
+        """Obtain the queue storing events of the specified name.
+
+        If no event of this name has been polled, wait for one to.
+
+        Returns:
+            queue: A queue storing all the events of the specified name.
+                None if timed out.
+            timeout: Number of seconds to wait for the operation.
+
+        Raises:
+            queue.Empty: Raised if the queue does not exist and timeout has
+                passed.
+        """
+        self.lock.acquire()
+        if not event_name in self.event_dict or self.event_dict[event_name] is None:
+            self.event_dict[event_name] = queue.Queue()
+        self.lock.release()
+
+        event_queue = self.event_dict[event_name]
+        return event_queue
+
+    def handle_subscribed_event(self, event_obj, event_name):
+        """Execute the registered handler of an event.
+
+        Retrieve the handler and its arguments, and execute the handler in a
+            new thread.
+
+        Args:
+            event_obj: Json object of the event.
+            event_name: Name of the event to call handler for.
+        """
+        handler, args = self.handlers[event_name]
+        self.executor.submit(handler, event_obj, *args)
+
+
+    def _handle(self, event_handler, event_name, user_args, event_timeout,
+        cond, cond_timeout):
+        """Pop an event of specified type and calls its handler on it. If
+        condition is not None, block until condition is met or timeout.
+        """
+        if cond:
+            cond.wait(cond_timeout)
+        event = self.pop_event(event_name, event_timeout)
+        return event_handler(event, *user_args)
+
+    def handle_event(self, event_handler, event_name, user_args,
+        event_timeout=None, cond=None, cond_timeout=None):
+        """Handle events that don't have registered handlers
+
+        In a new thread, poll one event of specified type from its queue and
+        execute its handler. If no such event exists, the thread waits until
+        one appears.
+
+        Args:
+            event_handler: Handler for the event, which should take at least
+                one argument - the event json object.
+            event_name: Name of the event to be handled.
+            user_args: User arguments for the handler; to be passed in after
+                the event json.
+            event_timeout: Number of seconds to wait for the event to come.
+            cond: A condition to wait on before executing the handler. Should
+                be a threading.Event object.
+            cond_timeout: Number of seconds to wait before the condition times
+                out. Never times out if None.
+
+        Returns:
+            worker: A concurrent.Future object associated with the handler.
+                If blocking call worker.result() is triggered, the handler
+                needs to return something to unblock.
+        """
+        worker = self.executor.submit(self._handle, event_handler, event_name,
+            user_args, event_timeout, cond, cond_timeout)
+        return worker
+
+    def pop_all(self, event_name):
+        """Return and remove all stored events of a specified name.
+
+        Pops all events from their queue. May miss the latest ones.
+        If no event is available, return immediately.
+
+        Args:
+            event_name: Name of the events to be popped.
+
+        Returns:
+           results: List of the desired events.
+
+        Raises:
+            IllegalStateError: Raised if pop is called before the dispatcher
+                starts polling.
+        """
+        if not self.started:
+            raise IllegalStateError(("Dispatcher needs to be started before "
+                "popping."))
+        results = []
+        try:
+            self.lock.acquire()
+            while True:
+                e = self.event_dict[event_name].get(block=False)
+                results.append(e)
+        except (queue.Empty, KeyError):
+            return results
+        finally:
+            self.lock.release()
+
+    def clear_events(self, event_name):
+        """Clear all events of a particular name.
+
+        Args:
+            event_name: Name of the events to be popped.
+        """
+        self.lock.acquire()
+        try:
+            q = self.get_event_q(event_name)
+            q.queue.clear()
+        except queue.Empty:
+            return
+        finally:
+            self.lock.release()
+
+    def clear_all_events(self):
+        """Clear all event queues and their cached events."""
+        self.lock.acquire()
+        self.event_dict.clear()
+        self.lock.release()
diff --git a/acts/framework/acts/jsonrpc.py b/acts/framework/acts/jsonrpc.py
new file mode 100644
index 0000000..5c0ddc9
--- /dev/null
+++ b/acts/framework/acts/jsonrpc.py
@@ -0,0 +1,119 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+#   Copyright 2014- Google, Inc.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""
+A simple JSON RPC client.
+"""
+import json
+import time
+from urllib import request
+
+class HTTPError(Exception):
+    pass
+
+class RemoteError(Exception):
+    pass
+
+def JSONCounter():
+    """A counter that generates JSON RPC call IDs.
+
+    Follows the increasing integer sequence. Every time this function is
+    called, the next number in the sequence is returned.
+    """
+    i = 0
+    while True:
+        yield i
+        i += 1
+
+class JSONRPCClient:
+    COUNTER = JSONCounter()
+    headers = {'content-type': 'application/json'}
+    def __init__(self, baseurl):
+        self._baseurl = baseurl
+
+    def call(self, path, methodname=None, *args):
+        """Wrapper for the internal _call method.
+
+        A retry is performed if the initial call fails to compensate for
+        unstable networks.
+
+        Params:
+            path: Path of the rpc service to be appended to the base url.
+            methodname: Method name of the RPC call.
+            args: A tuple of arguments for the RPC call.
+
+        Returns:
+            The returned message of the JSON RPC call from the server.
+        """
+        try:
+            return self._call(path, methodname, *args)
+        except:
+            # Take five and try again
+            time.sleep(5)
+            return self._call(path, methodname, *args)
+
+    def _post_json(self, url, payload):
+        """Performs an HTTP POST request with a JSON payload.
+
+        Params:
+            url: The full URL to post the payload to.
+            payload: A JSON string to be posted to server.
+
+        Returns:
+            The HTTP response code and text.
+        """
+        req = request.Request(url)
+        req.add_header('Content-Type', 'application/json')
+        resp = request.urlopen(req, data=payload.encode("utf-8"))
+        txt = resp.read()
+        return resp.code, txt.decode('utf-8')
+
+    def _call(self, path, methodname=None, *args):
+        """Performs a JSON RPC call and return the response.
+
+        Params:
+            path: Path of the rpc service to be appended to the base url.
+            methodname: Method name of the RPC call.
+            args: A tuple of arguments for the RPC call.
+
+        Returns:
+            The returned message of the JSON RPC call from the server.
+
+        Raises:
+            HTTPError: Raised if the http post return code is not 200.
+            RemoteError: Raised if server returned an error.
+        """
+        jsonid = next(JSONRPCClient.COUNTER)
+        payload = json.dumps({"method": methodname,
+                              "params": args,
+                              "id": jsonid})
+        url = self._baseurl + path
+        status_code, text = self._post_json(url, payload)
+        if status_code != 200:
+            raise HTTPError(text)
+        r = json.loads(text)
+        if r['error']:
+            raise RemoteError(r['error'])
+        return r['result']
+
+    def sys(self, *args):
+        return self.call("sys", *args)
+
+    def __getattr__(self, name):
+        def rpc_call(*args):
+            return self.call('uci', name, *args)
+        return rpc_call
\ No newline at end of file
diff --git a/acts/framework/acts/keys.py b/acts/framework/acts/keys.py
new file mode 100644
index 0000000..698a9f3
--- /dev/null
+++ b/acts/framework/acts/keys.py
@@ -0,0 +1,104 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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
+
+"""This module has the global key values that are used across framework
+modules.
+"""
+class Config(enum.Enum):
+    """Enum values for test config related lookups.
+    """
+    # Keys used to look up values from test config files.
+    # These keys define the wording of test configs and their internal
+    # references.
+    key_log_path = "logpath"
+    key_testbed = "testbed"
+    key_testbed_name = "name"
+    key_test_paths = "testpaths"
+    key_android_device = "AndroidDevice"
+    key_access_point = "AP"
+    key_attenuator = "Attenuator"
+    key_port = "Port"
+    key_address = "Address"
+    key_iperf_server = "IPerfServer"
+    key_monsoon = "Monsoon"
+    key_adb_log_time_offset = "adb_log_time_offset"
+    key_adb_logcat_param = "adb_logcat_param"
+    # Internal keys, used internally, not exposed to user's config files.
+    ikey_lock = "lock"
+    ikey_user_param = "user_params"
+    ikey_android_device = "android_devices"
+    ikey_access_point = "access_points"
+    ikey_attenuator = "attenuators"
+    ikey_testbed_name = "testbed_name"
+    ikey_logger = "log"
+    ikey_logpath = "log_path"
+    ikey_monsoon = "monsoons"
+    ikey_reporter = "reporter"
+    ikey_adb_log_path = "adb_logcat_path"
+    ikey_adb_log_files = "adb_logcat_files"
+    ikey_iperf_server = "iperf_servers"
+    ikey_cli_args = "cli_args"
+    # module name of controllers
+    m_key_monsoon = "monsoon"
+    m_key_android_device = "android_device"
+    m_key_access_point = "access_point"
+    m_key_attenuator = "attenuator"
+    m_key_iperf_server = "iperf_server"
+
+    # A list of keys whose values in configs should not be passed to test
+    # classes without unpacking first.
+    reserved_keys = (key_testbed, key_log_path, key_test_paths)
+
+    controller_names = [
+        key_android_device,
+        key_access_point,
+        key_attenuator,
+        key_iperf_server,
+        key_monsoon
+    ]
+    tb_config_reserved_keys = controller_names + [key_testbed_name]
+
+def get_name_by_value(value):
+    for name, member in Config.__members__.items():
+        if member.value == value:
+            return name
+    return None
+
+def get_internal_value(external_value):
+    """Translates the value of an external key to the value of its
+    corresponding internal key.
+    """
+    return value_to_value(external_value, "i%s")
+
+def get_module_name(name_in_config):
+    """Translates the name of a controller in config file to its module name.
+    """
+    return value_to_value(name_in_config, "m_%s")
+
+def value_to_value(ref_value, pattern):
+    """Translates the value of a key to the value of its corresponding key. The
+    corresponding key is chosen based on the variable name pattern.
+    """
+    ref_key_name = get_name_by_value(ref_value)
+    if not ref_key_name:
+        return None
+    target_key_name = pattern % ref_key_name
+    try:
+        return getattr(Config, target_key_name).value
+    except AttributeError:
+        return None
\ No newline at end of file
diff --git a/acts/framework/acts/logger.py b/acts/framework/acts/logger.py
new file mode 100755
index 0000000..00d828f
--- /dev/null
+++ b/acts/framework/acts/logger.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 datetime
+import logging
+import os
+import re
+import sys
+
+from acts.utils import create_dir
+
+log_line_format = "%(asctime)s.%(msecs).03d %(levelname)s %(message)s"
+# The micro seconds are added by the format string above,
+# so the time format does not include ms.
+log_line_time_format = "%m-%d %H:%M:%S"
+log_line_timestamp_len = 18
+
+logline_timestamp_re = re.compile("\d\d-\d\d \d\d:\d\d:\d\d.\d\d\d")
+
+
+def _parse_logline_timestamp(t):
+    """Parses a logline timestamp into a tuple.
+
+    Args:
+        t: Timestamp in logline format.
+
+    Returns:
+        An iterable of date and time elements in the order of month, day, hour,
+        minute, second, microsecond.
+    """
+    date, time = t.split(' ')
+    month, day = date.split('-')
+    h, m, s = time.split(':')
+    s, ms = s.split('.')
+    return (month, day, h, m, s, ms)
+
+def is_valid_logline_timestamp(timestamp):
+    if len(timestamp) == log_line_timestamp_len:
+        if logline_timestamp_re.match(timestamp):
+            return True
+    return False
+
+def logline_timestamp_comparator(t1, t2):
+    """Comparator for timestamps in logline format.
+
+    Args:
+        t1: Timestamp in logline format.
+        t2: Timestamp in logline format.
+
+    Returns:
+        -1 if t1 < t2; 1 if t1 > t2; 0 if t1 == t2.
+    """
+    dt1 = _parse_logline_timestamp(t1)
+    dt2 = _parse_logline_timestamp(t2)
+    for u1, u2 in zip(dt1, dt2):
+        if u1 < u2:
+            return -1
+        elif u1 > u2:
+            return 1
+    return 0
+
+def _get_timestamp(time_format, delta=None):
+    t = datetime.datetime.now()
+    if delta:
+        t = t + datetime.timedelta(seconds=delta)
+    return t.strftime(time_format)[:-3]
+
+def epoch_to_log_line_timestamp(epoch_time):
+    d = datetime.datetime.fromtimestamp(epoch_time / 1000)
+    return d.strftime("%m-%d %H:%M:%S.%f")[:-3]
+
+def get_log_line_timestamp(delta=None):
+    """Returns a timestamp in the format used by log lines.
+
+    Default is current time. If a delta is set, the return value will be
+    the current time offset by delta seconds.
+
+    Args:
+        delta: Number of seconds to offset from current time; can be negative.
+
+    Returns:
+        A timestamp in log line format with an offset.
+    """
+    return _get_timestamp("%m-%d %H:%M:%S.%f", delta)
+
+def get_log_file_timestamp(delta=None):
+    """Returns a timestamp in the format used for log file names.
+
+    Default is current time. If a delta is set, the return value will be
+    the current time offset by delta seconds.
+
+    Args:
+        delta: Number of seconds to offset from current time; can be negative.
+
+    Returns:
+        A timestamp in log filen name format with an offset.
+    """
+    return _get_timestamp("%m-%d-%Y_%H-%M-%S-%f", delta)
+
+def get_test_logger(log_path, TAG, prefix=None, filename=None):
+    """Returns a logger object used for tests.
+
+    The logger object has a stream handler and a file handler. The stream
+    handler logs INFO level to the terminal, the file handler logs DEBUG
+    level to files.
+
+    Args:
+        log_path: Location of the log file.
+        TAG: Name of the logger's owner.
+        prefix: A prefix for each log line in terminal.
+        filename: Name of the log file. The default is the time the logger
+            is requested.
+
+    Returns:
+        A logger configured with one stream handler and one file handler
+    """
+    log = logging.getLogger(TAG)
+    if log.handlers:
+        # This logger has been requested before.
+        return log
+    log.propagate = False
+    log.setLevel(logging.DEBUG)
+    # Log info to stream
+    terminal_format = log_line_format
+    if prefix:
+        terminal_format = "[{}] {}".format(prefix, log_line_format)
+    c_formatter = logging.Formatter(terminal_format, log_line_time_format)
+    ch = logging.StreamHandler(sys.stdout)
+    ch.setFormatter(c_formatter)
+    ch.setLevel(logging.INFO)
+    # Log everything to file
+    f_formatter = logging.Formatter(log_line_format, log_line_time_format)
+    # All the logs of this test class go into one directory
+    if filename is None:
+        filename = get_log_file_timestamp()
+        create_dir(log_path)
+    fh = logging.FileHandler(os.path.join(log_path, 'test_run_details.txt'))
+    fh.setFormatter(f_formatter)
+    fh.setLevel(logging.DEBUG)
+    log.addHandler(ch)
+    log.addHandler(fh)
+    return log
+
+def kill_test_logger(logger):
+    """Cleans up a test logger object created by get_test_logger.
+
+    Args:
+        logger: The logging object to clean up.
+    """
+    for h in list(logger.handlers):
+        logger.removeHandler(h)
+        if isinstance(h, logging.FileHandler):
+            h.close()
+
+def get_test_reporter(log_path):
+    """Returns a file object used for reports.
+
+    Args:
+        log_path: Location of the report file.
+
+    Returns:
+        A file object.
+    """
+    create_dir(log_path)
+    f = open(os.path.join(log_path, 'test_run_summary.txt'), 'w')
+    return f
+
+def kill_test_reporter(reporter):
+    """Cleans up a test reporter object created by get_test_reporter.
+
+    Args:
+        reporter: The reporter file object to clean up.
+    """
+    reporter.close()
+
+def create_latest_log_alias(actual_path):
+    """Creates a symlink to the latest test run logs.
+
+    Args:
+        actual_path: The source directory where the latest test run's logs are.
+    """
+    link_path = os.path.join(os.path.dirname(actual_path), "latest")
+    if os.path.islink(link_path):
+        os.remove(link_path)
+    os.symlink(actual_path, link_path)
+
+def get_test_logger_and_reporter(log_path, TAG, prefix=None, filename=None):
+    """Returns a logger and a reporter of the same name.
+
+    Args:
+        log_path: Location of the report file.
+        TAG: Name of the logger's owner.
+        prefix: A prefix for each log line in terminal.
+        filename: Name of the files. The default is the time the objects
+            are requested.
+
+    Returns:
+        A log object and a reporter object.
+    """
+    if filename is None:
+        filename = get_log_file_timestamp()
+    create_dir(log_path)
+    logger = get_test_logger(log_path, TAG, prefix, filename)
+    reporter = get_test_reporter(log_path)
+    create_latest_log_alias(log_path)
+    return logger, reporter, filename
+
+def normalize_log_line_timestamp(log_line_timestamp):
+    """Replace special characters in log line timestamp with normal characters.
+
+    Args:
+        log_line_timestamp: A string in the log line timestamp format. Obtained
+            with get_log_line_timestamp.
+
+    Returns:
+        A string representing the same time as input timestamp, but without
+        special characters.
+    """
+    norm_tp = log_line_timestamp.replace(' ', '_')
+    norm_tp = norm_tp.replace(':', '-')
+    return norm_tp
+
+class LoggerProxy(object):
+    """This class is for situations where a logger may or may not exist.
+
+    e.g. In controller classes, sometimes we don't have a logger to pass in,
+    like during a quick try in python console. In these cases, we don't want to
+    crash on the log lines because logger is None, so we should set self.log to
+    an object of this class in the controller classes, instead of the actual
+    logger object.
+    """
+    def __init__(self, logger=None):
+        self.log = logger
+
+    def __getattr__(self, name):
+        def log_call(*args):
+            if self.log:
+                return getattr(self.log, name)(*args)
+            print(*args)
+        return log_call
\ No newline at end of file
diff --git a/acts/framework/acts/monsoon.py b/acts/framework/acts/monsoon.py
new file mode 100755
index 0000000..6896648
--- /dev/null
+++ b/acts/framework/acts/monsoon.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2015 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""Interface for a USB-connected Monsoon power meter
+(http://msoon.com/LabEquipment/PowerMonitor/).
+"""
+
+_Python3_author_ = 'angli@google.com (Ang Li)'
+_author_ = 'kens@google.com (Ken Shirriff)'
+
+import argparse
+import sys
+import time
+import collections
+
+from acts.controllers.monsoon import Monsoon
+
+def main(FLAGS):
+    """Simple command-line interface for Monsoon."""
+    if FLAGS.avg and FLAGS.avg < 0:
+        print("--avg must be greater than 0")
+        return
+
+    mon = Monsoon(serial=int(FLAGS.serialno[0]))
+
+    if FLAGS.voltage is not None:
+        mon.set_voltage(FLAGS.voltage)
+
+    if FLAGS.current is not None:
+        mon.set_max_current(FLAGS.current)
+
+    if FLAGS.status:
+        items = sorted(mon.status.items())
+        print("\n".join(["%s: %s" % item for item in items]))
+
+    if FLAGS.usbpassthrough:
+        mon.usb(FLAGS.usbpassthrough)
+
+    if FLAGS.startcurrent is not None:
+         mon.set_max_init_current(FLAGS.startcurrent)
+
+    if FLAGS.samples:
+        # Have to sleep a bit here for monsoon to be ready to lower the rate of
+        # socket read timeout.
+        time.sleep(1)
+        result = mon.take_samples(FLAGS.hz, FLAGS.samples,
+            sample_offset=FLAGS.offset, live=True)
+        print(repr(result))
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description=("This is a python utility "
+                 "tool to control monsoon power measurement boxes."))
+    parser.add_argument("--status", action="store_true",
+        help="Print power meter status.")
+    parser.add_argument("-avg", "--avg", type=int, default=0,
+        help="Also report average over last n data points.")
+    parser.add_argument("-v", "--voltage", type=float,
+        help="Set output voltage (0 for off)")
+    parser.add_argument("-c", "--current", type=float,
+        help="Set max output current.")
+    parser.add_argument("-sc", "--startcurrent", type=float,
+        help="Set max power-up/inital current.")
+    parser.add_argument("-usb", "--usbpassthrough", choices=("on", "off",
+        "auto"), help="USB control (on, off, auto).")
+    parser.add_argument("-sp", "--samples", type=int,
+        help="Collect and print this many samples")
+    parser.add_argument("-hz", "--hz", type=int,
+        help="Sample this many times per second.")
+    parser.add_argument("-d", "--device", help="Use this /dev/ttyACM... file.")
+    parser.add_argument("-sn", "--serialno", type=int, nargs=1, required=True,
+        help="The serial number of the Monsoon to use.")
+    parser.add_argument("--offset", type=int, nargs='?', default=0,
+        help="The number of samples to discard when calculating average.")
+    parser.add_argument("-r", "--ramp", action="store_true", help=("Gradually "
+        "increase voltage to prevent tripping Monsoon overvoltage"))
+    args = parser.parse_args()
+    main(args)
diff --git a/acts/framework/acts/records.py b/acts/framework/acts/records.py
new file mode 100644
index 0000000..b750e63
--- /dev/null
+++ b/acts/framework/acts/records.py
@@ -0,0 +1,294 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2015 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 is where all the record definitions and record containers live.
+"""
+
+import json
+import pprint
+
+from acts.signals import TestSignal
+from acts.utils import epoch_to_human_time
+from acts.utils import get_current_epoch_time
+
+
+class TestResultEnums(object):
+    """Enums used for TestResultRecord class.
+
+    Includes the tokens to mark test result with, and the string names for each
+    field in TestResultRecord.
+    """
+
+    RECORD_NAME = "Test Name"
+    RECORD_BEGIN_TIME = "Begin Time"
+    RECORD_END_TIME = "End Time"
+    RECORD_RESULT = "Result"
+    RECORD_UID = "UID"
+    RECORD_EXTRAS = "Extras"
+    RECORD_DETAILS = "Details"
+    TEST_RESULT_PASS = "PASS"
+    TEST_RESULT_FAIL = "FAIL"
+    TEST_RESULT_SKIP = "SKIP"
+
+class TestResultRecord(object):
+    """A record that holds the information of a test case execution.
+
+    Attributes:
+        test_name: A string representing the name of the test case.
+        begin_time: Epoch timestamp of when the test case started.
+        end_time: Epoch timestamp of when the test case ended.
+        self.uid: Unique identifier of a test case.
+        self.result: Test result, PASS/FAIL/SKIP.
+        self.extras: User defined extra information of the test result.
+        self.details: A string explaining the details of the test case.
+    """
+
+    def __init__(self, t_name):
+        self.test_name = t_name
+        self.begin_time = None
+        self.end_time = None
+        self.uid = None
+        self.result = None
+        self.extras = None
+        self.details = None
+
+    def test_begin(self):
+        """Call this when the test case it records begins execution.
+
+        Sets the begin_time of this record.
+        """
+        self.begin_time = get_current_epoch_time()
+
+    def _test_end(self, result, e):
+        """Class internal function to signal the end of a test case execution.
+
+        Args:
+            result: One of the TEST_RESULT enums in TestResultEnums.
+            e: A test termination signal (usually an exception object). It can
+                be any exception instance or of any subclass of
+                base_test._TestSignal.
+        """
+        self.end_time = get_current_epoch_time()
+        self.result = result
+        if isinstance(e, TestSignal):
+            self.details = e.details
+            self.extras = e.extras
+        else:
+            self.details = str(e)
+
+    def test_pass(self, e=None):
+        """To mark the test as passed in this record.
+
+        Args:
+            e: An instance of acts.signals.TestPass.
+        """
+        self._test_end(TestResultEnums.TEST_RESULT_PASS, e)
+
+    def test_fail(self, e=None):
+        """To mark the test as failed in this record.
+
+        Only test_fail does instance check because we want "assert xxx" to also
+        fail the test same way assert_true does.
+
+        Args:
+            e: An exception object. It can be an instance of AssertionError or
+                acts.base_test.TestFailure.
+        """
+        self._test_end(TestResultEnums.TEST_RESULT_FAIL, e)
+
+    def test_skip(self, e=None):
+        """To mark the test as skipped in this record.
+
+        Args:
+            e: An instance of acts.signals.TestSkip.
+        """
+        self._test_end(TestResultEnums.TEST_RESULT_SKIP, e)
+
+    def __str__(self):
+        d = self.to_dict()
+        l = ["%s = %s" % (k, v) for k, v in d.items()]
+        s = ', '.join(l)
+        return s
+
+    def __repr__(self):
+        """This returns a short string representation of the test record."""
+        t = epoch_to_human_time(self.begin_time)
+        return "%s %s %s" % (t, self.test_name, self.result)
+
+    def to_dict(self):
+        """Gets a dictionary representating the content of this class.
+
+        Returns:
+            A dictionary representating the content of this class.
+        """
+        d = {}
+        d[TestResultEnums.RECORD_NAME] = self.test_name
+        d[TestResultEnums.RECORD_BEGIN_TIME] = self.begin_time
+        d[TestResultEnums.RECORD_END_TIME] = self.end_time
+        d[TestResultEnums.RECORD_RESULT] = self.result
+        d[TestResultEnums.RECORD_UID] = self.uid
+        d[TestResultEnums.RECORD_EXTRAS] = self.extras
+        d[TestResultEnums.RECORD_DETAILS] = self.details
+        return d
+
+    def json_str(self):
+        """Converts this test record to a string in json format.
+
+        Format of the json string is:
+            {
+                'Test Name': <test name>,
+                'Begin Time': <epoch timestamp>,
+                'Details': <details>,
+                ...
+            }
+
+        Returns:
+            A json-format string representing the test record.
+        """
+        return json.dumps(self.to_dict())
+
+class TestResult(object):
+    """A class that contains metrics of a test run.
+
+    This class is essentially a container of TestResultRecord objects.
+
+    Attributes:
+        self.requested: A list of strings, each is the name of a test requested
+            by user.
+        self.failed: A list of records for tests failed.
+        self.executed: A list of records for tests that were actually executed.
+        self.passed: A list of records for tests passed.
+        self.skipped: A list of records for tests skipped.
+        self.unknown: A list of records for tests with unknown result token.
+    """
+
+    def __init__(self):
+        self.requested = []
+        self.failed = []
+        self.executed = []
+        self.passed = []
+        self.skipped = []
+        self.unknown = []
+
+    def __add__(self, r):
+        """Overrides '+' operator for TestResult class.
+
+        The add operator merges two TestResult objects by concatenating all of
+        their lists together.
+
+        Args:
+            r: another instance of TestResult to be added
+
+        Returns:
+            A TestResult instance that's the sum of two TestResult instances.
+        """
+        assert isinstance(r, TestResult)
+        sum_result = TestResult()
+        for name in sum_result.__dict__:
+            l_value = list(getattr(self, name))
+            r_value = list(getattr(r, name))
+            setattr(sum_result, name, l_value + r_value)
+        return sum_result
+
+    def add_record(self, record):
+        """Adds a test record to test result.
+
+        A record is considered executed once it's added to the test result.
+
+        Args:
+            record: A test record object to add.
+        """
+        self.executed.append(record)
+        if record.result == TestResultEnums.TEST_RESULT_FAIL:
+            self.failed.append(record)
+        elif record.result == TestResultEnums.TEST_RESULT_SKIP:
+            self.skipped.append(record)
+        elif record.result == TestResultEnums.TEST_RESULT_PASS:
+            self.passed.append(record)
+        else:
+            self.unknown.append(record)
+
+    def skip_all(self, e=None):
+        """Marks every requested test in this result obj as skipped.
+
+        Args:
+            e: An instance of acts.signals.TestSkip specifying the reason for
+                skipping.
+        """
+        for t_name in self.requested:
+            tr = TestResultRecord(t_name)
+            tr.test_begin()
+            tr.test_skip(e)
+            self.add_record(tr)
+
+    def json_str(self):
+        """Converts this test result to a string in json format.
+
+        Format of the json string is:
+            {
+                "Results": [
+                    {<executed test record 1>},
+                    {<executed test record 2>},
+                    ...
+                ],
+                "Summary": <summary dict>
+            }
+
+        Returns:
+            A json-format string representing the test results.
+        """
+        d = {}
+        executed = [record.to_dict() for record in self.executed]
+        d["Results"] = executed
+        d["Summary"] = self.summary_dict()
+        json_str = json.dumps(d, indent=4, sort_keys=True)
+        return json_str
+
+    def summary_str(self):
+        """Gets a string that summarizes the stats of this test result.
+
+        The summary rovides the counts of how many test cases fall into each
+        category, like "Passed", "Failed" etc.
+
+        Format of the string is:
+            Requested <int>, Executed <int>, ...
+
+        Returns:
+            A summary string of this test result.
+        """
+        l = ["%s %d" % (k, v) for k, v in self.summary_dict().items()]
+        # Sort the list so the order is the same every time.
+        msg = ", ".join(sorted(l))
+        return msg
+
+    def summary_dict(self):
+        """Gets a dictionary that summarizes the stats of this test result.
+
+        The summary rovides the counts of how many test cases fall into each
+        category, like "Passed", "Failed" etc.
+
+        Returns:
+            A dictionary with the stats of this test result.
+        """
+        d = {}
+        d["Requested"] = len(self.requested)
+        d["Executed"] = len(self.executed)
+        d["Passed"] = len(self.passed)
+        d["Failed"] = len(self.failed)
+        d["Skipped"] = len(self.skipped)
+        d["Unknown"] = len(self.unknown)
+        return d
diff --git a/acts/framework/acts/signals.py b/acts/framework/acts/signals.py
new file mode 100644
index 0000000..a937296
--- /dev/null
+++ b/acts/framework/acts/signals.py
@@ -0,0 +1,76 @@
+#!/usr/bin/python3.4
+#
+# Copyright 2015 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 is where all the test signal classes and related utilities live.
+"""
+
+import functools
+import json
+
+def generated_test(func):
+    """A decorator used to suppress result reporting for the test case that
+    kicks off a group of generated test cases.
+
+    Returns:
+        What the decorated function returns.
+    """
+    @functools.wraps(func)
+    def wrapper(*args, **kwargs):
+        func(*args, **kwargs)
+        raise TestSilent(
+            "Result reporting for %s is suppressed" % func.__name__)
+    return wrapper
+
+class TestSignalError(Exception):
+    """Raised when an error occurs inside a test signal."""
+
+class TestSignal(Exception):
+    """Base class for all test result control signals."""
+    def __init__(self, details, extras=None):
+        if not isinstance(details, str):
+            raise TestSignalError("Message has to be a string.")
+        self.details = details
+        try:
+            json.dumps(extras)
+            self.extras = extras
+        except TypeError:
+            raise TestSignalError(("Extras must be json serializable. %s "
+                                   "is not.") % extras)
+
+class TestFailure(TestSignal):
+    """Raised when a test has failed."""
+
+class TestPass(TestSignal):
+    """Raised when a test has passed."""
+
+class TestSkip(TestSignal):
+    """Raised when a test has been skipped."""
+
+class TestSilent(TestSignal):
+    """Raised when a test should not be reported. This should only be used for
+    generated test cases.
+    """
+
+class TestAbortClass(TestSignal):
+    """Raised when all subsequent test cases within the same test class should
+    be aborted.
+    """
+
+class TestAbortAll(TestSignal):
+    """Raised when all subsequent test cases should be aborted."""
+
+class ControllerError(Exception):
+    """Raised when an error occured in controller classes."""
\ No newline at end of file
diff --git a/acts/framework/acts/test_runner.py b/acts/framework/acts/test_runner.py
new file mode 100644
index 0000000..3f12f5e
--- /dev/null
+++ b/acts/framework/acts/test_runner.py
@@ -0,0 +1,328 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 argparse
+import functools
+import importlib
+import inspect
+import os
+import pkgutil
+import sys
+from urllib.error import URLError
+
+import acts.logger as logger
+
+from acts.keys import Config
+from acts.keys import get_internal_value
+from acts.keys import get_module_name
+from acts.records import TestResult
+from acts.signals import TestAbortAll
+from acts.utils import create_dir
+from acts.utils import find_files
+from acts.utils import load_config
+from acts.utils import start_standing_subprocess
+from acts.utils import stop_standing_subprocess
+
+adb_logcat_tag = "adb_logcat"
+
+class USERError(Exception):
+    """Raised when a problem is caused by user mistake, e.g. wrong command,
+    misformatted config, test info, wrong test paths etc.
+    """
+
+class TestRunner(object):
+    """The class that instantiates test classes, executes test cases, and
+    report results.
+        Attrubutes:
+        self.configs: A dictionary containing the configurations for this test
+            run. This is populated during instantiation.
+        self.procs: A dictionary keeping track of processes started by this
+            test run.
+        self.id: A string that is the unique identifier of this test run.
+        self.log_path: A string representing the path of the dir under which
+            all logs from this test run should be written.
+        self.log: The logger object used throughout this test run.
+        self.reporter: A file to write result summary to.
+        self.controller_destructors: A dictionary that holds the controller
+            distructors. Keys are controllers' names.
+        self.test_classes: A dictionary where we can look up the test classes
+            by name to instantiate.
+        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.
+        self.running: A boolean signifies whether this test run is ongoing or
+            not.
+    """
+    def __init__(self, test_configs, run_list):
+        self.configs = {}
+        self.procs = {}
+        tb = test_configs[Config.key_testbed.value]
+        self.testbed_name = tb[Config.key_testbed_name.value]
+        start_time = logger.get_log_file_timestamp()
+        self.id = "{}@{}".format(self.testbed_name, start_time)
+        # log_path should be set before parsing configs.
+        l_path = os.path.join(test_configs[Config.key_log_path.value],
+            self.testbed_name, start_time)
+        self.log_path = os.path.abspath(l_path)
+        (self.log,
+         self.reporter,
+         self.log_name) = logger.get_test_logger_and_reporter(
+            self.log_path,
+            self.id,
+            self.testbed_name)
+        self.controller_destructors = {}
+        self.run_list = run_list
+        self.parse_config(test_configs)
+        t_configs = test_configs[Config.key_test_paths.value]
+        self.test_classes = self.import_test_modules(t_configs)
+        self.set_test_util_logs()
+        self.results = TestResult()
+        self.running = False
+
+    def import_test_modules(self, test_paths):
+        """Imports test classes from test scripts.
+
+        1. Locate all .py files under test paths.
+        2. Import the .py files as modules.
+        3. Find the module members that are test classes.
+        4. Categorize the test classes by name.
+
+        Args:
+            test_paths: A list of directory paths where the test files reside.
+
+        Returns:
+            A dictionary where keys are test class name strings, values are actual
+            test classes that can be instantiated.
+        """
+        def is_testfile_name(name, ext):
+            if ext == ".py":
+                if name.endswith("Test") or name.endswith("_test"):
+                    return True
+            return False
+        file_list = find_files(test_paths, is_testfile_name)
+        test_classes = {}
+        for path, name, _ in file_list:
+            sys.path.append(path)
+            try:
+                module = importlib.import_module(name)
+            except ImportError:
+                for test_cls_name, _ in self.run_list:
+                    # Only block if a test class on the run list causes an import
+                    # error.
+                    if name == test_cls_name:
+                        raise USERError(("Encountered error importing test class "
+                            "%s, abort.") % test_cls_name)
+                continue
+            for member_name in dir(module):
+                if not member_name.startswith("__"):
+                    if member_name.endswith("Test"):
+                        test_class = getattr(module, member_name)
+                        if inspect.isclass(test_class):
+                            test_classes[member_name] = test_class
+        return test_classes
+
+    def parse_config(self, test_configs):
+        """Parses the test configuration and unpacks objects and parameters
+        into a dictionary to be passed to test classes.
+
+        Args:
+            test_configs: A json object representing the test configurations.
+        """
+        data = test_configs[Config.key_testbed.value]
+        testbed_configs = data[Config.key_testbed_name.value]
+        self.configs[Config.ikey_testbed_name.value] = testbed_configs
+        # Unpack controllers
+        for ctrl_name in Config.controller_names.value:
+            if ctrl_name in data:
+                module_name = get_module_name(ctrl_name)
+                module = importlib.import_module("acts.controllers.%s" %
+                    module_name)
+                # Create controller objects.
+                create = getattr(module, "create")
+                try:
+                    objects = create(data[ctrl_name], self.log)
+                    controller_var_name = get_internal_value(ctrl_name)
+                    self.configs[controller_var_name] = objects
+                    self.log.debug("Found %d objects for controller %s" %
+                        (len(objects), module_name))
+                    # Bind controller objects to their destructors.
+                    destroy_func = getattr(module, "destroy")
+                    self.controller_destructors[controller_var_name] = destroy_func
+                except:
+                    msg = ("Failed to initialize objects for controller {}, "
+                        "abort!").format(module_name)
+                    self.log.error(msg)
+                    self.clean_up()
+                    raise
+        test_runner_keys = (Config.key_adb_logcat_param.value,)
+        for key in test_runner_keys:
+            if key in test_configs:
+                setattr(self, key, test_configs[key])
+        # Unpack other params.
+        self.configs[Config.ikey_logpath.value] = self.log_path
+        self.configs[Config.ikey_logger.value] = self.log
+        self.configs[Config.ikey_reporter.value] = self.reporter
+        cli_args = test_configs[Config.ikey_cli_args.value]
+        self.configs[Config.ikey_cli_args.value] = cli_args
+        user_param_pairs = []
+        for item in test_configs.items():
+            if item[0] not in Config.reserved_keys.value:
+                user_param_pairs.append(item)
+        self.configs[Config.ikey_user_param.value] = dict(user_param_pairs)
+
+    def set_test_util_logs(self, module=None):
+        """Sets the log object to each test util module.
+
+        This recursively include all modules under acts.test_utils and sets the
+        main test logger to each module.
+
+        Args:
+            module: A module under acts.test_utils.
+        """
+        # Initial condition of recursion.
+        if not module:
+            module = importlib.import_module("acts.test_utils")
+        # Somehow pkgutil.walk_packages is not working for me.
+        # Using iter_modules for now.
+        pkg_iter = pkgutil.iter_modules(module.__path__, module.__name__ + '.')
+        for _, module_name, ispkg in pkg_iter:
+            m = importlib.import_module(module_name)
+            if ispkg:
+                self.set_test_util_logs(module=m)
+            else:
+                msg = "Setting logger to test util module %s" % module_name
+                self.log.debug(msg)
+                setattr(m, "log", self.log)
+
+    def run_test_class(self, test_cls_name, test_cases=None):
+        """Instantiates and executes a test class.
+
+        If test_cases is None, the test cases listed by self.tests will be
+        executed instead. If self.tests is empty as well, no test case in this
+        test class will be executed.
+
+        Args:
+            test_cls_name: Name of the test class to execute.
+            test_cases: List of test case names to execute within the class.
+
+        Returns:
+            A tuple, with the number of cases passed at index 0, and the total
+            number of test cases at index 1.
+        """
+        try:
+            test_cls = self.test_classes[test_cls_name]
+        except KeyError:
+            raise USERError(("Unable to locate class %s in any of the test "
+                "paths specified.") % test_cls_name)
+
+        with test_cls(self.configs) as test_cls_instance:
+            try:
+                cls_result = test_cls_instance.run(test_cases)
+                self.results += cls_result
+            except TestAbortAll as e:
+                self.results += e.results
+                raise e
+
+    def run(self):
+        if not self.running:
+            # Only do these if this is the first iteration.
+            self.start_adb_logcat()
+            self.running = True
+        self.log.debug("Executing run list {}.".format(self.run_list))
+        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 {} in test class {}."
+                                ).format(test_case_names, test_cls_name))
+            else:
+                self.log.debug("Executing test class {}".format(
+                    test_cls_name))
+            try:
+                self.run_test_class(test_cls_name, test_case_names)
+            except TestAbortAll as e:
+                msg = "Abort all subsequent test classes. Reason: %s" % str(e)
+                self.log.warning(msg)
+                raise
+
+    def stop(self):
+        """Releases resources from test run. Should be called right after run()
+        finishes.
+        """
+        if self.running:
+            msg = "\nSummary for test run %s: %s\n" % (self.id,
+                self.results.summary_str())
+            self.reporter.write(msg)
+            self._write_results_json_str()
+            self.log.info(msg.strip())
+            self.clean_up()
+            logger.kill_test_logger(self.log)
+            logger.kill_test_reporter(self.reporter)
+            self.stop_adb_logcat()
+            self.running = False
+
+    def clean_up(self):
+        for name, destroy in self.controller_destructors.items():
+            try:
+                self.log.debug("Destroying %s." % name)
+                destroy(self.configs[name])
+            except:
+                self.log.exception("Exception occurred destroying %s." % name)
+
+    def start_adb_logcat(self):
+        """Starts adb logcat for each device in separate subprocesses and save
+        the logs in files.
+        """
+        if Config.ikey_android_device.value not in self.configs:
+            self.log.debug("No android device available, skipping adb logcat.")
+            return
+        devices = self.configs[Config.ikey_android_device.value]
+        file_list = []
+        for d in devices:
+            # Disable adb log spam filter.
+            d.adb.shell("logpersist.start")
+            serial = d.serial
+            extra_param = ""
+            f_name = "adblog,{},{}.txt".format(d.model, serial)
+            if hasattr(self, Config.key_adb_logcat_param.value):
+                extra_param = getattr(self, Config.key_adb_logcat_param.value)
+            cmd = "adb -s {} logcat -v threadtime {} > {}".format(
+                serial, extra_param, os.path.join(self.log_path, f_name))
+            p = start_standing_subprocess(cmd)
+            self.procs[serial + adb_logcat_tag] = p
+            file_list.append(f_name)
+        if file_list:
+            self.configs[Config.ikey_adb_log_path.value] = self.log_path
+            self.configs[Config.ikey_adb_log_files.value] = file_list
+
+    def stop_adb_logcat(self):
+        """Stops all adb logcat subprocesses.
+        """
+        for k, p in self.procs.items():
+            if k[-len(adb_logcat_tag):] == adb_logcat_tag:
+                stop_standing_subprocess(p)
+
+    def _write_results_json_str(self):
+        """Writes out a json file with the test result info for easy parsing.
+
+        TODO(angli): This should be replaced by standard log record mechanism.
+        """
+        path = os.path.join(self.log_path, "test_run_summary.json")
+        with open(path, 'w') as f:
+            f.write(self.results.json_str())
+
+if __name__ == "__main__":
+    pass
diff --git a/acts/framework/acts/test_utils/__init__.py b/acts/framework/acts/test_utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/test_utils/__init__.py
diff --git a/acts/framework/acts/test_utils/bt/BleEnum.py b/acts/framework/acts/test_utils/bt/BleEnum.py
new file mode 100644
index 0000000..67e017f
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/BleEnum.py
@@ -0,0 +1,162 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 enum import Enum
+
+
+class ScanSettingsCallbackType(Enum):
+  CALLBACK_TYPE_ALL_MATCHES = 1
+  CALLBACK_TYPE_FIRST_MATCH = 2
+  CALLBACK_TYPE_MATCH_LOST = 4
+  CALLBACK_TYPE_FOUND_AND_LOST = 6
+
+
+class ScanSettingsMatchMode(Enum):
+  AGGRESIVE = 1
+  STICKY = 2
+
+
+class ScanSettingsMatchNum(Enum):
+  MATCH_NUM_ONE_ADVERTISEMENT = 1
+  MATCH_NUM_FEW_ADVERTISEMENT = 2
+  MATCH_NUM_MAX_ADVERTISEMENT = 3
+
+
+class ScanSettingsScanResultType(Enum):
+  SCAN_RESULT_TYPE_FULL = 0
+  SCAN_RESULT_TYPE_ABBREVIATED = 1
+
+
+class ScanSettingsScanMode(Enum):
+  SCAN_MODE_OPPORTUNISTIC = -1
+  SCAN_MODE_LOW_POWER = 0
+  SCAN_MODE_BALANCED = 1
+  SCAN_MODE_LOW_LATENCY = 2
+
+
+class ScanSettingsReportDelaySeconds(Enum):
+  MIN = 0
+  MAX = 9223372036854775807
+
+
+class AdvertiseSettingsAdvertiseType(Enum):
+  ADVERTISE_TYPE_NON_CONNECTABLE = 0
+  ADVERTISE_TYPE_CONNECTABLE = 1
+
+
+class AdvertiseSettingsAdvertiseMode(Enum):
+  ADVERTISE_MODE_LOW_POWER = 0
+  ADVERTISE_MODE_BALANCED = 1
+  ADVERTISE_MODE_LOW_LATENCY = 2
+
+
+class AdvertiseSettingsAdvertiseTxPower(Enum):
+  ADVERTISE_TX_POWER_ULTRA_LOW = 0
+  ADVERTISE_TX_POWER_LOW = 1
+  ADVERTISE_TX_POWER_MEDIUM = 2
+  ADVERTISE_TX_POWER_HIGH = 3
+
+
+class JavaInteger(Enum):
+  MIN = -2147483648
+  MAX = 2147483647
+
+
+class Uuids(Enum):
+  P_Service = "0000feef-0000-1000-8000-00805f9b34fb"
+  HR_SERVICE = "0000180d-0000-1000-8000-00805f9b34fb"
+
+
+class GattConnectionState(Enum):
+  STATE_DISCONNECTED = 0
+  STATE_CONNECTING = 1
+  STATE_CONNECTED = 2
+  STATE_DISCONNECTING = 3
+
+
+class BluetoothGattCharacteristic(Enum):
+  PROPERTY_BROADCAST = 0x01
+  PROPERTY_READ = 0x02
+  PROPERTY_WRITE_NO_RESPONSE = 0x04
+  PROPERTY_WRITE = 0x08
+  PROPERTY_NOTIFY = 0x10
+  PROPERTY_INDICATE = 0x20
+  PROPERTY_SIGNED_WRITE = 0x40
+  PROPERTY_EXTENDED_PROPS = 0x80
+  PERMISSION_READ = 0x01
+  PERMISSION_READ_ENCRYPTED = 0x02
+  PERMISSION_READ_ENCRYPTED_MITM = 0x04
+  PERMISSION_WRITE = 0x10
+  PERMISSION_WRITE_ENCRYPTED = 0x20
+  PERMISSION_WRITE_ENCRYPTED_MITM = 0x40
+  PERMISSION_WRITE_SIGNED = 0x80
+  PERMISSION_WRITE_SIGNED_MITM = 0x100
+  WRITE_TYPE_DEFAULT = 0x02
+  WRITE_TYPE_NO_RESPONSE = 0x01
+  WRITE_TYPE_SIGNED = 0x04
+  FORMAT_UINT8 = 0x11
+  FORMAT_UINT16 = 0x12
+  FORMAT_UINT32 = 0x14
+  FORMAT_SINT8 = 0x21
+  FORMAT_SINT16 = 0x22
+  FORMAT_SINT32 = 0x24
+  FORMAT_SFLOAT = 0x32
+  FORMAT_FLOAT = 0x34
+
+class BluetoothGattDescriptor(Enum):
+  ENABLE_NOTIFICATION_VALUE = [0x01, 0x00]
+  ENABLE_INDICATION_VALUE = [0x02, 0x00]
+  DISABLE_NOTIFICATION_VALUE = [0x00, 0x00]
+  PERMISSION_READ = 0x01
+  PERMISSION_READ_ENCRYPTED = 0x02
+  PERMISSION_READ_ENCRYPTED_MITM = 0x04
+  PERMISSION_WRITE = 0x10
+  PERMISSION_WRITE_ENCRYPTED = 0x20
+  PERMISSION_WRITE_ENCRYPTED_MITM = 0x40
+  PERMISSION_WRITE_SIGNED = 0x80
+  PERMISSION_WRITE_SIGNED_MITM = 0x100
+
+class BluetoothGattService(Enum):
+  SERVICE_TYPE_PRIMARY = 0
+  SERVICE_TYPE_SECONDARY = 1
+
+class BluetoothGattConnectionPriority(Enum):
+  CONNECTION_PRIORITY_BALANCED = 0
+  CONNECTION_PRIORITY_HIGH = 1
+  CONNECTION_PRIORITY_LOW_POWER = 2
+
+class BluetoothGatt(Enum):
+  GATT_SUCCESS = 0
+  GATT_FAILURE = 0x101
+
+class AdvertiseErrorCode(Enum):
+  DATA_TOO_LARGE = 1
+  TOO_MANY_ADVERTISERS = 2
+  ADVERTISE_ALREADY_STARTED = 3
+  BLUETOOTH_INTERNAL_FAILURE = 4
+  FEATURE_NOT_SUPPORTED = 5
+
+class BluetoothAdapterState(Enum):
+  STATE_OFF = 10
+  STATE_TURNING_ON = 11
+  STATE_ON = 12
+  STATE_TURNING_OFF = 13
+  STATE_BLE_TURNING_ON = 14
+  STATE_BLE_ON = 15
+  STATE_BLE_TURNING_OFF = 16
+
+class BluetoothMtuSize(Enum):
+  MIN = 23
+  MAX = 217
\ No newline at end of file
diff --git a/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py b/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py
new file mode 100644
index 0000000..e337181
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py
@@ -0,0 +1,71 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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.
+"""
+    Base Class for Defining Common Bluetooth Test Functionality
+"""
+
+import time
+from acts.base_test import BaseTestClass
+from acts.test_utils.bt.bt_test_utils import (log_energy_info,
+                                              reset_bluetooth,
+                                              setup_multiple_devices_for_bt_test,
+                                              take_btsnoop_logs)
+
+class BluetoothBaseTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+
+    # Use for logging in the test cases to facilitate
+    # faster log lookup and reduce ambiguity in logging.
+    def bt_test_wrap(fn):
+        def _safe_wrap_test_case(self, *args, **kwargs):
+            test_id = "{}:{}:{}".format(
+                self.__class__.__name__,
+                fn.__name__,
+                time.time())
+            log_string = "[Test ID] {}".format(test_id)
+            self.log.info(log_string)
+            return fn(self, *args, **kwargs)
+        return _safe_wrap_test_case
+
+    def setup_class(self):
+        return setup_multiple_devices_for_bt_test(self.droids, self.eds)
+
+    def setup_test(self):
+        self.log.debug(log_energy_info(self.droids, "Start"))
+        for e in self.eds:
+            e.clear_all_events()
+        return True
+
+    def teardown_test(self):
+        self.log.debug(log_energy_info(self.droids, "End"))
+        return True
+
+    def on_fail(self, test_name, begin_time):
+        self.log.debug("Test {} failed. Gathering bugreport and btsnoop logs".
+                       format(test_name))
+        take_btsnoop_logs(self.droids, self, test_name)
+        reset_bluetooth(self.droids, self.eds)
+
+        if "no_bug_report_on_fail" not in self.user_params:
+            try:
+                self.take_bug_reports(
+                    test_name, begin_time, self.android_devices)
+            except:
+                self.log.error("Failed to take a bug report for {}"
+                               .format(test_name))
+
diff --git a/acts/framework/acts/test_utils/bt/BtEnum.py b/acts/framework/acts/test_utils/bt/BtEnum.py
new file mode 100644
index 0000000..18de47b
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/BtEnum.py
@@ -0,0 +1,22 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 enum import Enum
+
+class BluetoothScanModeType(Enum):
+  STATE_OFF = -1
+  SCAN_MODE_NONE = 0
+  SCAN_MODE_CONNECTABLE = 1
+  SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3
diff --git a/acts/framework/acts/test_utils/bt/__init__.py b/acts/framework/acts/test_utils/bt/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/__init__.py
diff --git a/acts/framework/acts/test_utils/bt/bt_test_utils.py b/acts/framework/acts/test_utils/bt/bt_test_utils.py
new file mode 100644
index 0000000..4cea2e7
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/bt_test_utils.py
@@ -0,0 +1,693 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import random
+import pprint
+import string
+import queue
+import threading
+import time
+
+from contextlib import suppress
+
+from acts.logger import LoggerProxy
+from acts.test_utils.bt.BleEnum import *
+from acts.test_utils.bt.BtEnum import *
+from acts.utils import exe_cmd
+
+default_timeout = 10
+# bt discovery timeout
+default_discovery_timeout = 3
+log = LoggerProxy()
+
+# Callback strings
+characteristic_write_request = "GattServer{}onCharacteristicWriteRequest"
+characteristic_write = "GattConnect{}onCharacteristicWrite"
+descriptor_write_request = "GattServer{}onDescriptorWriteRequest"
+descriptor_write = "GattConnect{}onDescriptorWrite"
+read_remote_rssi = "GattConnect{}onReadRemoteRssi"
+gatt_services_discovered = "GattConnect{}onServicesDiscovered"
+scan_result = "BleScan{}onScanResults"
+scan_failed = "BleScan{}onScanFailed"
+service_added = "GattServer{}onServiceAdded"
+batch_scan_result = "BleScan{}onBatchScanResult"
+adv_fail = "BleAdvertise{}onFailure"
+adv_succ = "BleAdvertise{}onSuccess"
+bluetooth_off = "BluetoothStateChangedOff"
+bluetooth_on = "BluetoothStateChangedOn"
+mtu_changed = "GattConnect{}onMtuChanged"
+
+# rfcomm test uuids
+rfcomm_secure_uuid = "fa87c0d0-afac-11de-8a39-0800200c9a66"
+rfcomm_insecure_uuid = "8ce255c0-200a-11e0-ac64-0800200c9a66"
+
+advertisements_to_devices = {
+    "Nexus 4": 0,
+    "Nexus 5": 0,
+    "Nexus 5X":15,
+    "Nexus 7": 0,
+    "Nexus Player": 1,
+    "Nexus 6": 4,
+    "Nexus 6P": 4,
+    "AOSP on Shamu": 4,
+    "Nexus 9": 4,
+    "Sprout": 10,
+    "Micromax AQ4501": 10,
+    "4560MMX": 10,
+    "G Watch R": 1,
+    "Gear Live": 1,
+    "SmartWatch 3": 1,
+    "Zenwatch": 1,
+    "AOSP on Shamu": 4,
+    "MSM8992 for arm64": 9,
+    "LG Watch Urbane": 1,
+    "Pixel C":4,
+    "angler": 4,
+    "bullhead": 15,
+}
+
+batch_scan_supported_list = {
+    "Nexus 4": False,
+    "Nexus 5": False,
+    "Nexus 7": False,
+    "Nexus Player": True,
+    "Nexus 6": True,
+    "Nexus 6P": True,
+    "Nexus 5X": True,
+    "AOSP on Shamu": True,
+    "Nexus 9": True,
+    "Sprout": True,
+    "Micromax AQ4501": True,
+    "4560MMX": True,
+    "Pixel C":True,
+    "G Watch R": True,
+    "Gear Live": True,
+    "SmartWatch 3": True,
+    "Zenwatch": True,
+    "AOSP on Shamu": True,
+    "MSM8992 for arm64": True,
+    "LG Watch Urbane": True,
+    "angler": True,
+    "bullhead": True,
+}
+
+
+def generate_ble_scan_objects(droid):
+    filter_list = droid.bleGenFilterList()
+    scan_settings = droid.bleBuildScanSetting()
+    scan_callback = droid.bleGenScanCallback()
+    return filter_list, scan_settings, scan_callback
+
+
+def generate_ble_advertise_objects(droid):
+    advertise_callback = droid.bleGenBleAdvertiseCallback()
+    advertise_data = droid.bleBuildAdvertiseData()
+    advertise_settings = droid.bleBuildAdvertiseSettings()
+    return advertise_callback, advertise_data, advertise_settings
+
+
+def extract_string_from_byte_array(string_list):
+    """Extract the string from array of string list
+    """
+    start = 1
+    end = len(string_list) - 1
+    extract_string = string_list[start:end]
+    return extract_string
+
+
+def extract_uuidlist_from_record(uuid_string_list):
+    """Extract uuid from Service UUID List
+    """
+    start = 1
+    end = len(uuid_string_list) - 1
+    uuid_length = 36
+    uuidlist = []
+    while start < end:
+        uuid = uuid_string_list[start:(start + uuid_length)]
+        start += uuid_length + 1
+        uuidlist.append(uuid)
+    return uuidlist
+
+
+def build_advertise_settings(droid, mode, txpower, type):
+    """Build Advertise Settings
+    """
+    droid.bleSetAdvertiseSettingsAdvertiseMode(mode)
+    droid.bleSetAdvertiseSettingsTxPowerLevel(txpower)
+    droid.bleSetAdvertiseSettingsIsConnectable(type)
+    settings = droid.bleBuildAdvertiseSettings()
+    return settings
+
+def setup_multiple_devices_for_bt_test(droids, eds):
+    log.info("Setting up Android Devices")
+    threads = []
+    for i in range(len(droids)):
+        thread = threading.Thread(target=reset_bluetooth,
+                                      args=([droids[i]],[eds[i]]))
+        threads.append(thread)
+        thread.start()
+    for t in threads:
+        t.join()
+
+    for d in droids:
+        setup_result = d.bluetoothSetLocalName(generate_id_by_size(4))
+        if not setup_result:
+            return setup_result
+        d.bluetoothDisableBLE()
+        bonded_devices = d.bluetoothGetBondedDevices()
+        for b in bonded_devices:
+            d.bluetoothUnbond(b['address'])
+    for x in range(len(droids)):
+        droid, ed = droids[x], eds[x]
+        setup_result = droid.bluetoothConfigHciSnoopLog(True)
+        if not setup_result:
+            return setup_result
+    return setup_result
+
+
+def reset_bluetooth(droids, eds):
+    """Resets bluetooth on the list of android devices passed into the function.
+    :param android_devices: list of android devices
+    :return: bool
+    """
+    for x in range(len(droids)):
+        droid, ed = droids[x], eds[x]
+        log.info(
+            "Reset state of bluetooth on device: {}".format(
+                droid.getBuildSerial()))
+        if droid.bluetoothCheckState() is True:
+            droid.bluetoothToggleState(False)
+            expected_bluetooth_off_event_name = bluetooth_off
+            try:
+                ed.pop_event(
+                    expected_bluetooth_off_event_name, default_timeout)
+            except Exception:
+                log.info("Failed to toggle Bluetooth off.")
+                return False
+        # temp sleep for b/17723234
+        time.sleep(3)
+        droid.bluetoothToggleState(True)
+        expected_bluetooth_on_event_name = bluetooth_on
+        try:
+            ed.pop_event(expected_bluetooth_on_event_name, default_timeout)
+        except Exception:
+            log.info("Failed to toggle Bluetooth on.")
+            return False
+    return True
+
+
+def get_advanced_droid_list(droids, eds):
+    droid_list = []
+    for i in range(len(droids)):
+        d = droids[i]
+        e = eds[i]
+        model = d.getBuildModel()
+        print (model)
+        max_advertisements = 0
+        batch_scan_supported = True
+        if model in advertisements_to_devices.keys():
+            max_advertisements = advertisements_to_devices[model]
+        if model in batch_scan_supported_list.keys():
+            batch_scan_supported = batch_scan_supported_list[model]
+        role = {
+            'droid': d,
+            'ed': e,
+            'max_advertisements': max_advertisements,
+            'batch_scan_supported': batch_scan_supported
+        }
+        droid_list.append(role)
+    return droid_list
+
+
+def generate_id_by_size(size,
+                        chars=(string.ascii_lowercase +
+                               string.ascii_uppercase + string.digits)):
+    return ''.join(random.choice(chars) for _ in range(size))
+
+
+def cleanup_scanners_and_advertisers(scan_droid, scan_ed, scan_callback_list,
+                                     adv_droid, adv_ed, adv_callback_list):
+    """
+    Try to gracefully stop all scanning and advertising instances.
+    """
+    try:
+        for scan_callback in scan_callback_list:
+            scan_droid.bleStopBleScan(scan_callback)
+    except Exception:
+        reset_bluetooth([scan_droid], [scan_ed])
+    try:
+        for adv_callback in adv_callback_list:
+            adv_droid.bleStopBleAdvertising(adv_callback)
+    except Exception:
+        reset_bluetooth([adv_droid], [adv_ed])
+
+
+def setup_gatt_characteristics(droid, input):
+    characteristic_list = []
+    for item in input:
+        index = droid.gattServerCreateBluetoothGattCharacteristic(
+            item['uuid'],
+            item['property'],
+            item['permission'])
+        characteristic_list.append(index)
+    return characteristic_list
+
+
+def setup_gatt_descriptors(droid, input):
+    descriptor_list = []
+    for item in input:
+        index = droid.gattServerCreateBluetoothGattDescriptor(
+            item['uuid'],
+            item['property'],
+        )
+        descriptor_list.append(index)
+    log.info("setup descriptor list: {}".format(descriptor_list))
+    return descriptor_list
+
+
+def get_mac_address_of_generic_advertisement(scan_droid, scan_ed, adv_droid,
+                                             adv_ed):
+    adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+    adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+        AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+    adv_droid.bleSetAdvertiseSettingsIsConnectable(True)
+    adv_droid.bleSetAdvertiseSettingsTxPowerLevel(
+        AdvertiseSettingsAdvertiseTxPower.ADVERTISE_TX_POWER_HIGH.value)
+    advertise_callback, advertise_data, advertise_settings = (
+        generate_ble_advertise_objects(adv_droid))
+    adv_droid.bleStartBleAdvertising(
+        advertise_callback, advertise_data, advertise_settings)
+    adv_ed.pop_event("BleAdvertise{}onSuccess".format(
+        advertise_callback), default_timeout)
+    filter_list = scan_droid.bleGenFilterList()
+    scan_settings = scan_droid.bleBuildScanSetting()
+    scan_callback = scan_droid.bleGenScanCallback()
+    scan_droid.bleSetScanFilterDeviceName(adv_droid.bluetoothGetLocalName())
+    scan_droid.bleBuildScanFilter(filter_list)
+    scan_droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
+    event = scan_ed.pop_event(
+        "BleScan{}onScanResults".format(scan_callback), default_timeout)
+    mac_address = event['data']['Result']['deviceInfo']['address']
+    scan_droid.bleStopBleScan(scan_callback)
+    return mac_address, advertise_callback
+
+
+def setup_gatt_connection(cen_droid, cen_ed, mac_address, autoconnect):
+    test_result = True
+    gatt_callback = cen_droid.gattCreateGattCallback()
+    log.info("Gatt Connect to mac address {}.".format(mac_address))
+    bluetooth_gatt = cen_droid.gattClientConnectGatt(
+        gatt_callback, mac_address,
+        autoconnect)
+    event = cen_ed.pop_event(
+        "GattConnect{}onConnectionStateChange".format(gatt_callback),
+        default_timeout)
+    if event['data']['State'] != GattConnectionState.STATE_CONNECTED.value:
+        log.info("Could not establish a connection to peripheral. Event "
+                 "Details:".format(pprint.pformat(event)))
+        test_result = False
+    # To avoid race condition of quick connect/disconnect
+    time.sleep(1)
+    return test_result, bluetooth_gatt, gatt_callback
+
+
+def disconnect_gatt_connection(cen_droid, cen_ed, bluetooth_gatt, gatt_callback):
+    cen_droid.gattClientDisconnect(bluetooth_gatt)
+    event = cen_ed.pop_event(
+        "GattConnect{}onConnectionStateChange".format(gatt_callback),
+        default_timeout)
+    if event['data']['State'] != GattConnectionState.STATE_DISCONNECTED.value:
+        return False
+    return True
+
+
+def orchestrate_gatt_connection(cen_droid, cen_ed, per_droid, per_ed, le=True,
+                                mac_address=None):
+    adv_callback = None
+    if mac_address is None:
+        if le:
+            mac_address, adv_callback = (
+                get_mac_address_of_generic_advertisement(cen_droid, cen_ed,
+                                                         per_droid, per_ed))
+        else:
+            mac_address = get_bt_mac_address(cen_droid, per_droid, le)
+            adv_callback = None
+    autoconnect = False
+    test_result, bluetooth_gatt, gatt_callback = setup_gatt_connection(
+        cen_droid, cen_ed, mac_address, autoconnect)
+    if not test_result:
+        log.info("Could not connect to peripheral.")
+        return False
+    return bluetooth_gatt, gatt_callback, adv_callback
+
+
+def run_continuous_write_descriptor(
+        cen_droid, cen_ed, per_droid, per_ed, gatt_server, gatt_server_callback,
+        bluetooth_gatt, services_count, discovered_services_index):
+    log.info("starting continuous write")
+    bt_device_id = 0
+    status = 1
+    offset = 1
+    test_value = "1,2,3,4,5,6,7"
+    test_value_return = "1,2,3"
+    from contextlib import suppress
+    with suppress(Exception):
+        for x in range(100000):
+            for i in range(services_count):
+                characteristic_uuids = (
+                    cen_droid.gattClientGetDiscoveredCharacteristicUuids(
+                        discovered_services_index, i))
+                log.info(characteristic_uuids)
+                for characteristic in characteristic_uuids:
+                    descriptor_uuids = (
+                        cen_droid.gattClientGetDiscoveredDescriptorUuids(
+                            discovered_services_index, i, characteristic))
+                    log.info(descriptor_uuids)
+                    for descriptor in descriptor_uuids:
+                        log.info(
+                            "descriptor to be written {}".format(descriptor))
+                        cen_droid.gattClientDescriptorSetValue(
+                            bluetooth_gatt, discovered_services_index,
+                            i, characteristic, descriptor, test_value)
+                        cen_droid.gattClientWriteDescriptor(
+                            bluetooth_gatt, discovered_services_index,
+                            i, characteristic, descriptor)
+                        event = per_ed.pop_event(
+                            descriptor_write_request.format(
+                                gatt_server_callback), default_timeout)
+                        log.info(
+                            "onDescriptorWriteRequest event found: {}".format(
+                                event))
+                        request_id = event['data']['requestId']
+                        found_value = event['data']['value']
+                        if found_value != test_value:
+                            log.info(
+                                "Values didn't match. Found: {}, Expected: "
+                                "{}".format(found_value, test_value))
+                        per_droid.gattServerSendResponse(
+                            gatt_server, bt_device_id, request_id, status,
+                            offset, test_value_return)
+                        log.info("onDescriptorWrite event found: {}".format(
+                            cen_ed.pop_event(
+                                descriptor_write.format(bluetooth_gatt),
+                                default_timeout)))
+
+
+def setup_characteristics_and_descriptors(droid):
+    characteristic_input = [
+        {
+            'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+            'property': BluetoothGattCharacteristic.PROPERTY_WRITE.value |
+                    BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE.value,
+            'permission': BluetoothGattCharacteristic.PROPERTY_WRITE.value
+        },
+        {
+            'uuid': "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
+            'property': BluetoothGattCharacteristic.PROPERTY_NOTIFY.value |
+                    BluetoothGattCharacteristic.PROPERTY_READ.value,
+            'permission': BluetoothGattCharacteristic.PERMISSION_READ.value
+        },
+        {
+            'uuid': "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
+            'property': BluetoothGattCharacteristic.PROPERTY_NOTIFY.value |
+                    BluetoothGattCharacteristic.PROPERTY_READ.value,
+            'permission': BluetoothGattCharacteristic.PERMISSION_READ.value
+        },
+    ]
+    descriptor_input = [
+        {
+            'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+            'property': BluetoothGattDescriptor.PERMISSION_READ.value |
+                    BluetoothGattDescriptor.PERMISSION_WRITE.value,
+        },
+        {
+            'uuid': "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
+            'property': BluetoothGattDescriptor.PERMISSION_READ.value |
+                    BluetoothGattCharacteristic.PERMISSION_WRITE.value,
+        }
+    ]
+    characteristic_list = setup_gatt_characteristics(
+        droid, characteristic_input)
+    descriptor_list = setup_gatt_descriptors(droid, descriptor_input)
+    return characteristic_list, descriptor_list
+
+
+def setup_multiple_services(per_droid, per_ed):
+    gatt_server_callback = per_droid.gattServerCreateGattServerCallback()
+    gatt_server = per_droid.gattServerOpenGattServer(gatt_server_callback)
+    characteristic_list, descriptor_list = (
+        setup_characteristics_and_descriptors(per_droid))
+    per_droid.gattServerCharacteristicAddDescriptor(
+        characteristic_list[1], descriptor_list[0])
+    per_droid.gattServerCharacteristicAddDescriptor(
+        characteristic_list[2], descriptor_list[1])
+    gattService = per_droid.gattServerCreateService(
+        "00000000-0000-1000-8000-00805f9b34fb",
+        BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+    gattService2 = per_droid.gattServerCreateService(
+        "FFFFFFFF-0000-1000-8000-00805f9b34fb",
+        BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+    gattService3 = per_droid.gattServerCreateService(
+        "3846D7A0-69C8-11E4-BA00-0002A5D5C51B",
+        BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+    for characteristic in characteristic_list:
+        per_droid.gattServerAddCharacteristicToService(
+            gattService, characteristic)
+    per_droid.gattServerAddService(gatt_server, gattService)
+    per_ed.pop_event(service_added.format(gatt_server_callback),
+                     default_timeout)
+    for characteristic in characteristic_list:
+        per_droid.gattServerAddCharacteristicToService(
+            gattService2, characteristic)
+    per_droid.gattServerAddService(gatt_server, gattService2)
+    per_ed.pop_event(service_added.format(gatt_server_callback),
+                     default_timeout)
+    for characteristic in characteristic_list:
+        per_droid.gattServerAddCharacteristicToService(gattService3,
+                                                       characteristic)
+    per_droid.gattServerAddService(gatt_server, gattService3)
+    per_ed.pop_event(service_added.format(gatt_server_callback),
+                     default_timeout)
+    return gatt_server_callback, gatt_server
+
+
+def setup_characteristics_and_descriptors(droid):
+    characteristic_input = [
+        {
+            'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+            'property': BluetoothGattCharacteristic.PROPERTY_WRITE.value |
+            BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE.value,
+            'permission': BluetoothGattCharacteristic.PROPERTY_WRITE.value
+        },
+        {
+            'uuid': "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
+            'property': BluetoothGattCharacteristic.PROPERTY_NOTIFY.value |
+            BluetoothGattCharacteristic.PROPERTY_READ.value,
+            'permission': BluetoothGattCharacteristic.PERMISSION_READ.value
+        },
+        {
+            'uuid': "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
+            'property': BluetoothGattCharacteristic.PROPERTY_NOTIFY.value |
+            BluetoothGattCharacteristic.PROPERTY_READ.value,
+            'permission': BluetoothGattCharacteristic.PERMISSION_READ.value
+        },
+    ]
+    descriptor_input = [
+        {
+            'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+            'property': BluetoothGattDescriptor.PERMISSION_READ.value |
+            BluetoothGattDescriptor.PERMISSION_WRITE.value,
+        },
+        {
+            'uuid': "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
+            'property': BluetoothGattDescriptor.PERMISSION_READ.value |
+            BluetoothGattCharacteristic.PERMISSION_WRITE.value,
+        }
+    ]
+    characteristic_list = setup_gatt_characteristics(
+        droid, characteristic_input)
+    descriptor_list = setup_gatt_descriptors(droid, descriptor_input)
+    return characteristic_list, descriptor_list
+
+
+def get_device_local_info(droid):
+    local_info_dict = {}
+    local_info_dict['name'] = droid.bluetoothGetLocalName()
+    local_info_dict['uuids'] = droid.bluetoothGetLocalUuids()
+    return local_info_dict
+
+
+def enable_bluetooth(droid, ed):
+    if droid.bluetoothCheckState() is False:
+        droid.bluetoothToggleState(True)
+        if droid.bluetoothCheckState() is False:
+            return False
+    return True
+
+
+def disable_bluetooth(droid, ed):
+    if droid.bluetoothCheckState() is True:
+        droid.bluetoothToggleState(False)
+        if droid.bluetoothCheckState() is True:
+            return False
+    return True
+
+
+def set_bt_scan_mode(droid, ed, scan_mode_value):
+    if scan_mode_value == BluetoothScanModeType.STATE_OFF.value:
+        disable_bluetooth(droid, ed)
+        scan_mode = droid.bluetoothGetScanMode()
+        reset_bluetooth([droid], [ed])
+        if scan_mode != scan_mode_value:
+            return False
+    elif scan_mode_value == BluetoothScanModeType.SCAN_MODE_NONE.value:
+        droid.bluetoothMakeUndiscoverable()
+        scan_mode = droid.bluetoothGetScanMode()
+        if scan_mode != scan_mode_value:
+            return False
+    elif scan_mode_value == BluetoothScanModeType.SCAN_MODE_CONNECTABLE.value:
+        droid.bluetoothMakeUndiscoverable()
+        droid.bluetoothMakeConnectable()
+        scan_mode = droid.bluetoothGetScanMode()
+        if scan_mode != scan_mode_value:
+            return False
+    elif (scan_mode_value ==
+              BluetoothScanModeType.SCAN_MODE_CONNECTABLE_DISCOVERABLE.value):
+        droid.bluetoothMakeDiscoverable()
+        scan_mode = droid.bluetoothGetScanMode()
+        if scan_mode != scan_mode_value:
+            return False
+    else:
+        # invalid scan mode
+        return False
+    return True
+
+
+def set_device_name(droid, name):
+    droid.bluetoothSetLocalName(name)
+    # temporary (todo:tturney fix)
+    time.sleep(2)
+    droid_name = droid.bluetoothGetLocalName()
+    if droid_name != name:
+        return False
+    return True
+
+
+def check_device_supported_profiles(droid):
+    profile_dict = {}
+    profile_dict['hid'] = droid.bluetoothHidIsReady()
+    profile_dict['hsp'] = droid.bluetoothHspIsReady()
+    profile_dict['a2dp'] = droid.bluetoothA2dpIsReady()
+    profile_dict['avrcp'] = droid.bluetoothAvrcpIsReady()
+    return profile_dict
+
+
+def log_energy_info(droids, state):
+    return_string = "{} Energy info collection:\n".format(state)
+    for d in droids:
+        with suppress(Exception):
+            if (d.getBuildModel() == "Nexus 6" or d.getBuildModel() == "Nexus 9"
+                or d.getBuildModel() == "Nexus 6P"
+                or d.getBuildModel() == "Nexus5X"):
+
+                description = ("Device: {}\tEnergyStatus: {}\n".format(
+                    d.getBuildSerial(),
+                    d.bluetoothGetControllerActivityEnergyInfo(1)))
+                return_string = return_string + description
+    return return_string
+
+
+def pair_pri_to_sec(pri_droid, sec_droid):
+    sec_droid.bluetoothMakeDiscoverable(default_timeout)
+    pri_droid.bluetoothStartDiscovery()
+    target_name = sec_droid.bluetoothGetLocalName()
+    time.sleep(default_discovery_timeout)
+    discovered_devices = pri_droid.bluetoothGetDiscoveredDevices()
+    discovered = False
+    for device in discovered_devices:
+        log.info(device)
+        if 'name' in device and target_name == device['name']:
+            discovered = True
+            continue
+    if not discovered:
+        return False
+    pri_droid.bluetoothStartPairingHelper()
+    sec_droid.bluetoothStartPairingHelper()
+    result = pri_droid.bluetoothDiscoverAndBond(target_name)
+    return result
+
+
+def get_bt_mac_address(droid, droid1, make_undisocverable=True):
+    droid1.bluetoothMakeDiscoverable(default_timeout)
+    droid.bluetoothStartDiscovery()
+    mac = ""
+    target_name = droid1.bluetoothGetLocalName()
+    time.sleep(default_discovery_timeout)
+    discovered_devices = droid.bluetoothGetDiscoveredDevices()
+    for device in discovered_devices:
+        if 'name' in device.keys() and target_name == device['name']:
+            mac = device['address']
+            continue
+    if make_undisocverable:
+        droid1.bluetoothMakeUndiscoverable()
+    droid.bluetoothCancelDiscovery()
+    if mac == "":
+        return False
+    return mac
+
+
+def get_client_server_bt_mac_address(droid, droid1):
+    return get_bt_mac_address(droid, droid1), get_bt_mac_address(droid1, droid)
+
+
+def take_btsnoop_logs(droids, testcase, testname):
+    for d in droids:
+        take_btsnoop_log(d, testcase, testname)
+
+# TODO (tturney): Fix this.
+
+
+def take_btsnoop_log(droid, testcase, test_name):
+    """Grabs the btsnoop_hci log on a device and stores it in the log directory
+    of the test class.
+
+    If you want grab the btsnoop_hci log, call this function with android_device
+    objects in on_fail. Bug report takes a relative long time to take, so use
+    this cautiously.
+
+    Params:
+      test_name: Name of the test case that triggered this bug report.
+      android_device: The android_device instance to take bugreport on.
+    """
+    test_name = "".join(x for x in test_name if x.isalnum())
+    with suppress(Exception):
+        serial = droid.getBuildSerial()
+        device_model = droid.getBuildModel()
+        device_model = device_model.replace(" ", "")
+        out_name = ','.join((test_name, device_model, serial))
+        cmd = ''.join(("adb -s ", serial, " pull /sdcard/btsnoop_hci.log > ",
+                       testcase.log_path + "/" + out_name,
+                       ".btsnoop_hci.log"))
+        testcase.log.info("Test failed, grabbing the bt_snoop logs on {} {}."
+                          .format(device_model, serial))
+        exe_cmd(cmd)
+
+
+def rfcomm_connect(droid, device_address):
+    droid.bluetoothRfcommConnect(device_address)
+
+
+def rfcomm_accept(droid):
+    droid.bluetoothRfcommAccept()
diff --git a/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
new file mode 100644
index 0000000..2286c9b
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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.
+"""
+    Base Class for Defining Common Telephony Test Functionality
+"""
+
+import time
+from acts.base_test import BaseTestClass
+from acts.signals import TestSignal
+
+from .tel_test_utils import ensure_phones_default_state
+from .tel_test_utils import get_sub_ids_for_sim_slots
+from .tel_test_utils import set_phone_screen_on
+from .tel_test_utils import set_phone_silent_mode
+from .tel_test_utils import setup_droid_properties
+from .tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
+from .tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
+from .tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
+from .tel_defines import WIFI_VERBOSE_LOGGING_ENABLED
+from .tel_defines import WIFI_VERBOSE_LOGGING_DISABLED
+
+
+class TelephonyBaseTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+
+    # Use for logging in the test cases to facilitate
+    # faster log lookup and reduce ambiguity in logging.
+    def tel_test_wrap(fn):
+        def _safe_wrap_test_case(self, *args, **kwargs):
+            test_id = "{}:{}:{}".format(
+                self.__class__.__name__,
+                fn.__name__,
+                time.time())
+            log_string = "[Test ID] {}".format(test_id)
+            self.log.info(log_string)
+            try:
+                for ad in self.android_devices:
+                    ad.droid.logI("Started "+log_string)
+                # TODO: start QXDM Logging b/19002120
+                return fn(self, *args, **kwargs)
+            except TestSignal:
+                raise
+            except Exception as e:
+                self.log.error(str(e))
+                return False
+            finally:
+                # TODO: stop QXDM Logging b/19002120
+                for ad in self.android_devices:
+                    try:
+                        ad.adb.wait_for_device()
+                        ad.droid.logI("Finished "+log_string)
+                    except Exception as e:
+                        self.log.error(str(e))
+        return _safe_wrap_test_case
+
+    def setup_class(self):
+        for ad in self.android_devices:
+            setup_droid_properties(
+                self.log, ad, self.user_params["sim_conf_file"])
+            if not set_phone_screen_on(self.log, ad):
+                self.info.error("Failed to set phone screen-on time.")
+                return False
+            if not set_phone_silent_mode(self.log, ad):
+                self.info.error("Failed to set phone silent mode.")
+                return False
+
+            ad.droid.phoneAdjustPreciseCallStateListenLevel(
+                PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND, True)
+            ad.droid.phoneAdjustPreciseCallStateListenLevel(
+                PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING, True)
+            ad.droid.phoneAdjustPreciseCallStateListenLevel(
+                PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND, True)
+
+            if "enable_wifi_verbose_logging" in self.user_params:
+                ad.droid.wifiEnableVerboseLogging(WIFI_VERBOSE_LOGGING_ENABLED)
+
+        setattr(self, 'sim_sub_ids',
+                get_sub_ids_for_sim_slots(self.log,
+                                          self.android_devices))
+        return True
+
+    def teardown_class(self):
+        ensure_phones_default_state(self.log, self.android_devices)
+
+        for ad in self.android_devices:
+            ad.droid.phoneAdjustPreciseCallStateListenLevel(
+                PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND, False)
+            ad.droid.phoneAdjustPreciseCallStateListenLevel(
+                PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING, False)
+            ad.droid.phoneAdjustPreciseCallStateListenLevel(
+                PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND, False)
+            if "enable_wifi_verbose_logging" in self.user_params:
+                ad.droid.wifiEnableVerboseLogging(WIFI_VERBOSE_LOGGING_DISABLED)
+        return True
+
+    def setup_test(self):
+        return ensure_phones_default_state(self.log, self.android_devices)
+
+    def teardown_test(self):
+        return True
+
+    def on_fail(self, test_name, begin_time):
+        return True
+
+    def on_exception(self, test_name, begin_time):
+        # Since it's a debug flag, as long as it's "set" we consider it valid
+        if "no_bug_report_on_fail" not in self.user_params:
+            # magical sleep to ensure the runtime restart or reboot begins
+            time.sleep(1)
+            for ad in self.android_devices:
+                try:
+                    ad.adb.wait_for_device()
+                    ad.take_bug_reports(
+                        test_name, begin_time, self.android_devices)
+                    # FIXME(nharold): rename tombstone files correctly
+                    # TODO(nharold): make support generic and move to
+                    # base_test and utils respectively
+                    ad.adb.pull('/data/tombstones/', self.log_path)
+                except:
+                    ad.log.error("Failed to take a bug report for {}, {}"
+                                 .format(ad.serial, test_name))
diff --git a/acts/framework/acts/test_utils/tel/__init__.py b/acts/framework/acts/test_utils/tel/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/__init__.py
diff --git a/acts/framework/acts/test_utils/tel/tel_atten_utils.py b/acts/framework/acts/test_utils/tel/tel_atten_utils.py
new file mode 100644
index 0000000..7e9b649
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/tel_atten_utils.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import time
+import math
+from .tel_defines import *
+
+def get_atten(log, atten_obj):
+    """Get attenuator current attenuation value.
+
+    Args:
+        log: log object.
+        atten_obj: attenuator object.
+    Returns:
+        Current attenuation value.
+    """
+    return atten_obj.get_atten()
+
+def set_atten(log, atten_obj, target_atten, step_size=0, time_per_step=0):
+    """Set attenuator attenuation value.
+
+    Args:
+        log: log object.
+        atten_obj: attenuator object.
+        target_atten: target attenuation value.
+        step_size: step size (in unit of dBm) for 'attenuation value setting'.
+            This is optional. Default value is 0. If step_size is 0, it means
+            the setting will be done in only one step.
+        time_per_step: delay time (in unit of second) per step when setting
+            the attenuation value.
+            This is optional. Default value is 0.
+    Returns:
+        True is no error happened. Otherwise false.
+    """
+    try:
+        print_name = atten_obj.path
+    except AttributeError:
+        print_name = str(atten_obj)
+
+    current_atten = get_atten(log, atten_obj)
+    info = "set_atten {} from {} to {}".format(print_name, current_atten,
+            target_atten)
+    if step_size>0:
+        info +=", step size {}, time per step {}s.".format(step_size, time_per_step)
+    log.info(info)
+    try:
+        delta = target_atten - current_atten
+        if step_size>0:
+            number_of_steps = int(abs(delta)/step_size)
+            while number_of_steps > 0:
+                number_of_steps -=1
+                current_atten += math.copysign(step_size, (target_atten - current_atten))
+                atten_obj.set_atten(current_atten)
+                time.sleep(time_per_step)
+        atten_obj.set_atten(target_atten)
+    except Exception as e:
+        log.error("set_atten error happened: {}".format(e))
+        return False
+    return True
+
+def set_rssi(log, atten_obj, calibration_rssi, target_rssi, step_size=0,
+             time_per_step=0):
+    """Set RSSI value by changing attenuation.
+
+    Args:
+        log: log object.
+        atten_obj: attenuator object.
+        calibration_rssi: RSSI calibration information.
+        target_rssi: target RSSI value.
+        step_size: step size (in unit of dBm) for 'RSSI value setting'.
+            This is optional. Default value is 0. If step_size is 0, it means
+            the setting will be done in only one step.
+        time_per_step: delay time (in unit of second) per step when setting
+            the attenuation value.
+            This is optional. Default value is 0.
+    Returns:
+        True is no error happened. Otherwise false.
+    """
+    try:
+        print_name = atten_obj.path
+    except AttributeError:
+        print_name = str(atten_obj)
+
+    if target_rssi == MAX_RSSI_RESERVED_VALUE:
+        target_atten = ATTEN_MIN_VALUE
+    elif target_rssi == MIN_RSSI_RESERVED_VALUE:
+        target_atten = ATTEN_MAX_VALUE
+    else:
+        log.info("set_rssi {} to {}.".
+                 format(print_name, target_rssi))
+        target_atten = calibration_rssi - target_rssi
+
+    if target_atten < 0:
+        log.info("set_rssi: WARNING - you are setting an unreachable RSSI.")
+        log.info("max RSSI value on {} is {}. Setting attenuation to 0.".
+            format(wifi_or_cell, calibration_rssi))
+        target_atten = 0
+    if not set_atten(log, atten_obj, target_atten, step_size, time_per_step):
+        log.error("set_rssi to {}failed".format(target_rssi))
+        return False
+    return True
\ No newline at end of file
diff --git a/acts/framework/acts/test_utils/tel/tel_data_utils.py b/acts/framework/acts/test_utils/tel/tel_data_utils.py
new file mode 100644
index 0000000..bed714d
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/tel_data_utils.py
@@ -0,0 +1,401 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import random
+import string
+import time
+import warnings
+
+from acts.utils import rand_ascii_str
+from acts.test_utils.tel.tel_defines import *
+from acts.test_utils.tel.tel_test_utils import *
+
+def wifi_tethering_cleanup(log, provider, client_list):
+    """Clean up steps for WiFi Tethering.
+
+    Make sure provider turn off tethering.
+    Make sure clients reset WiFi and turn on cellular data.
+
+    Args:
+        log: log object.
+        provider: android object provide WiFi tethering.
+        client_list: a list of clients using tethered WiFi.
+
+    Returns:
+        True if no error happened. False otherwise.
+    """
+    for client in client_list:
+        client.droid.toggleDataConnection(True)
+        if not WifiUtils.wifi_reset(log, client):
+            log.error("Reset client WiFi failed. {}".format(client.serial))
+            return False
+    if not provider.droid.wifiIsApEnabled():
+        log.error("Provider WiFi tethering stopped.")
+        return False
+    if not WifiUtils.stop_wifi_tethering(log, provider):
+        log.error("Provider strop WiFi tethering failed.")
+        return False
+    return True
+
+def wifi_tethering_setup_teardown(log, provider, client_list,
+                                  ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                  check_interval=30, check_iteration=4,
+                                  do_cleanup=True,
+                                  ssid=None, password=None):
+    """Test WiFi Tethering.
+
+    Turn off WiFi on provider and clients.
+    Turn off data and reset WiFi on clients.
+    Verify no Internet access on clients.
+    Turn on WiFi tethering on provider.
+    Clients connect to provider's WiFI.
+    Verify Internet on provider and clients.
+    Tear down WiFi tethering setup and clean up.
+
+    Args:
+        log: log object.
+        provider: android object provide WiFi tethering.
+        client_list: a list of clients using tethered WiFi.
+        ap_band: setup WiFi tethering on 2G or 5G.
+            This is optional, default value is WifiUtils.WIFI_CONFIG_APBAND_2G
+        check_interval: delay time between each around of Internet connection check.
+            This is optional, default value is 30 (seconds).
+        check_iteration: check Internet connection for how many times in total.
+            This is optional, default value is 4 (4 times).
+        do_cleanup: after WiFi tethering test, do clean up to tear down tethering
+            setup or not. This is optional, default value is True.
+        ssid: use this string as WiFi SSID to setup tethered WiFi network.
+            This is optional. Default value is None.
+            If it's None, a random string will be generated.
+        password: use this string as WiFi password to setup tethered WiFi network.
+            This is optional. Default value is None.
+            If it's None, a random string will be generated.
+
+    Returns:
+        True if no error happened. False otherwise.
+    """
+    log.info("--->Start wifi_tethering_setup_teardown<---")
+    log.info("Provider: {}".format(provider.serial))
+    WifiUtils.wifi_toggle_state(log, provider, False)
+
+    if ssid is None:
+        ssid = rand_ascii_str(10)
+    if password is None:
+        password = rand_ascii_str(8)
+
+    # No password
+    if password == "":
+        password = None
+
+    try:
+        for client in client_list:
+            log.info("Client: {}".format(client.serial))
+            WifiUtils.wifi_toggle_state(log, client, False)
+            client.droid.toggleDataConnection(False)
+        log.info("WiFI Tethering: Verify client have no Internet access.")
+        for client in client_list:
+            if verify_http_connection(log, client):
+                log.error("Turn off Data on client fail. {}".format(client.serial))
+                return False
+
+        log.info("WiFI Tethering: Turn on WiFi tethering on {}. SSID: {}, password: {}".
+                 format(provider.serial, ssid, password))
+
+        if not WifiUtils.start_wifi_tethering(log, provider, ssid, password, ap_band):
+            log.error("Provider start WiFi tethering failed.")
+            return False
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+
+        log.info("Provider {} check Internet connection.".
+                 format(provider.serial))
+        if not verify_http_connection(log, provider):
+            return False
+        for client in client_list:
+            log.info("WiFI Tethering: {} connect to WiFi and verify AP band correct.".
+                     format(client.serial))
+            if not ensure_wifi_connected(log, client, ssid, password):
+                log.error("Client connect to WiFi failed.")
+                return False
+
+            wifi_info = client.droid.wifiGetConnectionInfo()
+            if ap_band == WifiUtils.WIFI_CONFIG_APBAND_5G:
+                if wifi_info["is_24ghz"]:
+                    log.error("Expected 5g network. WiFi Info: {}".
+                              format(wifi_info))
+                    return False
+            else:
+                if wifi_info["is_5ghz"]:
+                    log.error("Expected 2g network. WiFi Info: {}".
+                              format(wifi_info))
+                    return False
+
+            log.info("Client{} check Internet connection.".
+                     format(client.serial))
+            if (not wait_for_wifi_data_connection(log, client, True) or not
+                    verify_http_connection(log, client)):
+                log.error("No WiFi Data on client: {}.".format(client.serial))
+                return False
+
+        if not tethering_check_internet_connection(log, provider, client_list,
+                                                   check_interval,
+                                                   check_iteration):
+            return False
+
+    finally:
+        if (do_cleanup and
+            (not wifi_tethering_cleanup(log, provider, client_list))):
+            return False
+    return True
+
+def tethering_check_internet_connection(log, provider, client_list,
+                                        check_interval, check_iteration):
+    """During tethering test, check client(s) and provider Internet connection.
+
+    Do the following for <check_iteration> times:
+        Delay <check_interval> seconds.
+        Check Tethering provider's Internet connection.
+        Check each client's Internet connection.
+
+    Args:
+        log: log object.
+        provider: android object provide WiFi tethering.
+        client_list: a list of clients using tethered WiFi.
+        check_interval: delay time between each around of Internet connection check.
+        check_iteration: check Internet connection for how many times in total.
+
+    Returns:
+        True if no error happened. False otherwise.
+    """
+    for i in range(1, check_iteration):
+        time.sleep(check_interval)
+        log.info("Provider {} check Internet connection after {} seconds.".
+                      format(provider.serial, check_interval*i))
+        if not verify_http_connection(log, provider):
+            return False
+        for client in client_list:
+            log.info("Client {} check Internet connection after {} seconds.".
+                          format(client.serial, check_interval*i))
+            if not verify_http_connection(log, client):
+                return False
+    return True
+
+def wifi_cell_switching(log, ad, wifi_network_ssid, wifi_network_pass,
+                        nw_type=None):
+    """Test data connection network switching when phone camped on <nw_type>.
+
+    Ensure phone is camped on <nw_type>
+    Ensure WiFi can connect to live network,
+    Airplane mode is off, data connection is on, WiFi is on.
+    Turn off WiFi, verify data is on cell and browse to google.com is OK.
+    Turn on WiFi, verify data is on WiFi and browse to google.com is OK.
+    Turn off WiFi, verify data is on cell and browse to google.com is OK.
+
+    Args:
+        log: log object.
+        ad: android object.
+        wifi_network_ssid: ssid for live wifi network.
+        wifi_network_pass: password for live wifi network.
+        nw_type: network rat the phone should be camped on.
+
+    Returns:
+        True if pass.
+    """
+    # TODO: take wifi_cell_switching out of tel_data_utils.py
+    # b/23354769
+
+    try:
+        if not ensure_network_rat(log, ad, nw_type, WAIT_TIME_NW_SELECTION,
+                                  NETWORK_SERVICE_DATA):
+            log.error("Device failed to register in {}".format(nw_type))
+            return False
+
+        # Temporary hack to give phone enough time to register.
+        # TODO: Proper check using SL4A API.
+        time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+        # Ensure WiFi can connect to live network
+        log.info("Make sure phone can connect to live network by WIFI")
+        if not ensure_wifi_connected(log, ad,
+                                     wifi_network_ssid, wifi_network_pass):
+            log.error("WiFi connect fail.")
+            return False
+        log.info("Phone connected to WIFI.")
+
+        log.info("Step1 Airplane Off, WiFi On, Data On.")
+        toggle_airplane_mode(log, ad, False)
+        WifiUtils.wifi_toggle_state(log, ad, True)
+        ad.droid.toggleDataConnection(True)
+        #TODO: Add a check to ensure data routes through wifi here
+
+        log.info("Step2 WiFi is Off, Data is on Cell.")
+        WifiUtils.wifi_toggle_state(log, ad, False)
+        if (not wait_for_cell_data_connection(log, ad, True) or not
+                verify_http_connection(log, ad)):
+            log.error("Data did not return to cell")
+            return False
+
+        log.info("Step3 WiFi is On, Data is on WiFi.")
+        WifiUtils.wifi_toggle_state(log, ad, True)
+        if (not wait_for_wifi_data_connection(log, ad, True) or not
+                verify_http_connection(log, ad)):
+            log.error("Data did not return to WiFi")
+            return False
+
+        log.info("Step4 WiFi is Off, Data is on Cell.")
+        WifiUtils.wifi_toggle_state(log, ad, False)
+        if (not wait_for_cell_data_connection(log, ad, True) or not
+                verify_http_connection(log, ad)):
+            log.error("Data did not return to cell")
+            return False
+        return True
+
+    finally:
+        WifiUtils.wifi_toggle_state(log, ad, False)
+
+def airplane_mode_test(log, ad):
+    """ Test airplane mode basic on Phone and Live SIM.
+
+    Ensure phone attach, data on, WiFi off and verify Internet.
+    Turn on airplane mode to make sure detach.
+    Turn off airplane mode to make sure attach.
+    Verify Internet connection.
+
+    Args:
+        log: log object.
+        ad: android object.
+
+    Returns:
+        True if pass; False if fail.
+    """
+    if not ensure_phones_idle(log, [ad]):
+        log.error("Failed to return phones to idle.")
+        return False
+
+    try:
+        ad.droid.toggleDataConnection(True)
+        WifiUtils.wifi_toggle_state(log, ad, False)
+
+        log.info("Step1: ensure attach")
+        if not toggle_airplane_mode(log, ad, False):
+            log.error("Failed initial attach")
+            return False
+        if not verify_http_connection(log, ad):
+            log.error("Data not available on cell.")
+            return False
+
+        log.info("Step2: enable airplane mode and ensure detach")
+        if not toggle_airplane_mode(log, ad, True):
+            log.error("Failed to enable Airplane Mode")
+            return False
+        if not wait_for_cell_data_connection(log, ad, False):
+            log.error("Failed to disable cell data connection")
+            return False
+        if verify_http_connection(log, ad):
+            log.error("Data available in airplane mode.")
+            return False
+
+        log.info("Step3: disable airplane mode and ensure attach")
+        if not toggle_airplane_mode(log, ad, False):
+            log.error("Failed to disable Airplane Mode")
+            return False
+
+        if not wait_for_cell_data_connection(log, ad, True):
+            log.error("Failed to enable cell data connection")
+            return False
+
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+
+        log.info("Step4 verify internet")
+        return verify_http_connection(log, ad)
+    finally:
+        toggle_airplane_mode(log, ad, False)
+
+def data_connectivity_single_bearer(log, ad, nw_gen):
+    """Test data connection: single-bearer (no voice).
+
+    Turn off airplane mode, enable Cellular Data.
+    Ensure phone data generation is expected.
+    Verify Internet.
+    Disable Cellular Data, verify Internet is inaccessible.
+    Enable Cellular Data, verify Internet.
+
+    Args:
+        log: log object.
+        ad: android object.
+        nw_gen: network generation the phone should on.
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    ensure_phones_idle(log, [ad])
+
+    if not ensure_network_generation(log, ad, nw_gen,
+            WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+
+        log.error("Device failed to reselect in {}s.".format(
+            WAIT_TIME_NW_SELECTION))
+        return False
+
+    # Temporary hack to give phone enough time to register.
+    # TODO: Proper check using SL4A API.
+    time.sleep(5)
+
+    try:
+        log.info("Step1 Airplane Off, Data On.")
+        toggle_airplane_mode(log, ad, False)
+        ad.droid.toggleDataConnection(True)
+        if not wait_for_cell_data_connection(log, ad, True):
+            log.error("Failed to enable data connection.")
+            return False
+
+        log.info("Step2 Verify internet")
+        if not verify_http_connection(log, ad):
+            log.error("Data not available on cell.")
+            return False
+
+        log.info("Step3 Turn off data and verify not connected.")
+        ad.droid.toggleDataConnection(False)
+        if not wait_for_cell_data_connection(log, ad, False):
+            log.error("Step3 Failed to disable data connection.")
+            return False
+
+        if verify_http_connection(log, ad):
+            log.error("Step3 Data still available when disabled.")
+            return False
+
+        log.info("Step4 Re-enable data.")
+        ad.droid.toggleDataConnection(True)
+        if not wait_for_cell_data_connection(log, ad, True):
+            log.error("Step4 failed to re-enable data.")
+            return False
+        if not verify_http_connection(log, ad):
+            log.error("Data not available on cell.")
+            return False
+
+        if not is_droid_in_network_generation(
+                log, ad, nw_gen,
+                NETWORK_SERVICE_DATA):
+            log.error("Failed: droid is no longer on correct network")
+            log.info("Expected:{}, Current:{}".format(nw_gen,
+                rat_generation_from_type(
+                    get_network_rat_for_subscription(log, ad,
+                    ad.droid.subscriptionGetDefaultSubId(),
+                    NETWORK_SERVICE_DATA))))
+            return False
+        return True
+    finally:
+        ad.droid.toggleDataConnection(True)
diff --git a/acts/framework/acts/test_utils/tel/tel_defines.py b/acts/framework/acts/test_utils/tel/tel_defines.py
new file mode 100644
index 0000000..e012dd2
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/tel_defines.py
@@ -0,0 +1,538 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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.
+
+###############################################
+# TIMERS
+###############################################
+# Max time to wait for phone data/network connection state update
+WAIT_TIME_CONNECTION_STATE_UPDATE = 20
+
+# Max time to wait for network reselection
+WAIT_TIME_NW_SELECTION = 120
+
+# Wait time for call drop
+WAIT_TIME_CALL_DROP = 60
+
+# Time to wait after call setup before declaring
+# that the call is actually successful
+WAIT_TIME_IN_CALL = 15
+
+# Time to wait after phone receive incoming call before phone answer this call.
+WAIT_TIME_ANSWER_CALL = 2
+
+# Time to wait after phone receive incoming call before phone reject this call.
+WAIT_TIME_REJECT_CALL = WAIT_TIME_ANSWER_CALL
+
+# Time to wait after phone receive incoming video call before phone answer this call.
+WAIT_TIME_ANSWER_VIDEO_CALL = WAIT_TIME_ANSWER_CALL
+
+# Time to wait after caller make a call and before
+# callee start ringing
+WAIT_TIME_CALLEE_RINGING = 30
+
+# Time to leave a voice message after callee reject the incoming call
+WAIT_TIME_TO_LEAVE_VOICE_MAIL = 30
+
+# Time to wait after caller make a call and before
+# callee start ringing
+WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT = 30
+
+# Time to wait after ad end a call and before get
+# "onCallStatehangedIdle" event
+WAIT_TIME_HANGUP_TO_IDLE_EVENT = 30
+
+# Time to wait after toggle airplane mode and before
+# get expected event
+WAIT_TIME_AIRPLANEMODE_EVENT = 90
+
+# Time to wait after device sent an SMS and before
+# get "onSmsSentSuccess" event
+WAIT_TIME_SMS_SENT_SUCCESS = 60
+
+# Time to wait after MT SMS was sent and before device
+# actually receive this MT SMS.
+WAIT_TIME_SMS_RECEIVE = 120
+
+# (For IMS, e.g. VoLTE-VoLTE, WFC-WFC, VoLTE-WFC test only)
+# Time to wait after call setup before declaring
+# that the call is actually successful
+WAIT_TIME_IN_CALL_FOR_IMS = 30
+
+# Time delay to ensure user actions are performed in
+# 'human' time rather than at the speed of the script
+WAIT_TIME_ANDROID_STATE_SETTLING = 1
+
+# Time to wait after registration to ensure the phone
+# has sufficient time to reconfigure based on new network
+WAIT_TIME_BETWEEN_REG_AND_CALL = 5
+
+# Time to wait for IMS registration
+WAIT_TIME_IMS_REGISTRATION = 120
+
+# Max time to wait after initiating a call for telecom to report in-call
+WAIT_TIME_CALL_INITIATION = 15
+
+# Time to wait for VZW phone in phone setup function
+VZW_WAIT_TIME_IN_PHONE_SETUP_FUNC = 30
+
+# FIXME : This timer should only be used for wait after IMS Registration
+# Max time to wait for VoLTE enabled flag to be True
+WAIT_TIME_VOLTE_ENABLED = WAIT_TIME_IMS_REGISTRATION + 20
+
+# FIXME : This timer should only be used for wait after IMS Registration
+# Max time to wait for WFC enabled flag to be True
+WAIT_TIME_WFC_ENABLED = WAIT_TIME_IMS_REGISTRATION + 50
+
+# Maximum Wait for WiFi Manager to Connect to an AP
+WAIT_TIME_WIFI_CONNECTION = 30
+
+# During wifi tethering, wait time for data status change.
+WAIT_TIME_FOR_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING = 30
+
+# Maximum Wait time for Video Session Modify Messaging
+WAIT_TIME_VIDEO_SESSION_EVENT = 10
+
+# Max time to wait after a network connection for ConnectivityManager to
+# report a working user plane data connection
+WAIT_TIME_USER_PLANE_DATA = 20
+
+# Timeout value (second) for tethering entitlement check
+TETHERING_ENTITLEMENT_CHECK_TIMEOUT = 15
+
+# invalid SIM slot index
+INVALID_SIM_SLOT_INDEX = -1
+
+# WiFI RSSI is -127 if WiFi is not connected
+INVALID_WIFI_RSSI = -127
+
+# MAX and MIN value for attenuator settings
+ATTEN_MAX_VALUE = 90
+ATTEN_MIN_VALUE = 0
+
+MAX_RSSI_RESERVED_VALUE = 100
+MIN_RSSI_RESERVED_VALUE = -200
+
+# cellular weak RSSI value
+CELL_WEAK_RSSI_VALUE = -120
+# cellular strong RSSI value
+CELL_STRONG_RSSI_VALUE = -70
+# WiFi weak RSSI value
+WIFI_WEAK_RSSI_VALUE = -80
+
+# Wait time for rssi calibration.
+# This is the delay between <WiFi Connected> and <Turn on Screen to get RSSI>.
+WAIT_TIME_FOR_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED = 10
+# This is the delay between <Turn on Screen> and <Call API to get WiFi RSSI>.
+WAIT_TIME_FOR_WIFI_RSSI_CALIBRATION_SCREEN_ON = 2
+
+# These are used in phone_number_formatter
+PHONE_NUMBER_STRING_FORMAT_7_DIGIT = 7
+PHONE_NUMBER_STRING_FORMAT_10_DIGIT = 10
+PHONE_NUMBER_STRING_FORMAT_11_DIGIT = 11
+PHONE_NUMBER_STRING_FORMAT_12_DIGIT = 12
+
+# MAX screen-on time during test (in unit of second)
+MAX_SCREEN_ON_TIME = 1800
+
+# In Voice Mail box, press this digit to delete one message.
+VOICEMAIL_DELETE_DIGIT = '7'
+# MAX number of saved voice mail in voice mail box.
+MAX_SAVED_VOICE_MAIL = 25
+# Time to wait for each operation on voice mail box.
+VOICE_MAIL_SERVER_RESPONSE_DELAY = 10
+# Time to wait for voice mail count report correct result.
+MAX_WAIT_TIME_FOR_VOICE_MAIL_COUNT = 30
+
+# Time to wait after registration to ensure the phone
+# has sufficient time to reconfigure based on new network in Anritsu
+WAIT_TIME_ANRITSU_REG_AND_CALL = 10
+
+# Time to wait after registration before sending a command to Anritsu
+# to ensure the phone has sufficient time to reconfigure based on new
+# network in Anritsu
+WAIT_TIME_ANRITSU_REG_AND_OPER = 10
+
+# SIM1 slot index
+SIM1_SLOT_INDEX = 0
+
+# SIM2 slot index
+SIM2_SLOT_INDEX = 1
+
+# Data SIM change time
+WAIT_TIME_DATA_SUB_CHANGE = 150
+
+# Wait time for radio to up and running after reboot
+WAIT_TIME_AFTER_REBOOT = 10
+
+# Wait time for tethering test after reboot
+WAIT_TIME_FOR_TETHERING_AFTER_REBOOT = 10
+
+# invalid Subscription ID
+INVALID_SUB_ID = -1
+
+AOSP_PREFIX = "aosp_"
+
+INCALL_UI_DISPLAY_FOREGROUND = "foreground"
+INCALL_UI_DISPLAY_BACKGROUND = "background"
+INCALL_UI_DISPLAY_DEFAULT = "default"
+
+NETWORK_CONNECTION_TYPE_WIFI = 'wifi'
+NETWORK_CONNECTION_TYPE_CELL = 'cell'
+NETWORK_CONNECTION_TYPE_MMS = 'mms'
+NETWORK_CONNECTION_TYPE_HIPRI = 'hipri'
+NETWORK_CONNECTION_TYPE_UNKNOWN = 'unknown'
+
+TETHERING_MODE_WIFI = 'wifi'
+
+NETWORK_SERVICE_VOICE = 'voice'
+NETWORK_SERVICE_DATA = 'data'
+
+CARRIER_VZW = 'vzw'
+CARRIER_ATT = 'att'
+CARRIER_TMO = 'tmo'
+CARRIER_SPT = 'spt'
+CARRIER_UNKNOWN = 'unknown'
+
+RAT_FAMILY_CDMA = 'cdma'
+RAT_FAMILY_CDMA2000 = 'cdma2000'
+RAT_FAMILY_IDEN = 'iden'
+RAT_FAMILY_GSM = 'gsm'
+RAT_FAMILY_UMTS = 'umts'
+RAT_FAMILY_WLAN = 'wlan'
+RAT_FAMILY_LTE = 'lte'
+RAT_FAMILY_UNKNOWN = 'unknown'
+
+CAPABILITY_PHONE = 'phone'
+CAPABILITY_VOLTE = 'volte'
+CAPABILITY_VT = 'vt'
+CAPABILITY_WFC = 'wfc'
+CAPABILITY_MSIM = 'msim'
+CAPABILITY_OMADM = 'omadm'
+
+# Constant for operation direction
+MOBILE_ORIGINATED = "MO"
+MOBILE_TERMINATED = "MT"
+
+# Constant for call teardown side
+CALL_TEARDOWN_PHONE = "PHONE"
+CALL_TEARDOWN_REMOTE = "REMOTE"
+
+WIFI_VERBOSE_LOGGING_ENABLED = 1
+WIFI_VERBOSE_LOGGING_DISABLED = 0
+
+"""
+Begin shared constant define for both Python and Java
+"""
+
+# Constant for WiFi Calling WFC mode
+WFC_MODE_WIFI_ONLY = "WIFI_ONLY"
+WFC_MODE_CELLULAR_PREFERRED = "CELLULAR_PREFERRED"
+WFC_MODE_WIFI_PREFERRED = "WIFI_PREFERRED"
+WFC_MODE_DISABLED = "DISABLED"
+WFC_MODE_UNKNOWN = "UNKNOWN"
+
+# Constant for Video Telephony VT state
+VT_STATE_AUDIO_ONLY = "AUDIO_ONLY"
+VT_STATE_TX_ENABLED = "TX_ENABLED"
+VT_STATE_RX_ENABLED = "RX_ENABLED"
+VT_STATE_BIDIRECTIONAL = "BIDIRECTIONAL"
+VT_STATE_TX_PAUSED = "TX_PAUSED"
+VT_STATE_RX_PAUSED = "RX_PAUSED"
+VT_STATE_BIDIRECTIONAL_PAUSED = "BIDIRECTIONAL_PAUSED"
+VT_STATE_STATE_INVALID = "INVALID"
+
+# Constant for Video Telephony Video quality
+VT_VIDEO_QUALITY_DEFAULT = "DEFAULT"
+VT_VIDEO_QUALITY_UNKNOWN = "UNKNOWN"
+VT_VIDEO_QUALITY_HIGH = "HIGH"
+VT_VIDEO_QUALITY_MEDIUM = "MEDIUM"
+VT_VIDEO_QUALITY_LOW = "LOW"
+VT_VIDEO_QUALITY_INVALID = "INVALID"
+
+# Constant for Call State (for call object)
+CALL_STATE_ACTIVE = "ACTIVE"
+CALL_STATE_NEW = "NEW"
+CALL_STATE_DIALING = "DIALING"
+CALL_STATE_RINGING = "RINGING"
+CALL_STATE_HOLDING = "HOLDING"
+CALL_STATE_DISCONNECTED = "DISCONNECTED"
+CALL_STATE_PRE_DIAL_WAIT = "PRE_DIAL_WAIT"
+CALL_STATE_CONNECTING = "CONNECTING"
+CALL_STATE_DISCONNECTING = "DISCONNECTING"
+CALL_STATE_UNKNOWN = "UNKNOWN"
+CALL_STATE_INVALID = "INVALID"
+
+# Constant for PRECISE Call State (for call object)
+PRECISE_CALL_STATE_ACTIVE = "ACTIVE"
+PRECISE_CALL_STATE_ALERTING = "ALERTING"
+PRECISE_CALL_STATE_DIALING = "DIALING"
+PRECISE_CALL_STATE_INCOMING = "INCOMING"
+PRECISE_CALL_STATE_HOLDING = "HOLDING"
+PRECISE_CALL_STATE_DISCONNECTED = "DISCONNECTED"
+PRECISE_CALL_STATE_WAITING = "WAITING"
+PRECISE_CALL_STATE_DISCONNECTING = "DISCONNECTING"
+PRECISE_CALL_STATE_IDLE = "IDLE"
+PRECISE_CALL_STATE_UNKNOWN = "UNKNOWN"
+PRECISE_CALL_STATE_INVALID = "INVALID"
+
+# Constant for DC POWER STATE
+DC_POWER_STATE_LOW = "LOW"
+DC_POWER_STATE_HIGH = "HIGH"
+DC_POWER_STATE_MEDIUM = "MEDIUM"
+DC_POWER_STATE_UNKNOWN = "UNKNOWN"
+
+# Constant for Audio Route
+AUDIO_ROUTE_EARPIECE = "EARPIECE"
+AUDIO_ROUTE_BLUETOOTH = "BLUETOOTH"
+AUDIO_ROUTE_SPEAKER = "SPEAKER"
+AUDIO_ROUTE_WIRED_HEADSET = "WIRED_HEADSET"
+AUDIO_ROUTE_WIRED_OR_EARPIECE = "WIRED_OR_EARPIECE"
+
+# Constant for Call Capability
+CALL_CAPABILITY_HOLD = "HOLD"
+CALL_CAPABILITY_SUPPORT_HOLD = "SUPPORT_HOLD"
+CALL_CAPABILITY_MERGE_CONFERENCE = "MERGE_CONFERENCE"
+CALL_CAPABILITY_SWAP_CONFERENCE = "SWAP_CONFERENCE"
+CALL_CAPABILITY_UNUSED_1 = "UNUSED_1"
+CALL_CAPABILITY_RESPOND_VIA_TEXT = "RESPOND_VIA_TEXT"
+CALL_CAPABILITY_MUTE = "MUTE"
+CALL_CAPABILITY_MANAGE_CONFERENCE = "MANAGE_CONFERENCE"
+CALL_CAPABILITY_SUPPORTS_VT_LOCAL_RX = "SUPPORTS_VT_LOCAL_RX"
+CALL_CAPABILITY_SUPPORTS_VT_LOCAL_TX = "SUPPORTS_VT_LOCAL_TX"
+CALL_CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = "SUPPORTS_VT_LOCAL_BIDIRECTIONAL"
+CALL_CAPABILITY_SUPPORTS_VT_REMOTE_RX = "SUPPORTS_VT_REMOTE_RX"
+CALL_CAPABILITY_SUPPORTS_VT_REMOTE_TX = "SUPPORTS_VT_REMOTE_TX"
+CALL_CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = "SUPPORTS_VT_REMOTE_BIDIRECTIONAL"
+CALL_CAPABILITY_SEPARATE_FROM_CONFERENCE = "SEPARATE_FROM_CONFERENCE"
+CALL_CAPABILITY_DISCONNECT_FROM_CONFERENCE = "DISCONNECT_FROM_CONFERENCE"
+CALL_CAPABILITY_SPEED_UP_MT_AUDIO = "SPEED_UP_MT_AUDIO"
+CALL_CAPABILITY_CAN_UPGRADE_TO_VIDEO = "CAN_UPGRADE_TO_VIDEO"
+CALL_CAPABILITY_CAN_PAUSE_VIDEO = "CAN_PAUSE_VIDEO"
+CALL_CAPABILITY_UNKOWN = "UNKOWN"
+
+# Constant for Call Property
+CALL_PROPERTY_HIGH_DEF_AUDIO = "HIGH_DEF_AUDIO"
+CALL_PROPERTY_CONFERENCE = "CONFERENCE"
+CALL_PROPERTY_GENERIC_CONFERENCE = "GENERIC_CONFERENCE"
+CALL_PROPERTY_WIFI = "WIFI"
+CALL_PROPERTY_EMERGENCY_CALLBACK_MODE = "EMERGENCY_CALLBACK_MODE"
+CALL_PROPERTY_UNKNOWN = "UNKNOWN"
+
+# Constant for Call Presentation
+CALL_PRESENTATION_ALLOWED = "ALLOWED"
+CALL_PRESENTATION_RESTRICTED = "RESTRICTED"
+CALL_PRESENTATION_PAYPHONE = "PAYPHONE"
+CALL_PRESENTATION_UNKNOWN = "UNKNOWN"
+
+
+# Constant for Network RAT
+RAT_IWLAN = "IWLAN"
+RAT_LTE = "LTE"
+RAT_4G = "4G"
+RAT_3G = "3G"
+RAT_2G = "2G"
+RAT_WCDMA = "WCDMA"
+RAT_UMTS = "UMTS"
+RAT_1XRTT = "1XRTT"
+RAT_EDGE = "EDGE"
+RAT_GPRS = "GPRS"
+RAT_HSDPA = "HSDPA"
+RAT_HSUPA = "HSUPA"
+RAT_CDMA = "CDMA"
+RAT_EVDO = "EVDO"
+RAT_EVDO_0 = "EVDO_0"
+RAT_EVDO_A = "EVDO_A"
+RAT_EVDO_B = "EVDO_B"
+RAT_IDEN = "IDEN"
+RAT_EHRPD = "EHRPD"
+RAT_HSPA = "HSPA"
+RAT_HSPAP = "HSPAP"
+RAT_GSM = "GSM"
+RAT_TD_SCDMA = "TD_SCDMA"
+RAT_GLOBAL = "GLOBAL"
+RAT_UNKNOWN = "UNKNOWN"
+
+# NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE
+NETWORK_MODE_WCDMA_PREF     = 0 # GSM/WCDMA (WCDMA preferred)
+NETWORK_MODE_GSM_ONLY       = 1 # GSM only
+NETWORK_MODE_WCDMA_ONLY     = 2 # WCDMA only
+NETWORK_MODE_GSM_UMTS       = 3 # GSM/WCDMA (auto mode, according to PRL)
+                                #     AVAILABLE Application Settings menu
+NETWORK_MODE_CDMA           = 4 # CDMA and EvDo (auto mode, according to PRL)
+                                #    AVAILABLE Application Settings menu
+NETWORK_MODE_CDMA_NO_EVDO   = 5 # CDMA only
+NETWORK_MODE_EVDO_NO_CDMA   = 6 # EvDo only
+NETWORK_MODE_GLOBAL         = 7 # GSM/WCDMA, CDMA, and EvDo
+                                #    (auto mode, according to PRL)
+                                #     AVAILABLE Application Settings menu
+NETWORK_MODE_LTE_CDMA_EVDO  = 8 # LTE, CDMA and EvDo
+NETWORK_MODE_LTE_GSM_WCDMA  = 9 # LTE, GSM/WCDMA
+NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10 # LTE, CDMA, EvDo, GSM/WCDMA
+NETWORK_MODE_LTE_ONLY       = 11 # LTE Only mode
+NETWORK_MODE_LTE_WCDMA      = 12 # LTE/WCDMA
+NETWORK_MODE_TDSCDMA_ONLY            = 13 # TD-SCDMA only
+NETWORK_MODE_TDSCDMA_WCDMA           = 14 # TD-SCDMA and WCDMA
+NETWORK_MODE_LTE_TDSCDMA             = 15 # TD-SCDMA and LTE
+NETWORK_MODE_TDSCDMA_GSM             = 16 # TD-SCDMA and GSM
+NETWORK_MODE_LTE_TDSCDMA_GSM         = 17 # TD-SCDMA,GSM and LTE
+NETWORK_MODE_TDSCDMA_GSM_WCDMA       = 18 # TD-SCDMA, GSM/WCDMA
+NETWORK_MODE_LTE_TDSCDMA_WCDMA       = 19 # TD-SCDMA, WCDMA and LTE
+NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA   = 20 # TD-SCDMA, GSM/WCDMA and LTE
+NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA  = 21 # TD-SCDMA,EvDo,CDMA,GSM/WCDMA
+NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22 # TD-SCDMA/LTE/GSM/WCDMA,
+                                                  #    CDMA, and EvDo
+
+# Constant for Phone Type
+PHONE_TYPE_GSM = "GSM"
+PHONE_TYPE_NONE = "NONE"
+PHONE_TYPE_CDMA = "CDMA"
+PHONE_TYPE_SIP = "SIP"
+
+# Constant for SIM State
+SIM_STATE_READY = "READY"
+SIM_STATE_UNKNOWN = "UNKNOWN"
+SIM_STATE_ABSENT = "ABSENT"
+SIM_STATE_PUK_REQUIRED = "PUK_REQUIRED"
+SIM_STATE_PIN_REQUIRED = "PIN_REQUIRED"
+SIM_STATE_NETWORK_LOCKED = "NETWORK_LOCKED"
+SIM_STATE_NOT_READY = "NOT_READY"
+SIM_STATE_PERM_DISABLED = "PERM_DISABLED"
+SIM_STATE_CARD_IO_ERROR = "CARD_IO_ERROR"
+
+# Constant for Data Connection State
+DATA_STATE_CONNECTED = "CONNECTED"
+DATA_STATE_DISCONNECTED = "DISCONNECTED"
+DATA_STATE_CONNECTING = "CONNECTING"
+DATA_STATE_SUSPENDED = "SUSPENDED"
+DATA_STATE_UNKNOWN = "UNKNOWN"
+
+# Constant for Telephony Manager Call State
+TELEPHONY_STATE_RINGING = "RINGING"
+TELEPHONY_STATE_IDLE = "IDLE"
+TELEPHONY_STATE_OFFHOOK = "OFFHOOK"
+TELEPHONY_STATE_UNKNOWN = "UNKNOWN"
+
+# Constant for TTY Mode
+TTY_MODE_FULL = "FULL"
+TTY_MODE_HCO = "HCO"
+TTY_MODE_OFF = "OFF"
+TTY_MODE_VCO ="VCO"
+
+# Constant for Service State
+SERVICE_STATE_EMERGENCY_ONLY = "EMERGENCY_ONLY"
+SERVICE_STATE_IN_SERVICE = "IN_SERVICE"
+SERVICE_STATE_OUT_OF_SERVICE = "OUT_OF_SERVICE"
+SERVICE_STATE_POWER_OFF = "POWER_OFF"
+SERVICE_STATE_UNKNOWN = "UNKNOWN"
+
+# Constant for VoLTE Hand-over Service State
+VOLTE_SERVICE_STATE_HANDOVER_STARTED = "STARTED"
+VOLTE_SERVICE_STATE_HANDOVER_COMPLETED = "COMPLETED"
+VOLTE_SERVICE_STATE_HANDOVER_FAILED = "FAILED"
+VOLTE_SERVICE_STATE_HANDOVER_CANCELED = "CANCELED"
+VOLTE_SERVICE_STATE_HANDOVER_UNKNOWN = "UNKNOWN"
+
+# Constant for precise call state state listen level
+PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND = "FOREGROUND"
+PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING = "RINGING"
+PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND = "BACKGROUND"
+
+# Constant for Messaging Event Name
+EventSmsDeliverSuccess = "SmsDeliverSuccess"
+EventSmsDeliverFailure = "SmsDeliverFailure"
+EventSmsSentSuccess = "SmsSentSuccess"
+EventSmsSentFailure = "SmsSentFailure"
+EventSmsReceived = "SmsReceived"
+EventMmsSentSuccess = "MmsSentSuccess"
+EventMmsSentFailure = "MmsSentFailure"
+EventMmsDownloaded = "MmsDownloaded"
+EventWapPushReceived = "WapPushReceived"
+EventDataSmsReceived = "DataSmsReceived"
+EventCmasReceived = "CmasReceived"
+EventEtwsReceived = "EtwsReceived"
+
+# Constant for Telecom Call Event Name
+EventTelecomCallStateChanged = "TelecomCallStateChanged"
+EventTelecomCallParentChanged = "TelecomCallParentChanged"
+EventTelecomCallChildrenChanged = "TelecomCallChildrenChanged"
+EventTelecomCallDetailsChanged = "TelecomCallDetailsChanged"
+EventTelecomCallCannedTextResponsesLoaded = "TelecomCallCannedTextResponsesLoaded"
+EventTelecomCallPostDialWait = "TelecomCallPostDialWait"
+EventTelecomCallVideoCallChanged = "TelecomCallVideoCallChanged"
+EventTelecomCallDestroyed = "TelecomCallDestroyed"
+EventTelecomCallConferenceableCallsChanged = "TelecomCallConferenceableCallsChanged"
+
+# Constant for Video Call Event Name
+EventTelecomVideoCallSessionModifyRequestReceived = "TelecomVideoCallSessionModifyRequestReceived"
+EventTelecomVideoCallSessionModifyResponseReceived = "TelecomVideoCallSessionModifyResponseReceived"
+EventTelecomVideoCallSessionEvent = "TelecomVideoCallSessionEvent"
+EventTelecomVideoCallPeerDimensionsChanged = "TelecomVideoCallPeerDimensionsChanged"
+EventTelecomVideoCallVideoQualityChanged = "TelecomVideoCallVideoQualityChanged"
+EventTelecomVideoCallDataUsageChanged = "TelecomVideoCallDataUsageChanged"
+EventTelecomVideoCallCameraCapabilities = "TelecomVideoCallCameraCapabilities"
+
+# Constant for Video Call Call-Back Event Name
+EventSessionModifyRequestRceived = "SessionModifyRequestRceived"
+EventSessionModifyResponsetRceived = "SessionModifyResponsetRceived"
+EventSessionEvent = "SessionEvent"
+EventPeerDimensionsChanged = "PeerDimensionsChanged"
+EventVideoQualityChanged = "VideoQualityChanged"
+EventDataUsageChanged = "DataUsageChanged"
+EventCameraCapabilitiesChanged = "CameraCapabilitiesChanged"
+EventInvalid = "Invalid"
+
+# Constant for Video Call Session Event Name
+SessionEventRxPause = "SessionEventRxPause"
+SessionEventRxResume = "SessionEventRxResume"
+SessionEventTxStart = "SessionEventTxStart"
+SessionEventTxStop = "SessionEventTxStop"
+SessionEventCameraFailure = "SessionEventCameraFailure"
+SessionEventCameraReady = "SessionEventCameraReady"
+SessionEventUnknown = "SessionEventUnknown"
+
+# Constant for Other Event Name
+EventCallStateChanged = "CallStateChanged"
+EventPreciseStateChanged = "PreciseStateChanged"
+EventDataConnectionRealTimeInfoChanged = "DataConnectionRealTimeInfoChanged"
+EventDataConnectionStateChanged = "DataConnectionStateChanged"
+EventServiceStateChanged = "ServiceStateChanged"
+EventVolteServiceStateChanged = "VolteServiceStateChanged"
+EventMessageWaitingIndicatorChanged = "MessageWaitingIndicatorChanged"
+EventConnectivityChanged = "ConnectivityChanged"
+
+# Constant for Packet Keep Alive Call Back
+PacketKeepaliveCallBack = "PacketKeepliveCallBack"
+PacketKeepaliveCallBackStarted = "Started"
+PacketKeepaliveCallBackStopped = "Stopped"
+PacketKeepaliveCallBackError = "Error"
+PacketKeepaliveCallBackInvalid = "Invalid"
+
+# Constant for Network Call Back
+NetworkCallBack = "NetworkCallBack"
+NetworkCallBackPreCheck = "PreCheck"
+NetworkCallBackAvailable = "Available"
+NetworkCallBackLosing = "Losing"
+NetworkCallBackLost = "Lost"
+NetworkCallBackUnavailable = "Unavailable"
+NetworkCallBackCapabilitiesChanged = "CapabilitiesChanged"
+NetworkCallBackSuspended = "Suspended"
+NetworkCallBackResumed = "Resumed"
+NetworkCallBackLinkPropertiesChanged = "LinkPropertiesChanged"
+NetworkCallBackInvalid = "Invalid"
+
+"""
+End shared constant define for both Python and Java
+"""
diff --git a/acts/framework/acts/test_utils/tel/tel_lookup_tables.py b/acts/framework/acts/test_utils/tel/tel_lookup_tables.py
new file mode 100644
index 0000000..93dce95
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/tel_lookup_tables.py
@@ -0,0 +1,416 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+from acts.utils import NexusModelNames
+from acts.test_utils.tel import tel_defines
+
+# TODO: rename to remove the word "type"
+# rat_family_from_rat()
+def rat_family_from_type(rat_type):
+    return _TelTables.technology_tbl[rat_type]['rat_family']
+
+# TODO: rename to remove the word "type"
+# rat_generation_from_rat()
+def rat_generation_from_type(rat_type):
+    return _TelTables.technology_tbl[rat_type]['generation']
+
+def operator_name_from_plmn_id(plmn_id):
+    return _TelTables.operator_id_to_name[plmn_id]
+
+def is_valid_rat(rat_type):
+    return True if rat_type in _TelTables.technology_tbl else False
+
+def is_valid_generation(gen):
+    return True if gen in _TelTables.technology_gen_tbl else False
+
+def is_rat_svd_capable(rat):
+    return _TelTables.technology_tbl[rat]["simultaneous_voice_data"]
+
+def network_mode_by_operator_generation(operator, generation):
+    try:
+        return _TelTables.operator_network_mode_preference_tbl[operator][generation]
+    except KeyError:
+        return None
+
+# TODO: Create a networking lookup tables file and move
+def connection_type_from_type_string(input_string):
+    if input_string in _ConnectionTables.connection_type_tbl:
+        return _ConnectionTables.connection_type_tbl[input_string]
+    return tel_defines.NETWORK_CONNECTION_TYPE_UNKNOWN
+
+def is_user_plane_data_type(connection_type):
+    if connection_type in _ConnectionTables.user_plane_data_type:
+      return _ConnectionTables.user_plane_data_type[connection_type]
+    return False
+
+# For TMO, to check if voice mail count is correct after leaving a new voice message.
+def check_tmo_voice_mail_count(voice_mail_count_before, voice_mail_count_after):
+    return (voice_mail_count_after == -1)
+
+# For ATT, to check if voice mail count is correct after leaving a new voice message.
+def check_att_voice_mail_count(voice_mail_count_before, voice_mail_count_after):
+    return (voice_mail_count_after == (voice_mail_count_before + 1))
+
+# For SPT, to check if voice mail count is correct after leaving a new voice message.
+def check_spt_voice_mail_count(voice_mail_count_before, voice_mail_count_after):
+    return (voice_mail_count_after == (voice_mail_count_before + 1))
+
+# For TMO, get the voice mail number
+def get_tmo_voice_mail_number():
+    return "123"
+
+# For ATT, get the voice mail number
+def get_att_voice_mail_number():
+    return None
+
+# For SPT, get the voice mail number
+def get_spt_voice_mail_number():
+    return None
+
+def get_voice_mail_number_function(operator):
+    return _TelTables.voice_mail_number_get_function_tbl[operator]
+
+def get_voice_mail_count_check_function(operator):
+    return _TelTables.voice_mail_count_check_function_tbl[operator]
+
+class _ConnectionTables():
+    connection_type_tbl = {
+        'WIFI': tel_defines.NETWORK_CONNECTION_TYPE_WIFI,
+        'WIFI_P2P': tel_defines.NETWORK_CONNECTION_TYPE_WIFI,
+        'MOBILE': tel_defines.NETWORK_CONNECTION_TYPE_CELL,
+        'MOBILE_DUN': tel_defines.NETWORK_CONNECTION_TYPE_CELL,
+        'MOBILE_HIPRI': tel_defines.NETWORK_CONNECTION_TYPE_HIPRI,
+        # TODO: add support for 'MOBILE_SUPL', 'MOBILE_HIPRI', 'MOBILE_FOTA',
+        # 'MOBILE_IMS', 'MOBILE_CBS', 'MOBILE_IA', 'MOBILE_EMERGENCY'
+        'MOBILE_MMS': tel_defines.NETWORK_CONNECTION_TYPE_MMS
+    }
+
+    user_plane_data_type = {
+        tel_defines.NETWORK_CONNECTION_TYPE_WIFI: True,
+        tel_defines.NETWORK_CONNECTION_TYPE_CELL: False,
+        tel_defines.NETWORK_CONNECTION_TYPE_MMS: False,
+        tel_defines.NETWORK_CONNECTION_TYPE_UNKNOWN: False
+    }
+
+
+class _TelTables():
+    # Operator id mapping to operator name
+    # Reference: Pages 43-50 in
+    # https://www.itu.int/dms_pub/itu-t/opb/sp/T-SP-E.212B-2013-PDF-E.pdf [2013]
+
+    operator_id_to_name = {
+
+        #VZW (Verizon Wireless)
+        '310010': tel_defines.CARRIER_VZW,
+        '310012': tel_defines.CARRIER_VZW,
+        '310013': tel_defines.CARRIER_VZW,
+        '310590': tel_defines.CARRIER_VZW,
+        '310890': tel_defines.CARRIER_VZW,
+        '310910': tel_defines.CARRIER_VZW,
+        '310110': tel_defines.CARRIER_VZW,
+        '311270': tel_defines.CARRIER_VZW,
+        '311271': tel_defines.CARRIER_VZW,
+        '311272': tel_defines.CARRIER_VZW,
+        '311273': tel_defines.CARRIER_VZW,
+        '311274': tel_defines.CARRIER_VZW,
+        '311275': tel_defines.CARRIER_VZW,
+        '311276': tel_defines.CARRIER_VZW,
+        '311277': tel_defines.CARRIER_VZW,
+        '311278': tel_defines.CARRIER_VZW,
+        '311279': tel_defines.CARRIER_VZW,
+        '311280': tel_defines.CARRIER_VZW,
+        '311281': tel_defines.CARRIER_VZW,
+        '311282': tel_defines.CARRIER_VZW,
+        '311283': tel_defines.CARRIER_VZW,
+        '311284': tel_defines.CARRIER_VZW,
+        '311285': tel_defines.CARRIER_VZW,
+        '311286': tel_defines.CARRIER_VZW,
+        '311287': tel_defines.CARRIER_VZW,
+        '311288': tel_defines.CARRIER_VZW,
+        '311289': tel_defines.CARRIER_VZW,
+        '311390': tel_defines.CARRIER_VZW,
+        '311480': tel_defines.CARRIER_VZW,
+        '311481': tel_defines.CARRIER_VZW,
+        '311482': tel_defines.CARRIER_VZW,
+        '311483': tel_defines.CARRIER_VZW,
+        '311484': tel_defines.CARRIER_VZW,
+        '311485': tel_defines.CARRIER_VZW,
+        '311486': tel_defines.CARRIER_VZW,
+        '311487': tel_defines.CARRIER_VZW,
+        '311488': tel_defines.CARRIER_VZW,
+        '311489': tel_defines.CARRIER_VZW,
+
+        #TMO (T-Mobile USA)
+        '310160': tel_defines.CARRIER_TMO,
+        '310200': tel_defines.CARRIER_TMO,
+        '310210': tel_defines.CARRIER_TMO,
+        '310220': tel_defines.CARRIER_TMO,
+        '310230': tel_defines.CARRIER_TMO,
+        '310240': tel_defines.CARRIER_TMO,
+        '310250': tel_defines.CARRIER_TMO,
+        '310260': tel_defines.CARRIER_TMO,
+        '310270': tel_defines.CARRIER_TMO,
+        '310310': tel_defines.CARRIER_TMO,
+        '310490': tel_defines.CARRIER_TMO,
+        '310660': tel_defines.CARRIER_TMO,
+        '310800': tel_defines.CARRIER_TMO,
+
+        #ATT (AT&T and Cingular)
+        '310070': tel_defines.CARRIER_ATT,
+        '310560': tel_defines.CARRIER_ATT,
+        '310670': tel_defines.CARRIER_ATT,
+        '310680': tel_defines.CARRIER_ATT,
+        '310150': tel_defines.CARRIER_ATT,   #Cingular
+        '310170': tel_defines.CARRIER_ATT,   #Cingular
+        '310410': tel_defines.CARRIER_ATT,   #Cingular
+        '311180': tel_defines.CARRIER_ATT,   #Cingular Licensee Pacific Telesis Mobile Services, LLC
+
+        #Sprint (and Sprint-Nextel)
+        '310120': tel_defines.CARRIER_SPT,
+        '311490': tel_defines.CARRIER_SPT,
+        '311870': tel_defines.CARRIER_SPT,
+        '311880': tel_defines.CARRIER_SPT,
+        '312190': tel_defines.CARRIER_SPT,   #Sprint-Nextel Communications Inc
+        '316010': tel_defines.CARRIER_SPT    #Sprint-Nextel Communications Inc
+    }
+
+    technology_gen_tbl = [tel_defines.RAT_2G, tel_defines.RAT_3G,
+                          tel_defines.RAT_4G]
+
+    technology_tbl = {
+        tel_defines.RAT_1XRTT: {
+            'is_voice_rat': True,
+            'is_data_rat': False,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+        },
+        tel_defines.RAT_EDGE: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_2G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_GSM
+        },
+        tel_defines.RAT_GPRS: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_2G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_GSM
+        },
+        tel_defines.RAT_GSM: {
+            'is_voice_rat': True,
+            'is_data_rat': False,
+            'generation': tel_defines.RAT_2G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_GSM
+        },
+        tel_defines.RAT_UMTS: {
+            'is_voice_rat': True,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': True,
+            'rat_family': tel_defines.RAT_FAMILY_UMTS
+        },
+        tel_defines.RAT_WCDMA: {
+            'is_voice_rat': True,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': True,
+            'rat_family': tel_defines.RAT_FAMILY_UMTS
+        },
+        tel_defines.RAT_HSDPA: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_UMTS
+        },
+        tel_defines.RAT_HSUPA: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_UMTS
+        },
+        # TODO: Confirm whether this is for IS-95
+        tel_defines.RAT_CDMA: {
+            'is_voice_rat': True,
+            'is_data_rat': False,
+            'generation': tel_defines.RAT_2G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_CDMA
+        },
+        tel_defines.RAT_EVDO: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+        },
+        tel_defines.RAT_EVDO_0: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+        },
+        tel_defines.RAT_EVDO_A: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+        },
+        tel_defines.RAT_EVDO_B: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+        },
+        tel_defines.RAT_IDEN: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_2G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_IDEN
+        },
+        tel_defines.RAT_LTE: {
+            'is_voice_rat': True,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_4G,
+            'simultaneous_voice_data': True,
+            'rat_family': tel_defines.RAT_FAMILY_LTE
+        },
+        tel_defines.RAT_EHRPD: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_CDMA2000
+        },
+        tel_defines.RAT_HSPA: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': True,
+            'rat_family': tel_defines.RAT_FAMILY_UMTS
+        },
+        tel_defines.RAT_HSPAP: {
+            'is_voice_rat': False,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': True,
+            'rat_family': tel_defines.RAT_FAMILY_UMTS
+        },
+        tel_defines.RAT_IWLAN: {
+            'is_voice_rat': True,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_4G,
+            'simultaneous_voice_data': True,
+            'rat_family': tel_defines.RAT_FAMILY_WLAN
+        },
+        tel_defines.RAT_TD_SCDMA: {
+            'is_voice_rat': True,
+            'is_data_rat': True,
+            'generation': tel_defines.RAT_3G,
+            'simultaneous_voice_data': True,
+            'rat_family': tel_defines.RAT_FAMILY_UMTS
+        },
+        tel_defines.RAT_UNKNOWN: {
+            'is_voice_rat': False,
+            'is_data_rat': False,
+            'generation': tel_defines.RAT_UNKNOWN,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_UNKNOWN
+        },
+        tel_defines.RAT_GLOBAL: {
+            'is_voice_rat': False,
+            'is_data_rat': False,
+            'generation': tel_defines.RAT_UNKNOWN,
+            'simultaneous_voice_data': False,
+            'rat_family': tel_defines.RAT_FAMILY_UNKNOWN
+        }
+
+    }
+
+    voice_mail_number_get_function_tbl = {
+        tel_defines.CARRIER_TMO: get_tmo_voice_mail_number,
+        tel_defines.CARRIER_ATT: get_att_voice_mail_number,
+        tel_defines.CARRIER_SPT: get_spt_voice_mail_number
+    }
+
+    voice_mail_count_check_function_tbl = {
+        tel_defines.CARRIER_TMO: check_tmo_voice_mail_count,
+        tel_defines.CARRIER_ATT: check_att_voice_mail_count,
+        tel_defines.CARRIER_SPT: check_spt_voice_mail_count
+    }
+
+    _operator_na_cdma_tbl = {
+        tel_defines.RAT_2G: tel_defines.NETWORK_MODE_CDMA_NO_EVDO,
+        tel_defines.RAT_1XRTT: tel_defines.NETWORK_MODE_CDMA_NO_EVDO,
+        tel_defines.RAT_3G: tel_defines.NETWORK_MODE_CDMA,
+        tel_defines.RAT_EVDO: tel_defines.NETWORK_MODE_CDMA,
+        tel_defines.RAT_4G: tel_defines.NETWORK_MODE_LTE_CDMA_EVDO,
+        tel_defines.RAT_LTE: tel_defines.NETWORK_MODE_LTE_CDMA_EVDO,
+        tel_defines.RAT_GLOBAL: tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+    }
+
+    _operator_na_umts_tbl = {
+        tel_defines.RAT_2G: tel_defines.NETWORK_MODE_GSM_ONLY,
+        tel_defines.RAT_GSM: tel_defines.NETWORK_MODE_GSM_ONLY,
+        tel_defines.RAT_3G: tel_defines.NETWORK_MODE_WCDMA_PREF,
+        tel_defines.RAT_WCDMA: tel_defines.NETWORK_MODE_WCDMA_PREF,
+        tel_defines.RAT_4G: tel_defines.NETWORK_MODE_LTE_GSM_WCDMA,
+        tel_defines.RAT_LTE: tel_defines.NETWORK_MODE_LTE_GSM_WCDMA,
+        tel_defines.RAT_GLOBAL: tel_defines.NETWORK_MODE_LTE_GSM_WCDMA
+    }
+
+    operator_network_mode_preference_tbl = {
+        tel_defines.CARRIER_VZW: _operator_na_cdma_tbl,
+        tel_defines.CARRIER_SPT: _operator_na_cdma_tbl,
+        tel_defines.CARRIER_ATT: _operator_na_umts_tbl,
+        tel_defines.CARRIER_TMO: _operator_na_umts_tbl,
+    }
+
+device_capabilities = {
+    NexusModelNames.ONE:
+        [tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_MSIM],
+    NexusModelNames.N5:
+        [tel_defines.CAPABILITY_PHONE],
+    NexusModelNames.N5v2:
+        [tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE],
+    NexusModelNames.N6:
+        [tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+         tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
+         tel_defines.CAPABILITY_VT],
+    NexusModelNames.N6v2:
+        [tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE],
+}
+
+operator_capabilities = {
+    tel_defines.CARRIER_VZW:
+        [tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+         tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_VT],
+    tel_defines.CARRIER_ATT:
+        [tel_defines.CAPABILITY_PHONE],
+    tel_defines.CARRIER_TMO:
+        [tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE,
+         tel_defines.CAPABILITY_WFC],
+    tel_defines.CARRIER_SPT:
+        [tel_defines.CAPABILITY_PHONE],
+}
diff --git a/acts/framework/acts/test_utils/tel/tel_test_anritsu_utils.py b/acts/framework/acts/test_utils/tel/tel_test_anritsu_utils.py
new file mode 100644
index 0000000..8d8d824
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/tel_test_anritsu_utils.py
@@ -0,0 +1,1413 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 queue import Empty
+from datetime import datetime
+
+from acts.controllers.tel.md8475a import BtsNumber
+from acts.controllers.tel.md8475a import BtsTechnology
+from acts.controllers.tel.md8475a import BtsNwNameEnable
+from acts.controllers.tel.md8475a import CsfbType
+from acts.controllers.tel.md8475a import ReturnToEUTRAN
+from acts.controllers.tel.md8475a import MD8475A
+from acts.controllers.tel.md8475a import BtsServiceState
+from acts.controllers.tel.md8475a import VirtualPhoneStatus
+from . tel_test_utils import *
+from acts.controllers.tel._anritsu_utils import AnritsuUtils
+
+# Test PLMN information
+TEST_PLMN_LTE_NAME = "MD8475A_LTE"
+TEST_PLMN_WCDMA_NAME = "MD8475A_WCDMA"
+TEST_PLMN_GSM_NAME = "MD8475A_GSM"
+TEST_PLMN_1X_NAME = "MD8475A_1X"
+TEST_PLMN_1_MCC = "001"
+TEST_PLMN_1_MNC = "01"
+DEFAULT_MCC = "001"
+DEFAULT_MNC = "01"
+DEFAULT_RAC = 1
+DEFAULT_LAC = 1
+
+# IP address information for internet sharing
+GATEWAY_IPV4_ADDRESS = "192.168.137.1"
+UE_IPV4_ADDRESS_1 = "192.168.137.2"
+UE_IPV4_ADDRESS_2 = "192.168.137.3"
+DNS_IPV4_ADDRESS = "192.168.137.1"
+CSCF_IPV4_ADDRESS = "192.168.137.1"
+
+# preferred network modes
+NETWORK_MODE_WCDMA_PREF = 0
+NETWORK_MODE_GSM_ONLY = 1
+NETWORK_MODE_WCDMA_ONLY = 2
+NETWORK_MODE_GSM_UMTS = 3
+NETWORK_MODE_CDMA = 4
+NETWORK_MODE_CDMA_NO_EVDO = 5
+NETWORK_MODE_EVDO_NO_CDMA = 6
+NETWORK_MODE_GLOBAL = 7
+NETWORK_MODE_LTE_CDMA_EVDO = 8
+NETWORK_MODE_LTE_GSM_WCDMA = 9
+NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10
+NETWORK_MODE_LTE_ONLY = 11
+NETWORK_MODE_LTE_WCDMA = 12
+
+# LTE BAND constants
+LTE_BAND_1 = 1
+LTE_BAND_2 = 2
+LTE_BAND_3 = 3
+LTE_BAND_4 = 4
+LTE_BAND_5 = 5
+LTE_BAND_7 = 7
+LTE_BAND_12 = 12
+LTE_BAND_13 = 13
+
+# WCDMA BAND constants
+WCDMA_BAND_1 = 1
+WCDMA_BAND_2 = 2
+WCDMA_BAND_4 = 4
+WCDMA_BAND_5 = 5
+WCDMA_BAND_8 = 8
+
+
+# GSM BAND constants
+GSM_BAND_GSM450 = "GSM450"
+GSM_BAND_GSM480 = "GSM480"
+GSM_BAND_GSM850 = "GSM850"
+GSM_BAND_PGSM900 = "P-GSM900"
+GSM_BAND_EGSM900 = "E-GSM900"
+GSM_BAND_RGSM900 = "R-GSM900"
+GSM_BAND_DCS1800 = "DCS1800"
+GSM_BAND_PCS1900 = "PCS1900"
+
+# CDMA 1X BAND constants
+CDMA_1X_BAND_0 = 0
+CDMA_1X_BAND_1 = 1
+
+# CDMA 1X DL Channel constants
+CDMA1X_CHANNEL_356 = 356
+
+# CDMA 1X SID constants
+CDMA1X_SID_0 = 0
+
+# CDMA 1X NID constants
+CDMA1X_NID_65535 = 65535
+
+
+# BANDWIDTH constants
+CDMA1X_NID_65535 = 65535
+
+# CMAS Message IDs
+CMAS_MESSAGE_PRESIDENTIAL_ALERT = hex(0x1112)
+CMAS_MESSAGE_EXTREME_IMMEDIATE_OBSERVED = hex(0x1113)
+CMAS_MESSAGE_EXTREME_IMMEDIATE_LIKELY = hex(0x1114)
+CMAS_MESSAGE_EXTREME_EXPECTED_OBSERVED = hex(0x1115)
+CMAS_MESSAGE_EXTREME_EXPECTED_LIKELY = hex(0x1116)
+CMAS_MESSAGE_SEVERE_IMMEDIATE_OBSERVED = hex(0x1117)
+CMAS_MESSAGE_SEVERE_IMMEDIATE_LIKELY = hex(0x1118)
+CMAS_MESSAGE_SEVERE_EXPECTED_OBSERVED = hex(0x1119)
+CMAS_MESSAGE_SEVERE_EXPECTED_LIKELY = hex(0x111A)
+CMAS_MESSAGE_CHILD_ABDUCTION_EMERGENCY = hex(0x111B)
+CMAS_MESSAGE_MONTHLY_TEST = hex(0x111C)
+CMAS_MESSAGE_CMAS_EXECERCISE = hex(0x111D)
+
+# ETWS Message IDs
+ETWS_WARNING_EARTHQUAKE = hex(0x1100)
+ETWS_WARNING_TSUNAMI = hex(0x1101)
+ETWS_WARNING_EARTHQUAKETSUNAMI = hex(0x1102)
+ETWS_WARNING_TEST_MESSAGE = hex(0x1103)
+ETWS_WARNING_OTHER_EMERGENCY = hex(0x1104)
+
+# C2K CMAS Message Constants
+CMAS_C2K_CATEGORY_PRESIDENTIAL = "Presidential"
+CMAS_C2K_CATEGORY_EXTREME = "Extreme"
+CMAS_C2K_CATEGORY_SEVERE = "Severe"
+CMAS_C2K_CATEGORY_AMBER = "AMBER"
+CMAS_C2K_CATEGORY_CMASTEST = "CMASTest"
+
+CMAS_C2K_PRIORITY_NORMAL = "Normal"
+CMAS_C2K_PRIORITY_INTERACTIVE = "Interactive"
+CMAS_C2K_PRIORITY_URGENT = "Urgent"
+CMAS_C2K_PRIORITY_EMERGENCY = "Emergency"
+
+CMAS_C2K_RESPONSETYPE_SHELTER = "Shelter"
+CMAS_C2K_RESPONSETYPE_EVACUATE = "Evacuate"
+CMAS_C2K_RESPONSETYPE_PREPARE = "Prepare"
+CMAS_C2K_RESPONSETYPE_EXECUTE = "Execute"
+CMAS_C2K_RESPONSETYPE_MONITOR = "Monitor"
+CMAS_C2K_RESPONSETYPE_AVOID = "Avoid"
+CMAS_C2K_RESPONSETYPE_ASSESS = "Assess"
+CMAS_C2K_RESPONSETYPE_NONE = "None"
+
+CMAS_C2K_SEVERITY_EXTREME = "Extreme"
+CMAS_C2K_SEVERITY_SEVERE = "Severe"
+
+CMAS_C2K_URGENCY_IMMEDIATE = "Immediate"
+CMAS_C2K_URGENCY_EXPECTED = "Expected"
+
+CMAS_C2K_CERTIANTY_OBSERVED = "Observed"
+CMAS_C2K_CERTIANTY_LIKELY = "Likely"
+
+
+#PDN Numbers
+PDN_NO_1 = 1
+
+#Cell Numbers
+CELL_1 = 1
+CELL_2 = 2
+
+def cb_serial_number():
+    """ CMAS/ETWS serial number generator """
+    i = 0x3000
+    while True:
+        yield i
+        i += 1
+
+def save_anritsu_log_files(anritsu_handle, test_name, user_params):
+    """ saves the anritsu smart studio log files
+        The logs should be saved in Anritsu system. Need to provide
+        log folder path in Anritsu system
+
+    Args:
+        anritsu_handle: anritusu device object.
+        test_name: test case name
+        user_params : user supplied parameters list
+
+    Returns:
+        None
+    """
+    md8475a_log_folder =  user_params["anritsu_log_file_path"]
+    file_name = getfilenamewithtimestamp(test_name)
+    seq_logfile = "{}\\{}_seq.csv".format(md8475a_log_folder, file_name)
+    msg_logfile = "{}\\{}_msg.csv".format(md8475a_log_folder, file_name)
+    trace_logfile = "{}\\{}_trace.lgex".format(md8475a_log_folder, file_name)
+    anritsu_handle.save_sequence_log(seq_logfile)
+    anritsu_handle.save_message_log(msg_logfile)
+    anritsu_handle.save_trace_log(trace_logfile, "BINARY", 1, 0, 0)
+    anritsu_handle.clear_sequence_log()
+    anritsu_handle.clear_message_log()
+
+
+def getfilenamewithtimestamp(test_name):
+    """ Gets the test name appended with current time
+
+    Args:
+        test_name : test case name
+
+    Returns:
+        string of test name appended with current time
+    """
+    time_stamp = datetime.now().strftime("%m-%d-%Y_%H-%M-%S")
+    return "{}_{}".format(test_name, time_stamp)
+
+
+def _init_lte_bts(bts, user_params, cell_no):
+    """ initializes the LTE BTS
+        All BTS parameters should be set here
+
+    Args:
+        bts: BTS object.
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        None
+    """
+    bts.nw_fullname_enable = BtsNwNameEnable.NAME_ENABLE
+    bts.nw_fullname = TEST_PLMN_LTE_NAME
+    bts.mcc = get_lte_mcc(user_params, cell_no)
+    bts.mnc = get_lte_mnc(user_params, cell_no)
+    bts.band = get_lte_band(user_params, cell_no)
+
+
+def _init_wcdma_bts(bts, user_params, cell_no):
+    """ initializes the WCDMA BTS
+        All BTS parameters should be set here
+
+    Args:
+        bts: BTS object.
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        None
+    """
+    bts.nw_fullname_enable = BtsNwNameEnable.NAME_ENABLE
+    bts.nw_fullname = TEST_PLMN_WCDMA_NAME
+    bts.mcc = get_lte_mcc(user_params, cell_no)
+    bts.mnc = get_lte_mnc(user_params, cell_no)
+    bts.band = get_wcdma_band(user_params, cell_no)
+    bts.rac = get_wcdma_rac(user_params, cell_no)
+    bts.lac = get_wcdma_lac(user_params, cell_no)
+
+
+def _init_gsm_bts(bts, user_params, cell_no):
+    """ initializes the GSM BTS
+        All BTS parameters should be set here
+
+    Args:
+        bts: BTS object.
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        None
+    """
+    bts.nw_fullname_enable = BtsNwNameEnable.NAME_ENABLE
+    bts.nw_fullname = TEST_PLMN_GSM_NAME
+    bts.mcc = get_lte_mcc(user_params, cell_no)
+    bts.mnc = get_lte_mnc(user_params, cell_no)
+    bts.band = get_gsm_band(user_params, cell_no)
+    bts.rac = get_gsm_rac(user_params, cell_no)
+    bts.lac = get_gsm_lac(user_params, cell_no)
+
+
+def _init_1x_bts(bts, user_params, cell_no):
+    """ initializes the 1X BTS
+        All BTS parameters should be set here
+
+    Args:
+        bts: BTS object.
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        None
+    """
+    bts.sector1_mcc = get_1x_mcc(user_params, cell_no)
+    bts.band = get_1x_band(user_params, cell_no)
+    bts.dl_channel = get_1x_channel(user_params, cell_no)
+    bts.sector1_sid = get_1x_sid(user_params, cell_no)
+    bts.sector1_nid = get_1x_nid(user_params, cell_no)
+
+
+def _init_evdo_bts(bts, user_params, cell_no):
+    """ initializes the EVDO BTS
+        All BTS parameters should be set here
+
+    Args:
+        bts: BTS object.
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        None
+    """
+    #TODO
+    pass
+
+
+def _init_PDN(anritsu_handle, pdn, ip_address):
+    """ initializes the PDN parameters
+        All PDN parameters should be set here
+
+    Args:
+        anritsu_handle: anritusu device object.
+        pdn: pdn object
+        ip_address : UE IP address
+
+    Returns:
+        None
+    """
+    # Setting IP address for internet connection sharing
+    anritsu_handle.gateway_ipv4addr = GATEWAY_IPV4_ADDRESS
+    pdn.ue_address_ipv4 = ip_address
+    pdn.primary_dns_address_ipv4 = DNS_IPV4_ADDRESS
+    pdn.secondary_dns_address_ipv4 = DNS_IPV4_ADDRESS
+    pdn.cscf_address_ipv4 = CSCF_IPV4_ADDRESS
+
+def set_system_model_lte_lte(anritsu_handle, user_params):
+    """ Configures Anritsu system for LTE and LTE simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Lte and Wcdma BTS objects
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.LTE,
+                                       BtsTechnology.LTE)
+    # setting BTS parameters
+    lte1_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    lte2_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+    _init_lte_bts(lte1_bts, user_params, CELL_1)
+    _init_lte_bts(lte2_bts, user_params, CELL_2)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [lte1_bts, lte2_bts]
+
+def set_system_model_wcdma_wcdma(anritsu_handle, user_params):
+    """ Configures Anritsu system for WCDMA and WCDMA simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Lte and Wcdma BTS objects
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.WCDMA,
+                                       BtsTechnology.WCDMA)
+    # setting BTS parameters
+    wcdma1_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    wcdma2_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+    _init_wcdma_bts(wcdma1_bts, user_params, CELL_1)
+    _init_wcdma_bts(wcdma2_bts, user_params, CELL_2)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [wcdma1_bts, wcdma2_bts]
+
+def set_system_model_lte_wcdma(anritsu_handle, user_params):
+    """ Configures Anritsu system for LTE and WCDMA simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Lte and Wcdma BTS objects
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.LTE,
+                                       BtsTechnology.WCDMA)
+    # setting BTS parameters
+    lte_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    wcdma_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+    _init_lte_bts(lte_bts, user_params, CELL_1)
+    _init_wcdma_bts(wcdma_bts, user_params, CELL_2)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [lte_bts, wcdma_bts]
+
+def set_system_model_lte_gsm(anritsu_handle, user_params):
+    """ Configures Anritsu system for LTE and GSM simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Lte and Wcdma BTS objects
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.LTE,
+                                       BtsTechnology.GSM)
+    # setting BTS parameters
+    lte_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    gsm_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+    _init_lte_bts(lte_bts, user_params, CELL_1)
+    _init_gsm_bts(gsm_bts, user_params, CELL_2)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [lte_bts, gsm_bts]
+
+def set_system_model_wcdma_gsm(anritsu_handle, user_params):
+    """ Configures Anritsu system for WCDMA and GSM simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Wcdma and Gsm BTS objects
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.WCDMA, BtsTechnology.GSM)
+    # setting BTS parameters
+    wcdma_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    gsm_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+    _init_wcdma_bts(wcdma_bts, user_params, CELL_1)
+    _init_gsm_bts(gsm_bts, user_params, CELL_2)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [wcdma_bts, gsm_bts]
+
+def set_system_model_gsm_gsm(anritsu_handle, user_params):
+    """ Configures Anritsu system for GSM and GSM simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Wcdma and Gsm BTS objects
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.GSM, BtsTechnology.GSM)
+    # setting BTS parameters
+    gsm1_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    gsm2_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+    _init_gsm_bts(gsm1_bts, user_params, CELL_1)
+    _init_gsm_bts(gsm2_bts, user_params, CELL_2)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [gsm1_bts, gsm2_bts]
+
+def set_system_model_lte(anritsu_handle, user_params):
+    """ Configures Anritsu system for LTE simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Lte BTS object
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.LTE)
+    # setting BTS parameters
+    lte_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    _init_lte_bts(lte_bts, user_params, CELL_1)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [lte_bts]
+
+
+def set_system_model_wcdma(anritsu_handle, user_params):
+    """ Configures Anritsu system for WCDMA simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Wcdma BTS object
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.WCDMA)
+    # setting BTS parameters
+    wcdma_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    _init_wcdma_bts(wcdma_bts, user_params, CELL_1)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [wcdma_bts]
+
+
+def set_system_model_gsm(anritsu_handle, user_params):
+    """ Configures Anritsu system for GSM simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Gsm BTS object
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.GSM)
+    # setting BTS parameters
+    gsm_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    _init_gsm_bts(gsm_bts, user_params, CELL_1)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [gsm_bts]
+
+
+def set_system_model_1x(anritsu_handle, user_params):
+    """ Configures Anritsu system for CDMA 1X simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Cdma 1x BTS object
+    """
+    PDN_ONE = 1
+    anritsu_handle.set_simulation_model(BtsTechnology.CDMA1X)
+    # setting BTS parameters
+    cdma1x_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    _init_1x_bts(cdma1x_bts, user_params, CELL_1)
+    pdn1 = anritsu_handle.get_PDN(PDN_ONE)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [cdma1x_bts]
+
+
+def set_system_model_1x_evdo(anritsu_handle, user_params):
+    """ Configures Anritsu system for CDMA 1X simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Cdma 1x BTS object
+    """
+    PDN_ONE = 1
+    anritsu_handle.set_simulation_model(BtsTechnology.CDMA1X,
+                                        BtsTechnology.EVDO)
+    # setting BTS parameters
+    cdma1x_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    evdo_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+    _init_1x_bts(cdma1x_bts, user_params, CELL_1)
+    _init_evdo_bts(evdo_bts, user_params, CELL_1)
+    pdn1 = anritsu_handle.get_PDN(PDN_ONE)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDRESS_1)
+    return [cdma1x_bts]
+
+
+def wait_for_bts_state(log, btsnumber, state, timeout=30):
+    """ Waits for BTS to be in the specified state ("IN" or "OUT")
+
+    Args:
+        btsnumber: BTS number.
+        state: expected state
+
+    Returns:
+        True for success False for failure
+    """
+    #  state value are "IN" and "OUT"
+    status = False
+    sleep_interval = 1
+    wait_time = timeout
+
+    if state is "IN":
+        service_state = BtsServiceState.SERVICE_STATE_IN
+    elif state is "OUT":
+        service_state = BtsServiceState.SERVICE_STATE_OUT
+    else:
+       log.info("wrong state value")
+       return status
+
+    if btsnumber.service_state is service_state:
+        log.info("BTS state is already in {}".format(state))
+        return True
+
+    # set to desired service state
+    btsnumber.service_state = service_state
+
+    while wait_time > 0:
+        if service_state == btsnumber.service_state:
+            status = True
+            break
+        time.sleep(sleep_interval)
+        wait_time = wait_time - sleep_interval
+
+    if not status:
+        log.info("Timeout: Expected BTS state is not received.")
+    return status
+
+
+def call_mo_setup_teardown(log, ad, virtual_phone_handle, callee_number,
+        teardown_side=CALL_TEARDOWN_PHONE, emergency=False):
+    """ Makes a MO call and tear down the call
+
+    Args:
+        ad: Android device object.
+        virtual_phone_handle: Anritus virtual phone handle
+        callee_number = Number to be called
+        teardown_side = specifiy the side to end the call (Phone or remote)
+        emergency : specify the call is emergency
+
+    Returns:
+        True for success False for failure
+    """
+    log.info("Making Call to " + callee_number)
+
+    try:
+        if not wait_for_virtualphone_state(log, virtual_phone_handle,
+                                           VirtualPhoneStatus.STATUS_IDLE):
+            raise Exception("Virtual Phone is not in a state to start call")
+
+        if not initiate_call(log, ad, callee_number, emergency):
+            raise Exception("Initiate call failed.")
+
+        # check Virtual phone answered the call
+        if not wait_for_virtualphone_state(log, virtual_phone_handle,
+                     VirtualPhoneStatus.STATUS_VOICECALL_INPROGRESS):
+            raise Exception("Virtual Phone did not answer the call.")
+
+        time.sleep(WAIT_TIME_IN_CALL)
+
+        if not ad.droid.telecomIsInCall():
+            raise Exception(
+                "Call ended before delay_in_call.")
+    except Exception:
+        return False
+
+    if ad.droid.telecomIsInCall():
+        if teardown_side is CALL_TEARDOWN_REMOTE:
+            log.info("Disconnecting the call from Remote")
+            virtual_phone_handle.set_voice_on_hook()
+        else:
+            log.info("Disconnecting the call from Phone")
+            ad.droid.telecomEndCall()
+
+    wait_for_virtualphone_state(log, virtual_phone_handle,
+                                VirtualPhoneStatus.STATUS_IDLE)
+    ensure_phone_idle(log, ad)
+    return True
+
+
+def call_mt_setup_teardown(log, ad, virtual_phone_handle, caller_number=None,
+        teardown_side=CALL_TEARDOWN_PHONE, rat=""):
+    """ Makes a call from Anritsu Virtual phone to device and tear down the call
+
+    Args:
+        ad: Android device object.
+        virtual_phone_handle: Anritus virtual phone handle
+        caller_number =  Caller number
+        teardown_side = specifiy the side to end the call (Phone or remote)
+
+    Returns:
+        True for success False for failure
+    """
+    log.info("Receive MT Call - Making a call to the phone from remote")
+    try:
+        if not wait_for_virtualphone_state(log, virtual_phone_handle,
+                                             VirtualPhoneStatus.STATUS_IDLE):
+            raise Exception("Virtual Phone is not in a state to start call")
+        if caller_number is not None:
+            if rat == RAT_1XRTT:
+                virtual_phone_handle.id_c2k = caller_number
+            else:
+                virtual_phone_handle.id = caller_number
+        virtual_phone_handle.set_voice_off_hook()
+
+        if not wait_and_answer_call(log, ad, caller_number):
+            raise Exception("Answer call Fail")
+
+        time.sleep(WAIT_TIME_IN_CALL)
+
+        if not ad.droid.telecomIsInCall():
+            raise Exception(
+                "Call ended before delay_in_call.")
+    except Exception:
+        return False
+
+    if ad.droid.telecomIsInCall():
+        if teardown_side is CALL_TEARDOWN_REMOTE:
+            log.info("Disconnecting the call from Remote")
+            virtual_phone_handle.set_voice_on_hook()
+        else:
+            log.info("Disconnecting the call from Phone")
+            ad.droid.telecomEndCall()
+
+    wait_for_virtualphone_state(log, virtual_phone_handle,
+                                VirtualPhoneStatus.STATUS_IDLE)
+    ensure_phone_idle(log, ad)
+
+    return True
+
+def wait_for_sms_deliver_success(log, ad, time_to_wait=60):
+    sms_deliver_event = EventSmsDeliverSuccess
+    sleep_interval = 2
+    status = False
+    event = None
+
+    try:
+        event = ad.ed.pop_event(sms_deliver_event, time_to_wait)
+        status = True
+    except Empty:
+        log.info("Timeout: Expected event is not received.")
+    return status
+
+def wait_for_sms_sent_success(log, ad, time_to_wait=60):
+    sms_sent_event = EventSmsSentSuccess
+    sleep_interval = 2
+    status = False
+    event = None
+
+    try:
+        event = ad.ed.pop_event(sms_sent_event, time_to_wait)
+        log.info(event)
+        status = True
+    except Empty:
+        log.info("Timeout: Expected event is not received.")
+    return status
+
+def wait_for_incoming_sms(log, ad, time_to_wait=60):
+    sms_received_event = EventSmsReceived
+    sleep_interval = 2
+    status = False
+    event = None
+
+    try:
+        event = ad.ed.pop_event(sms_received_event, time_to_wait)
+        log.info(event)
+        status = True
+    except Empty:
+        log.info("Timeout: Expected event is not received.")
+    return status, event
+
+def verify_anritsu_received_sms(log, vp_handle, receiver_number, message, rat):
+    if rat == RAT_1XRTT:
+        receive_sms = vp_handle.receiveSms_c2k()
+    else:
+        receive_sms = vp_handle.receiveSms()
+
+    if receive_sms == "NONE":
+        return False
+    split = receive_sms.split('&')
+    text = ""
+    if rat == RAT_1XRTT:
+        #TODO There is some problem when retreiving message with é from Anritsu.
+        #Anritsu receives the message with é.
+        return True
+    for i in range(len(split)):
+        if split[i].startswith('Text='):
+            text = split[i][5:]
+            text = AnritsuUtils.gsm_decode(text)
+            break
+    # TODO Verify Phone number
+    if text != message:
+        log.error("Wrong message received")
+        return False
+    return True
+
+def sms_mo_send(log, ad, vp_handle, receiver_number, message, rat=""):
+    try:
+        if not wait_for_virtualphone_state(log, vp_handle,
+                                           VirtualPhoneStatus.STATUS_IDLE):
+            raise Exception("Virtual Phone is not in a state to receive SMS")
+        log.info("Sending SMS to " + receiver_number)
+        ad.droid.smsSendTextMessage(receiver_number, message, False)
+        log.info("Waiting for SMS sent event")
+        test_status = wait_for_sms_sent_success(log, ad)
+        if not test_status:
+            raise Exception("Failed to send SMS")
+        if not verify_anritsu_received_sms(log, vp_handle,receiver_number, message, rat):
+            raise Exception("Anritsu didn't receive message")
+    except Exception as e:
+        log.error("Exception :" + str(e))
+        return False
+    return True
+
+def sms_mt_receive_verify(log, ad, vp_handle, sender_number, message, rat=""):
+    ad.droid.smsStartTrackingIncomingMessage()
+    try:
+        if not wait_for_virtualphone_state(log, vp_handle,
+                                           VirtualPhoneStatus.STATUS_IDLE):
+            raise Exception("Virtual Phone is not in a state to receive SMS")
+        log.info("Waiting for Incoming SMS from " + sender_number)
+        if rat == RAT_1XRTT:
+            vp_handle.sendSms_c2k(sender_number,  message)
+        else:
+            vp_handle.sendSms(sender_number,  message)
+        test_status, event = wait_for_incoming_sms(log, ad)
+        if not test_status:
+            raise Exception("Failed to receive SMS")
+        log.info("Incoming SMS: Sender " + event['data']['Sender'])
+        log.info("Incoming SMS: Message " + event['data']['Text'])
+        if event['data']['Sender'] != sender_number:
+            raise Exception("Wrong sender Number")
+        if event['data']['Text'] != message:
+            raise Exception("Wrong message")
+    except Exception as e:
+        log.error("exception: " + str(e))
+        return False
+    finally:
+        ad.droid.smsStopTrackingIncomingMessage()
+    return True
+
+def wait_for_virtualphone_state(log, vp_handle, state, timeout=30):
+    """ Waits for Anritsu Virtual phone to be in expected state
+
+    Args:
+        ad: Android device object.
+        vp_handle: Anritus virtual phone handle
+        state =  expected state
+
+    Returns:
+        True for success False for failure
+    """
+    status = False
+    sleep_interval = 1
+    wait_time = timeout
+    while wait_time > 0:
+        if vp_handle.status == state:
+            log.info(vp_handle.status)
+            status = True
+            break
+        time.sleep(sleep_interval)
+        wait_time = wait_time - sleep_interval
+
+    if not status:
+        log.info("Timeout: Expected state is not received.")
+    return status
+
+# There is a difference between CMAS/ETWS message formation in LTE/WCDMA and CDMA 1X
+# LTE and CDMA : 3GPP
+# CDMA 1X: 3GPP2
+# hence different functions
+def cmas_receive_verify_message_lte_wcdma(log, ad, anritsu_handle, serial_number,
+        message_id, warning_message):
+    """ Makes Anritsu to send a CMAS message and phone and verifies phone
+        receives the message on LTE/WCDMA
+
+    Args:
+        ad: Android device object.
+        anritsu_handle: Anritus device object
+        serial_number =  serial number of CMAS message
+        message_id =  CMAS message ID
+        warning_message =  CMAS warning message
+
+    Returns:
+        True for success False for failure
+    """
+    status = False
+    event = None
+    ad.droid.smsStartTrackingGsmEmergencyCBMessage()
+    anritsu_handle.send_cmas_lte_wcdma(hex(serial_number), message_id,
+                                       warning_message)
+    try:
+        log.info("Waiting for CMAS Message")
+        event = ad.ed.pop_event(EventCmasReceived, 60)
+        status = True
+        log.info(event)
+        if warning_message != event['data']['message']:
+            log.info("Wrong warning messgae received")
+            status = False
+        if message_id != hex(event['data']['serviceCategory']):
+            log.info("Wrong warning messgae received")
+            status = False
+    except Empty:
+        log.info("Timeout: Expected event is not received.")
+
+    ad.droid.smsStopTrackingGsmEmergencyCBMessage()
+    return status
+
+
+def cmas_receive_verify_message_cdma1x(log, ad, anritsu_handle,  message_id,
+        service_category, alert_text,
+        response_type=CMAS_C2K_RESPONSETYPE_SHELTER,
+        severity=CMAS_C2K_SEVERITY_EXTREME,
+        urgency=CMAS_C2K_URGENCY_IMMEDIATE,
+        certainty=CMAS_C2K_CERTIANTY_OBSERVED):
+    """ Makes Anritsu to send a CMAS message and phone and verifies phone
+        receives the message on CDMA 1X
+
+    Args:
+        ad: Android device object.
+        anritsu_handle: Anritus device object
+        serial_number =  serial number of CMAS message
+        message_id =  CMAS message ID
+        warning_message =  CMAS warning message
+
+    Returns:
+        True for success False for failure
+    """
+    status = False
+    event = None
+    ad.droid.smsStartTrackingCdmaEmergencyCBMessage()
+    anritsu_handle.send_cmas_etws_cdma1x(message_id, service_category, alert_text,
+        response_type, severity, urgency, certainty)
+    try:
+        log.info("Waiting for CMAS Message")
+        event = ad.ed.pop_event(EventCmasReceived, 60)
+        status = True
+        log.info(event)
+        if alert_text != event['data']['message']:
+            log.info("Wrong alert messgae received")
+            status = False
+
+        if event['data']['cmasResponseType'].lower() != response_type.lower():
+            log.info("Wrong response type received")
+            status = False
+
+        if event['data']['cmasUrgency'].lower() != urgency.lower():
+            log.info("Wrong cmasUrgency received")
+            status = False
+
+        if event['data']['cmasSeverity'].lower() != severity.lower():
+            Log.info("Wrong cmasSeverity received")
+            status = False
+    except Empty:
+        log.info("Timeout: Expected event is not received.")
+
+    ad.droid.smsStopTrackingCdmaEmergencyCBMessage()
+    return status
+
+
+def etws_receive_verify_message_lte_wcdma(log, ad, anritsu_handle,  serial_number,
+        message_id, warning_message):
+    """ Makes Anritsu to send a ETWS message and phone and verifies phone
+        receives the message on LTE/WCDMA
+
+    Args:
+        ad: Android device object.
+        anritsu_handle: Anritus device object
+        serial_number =  serial number of ETWS message
+        message_id =  ETWS message ID
+        warning_message =  ETWS warning message
+
+    Returns:
+        True for success False for failure
+    """
+    status = False
+    event = None
+    if message_id == ETWS_WARNING_EARTHQUAKE:
+        warning_type = "Earthquake"
+    elif message_id == ETWS_WARNING_EARTHQUAKETSUNAMI:
+        warning_type = "EarthquakeandTsunami"
+    elif message_id == ETWS_WARNING_TSUNAMI:
+        warning_type = "Tsunami"
+    elif message_id == ETWS_WARNING_TEST_MESSAGE:
+        warning_type = "test"
+    elif message_id == ETWS_WARNING_OTHER_EMERGENCY:
+        warning_type = "other"
+    ad.droid.smsStartTrackingGsmEmergencyCBMessage()
+    anritsu_handle.send_etws_lte_wcdma(hex(serial_number), message_id,
+                                       warning_type,
+                                       warning_message, "ON", "ON")
+    try:
+        log.info("Waiting for ETWS Message")
+        event = ad.ed.pop_event(EventEtwsReceived, 60)
+        status = True
+        log.info(event)
+        #TODO Event data verification
+    except Empty:
+        log.info("Timeout: Expected event is not received.")
+
+    ad.droid.smsStopTrackingGsmEmergencyCBMessage()
+    return status
+
+
+def etws_receive_verify_message_cdma1x(log, ad, anritsu_handle,  serial_number,
+        message_id, warning_message):
+    """ Makes Anritsu to send a ETWS message and phone and verifies phone
+        receives the message on CDMA1X
+
+    Args:
+        ad: Android device object.
+        anritsu_handle: Anritus device object
+        serial_number =  serial number of ETWS message
+        message_id =  ETWS message ID
+        warning_message =  ETWS warning message
+
+    Returns:
+        True for success False for failure
+    """
+    status = False
+    event = None
+    #TODO
+    return status
+
+def read_ue_identity(log, ad, anritsu_handle, identity_type):
+    """ Get the UE identity IMSI, IMEI, IMEISV
+
+    Args:
+        ad: Android device object.
+        anritsu_handle: Anritus device object
+        identity_type: Identity type(IMSI/IMEI/IMEISV)
+
+    Returns:
+        Requested Identity value
+    """
+    return anritsu_handle.get_ue_identity(identity_type)
+
+def get_lte_band(user_params, cell_no):
+    """ Returns the LTE BAND to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        LTE BAND to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_lte_band".format(cell_no)
+    try:
+        lte_band = user_params[key]
+    except KeyError:
+        lte_band = LTE_BAND_2
+    return lte_band
+
+
+def get_wcdma_band(user_params, cell_no):
+    """ Returns the WCDMA BAND to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        WCDMA BAND to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_wcdma_band".format(cell_no)
+    try:
+        wcdma_band = user_params[key]
+    except KeyError:
+        wcdma_band = WCDMA_BAND_1
+    return wcdma_band
+
+
+def get_gsm_band(user_params, cell_no):
+    """ Returns the GSM BAND to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        GSM BAND to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_gsm_band".format(cell_no)
+    try:
+        gsm_band = user_params[key]
+    except KeyError:
+        gsm_band = GSM_BAND_GSM850
+    return gsm_band
+
+
+def get_1x_band(user_params, cell_no):
+    """ Returns the 1X BAND to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        1X BAND to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_1x_band".format(cell_no)
+    try:
+        cdma_1x_band = user_params[key]
+    except KeyError:
+        cdma_1x_band = CDMA_1X_BAND_0
+    return cdma_1x_band
+
+def get_wcdma_rac(user_params, cell_no):
+    """ Returns the WCDMA RAC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        WCDMA RAC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_wcdma_rac".format(cell_no)
+    try:
+        wcdma_rac = user_params[key]
+    except KeyError:
+        wcdma_rac = DEFAULT_RAC
+    return wcdma_rac
+
+
+def get_gsm_rac(user_params, cell_no):
+    """ Returns the GSM RAC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        GSM RAC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_gsm_rac".format(cell_no)
+    try:
+        gsm_rac = user_params[key]
+    except KeyError:
+        gsm_rac = DEFAULT_RAC
+    return gsm_rac
+
+
+def get_wcdma_lac(user_params, cell_no):
+    """ Returns the WCDMA LAC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        WCDMA LAC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_wcdma_lac".format(cell_no)
+    try:
+        wcdma_lac = user_params[key]
+    except KeyError:
+        wcdma_lac = DEFAULT_LAC
+    return wcdma_lac
+
+
+def get_gsm_lac(user_params, cell_no):
+    """ Returns the GSM LAC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        GSM LAC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_gsm_lac".format(cell_no)
+    try:
+        gsm_lac = user_params[key]
+    except KeyError:
+        gsm_lac = DEFAULT_LAC
+    return gsm_lac
+
+
+def get_lte_mcc(user_params, cell_no):
+    """ Returns the LTE MCC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        LTE MCC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_lte_mcc".format(cell_no)
+    try:
+        lte_mcc = user_params[key]
+    except KeyError:
+        lte_mcc = DEFAULT_MCC
+    return lte_mcc
+
+
+def get_lte_mnc(user_params, cell_no):
+    """ Returns the LTE MNC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        LTE MNC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_lte_mnc".format(cell_no)
+    try:
+        lte_mnc = user_params[key]
+    except KeyError:
+        lte_mnc = DEFAULT_MNC
+    return lte_mnc
+
+
+def get_wcdma_mcc(user_params, cell_no):
+    """ Returns the WCDMA MCC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        WCDMA MCC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_wcdma_mcc".format(cell_no)
+    try:
+        wcdma_mcc = user_params[key]
+    except KeyError:
+        wcdma_mcc = DEFAULT_MCC
+    return wcdma_mcc
+
+
+def get_wcdma_mnc(user_params, cell_no):
+    """ Returns the WCDMA MNC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        WCDMA MNC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_wcdma_mnc".format(cell_no)
+    try:
+        wcdma_mnc = user_params[key]
+    except KeyError:
+        wcdma_mnc = DEFAULT_MNC
+    return wcdma_mnc
+
+
+def get_gsm_mcc(user_params, cell_no):
+    """ Returns the GSM MCC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        GSM MCC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_gsm_mcc".format(cell_no)
+    try:
+        gsm_mcc = user_params[key]
+    except KeyError:
+        gsm_mcc = DEFAULT_MCC
+    return gsm_mcc
+
+
+def get_gsm_mnc(user_params, cell_no):
+    """ Returns the GSM MNC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        GSM MNC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_gsm_mnc".format(cell_no)
+    try:
+        gsm_mnc = user_params[key]
+    except KeyError:
+        gsm_mnc = DEFAULT_MNC
+    return gsm_mnc
+
+
+def get_1x_mcc(user_params, cell_no):
+    """ Returns the 1X MCC to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        1X MCC to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_1x_mcc".format(cell_no)
+    try:
+        cdma_1x_mcc = user_params[key]
+    except KeyError:
+        cdma_1x_mcc = DEFAULT_MCC
+    return cdma_1x_mcc
+
+def get_1x_channel(user_params, cell_no):
+    """ Returns the 1X Channel to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        1X Channel to be used
+    """
+    key = "cell{}_1x_channel".format(cell_no)
+    try:
+        cdma_1x_channel = user_params[key]
+    except KeyError:
+        cdma_1x_channel = CDMA1X_CHANNEL_356
+    return cdma_1x_channel
+
+
+def get_1x_sid(user_params, cell_no):
+    """ Returns the 1X SID to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        1X SID to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_1x_sid".format(cell_no)
+    try:
+        cdma_1x_sid = user_params[key]
+    except KeyError:
+        cdma_1x_sid = CDMA1X_SID_0
+    return cdma_1x_sid
+
+
+def get_1x_nid(user_params, cell_no):
+    """ Returns the 1X NID to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        1X NID to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    key = "cell{}_1x_nid".format(cell_no)
+    try:
+        cdma_1x_nid = user_params[key]
+    except KeyError:
+        cdma_1x_nid = CDMA1X_NID_65535
+    return cdma_1x_nid
+
+
+def get_csfb_type(user_params):
+    """ Returns the CSFB Type to be used from the user specified parameters
+        or default value
+
+    Args:
+        user_params: pointer to user supplied parameters
+        cell_no: specify the cell number this BTS is configured
+        Anritsu supports two cells. so cell_1 or cell_2
+
+    Returns:
+        CSFB Type to be used
+    """
+    #TODO : Need to re look in the logic of passing parameters to test case
+    try:
+        csfb_type = user_params["csfb_type"]
+    except KeyError:
+        csfb_type = CsfbType.CSFB_TYPE_REDIRECTION
+    return csfb_type
diff --git a/acts/framework/acts/test_utils/tel/tel_test_utils.py b/acts/framework/acts/test_utils/tel/tel_test_utils.py
new file mode 100644
index 0000000..d884478
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/tel_test_utils.py
@@ -0,0 +1,3004 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import concurrent.futures
+import random
+import string
+import urllib.parse
+import time
+import warnings
+
+from queue import Empty
+from .tel_defines import *
+from .tel_lookup_tables import *
+from acts.event_dispatcher import EventDispatcher
+from acts.utils import load_config
+from acts.controllers.android_device import AndroidDevice
+from acts.logger import LoggerProxy
+log = LoggerProxy()
+
+class TelTestUtilsError(Exception):
+    pass
+
+def setup_droid_properties(log, ad, sim_filename):
+
+    # Check to see if droid already has this property
+    if hasattr(ad, 'cfg'):
+        return
+
+    device_props = {}
+    device_props['subscription'] = {}
+
+    try:
+        sim_data = load_config(sim_filename)
+    except Exception:
+        log.warning("Failed to load {}!".format(sim_filename))
+        sim_data = None
+    sub_info_list = ad.droid.subscriptionGetAllSubInfoList()
+    found_sims = 0
+    for sub_info in sub_info_list:
+        sub_id = sub_info['subscriptionId']
+        if sub_info['simSlotIndex'] is not INVALID_SIM_SLOT_INDEX:
+            found_sims += 1
+            sim_record = {}
+            try:
+                sim_serial = ad.droid.getSimSerialNumberForSubscription(sub_id)
+                if not sim_serial:
+                    log.error("Unable to find ICC-ID for SIM on {}!".format(
+                        ad.serial))
+                if sim_data is not None:
+                    number = sim_data[sim_serial]["phone_num"]
+                else:
+                    raise KeyError("No file to load phone number info!")
+            except KeyError:
+                number = ad.droid.getLine1NumberForSubscription(sub_id)
+            if not number or number == "":
+                raise TelTestUtilsError(
+                    "Failed to find valid phone number for {}"
+                    .format(ad.serial))
+
+            sim_record['phone_num'] = number
+            sim_record['operator'] = get_operator_name(log, ad, sub_id)
+            device_props['subscription'][sub_id] = sim_record
+            log.info("phone_info: <{}:{}>, <subId:{}> {} <{}>, ICC-ID:<{}>".
+                     format(ad.model, ad.serial, sub_id, number,
+                            get_operator_name(log, ad, sub_id),
+                            ad.droid.getSimSerialNumberForSubscription(sub_id)))
+
+    if found_sims == 0:
+        log.warning("No Valid SIMs found in device {}".format(ad.serial))
+
+    setattr(ad, 'cfg', device_props)
+
+def get_subid_from_slot_index(log, ad, sim_slot_index):
+    """ Get the subscription ID for a SIM at a particular slot
+
+    Args:
+        ad: android_device object.
+
+    Returns:
+        result: Subscription ID
+    """
+    subInfo = ad.droid.subscriptionGetAllSubInfoList()
+    for info in subInfo:
+        if info['simSlotIndex'] == sim_slot_index:
+            return info['subscriptionId']
+    return INVALID_SUB_ID
+
+def get_num_active_sims(log, ad):
+    """ Get the number of active SIM cards by counting slots
+
+    Args:
+        ad: android_device object.
+
+    Returns:
+        result: The number of loaded (physical) SIM cards
+    """
+    # using a dictionary as a cheap way to prevent double counting
+    # in the situation where multiple subscriptions are on the same SIM.
+    # yes, this is a corner corner case.
+    valid_sims = {}
+    subInfo = ad.droid.subscriptionGetAllSubInfoList()
+    for info in subInfo:
+        ssidx = info['simSlotIndex']
+        if ssidx == INVALID_SIM_SLOT_INDEX:
+            continue
+        valid_sims[ssidx] = True
+    return len(valid_sims.keys())
+
+def toggle_airplane_mode(log, ad, new_state=None):
+    """ Toggle the state of airplane mode.
+
+    Args:
+        ad: android_device object.
+        new_state: Airplane mode state to set to.
+            If None, opposite of the current state.
+
+    Returns:
+        result: True if operation succeed. False if error happens.
+    """
+    return toggle_airplane_mode_msim(log, ad, new_state)
+
+def is_expected_event(event_to_check, events_list):
+    """ check whether event is present in the event list
+
+    Args:
+        event_to_check: event to be checked.
+        events_list: list of events
+    Returns:
+        result: True if event present in the list. False if not.
+    """
+    for event in events_list:
+        if event in event_to_check['name']:
+            return True
+    return False
+
+def is_sim_ready(log, ad, sim_slot_id=None):
+    """ check whether SIM is ready.
+
+    Args:
+        ad: android_device object.
+        sim_slot_id: check the SIM status for sim_slot_id
+            This is optional. If this is None, check default SIM.
+
+    Returns:
+        result: True if all SIMs are ready. False if not.
+    """
+    if sim_slot_id is None:
+        status = ad.droid.getSimState()
+    else:
+        status = ad.droid.getSimStateForSlotId(sim_slot_id)
+    if status != SIM_STATE_READY:
+       log.info("Sim not ready")
+       return False
+    return True
+
+def _is_expecting_event(event_recv_list):
+    """ check for more event is expected in event list
+
+    Args:
+        event_recv_list: list of events
+    Returns:
+        result: True if more events are expected. False if not.
+    """
+    for state in event_recv_list:
+        if state is False:
+           return True
+    return False
+
+def _set_event_list(event_recv_list , sub_id_list, sub_id, value):
+    """ set received event in expected event list
+
+    Args:
+        event_recv_list: list of received events
+        sub_id_list: subscription ID list
+        sub_id: subscription id of current event
+        value: True or False
+    Returns:
+        None.
+    """
+    for i in range(len(sub_id_list)):
+        if sub_id_list[i] == sub_id:
+            event_recv_list[i] = value
+
+def toggle_airplane_mode_msim(log, ad, new_state=None):
+    """ Toggle the state of airplane mode.
+
+    Args:
+        ad: android_device object.
+        new_state: Airplane mode state to set to.
+            If None, opposite of the current state.
+
+    Returns:
+        result: True if operation succeed. False if error happens.
+    """
+    serial_number = ad.serial
+
+    ad.ed.clear_all_events()
+    sub_id_list = []
+
+    active_sub_info = ad.droid.subscriptionGetAllSubInfoList()
+    for info in active_sub_info :
+        sub_id_list.append(info['subscriptionId'])
+
+    cur_state = ad.droid.connectivityCheckAirplaneMode()
+    if cur_state == new_state:
+        log.info("Airplane mode already <{}> on {}".format(new_state,
+                                                            serial_number))
+        return True
+    elif new_state is None:
+        log.info("Current State {} New state {}".format(cur_state, new_state))
+
+    if new_state is None:
+        new_state = not cur_state
+
+    sub_event_name_list = []
+    if new_state:
+        sub_event_name_list.append(SERVICE_STATE_POWER_OFF)
+        log.info("Turn on airplane mode: " + serial_number)
+
+    else:
+        # If either one of these 3 events show up, it should be OK.
+        # Normal SIM, phone in service
+        sub_event_name_list.append(SERVICE_STATE_IN_SERVICE)
+        # NO SIM, or Dead SIM, or no Roaming coverage.
+        sub_event_name_list.append(SERVICE_STATE_OUT_OF_SERVICE)
+        sub_event_name_list.append(SERVICE_STATE_EMERGENCY_ONLY)
+        log.info("Turn off airplane mode: " + serial_number)
+
+    for sub_id in sub_id_list:
+        ad.droid.phoneStartTrackingServiceStateChangeForSubscription(sub_id)
+    ad.droid.connectivityToggleAirplaneMode(new_state)
+
+    event = None
+
+    try:
+        try:
+            event = ad.ed.wait_for_event(EventServiceStateChanged,
+                is_sub_event_match_for_sub_event_list,
+                timeout=WAIT_TIME_AIRPLANEMODE_EVENT,
+                sub_event_list=sub_event_name_list)
+        except Empty:
+            pass
+        if event is None:
+            log.error("Did not get expected sub_event {}".
+                format(sub_event_name_list))
+        log.info("Received event: {}".format(event))
+    finally:
+        for sub_id in sub_id_list:
+            ad.droid.phoneStopTrackingServiceStateChangeForSubscription(sub_id)
+
+    if new_state:
+        if (not ad.droid.connectivityCheckAirplaneMode() or
+                ad.droid.wifiCheckState() or
+                ad.droid.bluetoothCheckState()):
+            log.error("Airplane mode ON fail on {}".format(ad.serial))
+            return False
+    else:
+        if ad.droid.connectivityCheckAirplaneMode():
+            log.error("Airplane mode OFF fail on {}".format(ad.serial))
+            return False
+    return True
+
+def wait_and_answer_call(log, ad, incoming_number=None,
+                         delay_answer=WAIT_TIME_ANSWER_CALL,
+                         incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+    """Wait for an incoming call on default voice subscription and
+       accepts the call.
+
+    Args:
+        ad: android device object.
+        incoming_number: Expected incoming number.
+            Optional. Default is None
+        delay_answer: time to wait before answering the call
+            Optional. Default is 1
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+
+    Returns:
+        True: if incoming call is received and answered successfully.
+        False: for errors
+        """
+    return wait_and_answer_call_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId(),
+        incoming_number, delay_answer, incall_ui_display)
+
+def wait_for_ringing_event(log, ad, wait_time):
+    """Wait for ringing event.
+
+    Args:
+        log: log object.
+        ad: android device object.
+        wait_time: max time to wait for ringing event.
+
+    Returns:
+        event_ringing if received ringing event.
+        otherwise return None.
+    """
+    log.info("Wait for ringing.")
+    start_time = time.time()
+    remaining_time = wait_time
+    event_iter_timeout = 4
+    event_ringing = None
+
+    while remaining_time > 0:
+        try:
+            event_ringing =  ad.ed.wait_for_event(EventCallStateChanged,
+                             is_sub_event_match,
+                             timeout=event_iter_timeout,
+                             sub_event=TELEPHONY_STATE_RINGING)
+        except Empty:
+            if ad.droid.telecomIsRinging():
+                log.error("No Ringing event. But Callee in Ringing state.")
+                log.error("Test framework dropped event.")
+                return None
+        remaining_time = start_time + wait_time - time.time()
+        if event_ringing is not None:
+            break
+    if event_ringing is None:
+        log.error("No Ringing Event, Callee not in ringing state.")
+        log.error("No incoming call.")
+        return None
+
+    return event_ringing
+
+def wait_and_answer_call_for_subscription(log, ad, sub_id, incoming_number=None,
+                                          delay_answer=WAIT_TIME_ANSWER_CALL,
+                                          incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+    """Wait for an incoming call on specified subscription and
+       accepts the call.
+
+    Args:
+        ad: android device object.
+        sub_id: subscription ID
+        incoming_number: Expected incoming number.
+            Optional. Default is None
+        delay_answer: time to wait before answering the call
+            Optional. Default is 1
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+
+    Returns:
+        True: if incoming call is received and answered successfully.
+        False: for errors
+    """
+    ad.ed.clear_all_events()
+    ad.droid.phoneStartTrackingCallStateForSubscription(sub_id)
+    if (not ad.droid.telecomIsRinging() and
+            ad.droid.getCallStateForSubscription(sub_id) != TELEPHONY_STATE_RINGING):
+        try:
+            event_ringing = wait_for_ringing_event(log, ad,
+                                                   WAIT_TIME_CALLEE_RINGING)
+            if event_ringing is None:
+                log.error("No Ringing Event.")
+                return False
+        finally:
+            ad.droid.phoneStopTrackingCallStateChangeForSubscription(sub_id)
+
+        if not incoming_number:
+            result = True
+        else:
+            result = check_phone_number_match(event_ringing['data']['incomingNumber'],
+                                              incoming_number)
+
+        if not result:
+            log.error("Incoming Number not match")
+            log.error("Expected number:{}, actual number:{}".
+                      format(incoming_number,
+                             event_ringing['data']['incomingNumber']))
+            return False
+
+    ad.ed.clear_all_events()
+    ad.droid.phoneStartTrackingCallStateForSubscription(sub_id)
+    # Delay between ringing and answer.
+    time.sleep(delay_answer)
+
+    log.info("Accept on callee.")
+    ad.droid.telecomAcceptRingingCall()
+    try:
+        ad.ed.wait_for_event(EventCallStateChanged,
+                             is_sub_event_match,
+                             timeout=WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
+                             sub_event=TELEPHONY_STATE_OFFHOOK)
+    except Empty:
+        if not ad.droid.telecomIsInCall():
+            log.error("Accept call failed.")
+            return False
+    finally:
+        ad.droid.phoneStopTrackingCallStateChangeForSubscription(sub_id)
+    if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
+        ad.droid.telecomShowInCallScreen()
+    elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
+        ad.droid.showHomeScreen()
+    return True
+
+def wait_and_reject_call(log, ad, incoming_number=None,
+                         delay_reject=WAIT_TIME_REJECT_CALL):
+    """Wait for an incoming call on default voice subscription and
+       reject the call.
+
+    Args:
+        ad: android device object.
+        incoming_number: Expected incoming number.
+            Optional. Default is None
+        delay_reject: time to wait before rejecting the call
+            Optional. Default is WAIT_TIME_REJECT_CALL
+
+    Returns:
+        True: if incoming call is received and reject successfully.
+        False: for errors
+    """
+    return wait_and_reject_call_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId(),
+        incoming_number, delay_reject)
+
+def wait_and_reject_call_for_subscription(log, ad, sub_id, incoming_number=None,
+                                          delay_reject=WAIT_TIME_REJECT_CALL):
+    """Wait for an incoming call on specific subscription and
+       reject the call.
+
+    Args:
+        ad: android device object.
+        sub_id: subscription ID
+        incoming_number: Expected incoming number.
+            Optional. Default is None
+        delay_reject: time to wait before rejecting the call
+            Optional. Default is WAIT_TIME_REJECT_CALL
+
+    Returns:
+        True: if incoming call is received and reject successfully.
+        False: for errors
+    """
+    ad.ed.clear_all_events()
+    ad.droid.phoneStartTrackingCallStateForSubscription(sub_id)
+    if (not ad.droid.telecomIsRinging() and
+            ad.droid.getCallStateForSubscription(sub_id) != TELEPHONY_STATE_RINGING):
+        try:
+            event_ringing = wait_for_ringing_event(log, ad,
+                                                   WAIT_TIME_CALLEE_RINGING)
+            if event_ringing is None:
+                log.error("No Ringing Event.")
+                return False
+        finally:
+            ad.droid.phoneStopTrackingCallStateChangeForSubscription(sub_id)
+
+        if not incoming_number:
+            result = True
+        else:
+            result = check_phone_number_match(event_ringing['data']['incomingNumber'],
+                                              incoming_number)
+
+        if not result:
+            log.error("Incoming Number not match")
+            log.error("Expected number:{}, actual number:{}".
+                      format(incoming_number,
+                             event_ringing['data']['incomingNumber']))
+            return False
+
+    ad.ed.clear_all_events()
+    ad.droid.phoneStartTrackingCallStateForSubscription(sub_id)
+    # Delay between ringing and reject.
+    time.sleep(delay_reject)
+
+    log.info("Reject on callee.")
+    ad.droid.telecomEndCall()
+    try:
+        ad.ed.wait_for_event(EventCallStateChanged,
+                             is_sub_event_match,
+                             timeout=WAIT_TIME_HANGUP_TO_IDLE_EVENT,
+                             sub_event=TELEPHONY_STATE_IDLE)
+    except Empty:
+        log.error("No onCallStateChangedIdle event received.")
+        return False
+    finally:
+        ad.droid.phoneStopTrackingCallStateChangeForSubscription(sub_id)
+    return True
+
+
+def hangup_call(log, ad):
+    """Hang up ongoing active call.
+    """
+    ad.ed.clear_all_events()
+    ad.droid.phoneStartTrackingCallState()
+    log.info("Hangup call.")
+    ad.droid.telecomEndCall()
+
+    try:
+        ad.ed.wait_for_event(EventCallStateChanged,
+                             is_sub_event_match,
+                             timeout=WAIT_TIME_HANGUP_TO_IDLE_EVENT,
+                             sub_event=TELEPHONY_STATE_IDLE)
+    except Empty:
+        if ad.droid.telecomIsInCall():
+            log.error("Hangup call failed.")
+            return False
+    finally:
+        ad.droid.phoneStopTrackingCallStateChange()
+    return True
+
+def disconnect_call_by_id(log, ad, call_id):
+    """Disconnect call by call id.
+    """
+    ad.droid.telecomCallDisconnect(call_id)
+    return True
+
+def check_phone_number_match(number1, number2):
+    """Check whether two input phone numbers match or not.
+
+    Compare the two input phone numbers.
+    If they match, return True; otherwise, return False.
+    Currently only handle phone number with the following formats:
+        (US phone number format)
+        +1abcxxxyyyy
+        1abcxxxyyyy
+        abcxxxyyyy
+        abc xxx yyyy
+        abc.xxx.yyyy
+        abc-xxx-yyyy
+
+    Args:
+        number1: 1st phone number to be compared.
+        number2: 2nd phone number to be compared.
+
+    Returns:
+        True if two phone numbers match. Otherwise False.
+    """
+    # Remove "1"  or "+1"from front
+    if number1[0] == "1":
+        number1 = number1[1:]
+    elif number1[0:2] == "+1":
+        number1 = number1[2:]
+    if number2[0] == "1":
+        number2 = number2[1:]
+    elif number2[0:2] == "+1":
+        number2 = number2[2:]
+    # Remove white spaces, dashes, dots
+    number1 = number1.replace(" ", "").replace("-", "").replace(".", "")
+    number2 = number2.replace(" ", "").replace("-", "").replace(".", "")
+    return number1 == number2
+
+
+def initiate_call(log, ad_caller, callee_number, emergency=False):
+    """Make phone call from caller to callee.
+
+    Args:
+        ad_caller: Caller android device object.
+        callee_number: Callee phone number.
+        emergency : specify the call is emergency.
+            Optional. Default value is False.
+
+    Returns:
+        result: if phone call is placed successfully.
+    """
+    ad_caller.ed.clear_all_events()
+    sub_id = ad_caller.droid.subscriptionGetDefaultVoiceSubId()
+    ad_caller.droid.phoneStartTrackingCallStateForSubscription(sub_id)
+
+    wait_time_for_incall_state = WAIT_TIME_CALL_INITIATION
+
+    try:
+        # Make a Call
+        if emergency:
+            ad_caller.droid.phoneCallEmergencyNumber(callee_number)
+        else:
+            ad_caller.droid.phoneCallNumber(callee_number)
+
+        # Verify OFFHOOK event
+        if ad_caller.droid.getCallState() != TELEPHONY_STATE_OFFHOOK:
+            event_offhook =  ad_caller.ed.wait_for_event(EventCallStateChanged,
+                is_sub_event_match, timeout=wait_time_for_incall_state,
+                sub_event=TELEPHONY_STATE_OFFHOOK)
+    except Empty:
+        log.error("initiate_call did not receive Telephony OFFHOOK event.")
+        return False
+    finally:
+        ad_caller.droid.phoneStopTrackingCallStateChangeForSubscription(sub_id)
+
+    # Verify call state
+    while wait_time_for_incall_state > 0:
+        wait_time_for_incall_state -= 1
+        if (ad_caller.droid.telecomIsInCall() and
+            (ad_caller.droid.getCallState() == TELEPHONY_STATE_OFFHOOK) and
+            (ad_caller.droid.telecomGetCallState() == TELEPHONY_STATE_OFFHOOK)):
+            return True
+        time.sleep(1)
+    log.error("Make call fail. telecomIsInCall:{}, Telecom State:{},"
+        " Telephony State:{}".format(ad_caller.droid.telecomIsInCall(),
+        ad_caller.droid.getCallState(), ad_caller.droid.telecomGetCallState()))
+    return False
+
+def call_reject_leave_message(log, ad_caller, ad_callee,
+       verify_caller_func=None,
+       wait_time_in_call=WAIT_TIME_TO_LEAVE_VOICE_MAIL):
+    """On default voice subscription, Call from caller to callee,
+    reject on callee, caller leave a voice mail.
+
+    1. Caller call Callee.
+    2. Callee reject incoming call.
+    3. Caller leave a voice mail.
+    4. Verify callee received the voice mail notification.
+
+    Args:
+        ad_caller: caller android device object.
+        ad_callee: callee android device object.
+        verify_caller_func: function to verify caller is in correct state while in-call.
+            This is optional, default is None.
+        wait_time_in_call: time to wait when leaving a voice mail.
+            This is optional, default is WAIT_TIME_TO_LEAVE_VOICE_MAIL
+
+    Returns:
+        True: if voice message is received on callee successfully.
+        False: for errors
+    """
+    subid_caller = ad_caller.droid.subscriptionGetDefaultVoiceSubId()
+    subid_callee = ad_callee.droid.subscriptionGetDefaultVoiceSubId()
+    return call_reject_leave_message_for_subscription(log, ad_caller, ad_callee,
+                                                      subid_caller, subid_callee,
+                                                      verify_caller_func,
+                                                      wait_time_in_call)
+
+def call_reject_leave_message_for_subscription(log, ad_caller, ad_callee,
+                                               subid_caller, subid_callee,
+                                               verify_caller_func=None,
+                                               wait_time_in_call=WAIT_TIME_TO_LEAVE_VOICE_MAIL):
+    """On specific voice subscription, Call from caller to callee,
+    reject on callee, caller leave a voice mail.
+
+    1. Caller call Callee.
+    2. Callee reject incoming call.
+    3. Caller leave a voice mail.
+    4. Verify callee received the voice mail notification.
+
+    Args:
+        ad_caller: caller android device object.
+        ad_callee: callee android device object.
+        subid_caller: caller's subscription id.
+        subid_callee: callee's subscription id.
+        verify_caller_func: function to verify caller is in correct state while in-call.
+            This is optional, default is None.
+        wait_time_in_call: time to wait when leaving a voice mail.
+            This is optional, default is WAIT_TIME_TO_LEAVE_VOICE_MAIL
+
+    Returns:
+        True: if voice message is received on callee successfully.
+        False: for errors
+    """
+    class _CallSequenceException(Exception):
+        pass
+    # Currently this test utility only works for TMO and ATT and SPT.
+    # It does not work for VZW (see b/21559800)
+    # "with VVM TelephonyManager APIs won't work for vm"
+
+    caller_number = ad_caller.cfg['subscription'][subid_caller]['phone_num']
+    callee_number = ad_callee.cfg['subscription'][subid_callee]['phone_num']
+
+    log.info("Call from {} to {}".format(caller_number, callee_number))
+
+    try:
+
+        if not initiate_call(log, ad_caller, callee_number):
+            raise _CallSequenceException("Initiate call failed.")
+
+        if not wait_and_reject_call_for_subscription(
+                log, ad_callee, subid_callee, incoming_number=caller_number):
+            raise _CallSequenceException("Reject call fail.")
+
+        ad_callee.droid.phoneStartTrackingVoiceMailStateChangeForSubscription(
+            subid_callee)
+        voice_mail_count_before = ad_callee.droid.getVoiceMailCountForSubscription(
+            subid_callee)
+
+        # -1 means there are unread voice mail, but the count is unknown
+        # 0 means either this API not working (VZW) or no unread voice mail.
+        if voice_mail_count_before != 0:
+            log.warning("--Pending new Voice Mail, please clear on phone.--")
+
+        # ensure that all internal states are updated in telecom
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        ad_callee.ed.clear_all_events()
+
+        if verify_caller_func and not verify_caller_func(log, ad_caller):
+            raise _CallSequenceException(
+                "Caller not in correct state!")
+
+        # TODO: Need to play some sound to leave message. Otherwise carrier
+        # voice mail server have a big chance to drop this voice mail.
+
+        time.sleep(wait_time_in_call)
+
+        if not verify_caller_func:
+            caller_state_result = ad_caller.droid.telecomIsInCall()
+        else:
+            caller_state_result = verify_caller_func(log, ad_caller)
+        if not caller_state_result:
+            raise _CallSequenceException(
+                "Caller not in correct state after {} seconds".format(wait_time_in_call))
+
+        if not hangup_call(log, ad_caller):
+            raise _CallSequenceException(
+                "Error in Hanging-Up Call")
+
+        log.info("Wait for voice mail indicator on callee.")
+        try:
+            event = ad_callee.ed.wait_for_event(EventMessageWaitingIndicatorChanged,
+                                                _is_on_message_waiting_event_true)
+            log.info(event)
+        except Empty:
+            raise _CallSequenceException("No expected event {}.".
+                                         format(EventMessageWaitingIndicatorChanged))
+        voice_mail_count_after = ad_callee.droid.getVoiceMailCountForSubscription(
+            subid_callee)
+        log.info("getVoiceMailCount output - before: {}, after: {}".
+                 format(voice_mail_count_before, voice_mail_count_after))
+
+        # voice_mail_count_after should:
+        # either equals to (voice_mail_count_before + 1) [For ATT and SPT]
+        # or equals to -1 [For TMO]
+        # -1 means there are unread voice mail, but the count is unknown
+        if not check_voice_mail_count(log, ad_callee,
+                                      voice_mail_count_before,
+                                      voice_mail_count_after):
+            log.error("getVoiceMailCount output is incorrect.")
+            return False
+
+    except _CallSequenceException as e:
+        log.error(e)
+        return False
+    finally:
+        ad_callee.droid.phoneStopTrackingVoiceMailStateChangeForSubscription(
+            subid_callee)
+    return True
+
+def call_voicemail_erase_all_pending_voicemail(log, ad):
+    """Script for phone to erase all pending voice mail.
+    This script only works for TMO and ATT and SPT currently.
+    This script only works if phone have already set up voice mail options,
+    and phone should disable password protection for voice mail.
+
+    1. If phone don't have pending voice message, return True.
+    2. Dial voice mail number.
+        For TMO, the number is '123'
+        For ATT, the number is phone's number
+        For SPT, the number is phone's number
+    3. Wait for voice mail connection setup.
+    4. Wait for voice mail play pending voice message.
+    5. Send DTMF to delete one message.
+        The digit is '7'.
+    6. Repeat steps 4 and 5 until voice mail server drop this call.
+        (No pending message)
+    6. Check getVoiceMailCount result. it should be 0.
+
+    Args:
+        log: log object
+        ad: android device object
+    Returns:
+        False if error happens. True is succeed.
+    """
+    log.info("Erase all pending voice mail.")
+    if ad.droid.getVoiceMailCount() == 0:
+        log.info("No Pending voice mail.")
+        return True
+
+    voice_mail_number = get_voice_mail_number(log, ad)
+
+    if not initiate_call(log, ad, voice_mail_number):
+        log.error("Initiate call failed.")
+        return False
+    time.sleep(VOICE_MAIL_SERVER_RESPONSE_DELAY)
+    callId = ad.droid.telecomCallGetCallIds()[0]
+    time.sleep(VOICE_MAIL_SERVER_RESPONSE_DELAY)
+    count = MAX_SAVED_VOICE_MAIL
+    while(is_phone_in_call(log, ad) and (count > 0)):
+        log.info("Press 7 to delete voice mail.")
+        ad.droid.telecomCallPlayDtmfTone(callId, VOICEMAIL_DELETE_DIGIT)
+        ad.droid.telecomCallStopDtmfTone(callId)
+        time.sleep(VOICE_MAIL_SERVER_RESPONSE_DELAY)
+        count -= 1
+    log.info("Voice mail server dropped this call.")
+    # wait for getVoiceMailCount to update correct result
+    remaining_time = MAX_WAIT_TIME_FOR_VOICE_MAIL_COUNT
+    while((remaining_time > 0) and (ad.droid.getVoiceMailCount() != 0)):
+        time.sleep(1)
+        remaining_time -= 1
+    current_voice_mail_count = ad.droid.getVoiceMailCount()
+    log.info("getVoiceMailCount: {}".format(current_voice_mail_count))
+    return (current_voice_mail_count == 0)
+
+def _is_on_message_waiting_event_true(event):
+    """Private function to return if the received EventMessageWaitingIndicatorChanged
+    event 'MessageWaitingIndicator' field is True.
+    """
+    return event['data']['MessageWaitingIndicator']
+
+def call_setup_teardown(log, ad_caller, ad_callee, ad_hangup=None,
+       verify_caller_func=None, verify_callee_func=None,
+       wait_time_in_call=WAIT_TIME_IN_CALL,
+       incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+    """ Call process, including make a phone call from caller,
+    accept from callee, and hang up. The call is on default voice subscription
+
+    In call process, call from <droid_caller> to <droid_callee>,
+    after ringing, wait <delay_answer> to accept the call,
+    (optional)then hang up from <droid_hangup>.
+
+    Args:
+        ad_caller: Caller Android Device Object.
+        ad_callee: Callee Android Device Object.
+        ad_hangup: Android Device Object end the phone call.
+            Optional. Default value is None, and phone call will continue.
+        verify_call_mode_caller: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        verify_call_mode_caller: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+
+    Returns:
+        True if call process without any error.
+        False if error happened.
+
+    """
+    subid_caller = ad_caller.droid.subscriptionGetDefaultVoiceSubId()
+    subid_callee = ad_callee.droid.subscriptionGetDefaultVoiceSubId()
+    return call_setup_teardown_for_subscription(log, ad_caller, ad_callee,
+                                                subid_caller, subid_callee,
+                                                ad_hangup, verify_caller_func,
+                                                verify_callee_func,
+                                                wait_time_in_call,
+                                                incall_ui_display)
+
+
+def call_setup_teardown_for_subscription(log, ad_caller, ad_callee, subid_caller,
+                                         subid_callee, ad_hangup=None,
+                                         verify_caller_func=None,
+                                         verify_callee_func=None,
+                                         wait_time_in_call=WAIT_TIME_IN_CALL,
+                                         incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+    """ Call process, including make a phone call from caller,
+    accept from callee, and hang up. The call is on specified subscription
+
+    In call process, call from <droid_caller> to <droid_callee>,
+    after ringing, wait <delay_answer> to accept the call,
+    (optional)then hang up from <droid_hangup>.
+
+    Args:
+        ad_caller: Caller Android Device Object.
+        ad_callee: Callee Android Device Object.
+        subid_caller: Caller subscription ID
+        subid_callee: Callee subscription ID
+        ad_hangup: Android Device Object end the phone call.
+            Optional. Default value is None, and phone call will continue.
+        verify_call_mode_caller: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        verify_call_mode_caller: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+
+    Returns:
+        True if call process without any error.
+        False if error happened.
+
+    """
+    class _CallSequenceException(Exception):
+        pass
+
+    caller_number = ad_caller.cfg['subscription'][subid_caller]['phone_num']
+    callee_number = ad_callee.cfg['subscription'][subid_callee]['phone_num']
+
+    log.info("Call from {} to {}".format(caller_number, callee_number))
+
+    try:
+        if not initiate_call(log, ad_caller, callee_number):
+            raise _CallSequenceException("Initiate call failed.")
+
+        if not wait_and_answer_call_for_subscription(
+                log, ad_callee, subid_callee, incoming_number=caller_number,
+                incall_ui_display=incall_ui_display):
+            raise _CallSequenceException("Answer call fail.")
+
+        # ensure that all internal states are updated in telecom
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+
+        if verify_caller_func and not verify_caller_func(log, ad_caller):
+            raise _CallSequenceException(
+                "Caller not in correct state!")
+        if verify_callee_func and not verify_callee_func(log, ad_callee):
+            raise _CallSequenceException(
+                "Callee not in correct state!")
+
+        time.sleep(wait_time_in_call)
+
+        if not verify_caller_func:
+            caller_state_result = ad_caller.droid.telecomIsInCall()
+        else:
+            caller_state_result = verify_caller_func(log, ad_caller)
+        if not caller_state_result:
+            raise _CallSequenceException(
+                "Caller not in correct state after {} seconds".format(wait_time_in_call))
+
+        if not verify_callee_func:
+            callee_state_result = ad_callee.droid.telecomIsInCall()
+        else:
+            callee_state_result = verify_callee_func(log, ad_callee)
+        if not callee_state_result:
+            raise _CallSequenceException(
+                "Callee not in correct state after {} seconds".format(wait_time_in_call))
+
+        if not ad_hangup:
+            return True
+
+        if not hangup_call(log, ad_hangup):
+            raise _CallSequenceException(
+                "Error in Hanging-Up Call")
+
+        return True
+
+    except _CallSequenceException as e:
+        log.error(e)
+        return False
+    finally:
+        if ad_hangup:
+            for ad in [ad_caller, ad_callee]:
+                try:
+                    if ad.droid.telecomIsInCall():
+                        ad.droid.telecomEndCall()
+                except Exception as e:
+                    log.error(str(e))
+
+def phone_number_formatter(input_string, format):
+    """Get expected format of input phone number string.
+
+    Args:
+        input_string: (string) input phone number.
+            The input could be 10/11/12 digital, with or without " "/"-"/"."
+        format: (int) expected format, this could be 7/10/11/12
+            if format is 7: output string would be 7 digital number.
+            if format is 10: output string would be 10 digital (standard) number.
+            if format is 11: output string would be "1" + 10 digital number.
+            if format is 12: output string would be "+1" + 10 digital number.
+
+    Returns:
+        If no error happen, return phone number in expected format.
+        Else, return None.
+    """
+    # make sure input_string is 10 digital
+    # Remove white spaces, dashes, dots
+    input_string = input_string.replace(" ", "").replace("-", "").replace(".", "")
+    # Remove "1"  or "+1"from front
+    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"):
+        input_string = input_string[2:]
+    elif (len(input_string) == PHONE_NUMBER_STRING_FORMAT_7_DIGIT and
+          format == PHONE_NUMBER_STRING_FORMAT_7_DIGIT):
+        return input_string
+    elif len(input_string) != PHONE_NUMBER_STRING_FORMAT_10_DIGIT:
+        return None
+    # change input_string according to format
+    if format == PHONE_NUMBER_STRING_FORMAT_12_DIGIT:
+        input_string = "+1"+input_string
+    elif format == PHONE_NUMBER_STRING_FORMAT_11_DIGIT:
+        input_string = "1"+input_string
+    elif format == PHONE_NUMBER_STRING_FORMAT_10_DIGIT:
+        input_string = input_string
+    elif format == PHONE_NUMBER_STRING_FORMAT_7_DIGIT:
+        input_string = input_string[3:]
+    else:
+        return None
+    return input_string
+
+def get_internet_connection_type(log, ad):
+    """Get current active connection type name.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+    Returns:
+        current active connection type name.
+    """
+    if not ad.droid.connectivityNetworkIsConnected():
+        return 'none'
+    return connection_type_from_type_string(
+        ad.droid.connectivityNetworkGetActiveConnectionTypeName())
+
+def verify_http_connection(log, ad, url="http://www.google.com/", retry=3, retry_interval=5):
+    """Make ping request and return status.
+
+    Args:
+        ad: Android Device Object.
+        url: Optional. The ping request will be made to this URL.
+            Default Value is "http://www.google.com/".
+
+    """
+    for i in range(0, retry+1):
+
+        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:
+            log.info("Verify Internet succeeded after {}s."
+                     .format(i*retry_interval) if i > 0 else
+                     "Verify Internet succeeded.")
+            return True
+        else:
+            if i < retry:
+                time.sleep(retry_interval)
+    log.info("Verify Internet retry failed after {}s"
+             .format(i*retry_interval))
+    return False
+
+
+def _connection_state_change(_event, target_state, connection_type):
+    if connection_type:
+        if 'TypeName' not in _event['data']:
+            return False
+        connection_type_string_in_event = _event['data']['TypeName']
+        cur_type = connection_type_from_type_string(connection_type_string_in_event)
+        if cur_type != connection_type:
+            log.info(
+                "_connection_state_change expect: {}, received: {} <type {}>".
+                format(connection_type, connection_type_string_in_event, cur_type))
+            return False
+
+    if 'isConnected' in _event['data'] and _event['data']['isConnected'] == target_state:
+        return True
+    return False
+
+
+def wait_for_cell_data_connection(log, ad, state,
+                                  timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+    """Wait for data connection status to be expected value for default
+       data subscription.
+
+    Wait for the data connection status to be DATA_STATE_CONNECTED
+        or DATA_STATE_DISCONNECTED.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        state: Expected status: True or False.
+            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
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    sub_id = ad.droid.subscriptionGetDefaultDataSubId()
+    return wait_for_cell_data_connection_for_subscription(log, ad, sub_id, state,
+                                                          timeout_value)
+
+def _is_data_connection_state_match(log, ad, expected_data_connection_state):
+    return (expected_data_connection_state == ad.droid.getDataConnectionState())
+
+def _is_network_connected_state_match(log, ad, expected_network_connected_state):
+    return (expected_network_connected_state == ad.droid.connectivityNetworkIsConnected())
+
+def wait_for_cell_data_connection_for_subscription(log, ad, sub_id, state,
+                                  timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+    """Wait for data connection status to be expected value for specified
+       subscrption id.
+
+    Wait for the data connection status to be DATA_STATE_CONNECTED
+        or DATA_STATE_DISCONNECTED.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        sub_id: subscription Id
+        state: Expected status: True or False.
+            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
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    state_str, tel_sub_event_name = {
+        True: (DATA_STATE_CONNECTED, DATA_STATE_CONNECTED),
+        False: (DATA_STATE_DISCONNECTED, DATA_STATE_DISCONNECTED)
+    }[state]
+
+    ad.ed.clear_all_events()
+    ad.droid.phoneStartTrackingDataConnectionStateChangeForSubscription(sub_id)
+    ad.droid.connectivityStartTrackingConnectivityStateChange()
+    try:
+        # TODO There is no framework API to get data connection state by sub id
+        data_state = ad.droid.getDataConnectionState()
+        if data_state == state_str:
+            return _wait_for_nw_data_connection(log, ad, state,
+                                                NETWORK_CONNECTION_TYPE_CELL,
+                                                timeout_value)
+
+        try:
+            event = ad.ed.wait_for_event(EventDataConnectionStateChanged,
+                                         is_sub_event_match,
+                                         timeout=timeout_value,
+                                         sub_event=tel_sub_event_name)
+        except Empty:
+            log.debug("No expected event EventDataConnectionStateChanged {}.".
+                      format(tel_sub_event_name))
+
+        # TODO: Wait for <WAIT_TIME_CONNECTION_STATE_UPDATE> seconds for
+        # data connection state.
+        # Otherwise, the network state will not be correct.
+        # The bug is tracked here: b/20921915
+        # will remove this sleep once bug fixed.
+
+        # FIXME: previously we use _is_data_connection_state_match
+        # but getDataConnectionState sometimes return wrong value. (b/22612607)
+        # Use _is_network_connected_state_match temporarily, until b/22612607 fixed.
+
+        if _wait_for_droid_in_state(log, ad,
+            WAIT_TIME_CONNECTION_STATE_UPDATE, _is_network_connected_state_match,
+            state):
+            return _wait_for_nw_data_connection(log, ad, state,
+                                                NETWORK_CONNECTION_TYPE_CELL,
+                                                timeout_value)
+        else:
+            return False
+
+    finally:
+        ad.droid.phoneStopTrackingDataConnectionStateChangeForSubscription(sub_id)
+
+
+def wait_for_wifi_data_connection(log, ad, state,
+                                  timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+    """Wait for data connection status to be expected value and connection is by WiFi.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        state: Expected status: True or False.
+            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
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    log.info("{} wait_for_wifi_data_connection".format(ad.serial))
+    return _wait_for_nw_data_connection(log, ad, state,
+                                        NETWORK_CONNECTION_TYPE_WIFI,
+                                        timeout_value)
+
+
+def wait_for_data_connection(log, ad, state,
+                             timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+    """Wait for data connection status to be expected value.
+
+    Wait for the data connection status to be DATA_STATE_CONNECTED
+        or DATA_STATE_DISCONNECTED.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        state: Expected status: True or False.
+            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
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    return _wait_for_nw_data_connection(log, ad, state, None, timeout_value)
+
+
+def _wait_for_nw_data_connection(log, ad, is_connected, connection_type=None,
+                                 timeout_value=EventDispatcher.DEFAULT_TIMEOUT):
+    """Wait for data connection status to be expected value.
+
+    Wait for the data connection status to be DATA_STATE_CONNECTED
+        or DATA_STATE_DISCONNECTED.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        is_connected: Expected connection status: True or False.
+            If True, it will wait for status to be DATA_STATE_CONNECTED.
+            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+        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
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    ad.ed.clear_all_events()
+    ad.droid.connectivityStartTrackingConnectivityStateChange()
+    try:
+        cur_data_connection_state = ad.droid.connectivityNetworkIsConnected()
+        if is_connected == cur_data_connection_state:
+            current_type = get_internet_connection_type(log, ad)
+            log.info("_wait_for_nw_data_connection: current connection type: {}".
+                     format(current_type))
+            if not connection_type:
+                return True
+            else:
+                if not is_connected and current_type != connection_type:
+                    log.info("wait_for_nw_data_connection success: {} data not on {}!".
+                        format(ad.serial, connection_type))
+                    return True
+                elif is_connected and current_type == connection_type:
+                    log.info("wait_for_nw_data_connection success: {} data on {}!".
+                        format(ad.serial, connection_type))
+                    return True
+        else:
+            log.info("{} current state: {} target: {}".
+                format(ad.serial, cur_data_connection_state, is_connected))
+
+        try:
+            event = ad.ed.wait_for_event(EventConnectivityChanged,
+                                         _connection_state_change,
+                                         timeout_value,
+                                         is_connected, connection_type)
+            log.info("_wait_for_nw_data_connection received event:{}".format(event))
+        except Empty:
+            pass
+
+        log.info("_wait_for_nw_data_connection: check connection after wait event.")
+        # TODO: Wait for <WAIT_TIME_CONNECTION_STATE_UPDATE> seconds for
+        # data connection state.
+        # Otherwise, the network state will not be correct.
+        # The bug is tracked here: b/20921915
+        # will remove this sleep once bug fixed.
+        if _wait_for_droid_in_state(log, ad,
+            WAIT_TIME_CONNECTION_STATE_UPDATE, _is_network_connected_state_match,
+            is_connected):
+            current_type = get_internet_connection_type(log, ad)
+            log.info("_wait_for_nw_data_connection: current connection type: {}".
+                     format(current_type))
+            if not connection_type:
+                return True
+            else:
+                if not is_connected and current_type != connection_type:
+                    log.info("wait_for_nw_data_connection after event wait, success: {} data not on {}!".
+                        format(ad.serial, connection_type))
+                    return True
+                elif is_connected and current_type == connection_type:
+                    log.info("wait_for_nw_data_connection after event wait, success: {} data on {}!".
+                        format(ad.serial, connection_type))
+                    return True
+                else:
+                    return False
+        else:
+            return False
+    except Exception as e:
+        log.error("tel_test_utils._wait_for_nw_data_connection threw Random exception {}".
+            format(str(e)))
+        return False
+    finally:
+        ad.droid.connectivityStopTrackingConnectivityStateChange()
+
+def verify_incall_state(log, ads, expected_status):
+    """Verify phones in incall state or not.
+
+    Verify if all phones in the array <ads> are in <expected_status>.
+
+    Args:
+        log: Log object.
+        ads: Array of Android Device Object. All droid in this array will be tested.
+        expected_status: If True, verify all Phones in incall state.
+            If False, verify all Phones not in incall state.
+
+    """
+    result = True
+    for ad in ads:
+        if ad.droid.telecomIsInCall() is not expected_status:
+            log.error("Verify_incall_state: {} status:{}, expected:{}".
+                format(ad.serial, ad.droid.telecomIsInCall(), expected_status))
+            result = False
+    return result
+
+def verify_active_call_number(log, ad, expected_number):
+    """Verify the number of current active call.
+
+    Verify if the number of current active call in <ad> is
+        equal to <expected_number>.
+
+    Args:
+        ad: Android Device Object.
+        expected_number: Expected active call number.
+    """
+    calls = ad.droid.telecomCallGetCallIds()
+    if calls is None:
+        actual_number = 0
+    else:
+        actual_number = len(calls)
+    if actual_number != expected_number:
+        log.error("Active Call number in {}".format(ad.serial))
+        log.error("Expected:{}, Actual:{}".format(expected_number, actual_number))
+        return False
+    return True
+
+
+def num_active_calls(log, ad):
+    """Get the count of current active calls.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+
+    Returns:
+        Count of current active calls.
+    """
+    calls = ad.droid.telecomCallGetCallIds()
+    return len(calls) if calls else 0
+
+
+def toggle_volte(log, ad, new_state=None):
+    """Toggle enable/disable VoLTE for default voice subscription.
+
+    Args:
+        ad: Android device object.
+        new_state: VoLTE mode state to set to.
+            True for enable, False for disable.
+            If None, opposite of the current state.
+
+    Raises:
+        TelTestUtilsError if platform does not support VoLTE.
+    """
+    return toggle_volte_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId(), new_state)
+
+
+def toggle_volte_for_subscription(log, ad, sub_id, new_state=None):
+    """Toggle enable/disable VoLTE for specified voice subscription.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription ID
+        new_state: VoLTE mode state to set to.
+            True for enable, False for disable.
+            If None, opposite of the current state.
+
+    Raises:
+        TelTestUtilsError if platform does not support VoLTE.
+    """
+    # TODO Need to take care of sub_id. No framework API support to get IMS
+    # setting for subscription
+    if not ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform():
+        raise TelTestUtilsError("VoLTE not supported by platform.")
+    current_state = ad.droid.imsIsEnhanced4gLteModeSettingEnabledByUser()
+    if new_state is None:
+        new_state = not current_state
+    if new_state != current_state:
+        ad.droid.imsSetEnhanced4gMode(new_state)
+    return True
+
+def set_wfc_mode(log, ad, wfc_mode):
+    """Set WFC enable/disable and mode.
+
+    Args:
+        log: Log object
+        ad: Android device object.
+        wfc_mode: WFC mode to set to.
+            Valid mode includes: WFC_MODE_WIFI_ONLY, WFC_MODE_CELLULAR_PREFERRED,
+            WFC_MODE_WIFI_PREFERRED, WFC_MODE_DISABLED.
+
+    Returns:
+        True if success. False if ad does not support WFC or error happened.
+    """
+    try:
+        log.info("{} set wfc mode to {}".format(ad.serial, wfc_mode))
+        if not ad.droid.imsIsWfcEnabledByPlatform():
+            if wfc_mode == WFC_MODE_DISABLED:
+                return True
+            else:
+                log.error("WFC not supported by platform.")
+                return False
+
+        ad.droid.imsSetWfcMode(wfc_mode)
+
+    except Exception as e:
+        log.error(e)
+        return False
+
+    return True
+
+
+def set_preferred_network_type(log, ad, network_type):
+    """Set preferred network type for default subscription.
+
+    Args:
+        ad: android_device object
+        network_type: Network type string. For example, "3G", "LTE", "2G".
+
+    Raises:
+        TelTestUtilsError if type is not supported.
+    """
+    return set_preferred_network_type_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId(), network_type)
+
+
+def set_preferred_network_type_for_subscription(log, ad, sub_id, network_type):
+    """Set preferred network type for specified subscription.
+
+    Args:
+        ad: android_device object
+        sub_id: subscription ID
+        network_type: Network type string. For example, "3G", "LTE", "2G".
+
+    Raises:
+        TelTestUtilsError if type is not supported.
+    """
+
+    operator = get_operator_name(log, ad, sub_id)
+    if operator is None:
+        log.error('Unknown operator for {}'.format(ad.serial))
+        return False
+
+    # Temporarily using the integer network mode setting due to b/24880020
+    # We should switch back to "safe" api once resolved
+    network_mode = network_mode_by_operator_generation(operator, network_type)
+    if network_mode is None:
+        log.error("Couldn't determine appropriate network mode for operator"
+                  .format(operator))
+        return False
+
+    ad.droid.setPreferredNetworkForSubscription(sub_id, network_mode)
+
+    return True
+
+def is_droid_in_network_generation(log, ad, nw_gen, voice_or_data):
+    """Checks if a droid in expected network generation ("2g", "3g" or "4g").
+
+    Args:
+        log: log object.
+        ad: android device.
+        nw_gen: expected generation "4g", "3g", "2g".
+        voice_or_data: check voice network generation or data network generation
+            This parameter is optional. If voice_or_data is None, then if
+            either voice or data in expected generation, function will return True.
+
+    Returns:
+        True if droid in expected network generation. Otherwise False.
+    """
+    return is_droid_in_network_generation_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId(), nw_gen, voice_or_data)
+
+def is_droid_in_network_generation_for_subscription(log, ad, sub_id, nw_gen, voice_or_data):
+    """Checks if a droid in expected network generation ("2g", "3g" or "4g").
+
+    Args:
+        log: log object.
+        ad: android device.
+        nw_gen: expected generation "4g", "3g", "2g".
+        voice_or_data: check voice network generation or data network generation
+            This parameter is optional. If voice_or_data is None, then if
+            either voice or data in expected generation, function will return True.
+
+    Returns:
+        True if droid in expected network generation. Otherwise False.
+    """
+    service_list = ["data", "voice"]
+
+    if voice_or_data:
+        service_list = [voice_or_data]
+
+    for service in service_list:
+        nw_rat = get_network_rat_for_subscription(log, ad, sub_id, service)
+
+        if nw_rat == RAT_UNKNOWN or not is_valid_rat(nw_rat):
+            continue
+
+        if rat_generation_from_type(nw_rat) == nw_gen:
+            return True
+        else:
+            return False
+
+    return False
+
+
+def is_droid_in_network_rat(log, ad, rat, voice_or_data=None):
+    """Checks if a droid in expected network rat for default subscription Id.
+
+    Args:
+        log: log object.
+        ad: android device.
+        rat: expected network rat
+        voice_or_data: check voice network rat or data network rat
+            This parameter is optional. If voice_or_data is None, then if
+            either voice or data in expected rat, function will return True.
+
+    Returns:
+        True if droid in expected network rat. Otherwise False.
+    """
+    return is_droid_in_network_rat_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId(), rat, voice_or_data)
+
+def is_droid_not_in_network_rat(log, ad, rat, voice_or_data=None):
+    """Checks if a droid not in network rat for default subscription Id.
+
+    Args:
+        log: log object.
+        ad: android device.
+        rat: network rat
+        voice_or_data: check voice network rat or data network rat
+            This parameter is optional. If voice_or_data is None, then if
+            either voice or data in expected rat, function will return True.
+
+    Returns:
+        True if droid in expected network rat. Otherwise False.
+    """
+    return is_droid_not_in_network_rat_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId(), rat, voice_or_data)
+
+
+def is_droid_in_network_rat_for_subscription(log, ad, sub_id, rat,
+                                             voice_or_data=None):
+    """Checks if a droid in expected network rat for specified subscription Id.
+
+    Args:
+        log: log object.
+        ad: android device.
+        sub_id: subscription Id
+        rat: expected network rat
+        voice_or_data: check voice network rat or data network rat
+            This parameter is optional. If voice_or_data is None, then if
+            either voice or data in expected rat, function will return True.
+
+    Returns:
+        True if droid in expected network rat. Otherwise False.
+    """
+    service_list = [NETWORK_SERVICE_DATA, NETWORK_SERVICE_VOICE]
+
+    if voice_or_data:
+        service_list = [voice_or_data]
+
+    if not is_valid_rat(rat):
+        raise TelTestUtilsError("Invalid RAT {}".format(rat))
+
+    for service in service_list:
+        current_rat = get_network_rat_for_subscription(log, ad, sub_id, service)
+        if rat_family_from_type(rat) == rat_family_from_type(current_rat):
+            return True
+
+    return False
+
+def is_droid_not_in_network_rat_for_subscription(log, ad, sub_id, rat,
+                                                 voice_or_data=None):
+    """Checks if a droid not in expected network rat for specified subscription Id.
+
+    Args:
+        log: log object.
+        ad: android device.
+        sub_id: subscription Id
+        rat: expected network rat
+        voice_or_data: check voice network rat or data network rat
+            This parameter is optional. If voice_or_data is None, then if
+            either voice or data in expected rat, function will return True.
+
+    Returns:
+        True if droid not in expected network rat. Otherwise False.
+    """
+    return not is_droid_in_network_rat_for_subscription(
+        log, ad, sub_id, rat, voice_or_data)
+
+def _wait_for_droid_in_state(log, ad, max_time, state_check_func, *args, **kwargs):
+    while max_time > 0:
+        if state_check_func(log, ad, *args, **kwargs):
+            return True
+
+        time.sleep(1)
+        max_time -= 1
+
+    return False
+
+
+def _wait_for_droid_in_state_for_subscription(log, ad, sub_id, max_time,
+                                              state_check_func, *args, **kwargs):
+    while max_time > 0:
+        if state_check_func(log, ad, sub_id, *args, **kwargs):
+            return True
+
+        time.sleep(1)
+        max_time -= 1
+
+    return False
+
+
+def _wait_for_droids_in_state(log, ads, max_time, state_check_func, *args, **kwargs):
+    while max_time > 0:
+        success = True
+        for ad in ads:
+            if not state_check_func(log, ad, *args, **kwargs):
+                success = False
+                break
+        if success:
+            return True
+
+        time.sleep(1)
+        max_time -= 1
+
+    return False
+
+def is_phone_in_call(log, ad):
+    """Return True if phone in call.
+
+    Args:
+        log: log object.
+        ad:  android device.
+    """
+    return ad.droid.telecomIsInCall()
+
+def is_phone_not_in_call(log, ad):
+    """Return True if phone not in call.
+
+    Args:
+        log: log object.
+        ad:  android device.
+    """
+    return not ad.droid.telecomIsInCall()
+
+def wait_for_droid_in_call(log, ad, max_time):
+    """Wait for android to be in call state.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        If phone become in call state within max_time, return True.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, is_phone_in_call)
+
+def wait_for_droid_not_in_call(log, ad, max_time):
+    """Wait for android to be not in call state.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        If phone become not in call state within max_time, return True.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, is_phone_not_in_call)
+
+def wait_for_droid_in_network_generation(
+        log, ad, network_type, max_time, voice_or_data=None):
+    """Wait for droid to be in certain connection mode (e.g. lte, 3g).
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: max number of seconds to wait (each droid in the droids list).
+        network_type: expected connection network type. e.g. lte, 3g, 2g.
+        voice_or_data: check droid's voice network type or data network type.
+            Optional, default value is None.
+
+    """
+    return _wait_for_droid_in_state(
+        log, ad, max_time,
+        is_droid_in_network_generation, network_type, voice_or_data)
+
+
+def wait_for_droid_in_network_generation_for_subscription(
+        log, ad, sub_id, network_type, max_time, voice_or_data=None):
+    """Wait for droid to be in certain connection mode (e.g. lte, 3g).
+
+    Args:
+        log: log object.
+        ad:  android device.
+        sub_id: subscription Id
+        max_time: max number of seconds to wait (each droid in the droids list).
+        network_type: expected connection network type. e.g. lte, 3g, 2g.
+        voice_or_data: check droid's voice network type or data network type.
+            Optional, default value is None.
+
+    """
+    return _wait_for_droid_in_state_for_subscription(
+        log, ad, sub_id, max_time, is_droid_in_network_generation_for_subscription,
+        network_type, voice_or_data)
+
+
+def wait_for_droid_in_network_rat(
+        log, ad, network_type, max_time, voice_or_data=None):
+    """Wait for droid to be in certain connection mode (e.g. lte, 3g).
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: max number of seconds to wait (each droid in the droids list).
+        network_type: expected connection network type. e.g. lte, 3g, 2g.
+        voice_or_data: check droid's voice network type or data network type.
+            Optional, default value is None.
+
+    """
+
+    return _wait_for_droid_in_state(
+        log, ad, max_time,
+        is_droid_in_network_rat, network_type, voice_or_data)
+
+def wait_for_droid_not_in_network_rat(
+        log, ad, network_type, max_time, voice_or_data=None):
+    """Wait for droid to be not in certain connection mode (e.g. lte, 3g).
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: max number of seconds to wait (each droid in the droids list).
+        network_type: connection network type. e.g. lte, 3g, 2g.
+        voice_or_data: check droid's voice network type or data network type.
+            Optional, default value is None.
+
+    """
+
+    return _wait_for_droid_in_state(
+        log, ad, max_time,
+        is_droid_not_in_network_rat, network_type, voice_or_data)
+
+
+def wait_for_droid_in_network_rat_for_subscription(
+        log, ad, sub_id, network_type, max_time, voice_or_data=None):
+    """Wait for droid to be in certain connection mode (e.g. lte, 3g).
+
+    Args:
+        log: log object.
+        ad:  android device.
+        sub_id: subscription Id
+        max_time: max number of seconds to wait (each droid in the droids list).
+        network_type: expected connection network type. e.g. lte, 3g, 2g.
+        voice_or_data: check droid's voice network type or data network type.
+            Optional, default value is None.
+
+    """
+
+    return _wait_for_droid_in_state_for_subscription(
+        log, ad, sub_id, max_time,
+        is_droid_in_network_rat_for_subscription, network_type, voice_or_data)
+
+
+def wait_for_droids_in_network_generation(
+        log, ads, network_type, max_time, voice_or_data=None):
+    """Wait for droid to be in certain connection mode (e.g. lte, 3g).
+
+    Args:
+        log: log object.
+        ads: array of android device.
+        max_time: max number of seconds to wait (each droid in the droids list).
+        network_type: expected connection network type. e.g. lte, 3g, 2g.
+        voice_or_data: check droid's voice network type or data network type.
+            Optional, default value is None.
+
+    """
+    # TODO(yangxliu): replace loop time wait with SL4A event.
+
+    return _wait_for_droids_in_state(
+        log, ads, max_time,
+        is_droid_in_network_generation, network_type, voice_or_data)
+
+
+def wait_for_droids_in_network_rat(
+        log, ads, network_type, max_time, voice_or_data=None):
+    """Wait for droid to be in certain connection mode (e.g. lte, 3g).
+
+    Args:
+        log: log object.
+        ads: array of android device.
+        max_time: max number of seconds to wait (each droid in the droids list).
+        network_type: expected connection network type. e.g. lte, 3g, 2g.
+        voice_or_data: check droid's voice network type or data network type.
+            Optional, default value is None.
+
+    """
+
+    return _wait_for_droids_in_state(
+        log, ads, max_time,
+        is_droid_in_network_rat, network_type, voice_or_data)
+
+
+def _is_attached(log, ad, voice_or_data):
+    return _is_attached_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
+
+def _is_attached_for_subscription(log, ad, sub_id, voice_or_data):
+    if get_network_rat_for_subscription(log, ad, sub_id,
+                                         voice_or_data) != RAT_UNKNOWN:
+        return True
+    else:
+        return False
+
+
+def wait_for_voice_attach(log, ad, max_time):
+    """Wait for android device to attach on voice.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device attach voice within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, _is_attached,
+                                    NETWORK_SERVICE_VOICE)
+
+def wait_for_voice_attach_for_subscription(log, ad, sub_id, max_time):
+    """Wait for android device to attach on voice in subscription id.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        sub_id: subscription id.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device attach voice within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state_for_subscription(
+        log, ad, sub_id, max_time, _is_attached_for_subscription,
+        NETWORK_SERVICE_VOICE)
+
+def wait_for_data_attach(log, ad, max_time):
+    """Wait for android device to attach on data.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device attach data within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, _is_attached,
+                                    NETWORK_SERVICE_DATA)
+
+def wait_for_data_attach_for_subscription(log, ad, sub_id, max_time):
+    """Wait for android device to attach on data in subscription id.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        sub_id: subscription id.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device attach data within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state_for_subscription(
+        log, ad, sub_id, max_time, _is_attached_for_subscription,
+        NETWORK_SERVICE_DATA)
+
+def _is_ims_registered(log, ad):
+    return ad.droid.isImsRegistered()
+
+def wait_for_ims_registered(log, ad, max_time):
+    """Wait for android device to register on ims.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device register ims successfully within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, _is_ims_registered)
+
+def _is_volte_enabled(log, ad):
+    return ad.droid.isVolteAvailable()
+
+def _is_video_enabled(log, ad):
+    return ad.droid.isVideoCallingAvailable()
+
+def wait_for_volte_enabled(log, ad, max_time):
+    """Wait for android device to report VoLTE enabled bit true.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device report VoLTE enabled bit true within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, _is_volte_enabled)
+
+def wait_for_video_enabled(log, ad, max_time):
+    """Wait for android device to report Video Telephony enabled bit true.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device report Video Telephony enabled bit true within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, _is_video_enabled)
+
+def is_wfc_enabled(log, ad):
+    """Return True if WiFi Calling feature bit is True.
+
+    Args:
+        log: log object.
+        ad: android device.
+
+    Returns:
+        Return True if WiFi Calling feature bit is True.
+        Return False if WiFi Calling feature bit is False.
+    """
+    return ad.droid.isWifiCallingAvailable()
+
+def wait_for_wfc_enabled(log, ad, max_time):
+    """Wait for android device to report WiFi Calling enabled bit true.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device report WiFi Calling enabled bit true within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, is_wfc_enabled)
+
+def wait_for_wfc_disabled(log, ad, max_time):
+    """Wait for android device to report WiFi Calling enabled bit false.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device report WiFi Calling enabled bit false within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time,
+        lambda log, ad : not is_wfc_enabled(log, ad))
+
+def get_phone_number(log, ad):
+    """Get phone number for default subscription
+
+    Args:
+        log: log object.
+        ad: Android device object.
+
+    Returns:
+        Phone number.
+    """
+    return get_phone_number_for_subscription(log, ad,
+        ad.droid.subscriptionGetDefaultVoiceSubId())
+
+def get_phone_number_for_subscription(log, ad, subid):
+    """Get phone number for subscription
+
+    Args:
+        log: log object.
+        ad: Android device object.
+        subid: subscription id.
+
+    Returns:
+        Phone number.
+    """
+    number = None
+    try:
+        number = ad.cfg['subscription'][subid]['phone_num']
+    except KeyError:
+        number = ad.droid.getLine1NumberForSubscription(subid)
+    return number
+
+def set_phone_number(log, ad, phone_num):
+    """Set phone number for default subscription
+
+    Args:
+        log: log object.
+        ad: Android device object.
+        phone_num: phone number string.
+
+    Returns:
+        True if success.
+    """
+    return set_phone_number_for_subscription(log, ad,
+        ad.droid.subscriptionGetDefaultVoiceSubId(), phone_num)
+
+def set_phone_number_for_subscription(log, ad, subid, phone_num):
+    """Set phone number for subscription
+
+    Args:
+        log: log object.
+        ad: Android device object.
+        subid: subscription id.
+        phone_num: phone number string.
+
+    Returns:
+        True if success.
+    """
+    try:
+        ad.cfg['subscription'][subid]['phone_num'] = phone_num
+    except Exception:
+        return False
+    return True
+
+def get_operator_name(log, ad, subId=None):
+    """Get operator name (e.g. vzw, tmo) of droid.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription ID
+            Optional, default is None
+
+    Returns:
+        Operator name.
+    """
+    try:
+        if subId is not None:
+            result = operator_name_from_plmn_id(
+                ad.droid.getSimOperatorForSubscription(subId))
+        else:
+            result = operator_name_from_plmn_id(
+                ad.droid.getSimOperator())
+    except KeyError:
+        result = CARRIER_UNKNOWN
+    return result
+
+def is_sms_match(event, phonenumber_tx, text):
+    """Return True if 'text' equals to event['data']['Text']
+        and phone number match.
+
+    Args:
+        event: Event object to verify.
+        phonenumber_tx: phone number for sender.
+        text: text string to verify.
+
+    Returns:
+        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)
+
+def is_sms_partial_match(event, phonenumber_tx, text):
+    """Return True if 'text' starts with event['data']['Text']
+        and phone number match.
+
+    Args:
+        event: Event object to verify.
+        phonenumber_tx: phone number for sender.
+        text: text string to verify.
+
+    Returns:
+        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']))
+
+def sms_send_receive_verify(log, ad_tx, ad_rx, array_message):
+    """Send SMS, receive SMS, and verify content and sender's number.
+
+        Send (several) SMS from droid_tx to droid_rx.
+        Verify SMS is sent, delivered and received.
+        Verify received content and sender's number are correct.
+
+    Args:
+        log: Log object.
+        ad_tx: Sender's Android Device Object
+        ad_rx: Receiver's Android Device Object
+        array_message: the array of message to send/receive
+    """
+    return sms_send_receive_verify_for_subscription(log, ad_tx, ad_rx,
+                     ad_tx.droid.subscriptionGetDefaultSmsSubId(),
+                     ad_rx.droid.subscriptionGetDefaultSmsSubId(),
+                     array_message)
+
+def wait_for_matching_sms(log, ad_rx, phonenumber_tx, text,
+        allow_multi_part_long_sms=True):
+    """Wait for matching incoming SMS.
+
+    Args:
+        log: Log object.
+        ad_rx: Receiver's Android Device Object
+        phonenumber_tx: Sender's phone number.
+        text: SMS content string.
+        allow_multi_part_long_sms: is long SMS allowed to be received as
+            multiple short SMS. This is optional, default value is True.
+
+    Returns:
+        True if matching incoming SMS is received.
+    """
+    if not allow_multi_part_long_sms:
+        try:
+            ad_rx.ed.wait_for_event(EventSmsReceived, is_sms_match,
+                                    WAIT_TIME_SMS_RECEIVE,
+                                    phonenumber_tx, text)
+            return True
+        except Empty:
+            log.error("No matched SMS received event.")
+            return False
+    else:
+        try:
+            received_sms = ''
+            while(text != ''):
+                event = ad_rx.ed.wait_for_event(EventSmsReceived,
+                                                is_sms_partial_match,
+                                                WAIT_TIME_SMS_RECEIVE,
+                                                phonenumber_tx, text)
+                text = text[len(event['data']['Text']):]
+                received_sms += event['data']['Text']
+            return True
+        except Empty:
+            log.error("No matched SMS received event.")
+            if received_sms != '':
+                log.error("Only received partial matched SMS: {}".
+                    format(received_sms))
+            return False
+
+def sms_send_receive_verify_for_subscription(log, ad_tx, ad_rx, subid_tx,
+        subid_rx, array_message):
+    """Send SMS, receive SMS, and verify content and sender's number.
+
+        Send (several) SMS from droid_tx to droid_rx.
+        Verify SMS is sent, delivered and received.
+        Verify received content and sender's number are correct.
+
+    Args:
+        log: Log object.
+        ad_tx: Sender's Android Device Object..
+        ad_rx: Receiver's Android Device Object.
+        subid_tx: Sender's subsciption ID to be used for SMS
+        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 text in array_message:
+        log.info("Sending SMS {} to {}, len: {}, content: {}.".
+                 format(phonenumber_tx, phonenumber_rx, len(text), text))
+        result = False
+        ad_rx.ed.clear_all_events()
+        ad_rx.droid.smsStartTrackingIncomingSmsMessage()
+        try:
+            ad_tx.droid.smsSendTextMessage(phonenumber_rx, text, True)
+
+            try:
+                ad_tx.ed.pop_event(EventSmsSentSuccess, WAIT_TIME_SMS_SENT_SUCCESS)
+            except Empty:
+                log.error("No sent_success event.")
+                return False
+
+            if not wait_for_matching_sms(log, ad_rx, phonenumber_tx, text,
+                                allow_multi_part_long_sms=True):
+                return False
+        finally:
+            ad_rx.droid.smsStopTrackingIncomingSmsMessage()
+    return True
+
+def mms_send_receive_verify(log, ad_tx, ad_rx, array_message):
+    """Send SMS, receive SMS, and verify content and sender's number.
+
+        Send (several) SMS from droid_tx to droid_rx.
+        Verify SMS is sent, delivered and received.
+        Verify received content and sender's number are correct.
+
+    Args:
+        log: Log object.
+        ad_tx: Sender's Android Device Object
+        ad_rx: Receiver's Android Device Object
+        array_message: the array of message to send/receive
+    """
+    return mms_send_receive_verify_for_subscription(log, ad_tx, ad_rx,
+                     ad_tx.droid.subscriptionGetDefaultSmsSubId(),
+                     ad_rx.droid.subscriptionGetDefaultSmsSubId(),
+                     array_message)
+
+
+#FIXME: This function is still a WIP and is disabled
+def mms_send_receive_verify_for_subscription(log, ad_tx, ad_rx, subid_tx,
+        subid_rx, array_payload):
+    """Send SMS, receive SMS, and verify content and sender's number.
+
+        Send (several) SMS from droid_tx to droid_rx.
+        Verify SMS is sent, delivered and received.
+        Verify received content and sender's number are correct.
+
+    Args:
+        log: Log object.
+        ad_tx: Sender's Android Device Object..
+        ad_rx: Receiver's Android Device Object.
+        subid_tx: Sender's subsciption ID to be used for SMS
+        subid_rx: Receiver's subsciption ID to be used for SMS
+        array_message: the array of message to send/receive
+    """
+
+    log.error("Function is non-working: b/21569494")
+    return False
+
+    phonenumber_tx = ad_tx.cfg['subscription'][subid_tx]['phone_num']
+    phonenumber_rx = ad_rx.cfg['subscription'][subid_rx]['phone_num']
+    for subject, message, filename in array_payload:
+        log.info("Sending MMS {} to {}, subject: {}, message: {}.".
+                 format(phonenumber_tx, phonenumber_rx, subject, message))
+        result = False
+        ad_rx.ed.clear_all_events()
+        ad_rx.droid.smsStartTrackingIncomingMmsMessage()
+        ad_rx.droid.smsStartTrackingIncomingSmsMessage()
+        try:
+            ad_tx.droid.smsSendMultimediaMessage(
+                phonenumber_rx,
+                subject,
+                message,
+                phonenumber_tx,
+                filename
+            )
+
+            ad_tx.ed.pop_event(EventMmsSentSuccess, WAIT_TIME_SMS_SENT_SUCCESS)
+
+            start_time = time.time()
+            remaining_time = WAIT_TIME_SMS_RECEIVE
+            while remaining_time > 0:
+                event = ad_rx.ed.pop_event(EventSmsReceived, remaining_time)
+                if check_phone_number_match(
+                        event['data']['Sender'],
+                        phonenumber_tx):
+                    log.debug("Received SMS Indication")
+                    while remaining_time > 0:
+                        event = ad_rx.ed.pop_event(
+                            EventDataSmsReceived, remaining_time)
+                        if check_phone_number_match(
+                                event['data']['Sender'],
+                                phonenumber_tx):
+                                result = True
+                                break
+                        remaining_time = time.time() - start_time
+                remaining_time = time.time() - start_time
+
+            if not result:
+                log.info("Expected sender:" + phonenumber_tx)
+                log.error("Received sender:" + event['data']['Sender'])
+                log.error("Failed in verify receiving MMS.")
+                return False
+        finally:
+            ad_rx.droid.smsStopTrackingIncomingSmsMessage()
+            ad_rx.droid.smsStopTrackingIncomingMmsMessage()
+    return True
+
+
+def get_preferred_network_rat(log, ad):
+    """Get preferred network type settings for default subscription
+
+    Args:
+        ad: Android Device Object
+
+    Returns:
+        Current voice/data network type.
+    """
+    return get_preferred_network_rat_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def get_preferred_network_rat_for_subscription(log, ad, sub_id):
+    """Get preferred network type settings for specified subscription
+
+    Args:
+        ad: Android Device Object
+        sub_id: subscription ID
+
+    Returns:
+        Current voice/data network type.
+    """
+
+    ret_val = ad.droid.phoneGetPreferredNetworkTypeForSubscription(sub_id)
+
+    if ret_val is None:
+        log.error("get_preferred_network_rat(): Unexpected null return value")
+        return RAT_UNKNOWN
+    else:
+        return ret_val
+
+
+def get_network_rat(log, ad, voice_or_data):
+    """Get current network type (Voice network type, or data network type)
+       for default subscription id
+
+    Args:
+        ad: Android Device Object
+        voice_or_data: Input parameter indicating to get voice network type or
+            data network type.
+
+    Returns:
+        Current voice/data network type.
+    """
+    return get_network_rat_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
+
+
+def get_network_rat_for_subscription(log, ad, sub_id, voice_or_data):
+    """Get current network type (Voice network type, or data network type)
+       for specified subscription id
+
+    Args:
+        ad: Android Device Object
+        sub_id: subscription ID
+        voice_or_data: Input parameter indicating to get voice network type or
+            data network type.
+
+    Returns:
+        Current voice/data network type.
+    """
+    if voice_or_data == NETWORK_SERVICE_VOICE:
+        ret_val = ad.droid.getVoiceNetworkTypeForSubscription(sub_id)
+    elif voice_or_data == NETWORK_SERVICE_DATA:
+        ret_val = ad.droid.getDataNetworkTypeForSubscription(sub_id)
+    else:
+        ret_val = ad.droid.getNetworkTypeForSubscription(sub_id)
+
+    if ret_val is None:
+        log.error("get_network_rat(): Unexpected null return value")
+        return RAT_UNKNOWN
+    else:
+        return ret_val
+
+
+def get_network_gen(log, ad, voice_or_data):
+    """Get current network generation string (Voice network type, or data network type)
+
+    Args:
+        ad: Android Device Object
+        voice_or_data: Input parameter indicating to get voice network generation
+            or data network generation.
+
+    Returns:
+        Current voice/data network generation.
+    """
+    return get_network_gen_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
+
+def get_network_gen_for_subscription(log, ad, sub_id, voice_or_data):
+    """Get current network generation string (Voice network type, or data network type)
+
+    Args:
+        ad: Android Device Object
+        voice_or_data: Input parameter indicating to get voice network generation
+            or data network generation.
+
+    Returns:
+        Current voice/data network generation.
+    """
+    try:
+        return rat_generation_from_type(get_network_rat_for_subscription(log, ad,
+                                                          sub_id, voice_or_data))
+    except KeyError:
+        log.error("KeyError happened in get_network_gen, ad:{}, d/v: {}, rat: {}".
+            format(ad.serial, voice_or_data, get_network_rat_for_subscription(log, ad,
+                                                           sub_id, voice_or_data)))
+        return RAT_UNKNOWN
+
+def ensure_network_generation(log, ad, generation, max_wait_time=120,
+                              voice_or_data=None, toggle_apm_after_setting=True):
+    """Ensure ad's network is <network generation> for default subscription ID.
+
+    Set preferred network generation to <generation>.
+    Toggle ON/OFF airplane mode if necessary.
+    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)
+
+def check_voice_mail_count(log, ad, voice_mail_count_before, voice_mail_count_after):
+    """function to check if voice mail count is correct after leaving a new voice message.
+    """
+    return get_voice_mail_count_check_function(get_operator_name(log, ad))(
+        voice_mail_count_before, voice_mail_count_after)
+
+def get_voice_mail_number(log, ad):
+    """function to get the voice mail number
+    """
+    voice_mail_number = get_voice_mail_number_function(get_operator_name(log, ad))()
+    if voice_mail_number is None:
+        return get_phone_number(log, ad)
+    return voice_mail_number
+
+
+def ensure_network_generation_for_subscription(log, ad, sub_id, generation,
+                                               max_wait_time=120,
+                                               voice_or_data=None,
+                                               toggle_apm_after_setting=True):
+    """Ensure ad's network is <network generation> for specified subscription ID.
+
+    Set preferred network generation to <generation>.
+    Toggle ON/OFF airplane mode if necessary.
+    Wait for ad in expected network type.
+    """
+
+    cur_pref_generation = rat_generation_from_type(
+        get_preferred_network_rat_for_subscription(log, ad, sub_id))
+
+    if cur_pref_generation != generation:
+        log.info("Set {} Gen {}, Cur Gen {}".format(
+            ad.serial, generation, cur_pref_generation))
+        set_preferred_network_type_for_subscription(log, ad, sub_id, generation)
+
+    if (get_network_gen_for_subscription(log, ad, sub_id, voice_or_data) == generation):
+            return True
+
+    log.info("NW Gen - {} current: {}, expected: {}".
+             format(ad.serial, get_network_gen_for_subscription(log, ad, sub_id,
+                                                    voice_or_data), generation))
+    if toggle_apm_after_setting:
+        toggle_airplane_mode(log, ad, True)
+        toggle_airplane_mode(log, ad, False)
+
+    result = wait_for_droids_in_network_generation(
+        log, [ad], generation, max_wait_time, voice_or_data)
+
+    log.info("End of ensure_network_generation NW Gen - {} current: {}, expected: {}".
+        format(ad.serial, get_network_gen_for_subscription(log, ad, sub_id,
+                                                           voice_or_data), generation))
+
+    return result
+
+
+def ensure_network_rat(
+        log, ad, network_rat, max_wait_time=120, voice_or_data=None):
+    """Ensure ad's network is <network_type_string> for default subscription ID.
+
+    Set preferred network to <network_type_string>.
+    Toggle ON/OFF airplane mode if necessary.
+    Wait for ad in expected network type.
+    """
+    return ensure_network_rat_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId(), network_rat,
+        max_wait_time, voice_or_data)
+
+
+def ensure_network_rat_for_subscription(
+        log, ad, sub_id, network_rat, max_wait_time=120, voice_or_data=None):
+    """Ensure ad's network is <network_type_string> for specified subscription ID.
+
+    Set preferred network to <network_type_string>.
+    Toggle ON/OFF airplane mode if necessary.
+    Wait for ad in expected network type.
+    """
+
+    cur_pref_nw_type = get_preferred_network_rat_for_subscription(log, ad, sub_id)
+
+    if rat_family_from_type(cur_pref_nw_type) != rat_family_from_type(
+            network_rat):
+        log.info("Set {} Type {}, Cur Type {}".format(
+            ad.serial, network_rat, cur_pref_nw_type))
+        set_preferred_network_type_for_subscription(log, ad, sub_id, network_rat)
+
+    if (rat_family_from_type(get_network_rat_for_subscription(log, ad, sub_id, voice_or_data)) ==
+            rat_family_from_type(network_rat)):
+        return True
+
+    current_rat = get_network_rat(log, ad, voice_or_data)
+    log.info("NW RAT - {} current: {}(family: {}), expected: {}(family: {})".
+             format(ad.serial, current_rat, rat_family_from_type(current_rat),
+                    network_rat, rat_family_from_type(network_rat)))
+    toggle_airplane_mode(log, ad, True)
+    time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+    toggle_airplane_mode(log, ad, False)
+
+    result = wait_for_droid_in_network_rat_for_subscription(
+        log, ad, sub_id, network_rat, max_wait_time, voice_or_data)
+
+    current_rat = get_network_rat(log, ad, voice_or_data)
+    log.info("End of ensure_network_rat. NW RAT - {} current: {}(family: {}), "
+             "expected: {}(family: {})".
+             format(ad.serial, current_rat, rat_family_from_type(current_rat),
+                    network_rat, rat_family_from_type(network_rat)))
+
+    return result
+
+def ensure_phones_idle(log, ads,
+                       settling_time=WAIT_TIME_ANDROID_STATE_SETTLING):
+    """Ensure ads idle (not in call).
+    """
+    for ad in ads:
+        if ad.droid.telecomIsInCall():
+            ad.droid.telecomEndCall()
+    # Leave the delay time to make sure droid can recover to idle from ongoing call.
+    time.sleep(settling_time)
+    return True
+
+def ensure_phone_idle(log, ad,
+                      settling_time=WAIT_TIME_ANDROID_STATE_SETTLING):
+    """Ensure ad idle (not in call).
+    """
+    return ensure_phones_idle(log, [ad], settling_time)
+
+def ensure_phone_default_state(log, ad):
+    """Ensure ad in default state.
+    Phone not in call.
+    Phone have no stored WiFi network and WiFi disconnected.
+    Phone not in airplane mode.
+    """
+    result = True
+    if ad.droid.telecomIsInCall():
+        ad.droid.telecomEndCall()
+    set_wfc_mode(log, ad, WFC_MODE_DISABLED)
+
+    # FIXME: bug/23906084 We shouldn't force the device using modes unavailable
+    #        to a normal user
+    if is_droid_in_network_rat(log, ad, RAT_GSM, NETWORK_SERVICE_VOICE):
+        log.error("Device is stuck in GSM... Attempting to Un-stick")
+        ad.droid.setPreferredNetwork(NETWORK_MODE_LTE_ONLY)
+        if not wait_for_droid_in_network_rat(
+                log, ad, RAT_LTE, WAIT_TIME_NW_SELECTION):
+            if not wait_for_droid_in_network_rat(
+                    log, ad, RAT_WCDMA, WAIT_TIME_NW_SELECTION):
+                log.error("Device failed to un-stick from GSM."
+                            "Game over man, game over.")
+                result = False
+
+    if not wait_for_droid_not_in_network_rat(log, ad, RAT_IWLAN,
+                                             WAIT_TIME_NW_SELECTION,
+                                             NETWORK_SERVICE_DATA):
+        log.error("ensure_phones_default_state: wait_for_droid_not_in iwlan fail {}.".
+            format(ad.serial))
+        result = False
+    if ((not WifiUtils.wifi_reset(log, ad)) or
+        (not WifiUtils.wifi_toggle_state(log, ad, False))):
+        log.error("ensure_phones_default_state:reset WiFi fail {}.".
+            format(ad.serial))
+        result = False
+    if not toggle_airplane_mode(log, ad, False):
+        log.error("ensure_phones_default_state:turn off airplane mode fail {}.".
+            format(ad.serial))
+        result = False
+    # make sure phone data is on
+    ad.droid.toggleDataConnection(True)
+
+    # Leave the delay time to make sure droid can recover to idle from ongoing call.
+    time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+    return result
+
+def ensure_phones_default_state(log, ads):
+    """Ensure ads in default state.
+    Phone not in call.
+    Phone have no stored WiFi network and WiFi disconnected.
+    Phone not in airplane mode.
+    """
+    tasks = []
+    for ad in ads:
+        tasks.append((ensure_phone_default_state, (log, ad)))
+    if not multithread_func(log, tasks):
+        log.error("Ensure_phones_default_state Fail.")
+        return False
+    return True
+
+def ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd=None, retry=1):
+    """Ensure ad connected to wifi.
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        wifi_ssid: WiFi network SSID.
+        wifi_pwd: WiFi network password. This is optional.
+
+    """
+    while(retry >= 0):
+        WifiUtils.wifi_reset(log, ad)
+        WifiUtils.wifi_toggle_state(log, ad, False)
+        WifiUtils.wifi_toggle_state(log, ad, True)
+        if WifiUtils.wifi_connect(log, ad, wifi_ssid, wifi_pwd):
+            return True
+        else:
+            log.info("ensure_wifi_connected: Connect WiFi failed, retry + 1.")
+            retry -= 1
+    return False
+
+def task_wrapper(task):
+    """Task wrapper for multithread_func
+
+    Args:
+        task[0]: function to be wrapped.
+        task[1]: function args.
+
+    Returns:
+        Return value of wrapped function call.
+    """
+    func = task[0]
+    params = task[1]
+    return func(*params)
+
+def multithread_func(log, tasks):
+    """Multi-thread function wrapper.
+
+    Args:
+        log: log object.
+        tasks: tasks to be executed in parallel.
+
+    Returns:
+        True if all tasks return True.
+        False if any task return False.
+    """
+    MAX_NUMBER_OF_WORKERS = 4
+    number_of_workers = min(MAX_NUMBER_OF_WORKERS, len(tasks))
+    executor = concurrent.futures.ThreadPoolExecutor(max_workers=number_of_workers)
+    results = list(executor.map(task_wrapper, tasks))
+    executor.shutdown()
+    log.info("multithread_func result: {}".format(results))
+    for r in results:
+        if not r:
+            return False
+    return True
+
+def set_phone_screen_on(log, ad, screen_on_time=MAX_SCREEN_ON_TIME):
+    """Set phone screen on time.
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        screen_on_time: screen on time.
+            This is optional, default value is MAX_SCREEN_ON_TIME.
+    Returns:
+        True if set successfully.
+    """
+    ad.droid.setScreenTimeout(screen_on_time)
+    return screen_on_time == ad.droid.getScreenTimeout()
+
+def set_phone_silent_mode(log, ad, silent_mode=True):
+    """Set phone silent mode.
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        silent_mode: set phone silent or not.
+            This is optional, default value is True (silent mode on).
+    Returns:
+        True if set successfully.
+    """
+    ad.droid.toggleRingerSilentMode(silent_mode)
+    return silent_mode == ad.droid.checkRingerSilentMode()
+
+def set_preferred_subid_for_sms(log, ad, sub_id):
+    """set subscription id for SMS
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        sub_id :Subscription ID.
+
+    """
+    log.info("Setting subscription:{} as Message SIM for {}".format(
+             sub_id, ad.serial))
+    ad.droid.subscriptionSetDefaultSmsSubId(sub_id)
+    # Wait to make sure settings take effect
+    time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+    return sub_id == ad.droid.subscriptionGetDefaultSmsSubId()
+
+def set_preferred_subid_for_data(log, ad, sub_id):
+    """set subscription id for data
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        sub_id :Subscription ID.
+
+    """
+    log.info("Setting subscription:{} as Data SIM for {}".format(
+             sub_id, ad.serial))
+    ad.droid.subscriptionSetDefaultDataSubId(sub_id)
+    time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+    # Wait to make sure settings take effect
+    # Data SIM change takes around 1 min
+    # Check whether data has changed to selected sim
+    if not wait_for_data_connection(log, ad, True, WAIT_TIME_DATA_SUB_CHANGE):
+        log.error("Data Connection failed - Not able to switch Data SIM")
+        return False
+    return True
+
+def set_preferred_subid_for_voice(log, ad, sub_id):
+    """set subscription id for voice
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        sub_id :Subscription ID.
+
+    """
+    log.info("Setting subscription:{} as Voice SIM for {}".format(
+             sub_id, ad.serial))
+    ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
+    ad.droid.telecomSetUserSelectedOutgoingPhoneAccountBySubId(sub_id)
+    # Wait to make sure settings take effect
+    time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+    return True
+
+def set_call_state_listen_level(log, ad, value, sub_id):
+    """Set call state listen level for subscription id.
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        value: True or False
+        sub_id :Subscription ID.
+
+    Returns:
+        True or False
+    """
+    if sub_id == INVALID_SUB_ID:
+        log.error("Invalid Subscription ID")
+        return False
+    ad.droid.phoneAdjustPreciseCallStateListenLevelForSubscription(
+               "Foreground", value, sub_id)
+    ad.droid.phoneAdjustPreciseCallStateListenLevelForSubscription(
+               "Ringing", value, sub_id)
+    ad.droid.phoneAdjustPreciseCallStateListenLevelForSubscription(
+               "Background", value, sub_id)
+    return True
+
+def setup_sim(log, ad, sub_id, voice=False, sms=False, data=False):
+    """set subscription id for voice, sms and data
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        sub_id :Subscription ID.
+        voice: True if to set subscription as default voice subscription
+        sms: True if to set subscription as default sms subscription
+        data: True if to set subscription as default data subscription
+
+    """
+    if sub_id == INVALID_SUB_ID:
+        log.error("Invalid Subscription ID")
+        return False
+    else:
+        if voice:
+            if not set_preferred_subid_for_voice(log, ad, sub_id):
+                return False
+        if sms:
+            if not set_preferred_subid_for_sms(log, ad, sub_id):
+                return False
+        if data:
+            if not set_preferred_subid_for_data(log, ad,sub_id):
+                return False
+    return True
+
+def get_sub_ids_for_sim_slots(log, ads):
+    """get subscription id for each sim slots avaialble in all devices
+       in  ads
+
+    Args:
+        log: Log object.
+        ads: Android device object list.
+
+    Returns:
+        list of sub ids for all devices.
+        it is a matrix.
+        eg: sim_sub_ids[0][0] is sub id for sim1 in device 1
+    """
+    sim_sub_ids = []
+    for index in range(len(ads)):
+        sim_sub_ids.append([])
+        sim_count = ads[index].droid.getSimCount()
+        for count in range(sim_count):
+            subid = get_subid_from_slot_index(log, ads[index], count)
+            sim_sub_ids[index].append(subid)
+    return sim_sub_ids
+
+def is_sub_event_match(event, sub_event):
+    """Return if subEvent field in "event" match "sub_event" or not.
+
+    Args:
+        event: event to test. This event need to have 'subEvent' field.
+        sub_event: sub_event to match.
+
+    Returns:
+        True if subEvent field in "event" match "sub_event".
+        False otherwise.
+    """
+    return is_sub_event_match_for_sub_event_list(event, [sub_event])
+
+def is_sub_event_match_for_sub_event_list(event, sub_event_list):
+    """Return if subEvent field in "event" match any one of the sub_event
+        in "sub_event_list" or not.
+
+    Args:
+        event: event to test. This event need to have 'subEvent' field.
+        sub_event_list: a list of sub_event to match.
+
+    Returns:
+        True if subEvent field in "event" match one of the sub_event in "sub_event_list".
+        False otherwise.
+    """
+    try:
+        sub = event['data']['subEvent']
+    except KeyError:
+        return False
+    for sub_event in sub_event_list:
+        if sub == sub_event:
+            return True
+    return False
+
+def is_network_call_back_event_match(event, network_callback_id, sub_event):
+    try:
+        return ((network_callback_id == event['data']['id']) and
+                (sub_event == event['data']['subEvent']))
+    except KeyError:
+        return False
+
+def is_build_id(log, ad, build_id):
+    """Return if ad's build id is the same as input parameter build_id.
+
+    Args:
+        log: log object.
+        ad: android device object.
+        build_id: android build id.
+
+    Returns:
+        True if ad's build id is the same as input parameter build_id.
+        False otherwise.
+    """
+    actual_bid = ad.droid.getBuildID()
+
+    log.info("{} BUILD DISPLAY: {}"
+             .format(ad.serial, ad.droid.getBuildDisplay()))
+    #In case we want to log more stuff/more granularity...
+    #log.info("{} BUILD ID:{} ".format(ad.serial, ad.droid.getBuildID()))
+    #log.info("{} BUILD FINGERPRINT: {} "
+    # .format(ad.serial), ad.droid.getBuildFingerprint())
+    #log.info("{} BUILD TYPE: {} "
+    # .format(ad.serial), ad.droid.getBuildType())
+    #log.info("{} BUILD NUMBER: {} "
+    # .format(ad.serial), ad.droid.getBuildNumber())
+    if actual_bid.upper() != build_id.upper():
+        log.error("{}: Incorrect Build ID".format(ad.model))
+        return False
+    return True
+
+def is_uri_equivalent(uri1, uri2):
+    """Check whether two input uris match or not.
+
+    Compare Uris.
+        If Uris are tel URI, it will only take the digit part
+        and compare as phone number.
+        Else, it will just do string compare.
+
+    Args:
+        uri1: 1st uri to be compared.
+        uri2: 2nd uri to be compared.
+
+    Returns:
+        True if two uris match. Otherwise False.
+    """
+    if uri1.startswith('tel:') and uri2.startswith('tel:'):
+        uri1_number = ''.join(i for i in urllib.parse.unquote(uri1) if i.isdigit())
+        uri2_number = ''.join(i for i in urllib.parse.unquote(uri2) if i.isdigit())
+        return check_phone_number_match(uri1_number, uri2_number)
+    else:
+        return uri1 == uri2
+
+def get_call_uri(ad, call_id):
+    """Get call's uri field.
+
+    Get Uri for call_id in ad.
+
+    Args:
+        ad: android device object.
+        call_id: the call id to get Uri from.
+
+    Returns:
+        call's Uri if call is active and have uri field. None otherwise.
+    """
+    try:
+        call_detail = ad.droid.telecomCallGetDetails(call_id)
+        return call_detail["Handle"]["Uri"]
+    except:
+        return None
+
+# FIXME: Remove wrapper class once wifi_utils methods updated
+class WifiUtils():
+
+    from acts.test_utils.wifi_test_utils \
+        import reset_droid_wifi as _reset_droid_wifi
+    from acts.test_utils.wifi_test_utils \
+        import wifi_connect as _wifi_connect
+    from acts.test_utils.wifi_test_utils \
+        import wifi_toggle_state as _wifi_toggle_state
+    from acts.test_utils.wifi_test_utils \
+        import start_wifi_tethering as _start_wifi_tethering
+    from acts.test_utils.wifi_test_utils \
+        import stop_wifi_tethering as _stop_wifi_tethering
+    from acts.test_utils.wifi_test_utils \
+        import WifiEnums as _WifiEnums
+
+    WIFI_CONFIG_APBAND_2G = _WifiEnums.WIFI_CONFIG_APBAND_2G
+    WIFI_CONFIG_APBAND_5G = _WifiEnums.WIFI_CONFIG_APBAND_5G
+    SSID_KEY = _WifiEnums.SSID_KEY
+
+    @staticmethod
+    def wifi_toggle_state(log, ad, state):
+        try:
+            WifiUtils._wifi_toggle_state(ad.droid, ad.ed, state)
+        except Exception as e:
+            log.error("WifiUtils.wifi_toggle_state exception: {}".format(e))
+            return False
+        return True
+
+    @staticmethod
+    def wifi_reset(log, ad, disable_wifi=True):
+        try:
+            WifiUtils._reset_droid_wifi(ad.droid, ad.ed)
+        except Exception as e:
+            log.error("WifiUtils.wifi_reset exception: {}".format(e))
+            return False
+        finally:
+            if disable_wifi is True:
+                ad.droid.wifiToggleState(False)
+                # Ensure toggle state has human-time to take effect
+            time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        return True
+
+    @staticmethod
+    def wifi_connect(log, ad, ssid, password=None):
+        if password == "":
+            password = None
+        try:
+            return WifiUtils._wifi_connect(ad, ssid, password)
+        except Empty:
+            # did not get event, then check connection info
+            try:
+                if ad.droid.wifiGetConnectionInfo()[WifiUtils.SSID_KEY] == ssid:
+                    return True
+                else:
+                    log.error("WifiUtils.wifi_connect not connected."
+                        "No event received. Expected SSID: {}, current SSID:{}".
+                        format(ssid,
+                            ad.droid.wifiGetConnectionInfo()[WifiUtils.SSID_KEY]))
+                    return False
+            except Exception as e:
+                log.error("WifiUtils.wifi_connect not connected, no event.")
+                return False
+        except Exception as e:
+            log.error("WifiUtils.wifi_connect exception: {}".format(e))
+            return False
+
+    @staticmethod
+    def start_wifi_tethering(log, ad, ssid, password, ap_band=None):
+        try:
+            return WifiUtils._start_wifi_tethering(ad, ssid, password, ap_band)
+        except Exception as e:
+            log.error("WifiUtils.start_wifi_tethering exception: {}".format(e))
+            return False
+
+    @staticmethod
+    def stop_wifi_tethering(log, ad):
+        try:
+            WifiUtils._stop_wifi_tethering(ad)
+            return True
+        except Exception as e:
+            log.error("WifiUtils.stop_wifi_tethering exception: {}".format(e))
+            return False
diff --git a/acts/framework/acts/test_utils/tel/tel_video_utils.py b/acts/framework/acts/test_utils/tel/tel_video_utils.py
new file mode 100644
index 0000000..d8895b1
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/tel_video_utils.py
@@ -0,0 +1,837 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts.test_utils.tel.tel_lookup_tables import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+
+def phone_setup_video(log, ad):
+    """Setup phone default sub_id to make video call
+
+    Args:
+        log: log object.
+        ad: android device object
+
+    Returns:
+        True if ad (default sub_id) is setup correctly and idle for video call.
+    """
+    return phone_setup_video_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_setup_video_for_subscription(log, ad, sub_id):
+    """Setup phone sub_id to make video call
+
+    Args:
+        log: log object.
+        ad: android device object
+        sub_id: ad's sub id.
+
+    Returns:
+        True if ad (sub_id) is setup correctly and idle for video call.
+    """
+    toggle_airplane_mode(log, ad, False)
+    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+        log.error("{} Disable WFC failed.".format(ad.serial))
+        return False
+    toggle_volte_for_subscription(log, ad, sub_id, True)
+    if not ensure_network_rat_for_subscription(
+            log, ad, sub_id, RAT_LTE, WAIT_TIME_NW_SELECTION,
+            NETWORK_SERVICE_DATA):
+        log.error("{} failed to select LTE (data) within {}s.".
+                  format(ad.serial, WAIT_TIME_NW_SELECTION))
+        return False
+    return phone_idle_video_for_subscription(log, ad, sub_id)
+
+
+def phone_idle_video(log, ad):
+    """Return if phone (default sub_id) is idle for video call.
+
+    Args:
+        log: log object.
+        ad: android device object
+
+    Returns:
+        True if ad is idle for video call.
+    """
+    return phone_idle_video_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+def phone_idle_video_for_subscription(log, ad, sub_id):
+    """Return if phone (sub_id) is idle for video call.
+
+    Args:
+        log: log object.
+        ad: android device object
+        sub_id: ad's sub id
+
+    Returns:
+        True if ad (sub_id) is idle for video call.
+    """
+    if not wait_for_droid_in_network_rat_for_subscription(
+            log, ad, sub_id, RAT_LTE, WAIT_TIME_NW_SELECTION,
+            NETWORK_SERVICE_VOICE):
+        log.error("{} voice not in LTE mode.".format(ad.serial))
+        return False
+    if not wait_for_video_enabled(log, ad, WAIT_TIME_VOLTE_ENABLED):
+        log.error("{} failed to <report volte enabled true> within {}s.".
+                  format(ad.serial, WAIT_TIME_VOLTE_ENABLED))
+        return False
+    return True
+
+def is_phone_in_call_video(log, ad):
+    """Return if ad is in a video call (in expected video state).
+
+    Args:
+        log: log object.
+        ad: android device object
+        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 (for sub_id) is in a video call (in expected video state).
+    """
+    return is_phone_in_call_video_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+def is_phone_in_call_video_for_subscription(log, ad, sub_id, video_state=None):
+    """Return if ad (for sub_id) is in a video 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
+    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_PAUSED: False,
+                VT_STATE_STATE_INVALID: False
+            }[state]:
+                return True
+        else:
+            if state == video_state:
+                return True
+        log.info("Non-Video-State: {}".format(state))
+    log.error("Phone not in video call. Call list: {}".format(call_list))
+    return False
+
+def is_phone_in_call_video_bidirectional(log, ad):
+    """Return if phone in bi-directional video call.
+
+    Args:
+        log: log object.
+        ad: android device object
+
+    Returns:
+        True if phone in bi-directional video call.
+    """
+    return is_phone_in_call_video_bidirectional_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+def is_phone_in_call_video_bidirectional_for_subscription(log, ad, sub_id):
+    """Return if phone in bi-directional video call for subscription id.
+
+    Args:
+        log: log object.
+        ad: android device object
+        sub_id: subscription id.
+
+    Returns:
+        True if phone in bi-directional video call.
+    """
+    log.info("Verify if {}(subid {}) in bi-directional video call.".
+             format(ad.serial, sub_id))
+    return is_phone_in_call_video_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.
+
+    Args:
+        log: log object.
+        ad: android device object
+
+    Returns:
+        True if phone in tx_enabled video call.
+    """
+    return is_phone_in_call_video_tx_enabled_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+def is_phone_in_call_video_tx_enabled_for_subscription(log, ad, sub_id):
+    """Return if phone in tx_enabled video call for subscription id.
+
+    Args:
+        log: log object.
+        ad: android device object
+        sub_id: subscription id.
+
+    Returns:
+        True if phone in tx_enabled video call.
+    """
+    log.info("Verify if {}(subid {}) in tx_enabled video call.".
+             format(ad.serial, sub_id))
+    return is_phone_in_call_video_for_subscription(log, ad, sub_id,
+                                                   VT_STATE_TX_ENABLED)
+
+def is_phone_in_call_video_rx_enabled(log, ad):
+    """Return if phone in rx_enabled video call.
+
+    Args:
+        log: log object.
+        ad: android device object
+
+    Returns:
+        True if phone in rx_enabled video call.
+    """
+    return is_phone_in_call_video_rx_enabled_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+def is_phone_in_call_video_rx_enabled_for_subscription(log, ad, sub_id):
+    """Return if phone in rx_enabled video call for subscription id.
+
+    Args:
+        log: log object.
+        ad: android device object
+        sub_id: subscription id.
+
+    Returns:
+        True if phone in rx_enabled video call.
+    """
+    log.info("Verify if {}(subid {}) in rx_enabled video call.".
+             format(ad.serial, sub_id))
+    return is_phone_in_call_video_for_subscription(log, ad, sub_id,
+                                                   VT_STATE_RX_ENABLED)
+
+def is_phone_in_call_voice_hd(log, ad):
+    """Return if phone in hd voice call.
+
+    Args:
+        log: log object.
+        ad: android device object
+
+    Returns:
+        True if phone in hd voice call.
+    """
+    return is_phone_in_call_voice_hd_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+def is_phone_in_call_voice_hd_for_subscription(log, ad, sub_id):
+    """Return if phone in hd voice call for subscription id.
+
+    Args:
+        log: log object.
+        ad: android device object
+        sub_id: subscription id.
+
+    Returns:
+        True if phone in hd voice call.
+    """
+    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
+    for call in ad.droid.telecomCallGetCallIds():
+        state = ad.droid.telecomCallVideoGetState(call)
+        if (state == VT_STATE_AUDIO_ONLY and is_call_hd(log, ad, call)):
+            return True
+        log.info("Non-HDAudio-State: {}, property: {}".
+                 format(state, ad.droid.telecomCallGetProperties(call)))
+    return False
+
+def initiate_video_call(log, ad_caller, callee_number):
+    """Make phone call from caller to callee.
+
+    Args:
+        ad_caller: Caller android device object.
+        callee_number: Callee phone number.
+        emergency : specify the call is emergency.
+            Optional. Default value is False.
+
+    Returns:
+        result: if phone call is placed successfully.
+    """
+
+    wait_time_for_incall_state = WAIT_TIME_CALL_INITIATION
+    ad_caller.droid.phoneCallNumber(callee_number, True)
+    while wait_time_for_incall_state > 0:
+        wait_time_for_incall_state -= 1
+        if ad_caller.droid.telecomIsInCall():
+            return True
+        time.sleep(1)
+    log.error("Make call fail.")
+    return False
+
+
+def wait_and_answer_video_call(
+        log, ad, incoming_number=None,
+        delay_answer=WAIT_TIME_ANSWER_VIDEO_CALL,
+        video_state=VT_STATE_BIDIRECTIONAL,
+        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+    """Wait for an incoming call on default voice subscription and
+       accepts the call.
+
+    Args:
+        ad: android device object.
+        incoming_number: Expected incoming number.
+            Optional. Default is None
+        delay_answer: time to wait before answering the call
+            Optional. Default is 1
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+
+    Returns:
+        True: if incoming call is received and answered successfully.
+        False: for errors
+    """
+    return wait_and_answer_video_call_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId(),
+        incoming_number, delay_answer, video_state, incall_ui_display)
+
+
+def wait_and_answer_video_call_for_subscription(
+        log, ad, sub_id, incoming_number=None,
+        delay_answer=WAIT_TIME_ANSWER_VIDEO_CALL,
+        video_state=VT_STATE_BIDIRECTIONAL,
+        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+    """Wait for an incoming call on specified subscription and
+       accepts the call.
+
+    Args:
+        ad: android device object.
+        sub_id: subscription ID
+        incoming_number: Expected incoming number.
+            Optional. Default is None
+        delay_answer: time to wait befor answering the call
+            Optional. Default is 1
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+
+    Returns:
+        True: if incoming call is received and answered successfully.
+        False: for errors
+    """
+    ad.ed.clear_all_events()
+    ad.droid.phoneStartTrackingCallStateForSubscription(sub_id)
+    if (not ad.droid.telecomIsRinging() and
+            ad.droid.getCallStateForSubscription(sub_id) != TELEPHONY_STATE_RINGING):
+        try:
+            event_ringing = wait_for_ringing_event(log, ad,
+                                                   WAIT_TIME_CALLEE_RINGING)
+            if event_ringing is None:
+                log.error("No Ringing Event.")
+                return False
+        finally:
+            ad.droid.phoneStopTrackingCallStateChangeForSubscription(sub_id)
+
+        if not incoming_number:
+            result = True
+        else:
+            result = check_phone_number_match(
+                event_ringing['data']['incomingNumber'], incoming_number)
+
+        if not result:
+            log.error("Incoming Number not match")
+            log.error("Expected number:{}, actual number:{}".
+                      format(incoming_number,
+                             event_ringing['data']['incomingNumber']))
+            return False
+
+    ad.ed.clear_all_events()
+
+    ringing_call_id = None
+
+    #FIXME: magic polling loop
+    for i in range(10):
+        calls = ad.droid.telecomCallGetCallIds()
+        for call in calls:
+            call_state = ad.droid.telecomCallGetCallState(call)
+            if call_state == CALL_STATE_RINGING:
+                ringing_call_id = call
+                break
+        #FIXME: magic number is magical
+        time.sleep(.2)
+
+    if not ringing_call_id:
+        log.error("Couldn't find a ringing call by ID!")
+        return False
+
+    ad.droid.phoneStartTrackingCallStateForSubscription(sub_id)
+    # Delay between ringing and answer.
+    time.sleep(delay_answer)
+
+    log.info("Accept on callee.")
+    ad.droid.telecomCallAnswer(ringing_call_id, video_state)
+
+    try:
+        ad.ed.wait_for_event(EventCallStateChanged,
+                             is_sub_event_match,
+                             timeout=WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
+                             sub_event=TELEPHONY_STATE_OFFHOOK)
+    except Empty:
+        if not ad.droid.telecomIsInCall():
+            log.error("Accept call failed.")
+            return False
+    finally:
+        ad.droid.phoneStopTrackingCallStateChangeForSubscription(sub_id)
+    if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
+        ad.droid.telecomShowInCallScreen()
+    elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
+        ad.droid.showHomeScreen()
+    return True
+
+
+def video_call_setup_teardown(
+        log, ad_caller, ad_callee, ad_hangup=None,
+        video_state=VT_STATE_BIDIRECTIONAL, verify_caller_func=None,
+        verify_callee_func=None, wait_time_in_call=WAIT_TIME_IN_CALL,
+        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+    """ Call process, including make a phone call from caller,
+    accept from callee, and hang up. The call is on default subscription
+
+    In call process, call from <droid_caller> to <droid_callee>,
+    after ringing, wait <delay_answer> to accept the call,
+    (optional)then hang up from <droid_hangup>.
+
+    Args:
+        ad_caller: Caller Android Device Object.
+        ad_callee: Callee Android Device Object.
+        ad_hangup: Android Device Object end the phone call.
+            Optional. Default value is None, and phone call will continue.
+        video_state: video state for VT call.
+            Optional. Default value is VT_STATE_BIDIRECTIONAL
+        verify_caller_func: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        verify_callee_func: func_ptr to verify callee in correct mode
+            Optional. Default is None
+        wait_time_in_call: wait time during call.
+            Optional. Default is WAIT_TIME_IN_CALL.
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+
+    Returns:
+        True if call process without any error.
+        False if error happened.
+
+    """
+    return video_call_setup_teardown_for_subscription(
+        log, ad_caller, ad_callee,
+        ad_caller.droid.subscriptionGetDefaultVoiceSubId(),
+        ad_callee.droid.subscriptionGetDefaultVoiceSubId(),
+        ad_hangup, video_state, verify_caller_func,
+        verify_callee_func, wait_time_in_call,
+        incall_ui_display)
+
+
+#TODO: Might be able to refactor call_setup_teardown and add. Minimal changes.
+def video_call_setup_teardown_for_subscription(
+        log, ad_caller, ad_callee, subid_caller, subid_callee, ad_hangup=None,
+        video_state=VT_STATE_BIDIRECTIONAL, verify_caller_func=None,
+        verify_callee_func=None, wait_time_in_call=WAIT_TIME_IN_CALL,
+        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+    """ Call process, including make a phone call from caller,
+    accept from callee, and hang up. The call is on specified subscription
+
+    In call process, call from <droid_caller> to <droid_callee>,
+    after ringing, wait <delay_answer> to accept the call,
+    (optional)then hang up from <droid_hangup>.
+
+    Args:
+        ad_caller: Caller Android Device Object.
+        ad_callee: Callee Android Device Object.
+        subid_caller: Caller subscription ID
+        subid_callee: Callee subscription ID
+        ad_hangup: Android Device Object end the phone call.
+            Optional. Default value is None, and phone call will continue.
+        video_state: video state for VT call.
+            Optional. Default value is VT_STATE_BIDIRECTIONAL
+        verify_caller_func: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        verify_callee_func: func_ptr to verify callee in correct mode
+            Optional. Default is None
+        wait_time_in_call: wait time during call.
+            Optional. Default is WAIT_TIME_IN_CALL.
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+
+    Returns:
+        True if call process without any error.
+        False if error happened.
+
+    """
+    class _CallSequenceException(Exception):
+        pass
+
+    caller_number = ad_caller.cfg['subscription'][subid_caller]['phone_num']
+    callee_number = ad_callee.cfg['subscription'][subid_callee]['phone_num']
+
+    log.info("Call from {} to {}".format(caller_number, callee_number))
+
+    try:
+        if not initiate_video_call(log, ad_caller, callee_number):
+            raise _CallSequenceException("Initiate call failed.")
+
+        if not wait_and_answer_video_call_for_subscription(
+                log, ad_callee, subid_callee, incoming_number=caller_number,
+                video_state=video_state,
+                incall_ui_display=incall_ui_display):
+            raise _CallSequenceException("Answer call fail.")
+
+        # ensure that all internal states are updated in telecom
+        # FIXME: more magic sleeps
+        time.sleep(2)
+
+        # Check if caller/callee dropped call. This is temporary code.
+        if not verify_incall_state(log, [ad_callee, ad_caller], True):
+            raise _CallSequenceException("Call Drop!")
+        # Check Callee first
+        # in case of VT call drop, it usually start from callee
+        if verify_callee_func and not verify_callee_func(log, ad_callee):
+            raise _CallSequenceException(
+                "Callee not in correct state!")
+        if verify_caller_func and not verify_caller_func(log, ad_caller):
+            raise _CallSequenceException(
+                "Caller not in correct state!")
+
+        #FIXME: Replace with reducing the volume as we want
+        # to test route switching
+        ad_caller.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+        ad_callee.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+
+        time.sleep(wait_time_in_call)
+
+        # Check Callee first
+        # in case of VT call drop, it usually start from callee
+        if not verify_callee_func:
+            callee_state_result = ad_callee.droid.telecomIsInCall()
+        else:
+            callee_state_result = verify_callee_func(log, ad_callee)
+        if not callee_state_result:
+            raise _CallSequenceException(
+                "Callee not in correct state after {} seconds"
+                .format(wait_time_in_call))
+
+        if not verify_caller_func:
+            caller_state_result = ad_caller.droid.telecomIsInCall()
+        else:
+            caller_state_result = verify_caller_func(log, ad_caller)
+        if not caller_state_result:
+            raise _CallSequenceException(
+                "Caller not in correct state after {} seconds"
+                .format(wait_time_in_call))
+
+        if not ad_hangup:
+            return True
+
+        if not hangup_call(log, ad_hangup):
+            raise _CallSequenceException(
+                "Error in Hanging-Up Call")
+        return True
+
+    except _CallSequenceException as e:
+        log.error(e)
+        return False
+    finally:
+        if ad_hangup:
+            for ad in [ad_caller, ad_callee]:
+                try:
+                    if ad.droid.telecomIsInCall():
+                        ad.droid.telecomEndCall()
+                except Exception as e:
+                    log.error(str(e))
+
+
+def video_call_modify_video(
+        log, ad_requester, call_id_requester, ad_responder, call_id_responder,
+        video_state_request, video_quality_request=VT_VIDEO_QUALITY_DEFAULT,
+        video_state_response=None, video_quality_response=None,
+        verify_func_between_request_and_response=None):
+    """Modifies an ongoing call to change the video_call state
+
+    Args:
+        log: logger object
+        ad_requester: android_device object of the requester
+        call_id_requester: the call_id of the call placing the modify request
+        ad_requester: android_device object of the responder
+        call_id_requester: the call_id of the call receiving the modify request
+        video_state_request: the requested video state
+        video_quality_request: the requested video quality, defaults to
+            QUALITY_DEFAULT
+        video_state_response: the responded video state or, or (default)
+            match the request if None
+        video_quality_response: the responded video quality, or (default)
+            match the request if None
+
+    Returns:
+        A call_id corresponding to the first call in the state, or None
+    """
+
+    if not video_state_response:
+        video_state_response = video_state_request
+    if not video_quality_response:
+        video_quality_response = video_quality_request
+
+    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))
+
+    if cur_video_state == video_state_request:
+        return True
+
+    ad_responder.ed.clear_events(
+        EventTelecomVideoCallSessionModifyRequestReceived)
+
+    ad_responder.droid.telecomCallVideoStartListeningForEvent(
+        call_id_responder, EventSessionModifyRequestRceived
+    )
+
+    ad_requester.droid.telecomCallVideoSendSessionModifyRequest(
+        call_id_requester, video_state_request, video_quality_request)
+
+    try:
+        request_event = ad_responder.ed.pop_event(
+            EventTelecomVideoCallSessionModifyRequestReceived,
+            WAIT_TIME_VIDEO_SESSION_EVENT)
+        log.info(request_event)
+    except Empty:
+        log.error("Failed to receive SessionModifyRequest!")
+        return False
+    finally:
+        ad_responder.droid.telecomCallVideoStopListeningForEvent(
+            call_id_responder, EventSessionModifyRequestRceived
+        )
+
+    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
+
+    #FIXME: Replace with reducing the volume as we want to test route switching
+    ad_requester.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+
+    ad_requester.droid.telecomCallVideoStartListeningForEvent(
+        call_id_requester, EventSessionModifyResponsetRceived)
+
+    ad_responder.droid.telecomCallVideoSendSessionModifyResponse(
+        call_id_responder, video_state_response, video_quality_response)
+
+    try:
+        response_event = ad_requester.ed.pop_event(
+            EventTelecomVideoCallSessionModifyResponseReceived,
+            WAIT_TIME_VIDEO_SESSION_EVENT)
+        log.info(response_event)
+    except Empty:
+        log.error("Failed to receive SessionModifyResponse!")
+        return False
+    finally:
+        ad_requester.droid.telecomCallVideoStopListeningForEvent(
+            call_id_requester, EventSessionModifyResponsetRceived)
+
+    #FIXME: Replace with reducing the volume as we want to test route switching
+    ad_responder.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+
+    return True
+
+def is_call_id_in_video_state(log, ad, call_id, video_state):
+    """Return is the call_id is in expected video_state
+
+    Args:
+        log: logger object
+        ad: android_device object
+        call_id: call id
+        video_state: valid VIDEO_STATE
+
+    Returns:
+        True is call_id in expected video_state; False if not.
+    """
+    return video_state == ad.droid.telecomCallVideoGetState(call_id)
+
+def get_call_id_in_video_state(log, ad, video_state):
+    """Gets the first call reporting a given video_state
+        from among the active calls
+
+    Args:
+        log: logger object
+        ad: android_device object
+        video_state: valid VIDEO_STATE
+
+    Returns:
+        A call_id corresponding to the first call in the state, or None
+    """
+
+    if not ad.droid.telecomIsInCall():
+        log.error("{} not in call.".format(ad.serial))
+        return None
+    for call in ad.droid.telecomCallGetCallIds():
+        if is_call_id_in_video_state(log, ad, call, video_state):
+            return call
+    return None
+
+def video_call_downgrade(log,
+                         ad_requester, call_id_requester,
+                         ad_responder, call_id_responder,
+                         video_state_request=None,
+                         video_quality_request=VT_VIDEO_QUALITY_DEFAULT):
+    """Downgrade Video call to video_state_request.
+    Send telecomCallVideoSendSessionModifyRequest from ad_requester.
+    Get video call state from ad_requester and ad_responder.
+    Verify video calls states are correct and downgrade succeed.
+
+    Args:
+        log: logger object
+        ad_requester: android_device object of the requester
+        call_id_requester: the call_id of the call placing the modify request
+        ad_requester: android_device object of the responder
+        call_id_requester: the call_id of the call receiving the modify request
+        video_state_request: the requested downgrade video state
+            This parameter is optional. If this parameter is None:
+                if call_id_requester current is bi-directional, will downgrade to RX_ENABLED
+                if call_id_requester current is RX_ENABLED, will downgrade to AUDIO_ONLY
+        video_quality_request: the requested video quality, defaults to
+            QUALITY_DEFAULT
+    Returns:
+        True if downgrade succeed.
+    """
+    # TODO(yangxliu): This util may need further change.
+    if (call_id_requester is None) or (call_id_responder is None):
+        log.error("call_id_requester: {}, call_id_responder: {}".
+            format(call_id_requester, call_id_responder))
+        return False
+    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 == 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):
+            video_state_request = VT_STATE_AUDIO_ONLY
+        else:
+            log.error(
+                "Can Not Downgrade. ad: {}, current state {}"
+                .format(ad_requester.serial, current_video_state_requester))
+            return False
+    expected_video_state_responder = {
+        VT_STATE_AUDIO_ONLY: VT_STATE_AUDIO_ONLY,
+        VT_STATE_RX_ENABLED: VT_STATE_TX_ENABLED
+    }[video_state_request]
+
+    ad_requester.droid.telecomCallVideoStartListeningForEvent(
+        call_id_requester, EventSessionModifyResponsetRceived)
+
+    ad_requester.droid.telecomCallVideoSendSessionModifyRequest(
+        call_id_requester, video_state_request, video_quality_request)
+
+    try:
+        response_event = ad_requester.ed.pop_event(
+            EventTelecomVideoCallSessionModifyResponseReceived,
+            WAIT_TIME_VIDEO_SESSION_EVENT)
+        log.info(response_event)
+    except Empty:
+        log.error("Failed to receive SessionModifyResponse!")
+        return False
+    finally:
+        ad_requester.droid.telecomCallVideoStopListeningForEvent(
+            call_id_requester, EventSessionModifyResponsetRceived)
+
+    time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+    # FIXME: Replace with reducing the volume as we want
+    # to test route switching
+    ad_requester.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+    ad_responder.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
+
+    time.sleep(WAIT_TIME_IN_CALL)
+    if video_state_request != ad_requester.droid.telecomCallVideoGetState(
+            call_id_requester):
+        log.error("requester not in correct state. expected:{}, current:{}"
+                  .format(video_state_request,
+                          ad_requester.droid.telecomCallVideoGetState(
+                              call_id_requester)))
+        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)))
+        return False
+
+    return True
+
+def verify_video_call_in_expected_state(log, ad, call_id,
+                                        call_video_state, call_state):
+    """Return True if video call is in expected video state and call state.
+
+    Args:
+        log: logger object
+        ad: android_device object
+        call_id: ad's call id
+        call_video_state: video state to validate.
+        call_state: call state to validate.
+
+    Returns:
+        True if video call is in expected video state and call state.
+    """
+    if not is_call_id_in_video_state(log, ad, call_id, call_video_state):
+        log.error("Call is not in expected {} state. Current state {}".
+                  format(call_video_state,
+                         ad.droid.telecomCallVideoGetState(call_id)))
+        return False
+    if ad.droid.telecomCallGetCallState(call_id) != call_state:
+        log.error("Call is not in expected {} state. Current state {}".
+                  format(call_state,
+                         ad.droid.telecomCallGetCallState(call_id)))
+        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
new file mode 100644
index 0000000..8f7ba4f
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/tel_voice_utils.py
@@ -0,0 +1,1037 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from .tel_lookup_tables import *
+from .tel_test_utils import *
+
+def two_phone_call_leave_voice_mail(
+        log,
+        caller,
+        caller_idle_func,
+        caller_in_call_check_func,
+        callee,
+        callee_idle_func,
+        wait_time_in_call=WAIT_TIME_TO_LEAVE_VOICE_MAIL
+):
+    """Call from caller to callee, reject on callee, caller leave a voice mail.
+
+    1. Caller call Callee.
+    2. Callee reject incoming call.
+    3. Caller leave a voice mail.
+    4. Verify callee received the voice mail notification.
+
+    Args:
+        caller: caller android device object.
+        caller_idle_func: function to check caller's idle state.
+        caller_in_call_check_func: function to check caller's in-call state.
+        callee: callee android device object.
+        callee_idle_func: function to check callee's idle state.
+        wait_time_in_call: time to wait when leaving a voice mail.
+            This is optional, default is WAIT_TIME_TO_LEAVE_VOICE_MAIL
+
+    Returns:
+        True: if voice message is received on callee successfully.
+        False: for errors
+    """
+
+    ads = [caller, callee]
+
+    # Make sure phones are idle.
+    ensure_phones_idle(log, ads)
+    if caller_idle_func and not caller_idle_func(log, caller):
+        log.error("Caller Failed to Reselect")
+        return False
+    if callee_idle_func and not callee_idle_func(log, callee):
+        log.error("Callee Failed to Reselect")
+        return False
+
+    # Temporary hack to give phone enough time to register.
+    # TODO: Proper check using SL4A API.
+    time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+    # Make call and leave a message.
+    if not call_reject_leave_message(log,
+                                     caller,
+                                     callee,
+                                     caller_in_call_check_func,
+                                     wait_time_in_call):
+        log.error("make a call and leave a message failed.")
+        return False
+    return True
+
+def two_phone_call_short_seq(
+        log,
+        phone_a,
+        phone_a_idle_func,
+        phone_a_in_call_check_func,
+        phone_b,
+        phone_b_idle_func,
+        phone_b_in_call_check_func,
+        call_sequence_func=None,
+        wait_time_in_call=WAIT_TIME_IN_CALL
+):
+    """Call process short sequence.
+    1. Ensure phone idle and in idle_func check return True.
+    2. Call from PhoneA to PhoneB, accept on PhoneB.
+    3. Check phone state, hangup on PhoneA.
+    4. Ensure phone idle and in idle_func check return True.
+    5. Call from PhoneA to PhoneB, accept on PhoneB.
+    6. Check phone state, hangup on PhoneB.
+
+    Args:
+        phone_a: PhoneA's android device object.
+        phone_a_idle_func: function to check PhoneA's idle state.
+        phone_a_in_call_check_func: function to check PhoneA's in-call state.
+        phone_b: PhoneB's android device object.
+        phone_b_idle_func: function to check PhoneB's idle state.
+        phone_b_in_call_check_func: function to check PhoneB's in-call state.
+        call_sequence_func: default parameter, not implemented.
+        wait_time_in_call: time to wait in call.
+            This is optional, default is WAIT_TIME_IN_CALL
+
+    Returns:
+        True: if call sequence succeed.
+        False: for errors
+    """
+# TODO: call_sequence_func. Define the sequence of calls
+
+    ads = [phone_a, phone_b]
+
+    call_params = [(ads[0], ads[1], ads[0],
+                    phone_a_in_call_check_func,
+                    phone_b_in_call_check_func),
+                   (ads[0], ads[1], ads[1],
+                    phone_a_in_call_check_func,
+                    phone_b_in_call_check_func),
+                   ]
+
+    for param in call_params:
+        # Make sure phones are idle.
+        ensure_phones_idle(log, ads)
+        if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+            log.error("Phone A Failed to Reselect")
+            return False
+        if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
+            log.error("Phone B Failed to Reselect")
+            return False
+
+        # Temporary hack to give phone enough time to register.
+        # TODO: Proper check using SL4A API.
+        time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+        # Make call.
+        log.info("---> Call test: {} to {} <---".format(param[0].serial, param[1].serial))
+        if not call_setup_teardown(log, *param, wait_time_in_call=wait_time_in_call):
+            log.error("Call Iteration Failed")
+            return False
+
+    return True
+
+
+def two_phone_call_long_seq(
+        log,
+        phone_a,
+        phone_a_idle_func,
+        phone_a_in_call_check_func,
+        phone_b,
+        phone_b_idle_func,
+        phone_b_in_call_check_func,
+        call_sequence_func=None,
+        wait_time_in_call=WAIT_TIME_IN_CALL
+):
+    """Call process long sequence.
+    1. Ensure phone idle and in idle_func check return True.
+    2. Call from PhoneA to PhoneB, accept on PhoneB.
+    3. Check phone state, hangup on PhoneA.
+    4. Ensure phone idle and in idle_func check return True.
+    5. Call from PhoneA to PhoneB, accept on PhoneB.
+    6. Check phone state, hangup on PhoneB.
+    7. Ensure phone idle and in idle_func check return True.
+    8. Call from PhoneB to PhoneA, accept on PhoneA.
+    9. Check phone state, hangup on PhoneA.
+    10. Ensure phone idle and in idle_func check return True.
+    11. Call from PhoneB to PhoneA, accept on PhoneA.
+    12. Check phone state, hangup on PhoneB.
+
+    Args:
+        phone_a: PhoneA's android device object.
+        phone_a_idle_func: function to check PhoneA's idle state.
+        phone_a_in_call_check_func: function to check PhoneA's in-call state.
+        phone_b: PhoneB's android device object.
+        phone_b_idle_func: function to check PhoneB's idle state.
+        phone_b_in_call_check_func: function to check PhoneB's in-call state.
+        call_sequence_func: default parameter, not implemented.
+        wait_time_in_call: time to wait in call.
+            This is optional, default is WAIT_TIME_IN_CALL
+
+    Returns:
+        True: if call sequence succeed.
+        False: for errors
+    """
+# TODO: call_sequence_func. Define the sequence of calls
+
+    ads = [phone_a, phone_b]
+
+    call_params = [(ads[0], ads[1], ads[0],
+                    phone_a_in_call_check_func,
+                    phone_b_in_call_check_func),
+                   (ads[0], ads[1], ads[1],
+                    phone_a_in_call_check_func,
+                    phone_b_in_call_check_func),
+                   (ads[1], ads[0], ads[0],
+                    phone_b_in_call_check_func,
+                    phone_a_in_call_check_func),
+                   (ads[1], ads[0], ads[1],
+                    phone_b_in_call_check_func,
+                    phone_a_in_call_check_func),
+                   ]
+
+    for param in call_params:
+        # Make sure phones are idle.
+        ensure_phones_idle(log, ads)
+        if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+            log.error("Phone A Failed to Reselect")
+            return False
+        if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
+            log.error("Phone B Failed to Reselect")
+            return False
+
+        # Temporary hack to give phone enough time to register.
+        # TODO: Proper check using SL4A API.
+        time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+        # Make call.
+        log.info("---> Call test: {} to {} <---".format(param[0].serial, param[1].serial))
+        if not call_setup_teardown(log, *param, wait_time_in_call=wait_time_in_call):
+            log.error("Call Iteration Failed")
+            return False
+
+    return True
+
+
+def phone_setup_volte(log, ad):
+    """Setup VoLTE enable.
+
+    Args:
+        ad: android device object.
+
+    Returns:
+        True: if VoLTE is enabled successfully.
+        False: for errors
+    """
+    return phone_setup_volte_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_setup_volte_for_subscription(log, ad, sub_id):
+    """Setup VoLTE enable for subscription id.
+
+    Args:
+        ad: android device object.
+        sub_id: subscription id.
+
+    Returns:
+        True: if VoLTE is enabled successfully.
+        False: for errors
+    """
+    toggle_airplane_mode(log, ad, False)
+    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+        log.error("{} Disable WFC failed.".format(ad.serial))
+        return False
+    toggle_volte_for_subscription(log, ad, sub_id, True)
+    if not ensure_network_rat_for_subscription(
+            log, ad, sub_id, RAT_LTE, WAIT_TIME_NW_SELECTION,
+            NETWORK_SERVICE_DATA):
+        return False
+    return phone_idle_volte_for_subscription(log, ad, sub_id)
+
+def phone_setup_iwlan(log, ad, is_airplane_mode, wfc_mode,
+                      wifi_ssid=None, wifi_pwd=None):
+    """Phone setup function for epdg call test.
+    Set WFC mode according to wfc_mode.
+    Set airplane mode according to is_airplane_mode.
+    Make sure phone connect to WiFi. (If wifi_ssid is not None.)
+    Wait for phone to be in iwlan data network type.
+    Wait for phone to report wfc enabled flag to be true.
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        is_airplane_mode: True to turn on airplane mode. False to turn off airplane mode.
+        wfc_mode: WFC mode to set to.
+        wifi_ssid: WiFi network SSID. This is optional.
+            If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
+        wifi_pwd: WiFi network password. This is optional.
+
+    Returns:
+        True if success. False if fail.
+    """
+    return phone_setup_iwlan_for_subscription(log, ad,
+        ad.droid.subscriptionGetDefaultVoiceSubId(), is_airplane_mode, wfc_mode,
+        wifi_ssid, wifi_pwd)
+
+def phone_setup_iwlan_for_subscription(log, ad, sub_id, is_airplane_mode, wfc_mode,
+                                       wifi_ssid=None, wifi_pwd=None):
+    """Phone setup function for epdg call test for subscription id.
+    Set WFC mode according to wfc_mode.
+    Set airplane mode according to is_airplane_mode.
+    Make sure phone connect to WiFi. (If wifi_ssid is not None.)
+    Wait for phone to be in iwlan data network type.
+    Wait for phone to report wfc enabled flag to be true.
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        sub_id: subscription id.
+        is_airplane_mode: True to turn on airplane mode. False to turn off airplane mode.
+        wfc_mode: WFC mode to set to.
+        wifi_ssid: WiFi network SSID. This is optional.
+            If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
+        wifi_pwd: WiFi network password. This is optional.
+
+    Returns:
+        True if success. False if fail.
+    """
+
+    toggle_airplane_mode(log, ad, False)
+    toggle_volte(log, ad, True)
+    if not is_airplane_mode and not ensure_network_rat_for_subscription(
+        log, ad, sub_id, RAT_LTE, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            return False
+
+    if not set_wfc_mode(log, ad, wfc_mode):
+        log.error("{} set WFC mode failed.".format(ad.serial))
+        return False
+
+    toggle_airplane_mode(log, ad, is_airplane_mode)
+
+    if wifi_ssid is not None:
+        if not ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd):
+            log.error("{} connect to WiFi failed.".format(ad.serial))
+            return False
+
+    return phone_idle_iwlan_for_subscription(log, ad, sub_id)
+
+def phone_setup_iwlan_cellular_preferred(log, ad, wifi_ssid=None,
+                                                 wifi_pwd=None):
+    """Phone setup function for iwlan Non-APM CELLULAR_PREFERRED test.
+    Set WFC mode according to CELLULAR_PREFERRED.
+    Set airplane mode according to False.
+    Make sure phone connect to WiFi. (If wifi_ssid is not None.)
+    Make sure phone don't report iwlan data network type.
+    Make sure phone don't report wfc enabled flag to be true.
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        wifi_ssid: WiFi network SSID. This is optional.
+            If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
+        wifi_pwd: WiFi network password. This is optional.
+
+    Returns:
+        True if success. False if fail.
+    """
+    toggle_airplane_mode(log, ad, False)
+    toggle_volte(log, ad, True)
+    if not ensure_network_rat(
+            log, ad, RAT_LTE, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+        return False
+    if not set_wfc_mode(log, ad, WFC_MODE_CELLULAR_PREFERRED):
+        log.error("{} set WFC mode failed.".format(ad.serial))
+        return False
+    if wifi_ssid is not None:
+        if not ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd):
+            log.error("{} connect to WiFi failed.".format(ad.serial))
+            return False
+    if wait_for_droid_in_network_rat(
+        log, ad, RAT_IWLAN, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+        log.error("{} data rat in iwlan mode.".format(ad.serial))
+        return False
+    if wait_for_wfc_enabled(log, ad, WAIT_TIME_WFC_ENABLED):
+        log.error("{} should not <report wfc enabled true> within {}s.".
+                       format(ad.serial, WAIT_TIME_WFC_ENABLED))
+        return False
+    return True
+
+def phone_setup_csfb(log, ad):
+    """Setup phone for CSFB call test.
+
+    Setup Phone to be in 4G mode.
+    Disabled VoLTE.
+
+    Args:
+        ad: Android device object.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    return phone_setup_csfb_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_setup_csfb_for_subscription(log, ad, sub_id):
+    """Setup phone for CSFB call test for subscription id.
+
+    Setup Phone to be in 4G mode.
+    Disabled VoLTE.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    toggle_airplane_mode(log, ad, False)
+    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+        log.error("{} Disable WFC failed.".format(ad.serial))
+        return False
+    if ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform():
+        toggle_volte(log, ad, False)
+    if not ensure_network_rat_for_subscription(
+            log, ad, sub_id, RAT_LTE, WAIT_TIME_NW_SELECTION,
+            NETWORK_SERVICE_DATA):
+        return False
+
+    # FIXME: Delete when getVoiceNetworkType() is fixed
+    if get_operator_name(log, ad, sub_id) == CARRIER_VZW:
+        time.sleep(VZW_WAIT_TIME_IN_PHONE_SETUP_FUNC)
+
+    return phone_idle_csfb_for_subscription(log, ad, sub_id)
+
+
+def phone_setup_3g(log, ad):
+    """Setup phone for 3G call test.
+
+    Args:
+        ad: Android device object.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    return phone_setup_3g_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_setup_3g_for_subscription(log, ad, sub_id):
+    """Setup phone for 3G call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    toggle_airplane_mode(log, ad, False)
+    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+        log.error("{} Disable WFC failed.".format(ad.serial))
+        return False
+    if not ensure_network_generation_for_subscription(
+            log, ad, sub_id, RAT_3G, WAIT_TIME_NW_SELECTION,
+            NETWORK_SERVICE_DATA):
+        return False
+
+    # FIXME: Delete when getVoiceNetworkType() is fixed
+    if get_operator_name(log, ad, sub_id) == CARRIER_VZW:
+        time.sleep(VZW_WAIT_TIME_IN_PHONE_SETUP_FUNC)
+
+    return wait_for_droid_in_network_generation_for_subscription(
+        log, ad, sub_id, RAT_3G, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_VOICE)
+
+def phone_setup_voice_3g(log, ad):
+    return phone_setup_voice_3g_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_setup_voice_3g_for_subscription(log, ad, sub_id):
+    toggle_airplane_mode(log, ad, False)
+    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+        log.error("{} Disable WFC failed.".format(ad.serial))
+        return False
+
+    return ensure_network_generation_for_subscription(
+        log, ad, sub_id, RAT_3G, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_VOICE)
+
+def phone_setup_2g(log, ad):
+    """Setup phone for 2G call test.
+
+    Args:
+        ad: Android device object.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    return phone_setup_2g_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_setup_2g_for_subscription(log, ad, sub_id):
+    """Setup phone for 2G call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    toggle_airplane_mode(log, ad, False)
+    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+        log.error("{} Disable WFC failed.".format(ad.serial))
+        return False
+    if not ensure_network_generation_for_subscription(
+            log, ad, sub_id, RAT_2G, WAIT_TIME_NW_SELECTION,
+            NETWORK_SERVICE_DATA):
+        return False
+    return ensure_network_generation_for_subscription(
+        log, ad, sub_id, RAT_2G, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_VOICE)
+
+def phone_setup_voice_2g(log, ad):
+    return phone_setup_voice_2g_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_setup_voice_2g_for_subscription(log, ad, sub_id):
+    toggle_airplane_mode(log, ad, False)
+    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+        log.error("{} Disable WFC failed.".format(ad.serial))
+        return False
+
+    return ensure_network_generation_for_subscription(
+        log, ad, sub_id, RAT_2G, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_VOICE)
+
+def phone_setup_voice_general(log, ad):
+    """Setup phone for voice general call test.
+
+    Make sure phone attached to voice.
+    Make necessary delay.
+
+    Args:
+        ad: Android device object.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    return phone_setup_voice_general_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_setup_voice_general_for_subscription(log, ad, sub_id):
+    """Setup phone for voice general call test for subscription id.
+
+    Make sure phone attached to voice.
+    Make necessary delay.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    toggle_airplane_mode(log, ad, False)
+    if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
+                                          WAIT_TIME_NW_SELECTION):
+        # if phone can not attach voice, try phone_setup_3g
+        return phone_setup_3g_for_subscription(log, ad, sub_id)
+
+   # FIXME: Delete when getVoiceNetworkType() is fixed
+    if get_operator_name(log, ad, sub_id) == CARRIER_VZW:
+        time.sleep(VZW_WAIT_TIME_IN_PHONE_SETUP_FUNC)
+
+    return True
+
+def phone_idle_volte(log, ad):
+    """Return if phone is idle for VoLTE call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_volte_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_idle_volte_for_subscription(log, ad, sub_id):
+    """Return if phone is idle for VoLTE call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not wait_for_droid_in_network_rat_for_subscription(
+        log, ad, sub_id, RAT_LTE, WAIT_TIME_NW_SELECTION,
+        NETWORK_SERVICE_VOICE):
+        log.error("{} voice rat not in LTE mode.".format(ad.serial))
+        return False
+    if not wait_for_volte_enabled(log, ad, WAIT_TIME_VOLTE_ENABLED):
+        log.error("{} failed to <report volte enabled true> within {}s.".
+                  format(ad.serial, WAIT_TIME_VOLTE_ENABLED))
+        return False
+    return True
+
+def phone_idle_iwlan(log, ad):
+    """Return if phone is idle for WiFi calling call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_iwlan_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+def phone_idle_iwlan_for_subscription(log, ad, sub_id):
+    """Return if phone is idle for WiFi calling call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not wait_for_droid_in_network_rat_for_subscription(
+        log, ad, sub_id, RAT_IWLAN, WAIT_TIME_NW_SELECTION,
+        NETWORK_SERVICE_DATA):
+        log.error("{} data rat not in iwlan mode.".format(ad.serial))
+        return False
+    if not wait_for_wfc_enabled(log, ad, WAIT_TIME_WFC_ENABLED):
+        log.error("{} failed to <report wfc enabled true> within {}s.".
+                  format(ad.serial, WAIT_TIME_WFC_ENABLED))
+        return False
+    return True
+
+
+def phone_idle_csfb(log, ad):
+    """Return if phone is idle for CSFB call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_csfb_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_idle_csfb_for_subscription(log, ad, sub_id):
+    """Return if phone is idle for CSFB call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not wait_for_droid_in_network_rat_for_subscription(
+            log, ad, sub_id, RAT_LTE, WAIT_TIME_NW_SELECTION,
+            NETWORK_SERVICE_DATA):
+        log.error("{} data rat not in lte mode.".format(ad.serial))
+        return False
+
+#    FIXME: Re-enable when getVoiceNetworkType() is fixed
+#    FIXME:Support 2g
+#    if not wait_for_droid_in_network_generation(
+#            log, ad, WAIT_TIME_NW_SELECTION, RAT_3G, NETWORK_SERVICE_VOICE):
+#        return False
+    return True
+
+
+def phone_idle_3g(log, ad):
+    """Return if phone is idle for 3G call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_3g_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_idle_3g_for_subscription(log, ad, sub_id):
+    """Return if phone is idle for 3G call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    return wait_for_droid_in_network_generation_for_subscription(
+        log, ad, sub_id, RAT_3G, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_VOICE)
+
+
+def phone_idle_2g(log, ad):
+    """Return if phone is idle for 2G call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_2g_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def phone_idle_2g_for_subscription(log, ad, sub_id):
+    """Return if phone is idle for 2G call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    return wait_for_droid_in_network_generation_for_subscription(
+        log, ad, sub_id, RAT_2G, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_VOICE)
+
+def is_phone_in_call_volte(log, ad):
+    """Return if phone is in VoLTE call.
+
+    Args:
+        ad: Android device object.
+    """
+    return is_phone_in_call_volte_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+def is_phone_in_call_volte_for_subscription(log, ad, sub_id):
+    """Return if phone is in VoLTE call for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not ad.droid.telecomIsInCall():
+        log.error("{} not in call.".format(ad.serial))
+        return False
+    nw_type = get_network_rat_for_subscription(log, ad, sub_id,
+                                               NETWORK_SERVICE_VOICE)
+    if nw_type != RAT_LTE:
+        log.error("{} voice rat on: {}. Expected: LTE".format(ad.serial, nw_type))
+        return False
+    return True
+
+def is_phone_in_call_csfb(log, ad):
+    """Return if phone is in CSFB call.
+
+    Args:
+        ad: Android device object.
+    """
+    return is_phone_in_call_csfb_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def is_phone_in_call_csfb_for_subscription(log, ad, sub_id):
+    """Return if phone is in CSFB call for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not ad.droid.telecomIsInCall():
+        log.error("{} not in call.".format(ad.serial))
+        return False
+    nw_type = get_network_rat_for_subscription(log, ad, sub_id,
+                                               NETWORK_SERVICE_VOICE)
+    if nw_type == RAT_LTE:
+        log.error("{} voice rat on: {}. Expected: not LTE".format(ad.serial, nw_type))
+        return False
+    return True
+
+
+def is_phone_in_call_3g(log, ad):
+    """Return if phone is in 3G call.
+
+    Args:
+        ad: Android device object.
+    """
+    return is_phone_in_call_3g_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def is_phone_in_call_3g_for_subscription(log, ad, sub_id):
+    """Return if phone is in 3G call for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not ad.droid.telecomIsInCall():
+        log.error("{} not in call.".format(ad.serial))
+        return False
+    nw_gen = get_network_gen_for_subscription(log, ad, sub_id,
+                                              NETWORK_SERVICE_VOICE)
+    if nw_gen != RAT_3G:
+        log.error("{} voice rat on: {}. Expected: 3g".format(ad.serial, nw_gen))
+        return False
+    return True
+
+
+def is_phone_in_call_2g(log, ad):
+    """Return if phone is in 2G call.
+
+    Args:
+        ad: Android device object.
+    """
+    return is_phone_in_call_2g_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def is_phone_in_call_2g_for_subscription(log, ad, sub_id):
+    """Return if phone is in 2G call for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not ad.droid.telecomIsInCall():
+        log.error("{} not in call.".format(ad.serial))
+        return False
+    nw_gen = get_network_gen_for_subscription(log, ad, sub_id,
+                                              NETWORK_SERVICE_VOICE)
+    if nw_gen != RAT_2G:
+        log.error("{} voice rat on: {}. Expected: 2g".format(ad.serial, nw_gen))
+        return False
+    return True
+
+def is_phone_in_call_1x(log, ad):
+    """Return if phone is in 1x call.
+
+    Args:
+        ad: Android device object.
+    """
+    return is_phone_in_call_1x_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def is_phone_in_call_1x_for_subscription(log, ad, sub_id):
+    """Return if phone is in 1x call for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not ad.droid.telecomIsInCall():
+        log.error("{} not in call.".format(ad.serial))
+        return False
+    nw_type = get_network_rat_for_subscription(log, ad, sub_id,
+                                               NETWORK_SERVICE_VOICE)
+    if nw_type != RAT_1XRTT:
+        log.error("{} voice rat on: {}. Expected: 1xrtt".format(ad.serial, nw_type))
+        return False
+    return True
+
+def is_phone_in_call_wcdma(log, ad):
+    """Return if phone is in WCDMA call.
+
+    Args:
+        ad: Android device object.
+    """
+    return is_phone_in_call_wcdma_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultVoiceSubId())
+
+
+def is_phone_in_call_wcdma_for_subscription(log, ad, sub_id):
+    """Return if phone is in WCDMA call for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    # Currently checking 'umts'.
+    # Changes may needed in the future.
+    if not ad.droid.telecomIsInCall():
+        log.error("{} not in call.".format(ad.serial))
+        return False
+    nw_type = get_network_rat_for_subscription(log, ad, sub_id,
+                                               NETWORK_SERVICE_VOICE)
+    if nw_type != RAT_UMTS:
+        log.error("{} voice rat on: {}. Expected: umts".format(ad.serial, nw_type))
+        return False
+    return True
+
+def is_phone_in_call_iwlan(log, ad):
+    """Return if phone is in WiFi call.
+
+    Args:
+        ad: Android device object.
+    """
+    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:
+        log.error("{} data rat on: {}. Expected: iwlan".format(ad.serial, nw_type))
+        return False
+    if not is_wfc_enabled(log, ad):
+        log.error("{} WiFi Calling feature bit is False.".format(ad.serial))
+        return False
+    return True
+
+def is_phone_in_call_not_iwlan(log, ad):
+    """Return if phone is in WiFi call for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription 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:
+        log.error("{} data rat on: {}. Expected: not iwlan".format(ad.serial, nw_type))
+        return False
+    if is_wfc_enabled(log, ad):
+        log.error("{} WiFi Calling feature bit is True.".format(ad.serial))
+        return False
+    return True
+
+def swap_calls(log, ads, call_hold_id, call_active_id, num_swaps=1,
+               check_call_status=True):
+    """PhoneA in call with B and C. Swap active/holding call on PhoneA.
+
+    Swap call and check status on PhoneA.
+        (This step may have multiple times according to 'num_swaps'.)
+    Check if all 3 phones are 'in-call'.
+
+    Args:
+        ads: list of ad object, at least three need to pass in.
+            Swap operation will happen on ads[0].
+            ads[1] and ads[2] are call participants.
+        call_hold_id: id for the holding call in ads[0].
+            call_hold_id should be 'STATE_HOLDING' when calling this function.
+        call_active_id: id for the active call in ads[0].
+            call_active_id should be 'STATE_ACTIVE' when calling this function.
+        num_swaps: how many swap/check operations will be done before return.
+        check_call_status: THis is optional. Default value is True.
+            If this value is True, then call status (active/hold) will be
+            be checked after each swap operation.
+
+    Returns:
+        If no error happened, return True, otherwise, return False.
+    """
+    if check_call_status:
+        # Check status before swap.
+        if ads[0].droid.telecomCallGetCallState(call_active_id) != CALL_STATE_ACTIVE:
+            log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
+                format(call_active_id,
+                       ads[0].droid.telecomCallGetCallState(call_active_id)))
+            return False
+        if ads[0].droid.telecomCallGetCallState(call_hold_id) != CALL_STATE_HOLDING:
+            log.error("Call_id:{}, state:{}, expected: STATE_HOLDING".
+                format(call_hold_id,
+                       ads[0].droid.telecomCallGetCallState(call_hold_id)))
+            return False
+
+    i = 1
+    while(i <= num_swaps):
+        log.info("swap_test: {}. {} swap and check call status.".
+            format(i, ads[0].serial))
+        ads[0].droid.telecomCallHold(call_active_id)
+        time.sleep(WAIT_TIME_IN_CALL)
+        # Swap object reference
+        call_active_id, call_hold_id = call_hold_id, call_active_id
+        if check_call_status:
+            # Check status
+            if ads[0].droid.telecomCallGetCallState(call_active_id) != CALL_STATE_ACTIVE:
+                log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
+                    format(call_active_id,
+                            ads[0].droid.telecomCallGetCallState(call_active_id)))
+                return False
+            if ads[0].droid.telecomCallGetCallState(call_hold_id) != CALL_STATE_HOLDING:
+                log.error("Call_id:{}, state:{}, expected: STATE_HOLDING".
+                    format(call_hold_id,
+                           ads[0].droid.telecomCallGetCallState(call_hold_id)))
+                return False
+        # TODO: Future add voice check.
+
+        i += 1
+
+    #In the end, check all three phones are 'in-call'.
+    if not verify_incall_state(log, [ads[0], ads[1], ads[2]], True):
+        return False
+
+    return True
+
+
+def get_audio_route(log, ad):
+    """Gets the audio route for the active call
+
+    Args:
+        log: logger object
+        ad: android_device object
+
+    Returns:
+        Audio route string ["BLUETOOTH", "EARPIECE", "SPEAKER", "WIRED_HEADSET"
+            "WIRED_OR_EARPIECE"]
+    """
+
+    audio_state = ad.droid.telecomCallGetAudioState()
+    return audio_state["AudioRoute"]
+
+
+def set_audio_route(log, ad, route):
+    """Sets the audio route for the active call
+
+    Args:
+        log: logger object
+        ad: android_device object
+        route: string ["BLUETOOTH", "EARPIECE", "SPEAKER", "WIRED_HEADSET"
+            "WIRED_OR_EARPIECE"]
+
+    Returns:
+        If no error happened, return True, otherwise, return False.
+    """
+    ad.droid.telecomCallSetAudioRoute(route)
+    return True
+
+def is_property_in_call_properties(log, ad, call_id, expected_property):
+    """Return if the call_id has the expected property
+
+    Args:
+        log: logger object
+        ad: android_device object
+        call_id: call id.
+        expected_property: expected property.
+
+    Returns:
+        True if call_id has expected_property. False if not.
+    """
+    properties = ad.droid.telecomCallGetProperties(call_id)
+    return (expected_property in properties)
+
+def is_call_hd(log, ad, call_id):
+    """Return if the call_id is HD call.
+
+    Args:
+        log: logger object
+        ad: android_device object
+        call_id: call id.
+
+    Returns:
+        True if call_id is HD call. False if not.
+    """
+    return is_property_in_call_properties(log, ad, call_id,
+                                          CALL_PROPERTY_HIGH_DEF_AUDIO)
+
+def get_cep_conference_call_id(ad):
+    """Get CEP conference call id if there is an ongoing CEP conference call.
+
+    Args:
+        ad: android device object.
+
+    Returns:
+        call id for CEP conference call if there is an ongoing CEP conference call.
+        None otherwise.
+    """
+    for call in ad.droid.telecomCallGetCallIds():
+        if len(ad.droid.telecomCallGetCallChildren(call)) != 0:
+            return call
+    return None
\ No newline at end of file
diff --git a/acts/framework/acts/test_utils/wifi_test_utils.py b/acts/framework/acts/test_utils/wifi_test_utils.py
new file mode 100755
index 0000000..1d9b9a4
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi_test_utils.py
@@ -0,0 +1,875 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 Google, Inc.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import time
+import pprint
+
+from enum import IntEnum
+from queue import Empty
+
+from acts.logger import LoggerProxy
+from acts.utils import exe_cmd
+from acts.utils import require_sl4a
+from acts.utils import sync_device_time
+from acts.utils import trim_model_name
+
+log = LoggerProxy()
+
+# Number of seconds to wait for events that are supposed to happen quickly.
+# Like onSuccess for start background scan and confirmation on wifi state
+# change.
+SHORT_TIMEOUT = 30
+
+# The currently supported devices that existed before release
+#TODO: (navtejsingh) Need to clean up the below lists going forward
+K_DEVICES = ["hammerhead", "razor", "razorg"]
+L_DEVICES = ["shamu", "ryu"]
+L_TAP_DEVICES = ["volantis", "volantisg"]
+M_DEVICES = ["angler"]
+
+# Speed of light in m/s.
+SPEED_OF_LIGHT = 299792458
+
+DEFAULT_PING_ADDR = "http://www.google.com/robots.txt"
+
+class WifiEnums():
+
+    SSID_KEY = "SSID"
+    BSSID_KEY = "BSSID"
+    PWD_KEY = "password"
+    frequency_key = "frequency"
+    APBAND_KEY = "apBand"
+
+    WIFI_CONFIG_APBAND_2G = 0
+    WIFI_CONFIG_APBAND_5G = 1
+
+    WIFI_WPS_INFO_PBC     = 0;
+    WIFI_WPS_INFO_DISPLAY = 1;
+    WIFI_WPS_INFO_KEYPAD  = 2;
+    WIFI_WPS_INFO_LABEL   = 3;
+    WIFI_WPS_INFO_INVALID = 4;
+
+    class CountryCode():
+        CHINA = "CN"
+        JAPAN = "JP"
+        UK = "GB"
+        US = "US"
+        UNKNOWN = "UNKNOWN"
+
+    # Start of Macros for EAP
+    # EAP types
+    class Eap(IntEnum):
+        NONE = -1
+        PEAP = 0
+        TLS  = 1
+        TTLS = 2
+        PWD  = 3
+        SIM  = 4
+        AKA  = 5
+
+    # EAP Phase2 types
+    class EapPhase2(IntEnum):
+        NONE        = 0
+        PAP         = 1
+        MSCHAP      = 2
+        MSCHAPV2    = 3
+        GTC         = 4
+
+    class Enterprise:
+    # Enterprise Config Macros
+        EMPTY_VALUE      = "NULL"
+        EAP              = "eap"
+        PHASE2           = "phase2"
+        IDENTITY         = "identity"
+        ANON_IDENTITY    = "anonymous_identity"
+        PASSWORD         = "password"
+        SUBJECT_MATCH    = "subject_match"
+        ALTSUBJECT_MATCH = "altsubject_match"
+        DOM_SUFFIX_MATCH = "domain_suffix_match"
+        CLIENT_CERT      = "client_cert"
+        CA_CERT          = "ca_cert"
+        ENGINE           = "engine"
+        ENGINE_ID        = "engine_id"
+        PRIVATE_KEY_ID   = "key_id"
+        REALM            = "realm"
+        PLMN             = "plmn"
+        FQDN             = "FQDN"
+        FRIENDLY_NAME    = "providerFriendlyName"
+        ROAMING_IDS      = "roamingConsortiumIds"
+    # End of Macros for EAP
+
+    # Macros for wifi p2p.
+    WIFI_P2P_SERVICE_TYPE_ALL = 0
+    WIFI_P2P_SERVICE_TYPE_BONJOUR = 1
+    WIFI_P2P_SERVICE_TYPE_UPNP = 2
+    WIFI_P2P_SERVICE_TYPE_VENDOR_SPECIFIC = 255
+
+    class ScanResult:
+        CHANNEL_WIDTH_20MHZ = 0
+        CHANNEL_WIDTH_40MHZ = 1
+        CHANNEL_WIDTH_80MHZ = 2
+        CHANNEL_WIDTH_160MHZ = 3
+        CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
+
+    # Macros for wifi rtt.
+    class RttType(IntEnum):
+        TYPE_ONE_SIDED      = 1
+        TYPE_TWO_SIDED      = 2
+
+    class RttPeerType(IntEnum):
+        PEER_TYPE_AP        = 1
+        PEER_TYPE_STA       = 2 # Requires NAN.
+        PEER_P2P_GO         = 3
+        PEER_P2P_CLIENT     = 4
+        PEER_NAN            = 5
+
+    class RttPreamble(IntEnum):
+        PREAMBLE_LEGACY  = 0x01
+        PREAMBLE_HT      = 0x02
+        PREAMBLE_VHT     = 0x04
+
+    class RttBW(IntEnum):
+        BW_5_SUPPORT   = 0x01
+        BW_10_SUPPORT  = 0x02
+        BW_20_SUPPORT  = 0x04
+        BW_40_SUPPORT  = 0x08
+        BW_80_SUPPORT  = 0x10
+        BW_160_SUPPORT = 0x20
+
+    class Rtt(IntEnum):
+        STATUS_SUCCESS                  = 0
+        STATUS_FAILURE                  = 1
+        STATUS_FAIL_NO_RSP              = 2
+        STATUS_FAIL_REJECTED            = 3
+        STATUS_FAIL_NOT_SCHEDULED_YET   = 4
+        STATUS_FAIL_TM_TIMEOUT          = 5
+        STATUS_FAIL_AP_ON_DIFF_CHANNEL  = 6
+        STATUS_FAIL_NO_CAPABILITY       = 7
+        STATUS_ABORTED                  = 8
+        STATUS_FAIL_INVALID_TS          = 9
+        STATUS_FAIL_PROTOCOL            = 10
+        STATUS_FAIL_SCHEDULE            = 11
+        STATUS_FAIL_BUSY_TRY_LATER      = 12
+        STATUS_INVALID_REQ              = 13
+        STATUS_NO_WIFI                  = 14
+        STATUS_FAIL_FTM_PARAM_OVERRIDE  = 15
+
+        REASON_UNSPECIFIED              = -1
+        REASON_NOT_AVAILABLE            = -2
+        REASON_INVALID_LISTENER         = -3
+        REASON_INVALID_REQUEST          = -4
+
+    class RttParam:
+        device_type = "deviceType"
+        request_type = "requestType"
+        BSSID = "bssid"
+        channel_width = "channelWidth"
+        frequency = "frequency"
+        center_freq0 = "centerFreq0"
+        center_freq1 = "centerFreq1"
+        number_burst = "numberBurst"
+        interval = "interval"
+        num_samples_per_burst = "numSamplesPerBurst"
+        num_retries_per_measurement_frame = "numRetriesPerMeasurementFrame"
+        num_retries_per_FTMR = "numRetriesPerFTMR"
+        lci_request = "LCIRequest"
+        lcr_request = "LCRRequest"
+        burst_timeout = "burstTimeout"
+        preamble = "preamble"
+        bandwidth = "bandwidth"
+        margin = "margin"
+
+    RTT_MARGIN_OF_ERROR = {
+        RttBW.BW_80_SUPPORT: 2,
+        RttBW.BW_40_SUPPORT: 5,
+        RttBW.BW_20_SUPPORT: 5
+    }
+
+    # Macros as specified in the WifiScanner code.
+    WIFI_BAND_UNSPECIFIED = 0      # not specified
+    WIFI_BAND_24_GHZ = 1           # 2.4 GHz band
+    WIFI_BAND_5_GHZ = 2            # 5 GHz band without DFS channels
+    WIFI_BAND_5_GHZ_DFS_ONLY  = 4  # 5 GHz band with DFS channels
+    WIFI_BAND_5_GHZ_WITH_DFS  = 6  # 5 GHz band with DFS channels
+    WIFI_BAND_BOTH = 3             # both bands without DFS channels
+    WIFI_BAND_BOTH_WITH_DFS = 7    # both bands with DFS channels
+
+    REPORT_EVENT_AFTER_BUFFER_FULL = 0
+    REPORT_EVENT_AFTER_EACH_SCAN = 1
+    REPORT_EVENT_FULL_SCAN_RESULT = 2
+
+    # US Wifi frequencies
+    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
+                          2457, 2462]
+    DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580,
+                          5600, 5620, 5640, 5660, 5680, 5700, 5720]
+    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
+                               5825]
+    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
+
+    band_to_frequencies = {
+      WIFI_BAND_24_GHZ: ALL_2G_FREQUENCIES,
+      WIFI_BAND_5_GHZ: NONE_DFS_5G_FREQUENCIES,
+      WIFI_BAND_5_GHZ_DFS_ONLY: DFS_5G_FREQUENCIES,
+      WIFI_BAND_5_GHZ_WITH_DFS: ALL_5G_FREQUENCIES,
+      WIFI_BAND_BOTH: ALL_2G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES,
+      WIFI_BAND_BOTH_WITH_DFS: ALL_5G_FREQUENCIES + ALL_2G_FREQUENCIES
+    }
+
+    # All Wifi frequencies to channels lookup.
+    freq_to_channel = {
+        2412: 1,
+        2417: 2,
+        2422: 3,
+        2427: 4,
+        2432: 5,
+        2437: 6,
+        2442: 7,
+        2447: 8,
+        2452: 9,
+        2457: 10,
+        2462: 11,
+        2467: 12,
+        2472: 13,
+        2484: 14,
+        4915: 183,
+        4920: 184,
+        4925: 185,
+        4935: 187,
+        4940: 188,
+        4945: 189,
+        4960: 192,
+        4980: 196,
+        5035: 7,
+        5040: 8,
+        5045: 9,
+        5055: 11,
+        5060: 12,
+        5080: 16,
+        5170: 34,
+        5180: 36,
+        5190: 38,
+        5200: 40,
+        5210: 42,
+        5220: 44,
+        5230: 46,
+        5240: 48,
+        5260: 52,
+        5280: 56,
+        5300: 60,
+        5320: 64,
+        5500: 100,
+        5520: 104,
+        5540: 108,
+        5560: 112,
+        5580: 116,
+        5600: 120,
+        5620: 124,
+        5640: 128,
+        5660: 132,
+        5680: 136,
+        5700: 140,
+        5745: 149,
+        5765: 153,
+        5785: 157,
+        5805: 161,
+        5825: 165,
+    }
+
+    # All Wifi channels to frequencies lookup.
+    channel_2G_to_freq = {
+        1: 2412,
+        2: 2417,
+        3: 2422,
+        4: 2427,
+        5: 2432,
+        6: 2437,
+        7: 2442,
+        8: 2447,
+        9: 2452,
+        10: 2457,
+        11: 2462,
+        12: 2467,
+        13: 2472,
+        14: 2484
+    }
+
+    channel_5G_to_freq = {
+        183: 4915,
+        184: 4920,
+        185: 4925,
+        187: 4935,
+        188: 4940,
+        189: 4945,
+        192: 4960,
+        196: 4980,
+        7: 5035,
+        8: 5040,
+        9: 5045,
+        11: 5055,
+        12: 5060,
+        16: 5080,
+        34: 5170,
+        36: 5180,
+        38: 5190,
+        40: 5200,
+        42: 5210,
+        44: 5220,
+        46: 5230,
+        48: 5240,
+        52: 5260,
+        56: 5280,
+        60: 5300,
+        64: 5320,
+        100: 5500,
+        104: 5520,
+        108: 5540,
+        112: 5560,
+        116: 5580,
+        120: 5600,
+        124: 5620,
+        128: 5640,
+        132: 5660,
+        136: 5680,
+        140: 5700,
+        149: 5745,
+        153: 5765,
+        157: 5785,
+        161: 5805,
+        165: 5825
+    }
+
+class WifiEventNames:
+    WIFI_CONNECTED = "WifiNetworkConnected"
+    SUPPLICANT_CON_CHANGED = "SupplicantConnectionChanged"
+    WIFI_FORGET_NW_SUCCESS = "WifiManagerForgetNetworkOnSuccess"
+
+class WifiTestUtilsError(Exception):
+    pass
+
+class WifiChannelBase:
+    ALL_2G_FREQUENCIES = []
+    DFS_5G_FREQUENCIES = []
+    NONE_DFS_5G_FREQUENCIES = []
+    ALL_5G_FREQUENCIES = DFS_5G_FREQUENCIES + NONE_DFS_5G_FREQUENCIES
+    MIX_CHANNEL_SCAN = []
+
+    def band_to_freq(self, band):
+        _band_to_frequencies = {
+            WifiEnums.WIFI_BAND_24_GHZ: self.ALL_2G_FREQUENCIES,
+            WifiEnums.WIFI_BAND_5_GHZ: self.NONE_DFS_5G_FREQUENCIES,
+            WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY: self.DFS_5G_FREQUENCIES,
+            WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS: self.ALL_5G_FREQUENCIES,
+            WifiEnums.WIFI_BAND_BOTH: self.ALL_2G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES,
+            WifiEnums.WIFI_BAND_BOTH_WITH_DFS: self.ALL_5G_FREQUENCIES + self.ALL_2G_FREQUENCIES
+        }
+        return _band_to_frequencies[band]
+
+class WifiChannelUS(WifiChannelBase):
+    # US Wifi frequencies
+    ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
+                          2457, 2462]
+    NONE_DFS_5G_FREQUENCIES = [5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805,
+                               5825]
+    MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5280, 5260, 5300,5500, 5320,
+                        5520, 5560, 5700, 5745, 5805]
+
+    def __init__(self, model=None):
+        if model and trim_model_name(model) in K_DEVICES:
+            self.DFS_5G_FREQUENCIES = []
+            self.ALL_5G_FREQUENCIES = self.NONE_DFS_5G_FREQUENCIES
+            self.MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5240, 5745, 5765]
+        elif model and trim_model_name(model) in L_DEVICES:
+            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
+                                       5540, 5560, 5580, 5660, 5680, 5700]
+            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
+        elif model and trim_model_name(model) in L_TAP_DEVICES:
+            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520,
+                                       5540, 5560, 5580, 5660, 5680, 5700, 5720]
+            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
+        elif model and trim_model_name(model) in M_DEVICES:
+            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560,5580,
+                                       5600, 5620, 5640, 5660, 5680, 5700]
+            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
+        else:
+            self.DFS_5G_FREQUENCIES = [5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560,5580,
+                                       5600, 5620, 5640, 5660, 5680, 5700, 5720]
+            self.ALL_5G_FREQUENCIES = self.DFS_5G_FREQUENCIES + self.NONE_DFS_5G_FREQUENCIES
+
+def match_networks(target_params, networks):
+    """Finds the WiFi networks that match a given set of parameters in a list
+    of WiFi networks.
+
+    To be considered a match, a network needs to have all the target parameters
+    and the values of those parameters need to equal to those of the target
+    parameters.
+
+    Args:
+        target_params: The target parameters to match networks against.
+        networks: A list of dict objects representing WiFi networks.
+
+    Returns:
+        The networks that match the target parameters.
+    """
+    results = []
+    for n in networks:
+        if set(target_params.items()) <= set(n.items()):
+            results.append(n)
+    return results
+
+def wifi_toggle_state(droid, ed, new_state=None):
+  """Toggles the state of wifi.
+
+  Args:
+    droid: Sl4a session to use.
+    ed: event_dispatcher associated with the sl4a session.
+    new_state: Wifi state to set to. If None, opposite of the current state.
+
+  Returns:
+    True if the toggle was successful, False otherwise.
+  """
+  # Check if the new_state is already achieved, so we don't wait for the
+  # state change event by mistake.
+  if new_state == droid.wifiCheckState():
+    return True
+  droid.wifiStartTrackingStateChange()
+  log.info("Setting wifi state to {}".format(new_state))
+  droid.wifiToggleState(new_state)
+  try:
+    event = ed.pop_event(WifiEventNames.SUPPLICANT_CON_CHANGED, SHORT_TIMEOUT)
+    return event['data']['Connected'] == new_state
+  except Empty:
+    # Supplicant connection event is not always reliable. We double check here
+    # and call it a success as long as the new state equals the expected state.
+    return new_state == droid.wifiCheckState()
+  finally:
+    droid.wifiStopTrackingStateChange()
+
+def reset_droid_wifi(droid, ed):
+  """Disconnects and removes all configured Wifi networks on an android device.
+
+  Args:
+    droid: Sl4a session to use.
+    ed: Event dispatcher instance associated with the sl4a session.
+
+  Raises:
+    WIFIUTILError if forget network operation failed.
+  """
+  droid.wifiToggleState(True)
+  networks = droid.wifiGetConfiguredNetworks()
+  if not networks:
+    return
+  for n in networks:
+    droid.wifiForgetNetwork(n['networkId'])
+    try:
+      event = ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
+        SHORT_TIMEOUT)
+    except Empty:
+      raise WifiTestUtilsError("Failed to remove network {}.".format(n))
+
+def wifi_forget_network(ad, net_ssid):
+  """Remove configured Wifi network on an android device.
+
+  Args:
+    ad: android_device object for forget network.
+    net_ssid: ssid of network to be forget
+
+  Raises:
+    WIFIUTILError if forget network operation failed.
+  """
+  droid, ed = ad.droid, ad.ed
+  droid.wifiToggleState(True)
+  networks = droid.wifiGetConfiguredNetworks()
+  if not networks:
+    return
+  for n in networks:
+    if net_ssid in n[WifiEnums.SSID_KEY]:
+      droid.wifiForgetNetwork(n['networkId'])
+      try:
+        event = ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
+            SHORT_TIMEOUT)
+      except Empty:
+        raise WifiTestUtilsError("Failed to remove network {}.".format(n))
+
+def wifi_test_device_init(ad):
+    """Initializes an android device for wifi testing.
+
+    0. Make sure SL4A connection is established on the android device.
+    1. Disable location service's WiFi scan.
+    2. Turn WiFi on.
+    3. Clear all saved networks.
+    4. Set country code to US.
+    5. Enable WiFi verbose logging.
+    6. Sync device time with computer time.
+    7. Turn off cellular data.
+    """
+    require_sl4a((ad,))
+    ad.droid.wifiScannerToggleAlwaysAvailable(False)
+    msg = "Failed to turn off location service's scan."
+    assert not ad.droid.wifiScannerIsAlwaysAvailable(), msg
+    msg = "Failed to turn WiFi on %s" % ad.serial
+    assert wifi_toggle_state(ad.droid, ad.ed, True), msg
+    reset_droid_wifi(ad.droid, ad.ed)
+    msg = "Failed to clear configured networks."
+    assert not ad.droid.wifiGetConfiguredNetworks(), msg
+    ad.droid.wifiEnableVerboseLogging(1)
+    msg = "Failed to enable WiFi verbose loggin."
+    assert ad.droid.wifiGetVerboseLoggingLevel() == 1, msg
+    ad.droid.wifiScannerToggleAlwaysAvailable(False)
+    # We don't verify the following settings since they are not critical.
+    sync_device_time(ad)
+    ad.droid.toggleDataConnection(False)
+    # TODO(angli): need to verify the country code was actually set. No generic
+    # way to check right now.
+    ad.adb.shell("halutil -country %s" % WifiEnums.CountryCode.US)
+
+def sort_wifi_scan_results(results, key="level"):
+  """Sort wifi scan results by key.
+
+  Args:
+    results: A list of results to sort.
+    key: Name of the field to sort the results by.
+
+  Returns:
+    A list of results in sorted order.
+  """
+  return sorted(results, lambda d: (key not in d, d[key]))
+
+def start_wifi_connection_scan(droid, ed):
+    """Starts a wifi connection scan and wait for results to become available.
+
+    Args:
+        droid: Sl4a session to use.
+        ed: Event dispatcher instance associated with the sl4a session.
+    """
+    droid.wifiStartScan()
+    ed.pop_event("WifiManagerScanResultsAvailable", 60)
+
+def start_wifi_background_scan(ad, scan_setting):
+    """Starts wifi background scan.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        scan_setting: A dict representing the settings of the scan.
+
+    Returns:
+        If scan was started successfully, event data of success event is returned.
+    """
+    droid, ed = ad.droids[0], ad.eds[0]
+    idx = droid.wifiScannerStartBackgroundScan(scan_setting)
+    event = ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
+                         SHORT_TIMEOUT)
+    return event['data']
+
+def start_wifi_tracking_change(droid, ed):
+    """Starts tracking wifi change.
+
+    Args:
+        droid: Sl4a session to use.
+        ed: Event dispatcher instance associated with the sl4a session.
+
+    Returns:
+        If scan was started successfully, event data of success event is returned.
+    """
+    idx = droid.wifiScannerStartTrackingChange()
+    event = ed.pop_event("WifiScannerChange{}onSuccess".format(idx),
+                         SHORT_TIMEOUT)
+    return event['data']
+
+def start_wifi_tethering(ad, ssid, password, band=None):
+    """Starts wifi tethering on an android_device.
+
+    Args:
+        ad: android_device to start wifi tethering on.
+        ssid: The SSID the soft AP should broadcast.
+        password: The password the soft AP should use.
+        band: The band the soft AP should be set on. It should be either
+            WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
+
+    Returns:
+        True if soft AP was started successfully, False otherwise.
+    """
+    droid, ed = ad.droid, ad.ed
+    droid.wifiStartTrackingStateChange()
+    config = {
+        WifiEnums.SSID_KEY: ssid
+    }
+    if password:
+        config[WifiEnums.PWD_KEY] = password
+    if band:
+        config[WifiEnums.APBAND_KEY] = band
+    if not droid.wifiSetApEnabled(True, config):
+        return False
+    ed.pop_event("WifiManagerApEnabled", 30)
+    ed.wait_for_event("TetherStateChanged",
+        lambda x : x["data"]["ACTIVE_TETHER"], 30)
+    droid.wifiStopTrackingStateChange()
+    return True
+
+def stop_wifi_tethering(ad):
+    """Stops wifi tethering on an android_device.
+
+    Args:
+        ad: android_device to stop wifi tethering on.
+    """
+    droid, ed = ad.droid, ad.ed
+    droid.wifiStartTrackingStateChange()
+    droid.wifiSetApEnabled(False, None)
+    ed.pop_event("WifiManagerApDisabled", 30)
+    ed.wait_for_event("TetherStateChanged",
+        lambda x : not x["data"]["ACTIVE_TETHER"], 30)
+    droid.wifiStopTrackingStateChange()
+
+def wifi_connect(ad, ssid, pwd=None):
+    """Connects to a wifi network.
+
+    Initiate connection to a wifi network, wait for the "connected" event, then
+    confirm the connected ssid is the one requested. If pwd is not given, treat
+    the ssid as open network.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        ssid: SSID of the wifi network to connect to.
+        pwd: Password of the wifi network.
+
+    Returns:
+        True if connection is successful, False otherwise.
+    """
+    droid, ed = ad.droid, ad.ed
+    droid.wifiStartTrackingStateChange()
+    try:
+        c = {WifiEnums.SSID_KEY: ssid}
+        if pwd:
+            c["password"] = pwd
+        if droid.wifiConnect(c):
+            event = ed.pop_event("WifiNetworkConnected", 120)
+            return event['data'][WifiEnums.SSID_KEY] == ssid
+        return False
+    finally:
+        droid.wifiStopTrackingStateChange()
+
+def start_wifi_single_scan(ad, scan_setting):
+    """Starts wifi single shot scan.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        scan_setting: A dict representing the settings of the scan.
+
+    Returns:
+        If scan was started successfully, event data of success event is returned.
+    """
+    droid, ed = ad.droid, ad.ed
+    idx = droid.wifiScannerStartScan(scan_setting)
+    event = ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
+                         SHORT_TIMEOUT)
+    log.debug("event {}".format(event))
+    return event['data']
+
+def track_connection(ad, network_ssid, check_connection_count):
+    """Track wifi connection to network changes for given number of counts
+
+    Args:
+        ad: android_device object for forget network.
+        network_ssid: network ssid to which connection would be tracked
+        check_connection_count: Integer for maximum number network connection
+            check.
+    Returns:
+
+        True if connection to given network happen, else return False.
+    """
+    droid, ed = ad.droid, ad.ed
+    droid.wifiStartTrackingStateChange()
+    while check_connection_count > 0:
+      connect_network = ed.pop_event("WifiNetworkConnected", 120)
+      log.info("connect_network {}".format(connect_network))
+      if (WifiEnums.SSID_KEY in connect_network['data']
+                            and connect_network['data'][WifiEnums.SSID_KEY]==network_ssid):
+        return True
+      check_connection_count -= 1
+    droid.wifiStopTrackingStateChange()
+    return False
+
+def get_scan_time_and_channels(wifi_chs, scan_setting, stime_channel):
+    """Calculate the scan time required based on the band or channels in scan
+    setting
+
+    Args:
+        wifi_chs: Object of channels supported
+        scan_setting: scan setting used for start scan
+        stime_channel: scan time per channel
+
+    Returns:
+        scan_time: time required for completing a scan
+        scan_channels: channel used for scanning
+    """
+    scan_time = 0
+    scan_channels = []
+    if "band" in scan_setting and "channels" not in scan_setting:
+      scan_channels = wifi_chs.band_to_freq(scan_setting["band"])
+    elif "channels" in scan_setting and "band" not in scan_setting:
+      scan_channels = scan_setting["channels"]
+    scan_time = len(scan_channels) * stime_channel
+    for channel in scan_channels:
+      if channel in WifiEnums.DFS_5G_FREQUENCIES:
+        scan_time += 132 #passive scan time on DFS
+    return scan_time, scan_channels
+
+def start_wifi_track_bssid(ad, track_setting):
+    """Start tracking Bssid for the given settings.
+
+    Args:
+      ad: android_device object.
+      track_setting: Setting for which the bssid tracking should be started
+
+    Returns:
+      If tracking started successfully, event data of success event is returned.
+    """
+    droid, ed = ad.droid, ad.ed
+    idx = droid.wifiScannerStartTrackingBssids(
+        track_setting["bssidInfos"],
+        track_setting["apLostThreshold"]
+        )
+    event = ed.pop_event("WifiScannerBssid{}onSuccess".format(idx),
+                         SHORT_TIMEOUT)
+    return event['data']
+
+def convert_pem_key_to_pkcs8(in_file, out_file):
+    """Converts the key file generated by us to the format required by
+    Android using openssl.
+
+    The input file must have the extension "pem". The output file must
+    have the extension "der".
+
+    Args:
+        in_file: The original key file.
+        out_file: The full path to the converted key file, including
+        filename.
+    """
+    assert in_file.endswith(".pem")
+    assert out_file.endswith(".der")
+    cmd = ("openssl pkcs8 -inform PEM -in {} -outform DER -out {} -nocrypt"
+           " -topk8").format(in_file, out_file)
+    exe_cmd(cmd)
+
+def check_internet_connection(ad, ping_addr):
+    """Validate internet connection by pinging the address provided.
+
+    Args:
+        ad: android_device object.
+        ping_addr: address on internet for pinging.
+
+    Returns:
+        True, if address ping successful
+    """
+    droid, ed = ad.droid, ad.ed
+    ping = droid.httpPing(ping_addr)
+    log.info("Http ping result: {}".format(ping))
+    return ping
+
+#TODO(angli): This can only verify if an actual value is exactly the same.
+# Would be nice to be able to verify an actual value is one of serveral.
+def verify_wifi_connection_info(ad, expected_con):
+    """Verifies that the information of the currently connected wifi network is
+    as expected.
+
+    Args:
+        expected_con: A dict representing expected key-value pairs for wifi
+            connection. e.g. {"SSID": "test_wifi"}
+    """
+    current_con = ad.droid.wifiGetConnectionInfo()
+    log.debug("Current connection: %s" % current_con)
+    for k, expected_v in expected_con.items():
+        msg = "Field %s does not exist in wifi connection info %s." % (k,
+            current_con)
+        assert k in current_con, msg
+        actual_v = current_con[k].lower()
+        msg = "Expected %s to be %s, actual %s is %s." % (k, expected_v, k,
+            actual_v)
+        expected_v = expected_v.lower()
+        assert actual_v == expected_v, msg
+
+def eap_connect(config, ad, validate_con=True, ping_addr=DEFAULT_PING_ADDR):
+    """Connects to an enterprise network and verify connection.
+
+    This logic expect the enterprise network to have Internet access.
+
+    Args:
+        config: A dict representing a wifi enterprise configuration.
+        ad: The android_device to operate with.
+        validate_con: If True, validate Internet connection after connecting to
+            the network.
+
+    Returns:
+        True if the connection is successful and Internet access works.
+    """
+    droid, ed = ad.droid, ad.ed
+    start_wifi_connection_scan(droid, ed)
+    expect_ssid = None
+    if WifiEnums.SSID_KEY in config:
+        expect_ssid = config[WifiEnums.SSID_KEY]
+        log.info("Connecting to %s." % expect_ssid)
+    else:
+        log.info("Connecting.")
+    log.debug(pprint.pformat(config, indent=4))
+    droid.wifiEnterpriseConnect(config)
+    try:
+        event = ed.pop_event("WifiManagerEnterpriseConnectOnSuccess", 30)
+        log.info("Started connecting...")
+        event = ed.pop_event(WifiEventNames.WIFI_CONNECTED, 60)
+    except Empty:
+        log.info("Failed to connect.")
+        return False
+    log.debug(event)
+    if expect_ssid:
+        actual_ssid = event["data"][WifiEnums.SSID_KEY]
+        msg = "Expected SSID {}, got {}".format(expect_ssid, actual_ssid)
+        if expect_ssid != actual_ssid:
+            log.error(msg)
+            return False
+        log.info("Connected to %s." % expect_ssid)
+    else:
+        log.info("Connected successfully.")
+    if validate_con:
+        log.info("Checking Internet access.")
+        # Wait for data connection to stabilize.
+        time.sleep(4)
+        ping = droid.httpPing(ping_addr)
+        log.info("Http ping result: {}".format(ping))
+        if not ping:
+            log.error("No Internet access.")
+            return False
+    return True
+
+def expand_enterprise_config_by_phase2(config):
+    """Take an enterprise config and generate a list of configs, each with
+    a different phase2 auth type.
+
+    Args:
+        config: A dict representing enterprise config.
+
+    Returns
+        A list of enterprise configs.
+    """
+    results = []
+    for phase2_type in WifiEnums.EapPhase2:
+        # Skip a special case for passpoint TTLS.
+        if (WifiEnums.Enterprise.FQDN in config and
+            phase2_type == WifiEnums.EapPhase2.GTC):
+            continue
+        c = dict(config)
+        c[WifiEnums.Enterprise.PHASE2] = phase2_type
+        results.append(c)
+    return results
diff --git a/acts/framework/acts/utils.py b/acts/framework/acts/utils.py
new file mode 100755
index 0000000..aed0e05
--- /dev/null
+++ b/acts/framework/acts/utils.py
@@ -0,0 +1,496 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 base64
+import concurrent.futures
+import datetime
+import json
+import functools
+import os
+import random
+import re
+import signal
+import string
+import subprocess
+import time
+import traceback
+
+class NexusModelNames:
+    # TODO(angli): This will be fixed later by angli.
+    ONE = 'sprout'
+    N5 = 'hammerhead'
+    N5v2 = 'bullhead'
+    N6 = 'shamu'
+    N6v2 = 'angler'
+
+ascii_letters_and_digits = string.ascii_letters + string.digits
+valid_filename_chars = "-_." + ascii_letters_and_digits
+
+models = ("sprout", "occam", "hammerhead", "bullhead", "razor", "razorg",
+    "shamu", "angler", "volantis", "volantisg", "mantaray", "fugu", "ryu")
+
+manufacture_name_to_model = {
+    "flo": "razor",
+    "flo_lte": "razorg",
+    "flounder": "volantis",
+    "flounder_lte": "volantisg",
+    "dragon": "ryu"
+}
+
+GMT_to_olson = {
+    "GMT-9":  "America/Anchorage",
+    "GMT-8":  "US/Pacific",
+    "GMT-7":  "US/Mountain",
+    "GMT-6":  "US/Central",
+    "GMT-5":  "US/Eastern",
+    "GMT-4":  "America/Barbados",
+    "GMT-3":  "America/Buenos_Aires",
+    "GMT-2":  "Atlantic/South_Georgia",
+    "GMT-1":  "Atlantic/Azores",
+    "GMT+0":  "Africa/Casablanca",
+    "GMT+1":  "Europe/Amsterdam",
+    "GMT+2":  "Europe/Athens",
+    "GMT+3":  "Europe/Moscow",
+    "GMT+4":  "Asia/Baku",
+    "GMT+5":  "Asia/Oral",
+    "GMT+6":  "Asia/Almaty",
+    "GMT+7":  "Asia/Bangkok",
+    "GMT+8":  "Asia/Hong_Kong",
+    "GMT+9":  "Asia/Tokyo",
+    "GMT+10": "Pacific/Guam",
+    "GMT+11": "Pacific/Noumea",
+    "GMT+12": "Pacific/Fiji",
+    "GMT+13": "Pacific/Tongatapu",
+    "GMT-11": "Pacific/Midway",
+    "GMT-10": "Pacific/Honolulu"
+}
+
+def abs_path(path):
+    """Resolve the '.' and '~' in a path to get the absolute path.
+
+    Args:
+        path: The path to expand.
+
+    Returns:
+        The absolute path of the input path.
+    """
+    return os.path.abspath(os.path.expanduser(path))
+
+def create_dir(path):
+    """Creates a directory if it does not exist already.
+
+    Args:
+        path: The path of the directory to create.
+    """
+    full_path = abs_path(path)
+    if not os.path.exists(full_path):
+        os.makedirs(full_path)
+
+def get_current_epoch_time():
+    """Current epoch time in milliseconds.
+
+    Returns:
+        An integer representing the current epoch time in milliseconds.
+    """
+    return int(round(time.time() * 1000))
+
+def get_current_human_time():
+    """Returns the current time in human readable format.
+
+    Returns:
+        The current time stamp in Month-Day-Year Hour:Min:Sec format.
+    """
+    return time.strftime("%m-%d-%Y %H:%M:%S ")
+
+def epoch_to_human_time(epoch_time):
+    """Converts an epoch timestamp to human readable time.
+
+    This essentially converts an output of get_current_epoch_time to an output
+    of get_current_human_time
+
+    Args:
+        epoch_time: An integer representing an epoch timestamp in milliseconds.
+
+    Returns:
+        A time string representing the input time.
+        None if input param is invalid.
+    """
+    if isinstance(epoch_time, int):
+        try:
+            d = datetime.datetime.fromtimestamp(epoch_time / 1000)
+            return d.strftime("%m-%d-%Y %H:%M:%S ")
+        except ValueError:
+            return None
+
+def get_timezone_olson_id():
+    """Return the Olson ID of the local (non-DST) timezone.
+
+    Returns:
+        A string representing one of the Olson IDs of the local (non-DST)
+        timezone.
+    """
+    tzoffset = int(time.timezone/3600)
+    gmt = None
+    if tzoffset <= 0:
+        gmt = "GMT+{}".format(-tzoffset)
+    else:
+        gmt = "GMT-{}".format(tzoffset)
+    return GMT_to_olson[gmt]
+
+def find_files(paths, file_predicate):
+    """Locate files whose names and extensions match the given predicate in
+    the specified directories.
+
+    Args:
+        paths: A list of directory paths where to find the files.
+        file_predicate: A function that returns True if the file name and
+          extension are desired.
+
+    Returns:
+        A list of files that match the predicate.
+    """
+    file_list = []
+    for path in paths:
+        p = abs_path(path)
+        for dirPath, subdirList, fileList in os.walk(p):
+            for fname in fileList:
+                name, ext = os.path.splitext(fname)
+                if file_predicate(name, ext):
+                  file_list.append((dirPath, name, ext))
+    return file_list
+
+def load_config(file_full_path):
+    """Loads a JSON config file.
+
+    Returns:
+        A JSON object.
+    """
+    with open(file_full_path, 'r') as f:
+        conf = json.load(f)
+        return conf
+
+def load_file_to_base64_str(f_path):
+    """Loads the content of a file into a base64 string.
+
+    Args:
+        f_path: full path to the file including the file name.
+
+    Returns:
+        A base64 string representing the content of the file in utf-8 encoding.
+    """
+    path = abs_path(f_path)
+    with open(path, 'rb') as f:
+      f_bytes = f.read()
+      base64_str = base64.b64encode(f_bytes).decode("utf-8")
+      return base64_str
+
+def find_field(item_list, cond, comparator, target_field):
+    """Finds the value of a field in a dict object that satisfies certain
+    conditions.
+
+    Args:
+        item_list: A list of dict objects.
+        cond: A param that defines the condition.
+        comparator: A function that checks if an dict satisfies the condition.
+        target_field: Name of the field whose value to be returned if an item
+            satisfies the condition.
+
+    Returns:
+        Target value or None if no item satisfies the condition.
+    """
+    for item in item_list:
+       if comparator(item, cond) and target_field in item:
+          return item[target_field]
+    return None
+
+def rand_ascii_str(length):
+    """Generates a random string of specified length, composed of ascii letters
+    and digits.
+
+    Args:
+        length: The number of characters in the string.
+
+    Returns:
+        The random string generated.
+    """
+    letters = [random.choice(ascii_letters_and_digits) for i in range(length)]
+    return ''.join(letters)
+
+# Thead/Process related functions.
+def concurrent_exec(func, param_list):
+    """Executes a function with different parameters pseudo-concurrently.
+
+    This is basically a map function. Each element (should be an iterable) in
+    the param_list is unpacked and passed into the function. Due to Python's
+    GIL, there's no true concurrency. This is suited for IO-bound tasks.
+
+    Args:
+        func: The function that parforms a task.
+        param_list: A list of iterables, each being a set of params to be
+            passed into the function.
+    """
+    with concurrent.futures.ThreadPoolExecutor(max_workers=30) as executor:
+        # Start the load operations and mark each future with its params
+        future_to_params = {executor.submit(func, *p): p for p in param_list}
+        for future in concurrent.futures.as_completed(future_to_params):
+            params = future_to_params[future]
+            try:
+                data = future.result()
+            except Exception as exc:
+                print("{} generated an exception: {}".format(params,
+                    traceback.format_exc()))
+
+def exe_cmd(*cmds):
+    """Executes commands in a new shell.
+
+    Args:
+        cmds: A sequence of commands and arguments.
+
+    Returns:
+        The output of the command run.
+
+    Raises:
+        Exception is raised if an error occurred during the command execution.
+    """
+    cmd = ' '.join(cmds)
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
+    (out, err) = proc.communicate()
+    if not err:
+        return out
+    raise Exception(err)
+
+def require_sl4a(android_devices):
+    """Makes sure sl4a connection is established on the given AndroidDevice
+    objects.
+
+    Args:
+        android_devices: A list of AndroidDevice objects.
+
+    Raises:
+        AssertionError is raised if any given android device does not have SL4A
+        connection established.
+    """
+    for ad in android_devices:
+        msg = "SL4A connection not established properly on %s." % ad.serial
+        assert ad.droid, msg
+
+def start_standing_subprocess(cmd):
+    """Starts a non-blocking subprocess that is going to continue running after
+    this function returns.
+
+    A subprocess group is actually started by setting sid, so we can kill all
+    the processes spun out from the subprocess when stopping it. This is
+    necessary in case users pass in pipe commands.
+
+    Args:
+        cmd: Command to start the subprocess with.
+
+    Returns:
+        The subprocess that got started.
+    """
+    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True,
+                preexec_fn=os.setpgrp)
+    return p
+
+def stop_standing_subprocess(p):
+    """Stops a subprocess started by start_standing_subprocess.
+
+    Catches and ignores the PermissionError which only happens on Macs.
+
+    Args:
+        p: Subprocess to terminate.
+    """
+    try:
+        os.killpg(p.pid, signal.SIGTERM)
+    except PermissionError:
+        pass
+
+def sync_device_time(ad):
+    """Sync the time of an android device with the current system time.
+
+    Both epoch time and the timezone will be synced.
+
+    Args:
+        ad: The android device to sync time on.
+    """
+    droid = ad.droid
+    droid.setTimeZone(get_timezone_olson_id())
+    droid.setTime(get_current_epoch_time())
+
+# Timeout decorator block
+class TimeoutError(Exception):
+    """Exception for timeout decorator related errors.
+    """
+    pass
+
+def _timeout_handler(signum, frame):
+    """Handler function used by signal to terminate a timed out function.
+    """
+    raise TimeoutError()
+
+def timeout(sec):
+    """A decorator used to add time out check to a function.
+
+    Args:
+        sec: Number of seconds to wait before the function times out.
+            No timeout if set to 0
+
+    Returns:
+        What the decorated function returns.
+
+    Raises:
+        TimeoutError is raised when time out happens.
+    """
+    def decorator(func):
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            if sec:
+                signal.signal(signal.SIGALRM, _timeout_handler)
+                signal.alarm(sec)
+            try:
+                return func(*args, **kwargs)
+            except TimeoutError:
+                raise TimeoutError(("Function {} timed out after {} "
+                  "seconds.").format(func.__name__, sec))
+            finally:
+                signal.alarm(0)
+        return wrapper
+    return decorator
+
+def trim_model_name(model):
+    """Trim any prefix and postfix and return the android designation of the
+    model name.
+
+    e.g. "m_shamu" will be trimmed to "shamu".
+
+    Args:
+        model: model name to be trimmed.
+
+    Returns
+        Trimmed model name if one of the known model names is found.
+        None otherwise.
+    """
+    # Directly look up first.
+    if model in models:
+        return model
+    if model in manufacture_name_to_model:
+        return manufacture_name_to_model[model]
+    # If not found, try trimming off prefix/postfix and look up again.
+    tokens = re.split("_|-", model)
+    for t in tokens:
+        if t in models:
+            return t
+        if t in manufacture_name_to_model:
+            return manufacture_name_to_model[t]
+    return None
+
+def enable_doze(ad):
+    """Force the device into doze mode.
+
+    Args:
+        ad: android device object.
+
+    Returns:
+        True if device is in doze mode.
+        False otherwise.
+    """
+    ad.adb.shell("dumpsys battery unplug")
+    ad.adb.shell("dumpsys deviceidle enable")
+    if (ad.adb.shell("dumpsys deviceidle force-idle") !=
+        b'Now forced in to idle mode\r\n'):
+        return False
+    ad.droid.goToSleepNow()
+    time.sleep(5)
+    adb_shell_result = ad.adb.shell("dumpsys deviceidle step")
+    if adb_shell_result not in [b'Stepped to: IDLE_MAINTENANCE\r\n',
+                                b'Stepped to: IDLE\r\n']:
+        info = ("dumpsys deviceidle step: {}dumpsys battery: {}"
+                "dumpsys deviceidle: {}".
+                format(adb_shell_result.decode('utf-8'),
+                       ad.adb.shell("dumpsys battery").decode('utf-8'),
+                       ad.adb.shell("dumpsys deviceidle").decode('utf-8')))
+        print(info)
+        return False
+    return True
+
+def disable_doze(ad):
+    """Force the device not in doze mode.
+
+    Args:
+        ad: android device object.
+
+    Returns:
+        True if device is not in doze mode.
+        False otherwise.
+    """
+    ad.adb.shell("dumpsys deviceidle disable")
+    ad.adb.shell("dumpsys battery reset")
+    adb_shell_result = ad.adb.shell("dumpsys deviceidle step")
+    if ( adb_shell_result != b'Stepped to: ACTIVE\r\n'):
+        info = ("dumpsys deviceidle step: {}dumpsys battery: {}"
+                "dumpsys deviceidle: {}".
+                format(adb_shell_result.decode('utf-8'),
+                       ad.adb.shell("dumpsys battery").decode('utf-8'),
+                       ad.adb.shell("dumpsys deviceidle").decode('utf-8')))
+        print(info)
+        return False
+    return True
+
+def set_ambient_display(ad, new_state):
+    """Set "Ambient Display" in Settings->Display
+
+    Args:
+        ad: android device object.
+        new_state: new state for "Ambient Display". True or False.
+    """
+    ad.adb.shell("settings put secure doze_enabled {}".
+        format(1 if new_state else 0))
+
+def set_adaptive_brightness(ad, new_state):
+    """Set "Adaptive Brightness" in Settings->Display
+
+    Args:
+        ad: android device object.
+        new_state: new state for "Adaptive Brightness". True or False.
+    """
+    ad.adb.shell("settings put system screen_brightness_mode {}".
+        format(1 if new_state else 0))
+
+def set_auto_rotate(ad, new_state):
+    """Set "Auto-rotate" in QuickSetting
+
+    Args:
+        ad: android device object.
+        new_state: new state for "Auto-rotate". True or False.
+    """
+    ad.adb.shell("settings put system accelerometer_rotation {}".
+        format(1 if new_state else 0))
+
+def set_location_service(ad, new_state):
+    """Set Location service on/off in Settings->Location
+
+    Args:
+        ad: android device object.
+        new_state: new state for "Location service".
+            If new_state is False, turn off location service.
+            If new_state if True, set location service to "High accuracy".
+    """
+    if new_state:
+        ad.adb.shell("settings put secure location_providers_allowed +gps")
+        ad.adb.shell("settings put secure location_providers_allowed +network")
+    else:
+        ad.adb.shell("settings put secure location_providers_allowed -gps")
+        ad.adb.shell("settings put secure location_providers_allowed -network")
diff --git a/acts/framework/sample_config.json b/acts/framework/sample_config.json
new file mode 100644
index 0000000..a0244da
--- /dev/null
+++ b/acts/framework/sample_config.json
@@ -0,0 +1,23 @@
+{   "_description": "This is an example skeleton test configuration file.",
+    "testbed":
+    [
+        {
+            "_description": "A testbed with two android devices.",
+            "name": "Enterprise-D",
+            "AndroidDevice": ["<serial>", "<serial>"]
+        },
+        {
+            "_description": "A testbed with two android devices.",
+            "name": "Enterprise-E",
+            "AndroidDevice": [{"serial": "<serial>", "label": "caller"},
+                              {"serial": "<serial>", "label": "callee", "whatever": "anything"}]
+        },
+        {
+            "_description": "Another testbed with one android device.",
+            "name": "SampleTestBed"
+        }
+    ],
+    "logpath": "/tmp/logs",
+    "testpaths": ["./test_cases"],
+    "custom_param1": {"favorite_food": "Icecream!"}
+}
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
new file mode 100755
index 0000000..029b26e
--- /dev/null
+++ b/acts/framework/setup.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python3.4
+
+from setuptools import setup
+from setuptools import find_packages
+
+setup(
+    name='acts',
+    version = '0.9',
+    description = 'Android Comms Test Suite',
+    license = 'Apache2.0',
+    packages = find_packages(),
+    include_package_data = False,
+    install_requires = [
+        'pyserial',
+    ],
+    scripts = ['acts/act.py','acts/monsoon.py'],
+    url = "http://www.android.com/"
+)
diff --git a/acts/tests/google/SampleTest.py b/acts/tests/google/SampleTest.py
new file mode 100755
index 0000000..966aaac
--- /dev/null
+++ b/acts/tests/google/SampleTest.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2015 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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.base_test import BaseTestClass
+
+class SampleTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_make_toast",
+        )
+
+    """Tests"""
+    def test_make_toast(self):
+        for ad in self.android_devices:
+            ad.droid.makeToast("Hello World.")
+        return True
\ No newline at end of file
diff --git a/acts/tests/google/ble/api/BleAdvertiseApiTest.py b/acts/tests/google/ble/api/BleAdvertiseApiTest.py
new file mode 100644
index 0000000..ca4789d
--- /dev/null
+++ b/acts/tests/google/ble/api/BleAdvertiseApiTest.py
@@ -0,0 +1,1324 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See thea
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""
+Test script to exercise Ble Advertisement Api's. This exercises all getters and
+setters. This is important since there is a builder object that is immutable
+after you set all attributes of each object. If this test suite doesn't pass,
+then other test suites utilising Ble Advertisements will also fail.
+"""
+
+from acts.controllers.android import SL4AAPIError
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import adv_fail
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseTxPower
+from acts.test_utils.bt.BleEnum import JavaInteger
+
+# TODO: Refactor to work like BleScanApiTest.
+
+
+class BleAdvertiseVerificationError(Exception):
+
+    """Error in fetsching BleScanner Advertise result."""
+
+
+class BleAdvertiseApiTest(BluetoothBaseTest):
+    tests = None
+
+    def __init__(self, android_devices):
+        BluetoothBaseTest.__init__(self, android_devices)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.tests = (
+            "test_adv_settings_defaults",
+            "test_adv_data_defaults",
+            "test_adv_settings_set_adv_mode_balanced",
+            "test_adv_settings_set_adv_mode_low_power",
+            "test_adv_settings_set_adv_mode_low_latency",
+            "test_adv_settings_set_invalid_adv_mode",
+            "test_adv_settings_set_adv_tx_power_level_high",
+            "test_adv_settings_set_adv_tx_power_level_medium",
+            "test_adv_settings_set_adv_tx_power_level_low",
+            "test_adv_settings_set_adv_tx_power_level_ultra_low",
+            "test_adv_settings_set_invalid_adv_tx_power_level",
+            "test_adv_settings_set_is_connectable_true",
+            "test_adv_settings_set_is_connectable_false",
+            "test_adv_data_set_service_uuids_empty",
+            "test_adv_data_set_service_uuids_single",
+            "test_adv_data_set_service_uuids_multiple",
+            "test_adv_data_set_service_uuids_invalid_uuid",
+            "test_adv_data_set_service_data",
+            "test_adv_data_set_service_data_invalid_service_data",
+            "test_adv_data_set_service_data_invalid_service_data_uuid",
+            "test_adv_data_set_manu_id",
+            "test_adv_data_set_manu_id_invalid_manu_id",
+            "test_adv_data_set_manu_id_invalid_manu_specific_data",
+            "test_adv_data_set_manu_id_max",
+            "test_adv_data_set_include_tx_power_level_true",
+            "test_adv_data_set_include_tx_power_level_false",
+            "test_adv_data_set_include_device_name_true",
+            "test_adv_data_set_include_device_name_false",
+        )
+
+        if self.droid_list[0]['max_advertisements'] > 0:
+            self.tests = self.tests + (
+                "test_advertisement_greater_than_31_bytes",)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_defaults(self):
+        """Tests the default advertisement settings.
+
+        This builder object should have a proper "get" expectation for each
+        attribute of the builder object once it's built.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Get the attributes of the advertise settings object.
+        3. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 0
+        """
+        test_result = True
+        droid = self.droid
+        adv_settings = droid.bleBuildAdvertiseSettings()
+        adv_mode = droid.bleGetAdvertiseSettingsMode(adv_settings)
+        tx_power_level = droid.bleGetAdvertiseSettingsTxPowerLevel(
+            adv_settings)
+        is_connectable = droid.bleGetAdvertiseSettingsIsConnectable(
+            adv_settings)
+
+        exp_adv_mode = AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_POWER.value
+        exp_tx_power_level = AdvertiseSettingsAdvertiseTxPower.ADVERTISE_TX_POWER_MEDIUM.value
+        exp_is_connectable = True
+        if adv_mode != exp_adv_mode:
+            test_result = False
+            self.log.debug("exp filtering mode: {},"
+                           " found filtering mode: {}".
+                           format(exp_adv_mode, adv_mode))
+        if tx_power_level != exp_tx_power_level:
+            test_result = False
+            self.log.debug("exp tx power level: {},"
+                           " found filtering tx power level: {}".
+                           format(exp_tx_power_level, tx_power_level))
+        if exp_is_connectable != is_connectable:
+            test_result = False
+            self.log.debug("exp is connectable: {},"
+                           " found filtering is connectable: {}".
+                           format(exp_is_connectable, is_connectable))
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_defaults(self):
+        """Tests the default advertisement data.
+
+        This builder object should have a proper "get" expectation for each
+        attribute of the builder object once it's built.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Get the attributes of the advertise settings object.
+        3. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 0
+        """
+        test_result = True
+        droid = self.droid
+        adv_data = droid.bleBuildAdvertiseData()
+        service_uuids = droid.bleGetAdvertiseDataServiceUuids(adv_data)
+        include_tx_power_level = droid.bleGetAdvertiseDataIncludeTxPowerLevel(
+            adv_data)
+        include_device_name = droid.bleGetAdvertiseDataIncludeDeviceName(
+            adv_data)
+
+        exp_service_uuids = []
+        exp_include_tx_power_level = False
+        exp_include_device_name = False
+        self.log.debug("Step 4: Verify all defaults match exp values.")
+        if service_uuids != exp_service_uuids:
+            test_result = False
+            self.log.debug("exp filtering service uuids: {},"
+                           " found filtering service uuids: {}".
+                           format(exp_service_uuids, service_uuids))
+        if include_tx_power_level != exp_include_tx_power_level:
+            test_result = False
+            self.log.debug("exp filtering include tx power level:: {},"
+                           " found filtering include tx power level: {}".
+                           format(exp_include_tx_power_level,
+                                  include_tx_power_level))
+        if include_device_name != exp_include_device_name:
+            test_result = False
+            self.log.debug("exp filtering include tx power level: {},"
+                           " found filtering include tx power level: {}".
+                           format(exp_include_device_name,
+                                  include_device_name))
+        if not test_result:
+            self.log.debug("Some values didn't match the defaults.")
+        else:
+            self.log.debug("All default values passed.")
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_adv_mode_balanced(self):
+        """Tests advertise settings balanced mode.
+
+        This advertisement settings from "set" advertisement mode should match
+        the corresponding "get" function.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise mode attribute to balanced.
+        3. Get the attributes of the advertise settings object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_adv_mode = AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_BALANCED.value
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to {}".
+            format(exp_adv_mode))
+        return self.verify_adv_settings_adv_mode(droid,
+                                                 exp_adv_mode)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_adv_mode_low_power(self):
+        """Tests advertise settings low power mode.
+
+        This advertisement settings from "set" advertisement mode should match
+        the corresponding "get" function.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise mode attribute to low power mode.
+        3. Get the attributes of the advertise settings object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_adv_mode = AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_POWER.value
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to {}".
+            format(exp_adv_mode))
+        return self.verify_adv_settings_adv_mode(droid,
+                                                 exp_adv_mode)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_adv_mode_low_latency(self):
+        """Tests advertise settings low latency mode.
+
+        This advertisement settings from "set" advertisement mode should match
+        the corresponding "get" function.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise mode attribute to low latency mode.
+        3. Get the attributes of the advertise settings object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_adv_mode = AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to {}".
+            format(exp_adv_mode))
+        return self.verify_adv_settings_adv_mode(droid,
+                                                 exp_adv_mode)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_invalid_adv_mode(self):
+        """Tests advertise settings invalid advertising mode.
+
+        This advertisement settings from "set" advertisement mode should fail
+        when setting an invalid advertisement.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise mode attribute to -1.
+
+        Expected Result:
+        Building the advertise settings should fail.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 2
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_adv_mode = -1
+        self.log.debug("Step 2: Set the filtering mode to -1")
+        return self.verify_invalid_adv_settings_adv_mode(droid,
+                                                         exp_adv_mode)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_adv_tx_power_level_high(self):
+        """Tests advertise settings tx power level high.
+
+        This advertisement settings from "set" advertisement tx power level
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise mode attribute to tx power level high.
+        3. Get the attributes of the advertise settings object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_adv_tx_power = (AdvertiseSettingsAdvertiseTxPower
+                                       .ADVERTISE_TX_POWER_HIGH.value)
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to {}".
+            format(exp_adv_tx_power))
+        return self.verify_adv_settings_tx_power_level(droid,
+                                                       exp_adv_tx_power)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_adv_tx_power_level_medium(self):
+        """Tests advertise settings tx power level medium.
+
+        This advertisement settings from "set" advertisement tx power level
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise mode attribute to tx power level medium.
+        3. Get the attributes of the advertise settings object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        test_result = True
+        droid = self.droid
+        exp_adv_tx_power = (AdvertiseSettingsAdvertiseTxPower
+                                       .ADVERTISE_TX_POWER_MEDIUM.value)
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to {}".
+            format(exp_adv_tx_power))
+        return self.verify_adv_settings_tx_power_level(droid,
+                                                       exp_adv_tx_power)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_adv_tx_power_level_low(self):
+        """Tests advertise settings tx power level low.
+
+        This advertisement settings from "set" advertisement tx power level
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise mode attribute to tx power level low.
+        3. Get the attributes of the advertise settings object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_adv_tx_power = (AdvertiseSettingsAdvertiseTxPower
+                                       .ADVERTISE_TX_POWER_LOW.value)
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to ".
+            format(exp_adv_tx_power))
+        return self.verify_adv_settings_tx_power_level(droid,
+                                                       exp_adv_tx_power)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_adv_tx_power_level_ultra_low(self):
+        """Tests advertise settings tx power level ultra low.
+
+        This advertisement settings from "set" advertisement tx power level
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise mode attribute to tx power level ultra low.
+        3. Get the attributes of the advertise settings object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_adv_tx_power = (AdvertiseSettingsAdvertiseTxPower
+                                       .ADVERTISE_TX_POWER_ULTRA_LOW.value)
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to ".
+            format(exp_adv_tx_power))
+        return self.verify_adv_settings_tx_power_level(droid,
+                                                       exp_adv_tx_power)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_invalid_adv_tx_power_level(self):
+        """Tests advertise settings invalid advertising tx power level.
+
+        This advertisement settings from "set" advertisement mode should fail
+        when setting an invalid advertisement.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise tx power level attribute to -1.
+
+        Expected Result:
+        Building the advertise settings should fail.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_adv_tx_power = -1
+        self.log.debug("Step 2: Set the filtering mode to -1")
+        return self.verify_invalid_adv_settings_tx_power_level(droid,
+                                                               exp_adv_tx_power)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_is_connectable_true(self):
+        """Tests advertise settings is connectable true.
+
+        This advertisement settings from "set" advertisement tx power level
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise is connectable to true.
+        3. Get the attributes of the advertise settings object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_is_connectable = True
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to {}".
+            format(exp_is_connectable))
+        return self.verify_adv_settings_is_connectable(droid,
+                                                       exp_is_connectable)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_settings_set_is_connectable_false(self):
+        """Tests advertise settings is connectable false.
+
+        This advertisement settings from "set" advertisement tx power level
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new advertise settings object.
+        2. Set the advertise is connectable to false.
+        3. Get the attributes of the advertise settings object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_is_connectable = False
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to " + str(
+                exp_is_connectable))
+        return self.verify_adv_settings_is_connectable(droid,
+                                                       exp_is_connectable)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_service_uuids_empty(self):
+        """Tests advertisement data's service uuids to empty.
+
+        This advertisement data from "set" advertisement service uuid
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's service uuid to empty.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_service_uuids = []
+        self.log.debug(
+            "Step 2: Set the filtering data object's value to " + str(
+                exp_service_uuids))
+        return self.verify_adv_data_service_uuids(droid,
+                                                  exp_service_uuids)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_service_uuids_single(self):
+        """Tests advertisement data's service uuids to empty.
+
+        This advertisement data from "set" advertisement service uuid
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's service uuid to empty.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_service_uuids = ["00000000-0000-1000-8000-00805f9b34fb"]
+        self.log.debug(
+            "Step 2: Set the filtering data object's value to " + str(
+                exp_service_uuids))
+        return self.verify_adv_data_service_uuids(droid,
+                                                  exp_service_uuids)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_service_uuids_multiple(self):
+        """Tests advertisement data's service uuids to multiple uuids.
+
+        This advertisement data from "set" advertisement service uuid
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's service uuid to multiple uuids.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_service_uuids = ["00000000-0000-1000-8000-00805f9b34fb",
+                             "00000000-0000-1000-8000-00805f9b34fb"]
+        self.log.debug(
+            "Step 2: Set the filtering data object's value to " + str(
+                exp_service_uuids))
+        return self.verify_adv_data_service_uuids(droid,
+                                                  exp_service_uuids)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_service_uuids_invalid_uuid(self):
+        """Tests advertisement data's service uuids to an invalid uuid.
+
+        This advertisement data from "set" advertisement service uuid
+        should fail when there is an invalid service uuid.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's service uuid to an invalid uuid.
+
+        Expected Result:
+        Building the AdvertiseData should fail.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_service_uuids = ["0"]
+        self.log.debug(
+            "Step 2: Set the filtering data service uuids to " + str(
+                exp_service_uuids))
+        return self.verify_invalid_adv_data_service_uuids(droid,
+                                                          exp_service_uuids)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_service_data(self):
+        """Tests advertisement data's service data.
+
+        This advertisement data from "set" advertisement service data
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's service data.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_service_data_uuid = "00000000-0000-1000-8000-00805f9b34fb"
+        exp_service_data = "1,2,3"
+        self.log.debug(
+            "Step 2: Set the filtering data object's service data uuid to: {}, "
+            "service data: {}".format(exp_service_data_uuid, exp_service_data))
+        return self.verify_adv_data_service_data(droid,
+                                                 exp_service_data_uuid,
+                                                 exp_service_data)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_service_data_invalid_service_data(self):
+        """Tests advertisement data's invalid service data.
+
+        This advertisement data from "set" advertisement service data
+        should fail on an invalid value.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's service data to an invalid value.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_service_data_uuid = "00000000-0000-1000-8000-00805f9b34fb"
+        exp_service_data = "helloworld"
+        self.log.debug(
+            "Step 2: Set the filtering data object's service data uuid to: {}, "
+            "service data: {}".format(exp_service_data_uuid, exp_service_data))
+        return self.verify_invalid_adv_data_service_data(droid,
+                                                         exp_service_data_uuid,
+                                                         exp_service_data)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_service_data_invalid_service_data_uuid(self):
+        """Tests advertisement data's invalid service data and uuid.
+
+        This advertisement data from "set" advertisement service data
+        should fail on an invalid value.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's service data and uuid to an invalid value.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_service_data_uuid = "0"
+        exp_service_data = "1,2,3"
+        self.log.debug(
+            "Step 2: Set the filtering data object's service data uuid to: {}, "
+            "service data: {}".format(exp_service_data_uuid, exp_service_data))
+        return self.verify_invalid_adv_data_service_data(droid,
+                                                         exp_service_data_uuid,
+                                                         exp_service_data)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_manu_id(self):
+        """Tests advertisement data's manufacturers data and id.
+
+        This advertisement data from "set" advertisement manufacturers data
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's manufacturers data and id.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_manu_id = 0
+        exp_manu_specific_data = "1,2,3"
+        self.log.debug(
+            "Step 2: Set the filtering data object's service data manu id: {}"
+            ", manu specific data: {}".format(exp_manu_id,
+                                              exp_manu_specific_data))
+        return self.verify_adv_data_manu_id(droid,
+                                            exp_manu_id,
+                                            exp_manu_specific_data)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_manu_id_invalid_manu_id(self):
+        """Tests advertisement data's manufacturers invalid id.
+
+        This advertisement data from "set" advertisement manufacturers data
+        should not be successful on an invalid id.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's manufacturers id to -1.
+        3. Build the advertisement data.
+
+        Expected Result:
+        Building the advertisement data should fail.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_manu_id = -1
+        exp_manu_specific_data = "1,2,3"
+        self.log.debug(
+            "Step 2: Set the filtering data object's service data manu id: {}"
+            ", manu specific data: {}".format(exp_manu_id,
+                                              exp_manu_specific_data))
+        return self.verify_invalid_adv_data_manu_id(droid,
+                                                    exp_manu_id,
+                                                    exp_manu_specific_data)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_manu_id_invalid_manu_specific_data(self):
+        """Tests advertisement data's manufacturers invalid specific data.
+
+        This advertisement data from "set" advertisement manufacturers data
+        should not be successful on an invalid specific data.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's manufacturers specific data to helloworld.
+        3. Build the advertisement data.
+
+        Expected Result:
+        Building the advertisement data should fail.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_manu_id = 0
+        exp_manu_specific_data = "helloworld"
+        self.log.debug(
+            "Step 2: Set the filtering data object's service data manu id: {}"
+            ", manu specific data: {}".format(exp_manu_id,
+                                              exp_manu_specific_data))
+        return self.verify_invalid_adv_data_manu_id(droid,
+                                                    exp_manu_id,
+                                                    exp_manu_specific_data)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_manu_id_max(self):
+        """Tests advertisement data's manufacturers id to the max size.
+
+        This advertisement data from "set" advertisement manufacturers data
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's manufacturers id to JavaInterger.MAX value.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 3
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_manu_id = JavaInteger.MAX.value
+        exp_manu_specific_data = "1,2,3"
+        self.log.debug(
+            "Step 2: Set the filtering data object's service data manu id: {}"
+            ", manu specific data: {}".
+            format(exp_manu_id, exp_manu_specific_data))
+        return self.verify_adv_data_manu_id(droid,
+                                            exp_manu_id,
+                                            exp_manu_specific_data)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_include_tx_power_level_true(self):
+        """Tests advertisement data's include tx power level to True.
+
+        This advertisement data from "set" advertisement manufacturers data
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's include tx power level to True.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_include_tx_power_level = True
+        self.log.debug(
+            "Step 2: Set the filtering data object's include tx power level: "
+            "{}".format(exp_include_tx_power_level))
+        return self.verify_adv_data_include_tx_power_level(
+            droid, exp_include_tx_power_level)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_include_tx_power_level_false(self):
+        """Tests advertisement data's include tx power level to False.
+
+        This advertisement data from "set" advertisement manufacturers data
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's include tx power level to False.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_include_tx_power_level = False
+        self.log.debug(
+            "Step 2: Set the filtering data object's include tx power level: {}"
+            .format(exp_include_tx_power_level))
+        return self.verify_adv_data_include_tx_power_level(
+            droid , exp_include_tx_power_level)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_include_device_name_true(self):
+        """Tests advertisement data's include device name to True.
+
+        This advertisement data from "set" advertisement manufacturers data
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's include device name to True.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        droid = self.droid
+        exp_include_device_name = True
+        self.log.debug(
+            "Step 2: Set the filtering data object's include device name: {}"
+            .format(exp_include_device_name))
+        return self.verify_adv_data_include_device_name(droid,
+                                                        exp_include_device_name)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_adv_data_set_include_device_name_false(self):
+        """Tests advertisement data's include device name to False.
+
+        This advertisement data from "set" advertisement manufacturers data
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's include device name to False.
+        3. Get the attributes of the AdvertiseData object.
+        4. Compare the attributes found against the attributes exp.
+
+        Expected Result:
+        Found attributes should match expected attributes.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        self.log.debug("Step 1: Setup environment.")
+        test_result = True
+        droid = self.droid
+        exp_include_device_name = False
+        self.log.debug(
+            "Step 2: Set the filtering data object's include device name: {}".
+            format(exp_include_device_name))
+        return self.verify_adv_data_include_device_name(droid,
+                                                        exp_include_device_name)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_advertisement_greater_than_31_bytes(self):
+        """Tests advertisement data's size to be greater than 31 bytes.
+
+        This advertisement data from "set" advertisement manufacturers data
+        should match the corresponding "get" function.
+
+        Steps:
+        1. Build a new AdvertiseData object.
+        2. Set the AdvertiseData's size to be greater than 31 bytes
+        3. Build the advertisement data.
+
+        Expected Result:
+        Api fails to build the AdvertiseData.
+
+        Returns:
+          True is pass
+          False if fail
+
+        TAGS: LE, Advertising
+        Priority: 1
+        """
+        test_result = True
+        droid = self.droid
+        ed = self.ed
+        service_data = []
+        for i in range(25):
+            service_data.append(i)
+        droid.bleAddAdvertiseDataServiceData(
+            "0000110D-0000-1000-8000-00805F9B34FB", ','.join(
+                map(str, service_data)))
+        advcallback, adv_data, adv_settings = generate_ble_advertise_objects(
+            droid)
+        droid.bleStartBleAdvertising(advcallback, adv_data,
+                                     adv_settings)
+        try:
+            ed.pop_event(adv_fail.format(advcallback))
+        except SL4AAPIError:
+            self.log.info("{} event was not found.".format(
+                adv_fail.format(advcallback)))
+            return False
+        return test_result
+
+    # TODO: in code refactor, remove all verify helper functions.
+    def verify_adv_settings_adv_mode(self, droid, exp_adv_mode):
+        try:
+            droid.bleSetAdvertiseSettingsAdvertiseMode(exp_adv_mode)
+        except BleAdvertiseVerificationError as error:
+            self.log.debug(str(error))
+            return False
+        self.log.debug("Step 3: Get a filtering settings object's index.")
+        settings_index = droid.bleBuildAdvertiseSettings()
+        self.log.debug("Step 4: Get the filtering setting's filtering mode.")
+        adv_mode = droid.bleGetAdvertiseSettingsMode(settings_index)
+        if exp_adv_mode is not adv_mode:
+            self.log.debug("exp value: {}, Actual value: {}".
+                           format(exp_adv_mode, adv_mode))
+            return False
+        self.log.debug("Advertise Setting's filtering mode {} value "
+                       "test Passed.".format(exp_adv_mode))
+        return True
+
+    def verify_adv_settings_tx_power_level(self, droid, exp_adv_tx_power):
+        try:
+            droid.bleSetAdvertiseSettingsTxPowerLevel(
+                exp_adv_tx_power)
+        except BleAdvertiseVerificationError as error:
+            self.log.debug(str(error))
+            return False
+        self.log.debug("Step 3: Get a filtering settings object's index.")
+        settings_index = droid.bleBuildAdvertiseSettings()
+        self.log.debug("Step 4: Get the filtering setting's tx power level.")
+        adv_tx_power_level = droid.bleGetAdvertiseSettingsTxPowerLevel(
+            settings_index)
+        if exp_adv_tx_power is not adv_tx_power_level:
+            self.log.debug("exp value: {}, Actual value: {}".
+                           format(exp_adv_tx_power, adv_tx_power_level))
+            return False
+        self.log.debug("Advertise Setting's tx power level {}"
+                       "  value test Passed.".format(exp_adv_tx_power))
+        return True
+
+    def verify_adv_settings_is_connectable(self, droid,
+                                           exp_is_connectable):
+        try:
+            droid.bleSetAdvertiseSettingsIsConnectable(exp_is_connectable)
+        except BleAdvertiseVerificationError as error:
+            self.log.debug(str(error))
+            return False
+        self.log.debug("Step 3: Get a filtering settings object's index.")
+        settings_index = droid.bleBuildAdvertiseSettings()
+        self.log.debug(
+            "Step 4: Get the filtering setting's is connectable value.")
+        is_connectable = droid.bleGetAdvertiseSettingsIsConnectable(
+            settings_index)
+        if exp_is_connectable is not is_connectable:
+            self.log.debug("exp value: {}, Actual value: {}".
+                           format(exp_is_connectable, is_connectable))
+            return False
+        self.log.debug("Advertise Setting's is connectable {}"
+                       " value test Passed.".format(exp_is_connectable))
+        return True
+
+    def verify_adv_data_service_uuids(self, droid, exp_service_uuids):
+        try:
+            droid.bleSetAdvertiseDataSetServiceUuids(exp_service_uuids)
+        except BleAdvertiseVerificationError as error:
+            self.log.debug(str(error))
+            return False
+        self.log.debug("Step 3: Get a filtering data object's index.")
+        data_index = droid.bleBuildAdvertiseData()
+        self.log.debug("Step 4: Get the filtering data's service uuids.")
+        service_uuids = droid.bleGetAdvertiseDataServiceUuids(data_index)
+        if exp_service_uuids != service_uuids:
+            self.log.debug("exp value: {}, Actual value: {}".
+                           format(exp_service_uuids, service_uuids))
+            return False
+        self.log.debug("Advertise Data's service uuids {}, value test Passed.".
+                       format(exp_service_uuids))
+        return True
+
+    def verify_adv_data_service_data(
+        self, droid, exp_service_data_uuid,
+            exp_service_data):
+        try:
+            droid.bleAddAdvertiseDataServiceData(
+                exp_service_data_uuid, exp_service_data)
+        except BleAdvertiseVerificationError as error:
+            self.log.debug(str(error))
+            return False
+        self.log.debug("Step 3: Get a filtering data object's index.")
+        data_index = droid.bleBuildAdvertiseData()
+        self.log.debug("Step 4: Get the filtering data's service data.")
+        service_data = droid.bleGetAdvertiseDataServiceData(
+            data_index, exp_service_data_uuid)
+        if exp_service_data != service_data:
+            self.log.debug("exp value: {}, Actual value: {}".
+                           format(exp_service_data, service_data))
+            return False
+        self.log.debug("Advertise Data's service data uuid: {}, service data: "
+                       "{}, value test Passed.".format(exp_service_data_uuid,
+                                                       exp_service_data))
+        return True
+
+    def verify_adv_data_manu_id(
+        self, droid, exp_manu_id,
+            exp_manu_specific_data):
+        try:
+            droid.bleAddAdvertiseDataManufacturerId(exp_manu_id,
+                                                    exp_manu_specific_data)
+        except BleAdvertiseVerificationError as error:
+            self.log.debug(str(error))
+            return False
+        self.log.debug("Step 3: Get a filtering data object's index.")
+        data_index = droid.bleBuildAdvertiseData()
+        self.log.debug(
+            "Step 5: Get the filtering data's manu specific data.")
+        manu_specific_data = droid.bleGetAdvertiseDataManufacturerSpecificData(
+            data_index, exp_manu_id)
+        if exp_manu_specific_data != manu_specific_data:
+            self.log.debug("exp value: " + str(exp_manu_specific_data)
+                           + ", Actual value: " + str(manu_specific_data))
+            return False
+        self.log.debug("Advertise Data's manu id: " + str(
+                       exp_manu_id) + ", manu's specific data: " + str(
+                       exp_manu_specific_data)
+                       + "  value test Passed.")
+        return True
+
+    def verify_adv_data_include_tx_power_level(self, droid,
+                                               exp_include_tx_power_level):
+        try:
+            droid.bleSetAdvertiseDataIncludeTxPowerLevel(
+                exp_include_tx_power_level)
+        except BleAdvertiseVerificationError as error:
+            self.log.debug(str(error))
+            return False
+        self.log.debug("Step 3: Get a filtering settings object's index.")
+        data_index = droid.bleBuildAdvertiseData()
+        self.log.debug(
+            "Step 4: Get the filtering data's include tx power level.")
+        include_tx_power_level = droid.bleGetAdvertiseDataIncludeTxPowerLevel(
+            data_index)
+        if exp_include_tx_power_level is not include_tx_power_level:
+            self.log.debug("exp value: " + str(exp_include_tx_power_level)
+                           + ", Actual value: " + str(include_tx_power_level))
+            return False
+        self.log.debug(
+            "Advertise Setting's include tx power level " +
+            str(exp_include_tx_power_level)
+            + "  value test Passed.")
+        return True
+
+    def verify_adv_data_include_device_name(self, droid,
+                                            exp_include_device_name):
+        try:
+            droid.bleSetAdvertiseDataIncludeDeviceName(
+                exp_include_device_name)
+        except BleAdvertiseVerificationError as error:
+            self.log.debug(str(error))
+            return False
+        self.log.debug("Step 3: Get a filtering settings object's index.")
+        data_index = droid.bleBuildAdvertiseData()
+        self.log.debug("Step 4: Get the filtering data's include device name.")
+        include_device_name = droid.bleGetAdvertiseDataIncludeDeviceName(
+            data_index)
+        if exp_include_device_name is not include_device_name:
+            self.log.debug("exp value: {}, Actual value: {}".
+                           format(exp_include_device_name, include_device_name))
+            return False
+        self.log.debug(
+            "Advertise Setting's include device name {}"
+            " value test Passed.".format(exp_include_device_name))
+        return True
+
+    def verify_invalid_adv_settings_adv_mode(self, droid,
+                                             exp_adv_mode):
+        try:
+            droid.bleSetAdvertiseSettingsAdvertiseMode(exp_adv_mode)
+            droid.bleBuildAdvertiseSettings()
+            self.log.debug("Set Advertise settings invalid filtering mode "
+                           "passed with input as {}".format(exp_adv_mode))
+            return False
+        except SL4AAPIError:
+            self.log.debug("Set Advertise settings invalid filtering mode "
+                           "failed successfully with input as {}".
+                           format(exp_adv_mode))
+            return True
+
+    def verify_invalid_adv_settings_tx_power_level(self, droid,
+                                                   exp_adv_tx_power):
+        try:
+            droid.bleSetAdvertiseSettingsTxPowerLevel(
+                exp_adv_tx_power)
+            droid.bleBuildAdvertiseSettings()
+            self.log.debug("Set Advertise settings invalid tx power level "
+                           + " with input as {}".format(exp_adv_tx_power))
+            return False
+        except SL4AAPIError:
+            self.log.debug("Set Advertise settings invalid tx power level "
+                           "failed successfullywith input as {}".
+                           format(exp_adv_tx_power))
+            return True
+
+    def verify_invalid_adv_data_service_uuids(self, droid,
+                                              exp_service_uuids):
+        try:
+            droid.bleSetAdvertiseDataSetServiceUuids(exp_service_uuids)
+            droid.bleBuildAdvertiseData()
+            self.log.debug("Set Advertise Data service uuids "
+                           + " with input as {}".format(exp_service_uuids))
+            return False
+        except SL4AAPIError:
+            self.log.debug("Set Advertise Data invalid service uuids failed "
+                           "successfully with input as {}".
+                           format(exp_service_uuids))
+            return True
+
+    def verify_invalid_adv_data_service_data(self, droid,
+                                             exp_service_data_uuid,
+                                             exp_service_data):
+        try:
+            droid.bleAddAdvertiseDataServiceData(
+                exp_service_data_uuid, exp_service_data)
+            droid.bleBuildAdvertiseData()
+            self.log.debug("Set Advertise Data service data uuid: {},"
+                           ", service data: {}".
+                           format(exp_service_data_uuid, exp_service_data))
+            return False
+        except SL4AAPIError:
+            self.log.debug("Set Advertise Data service data uuid: " + str(
+                           exp_service_data_uuid) + ", service data: " + str(
+                           exp_service_data) + " failed successfully.")
+            return True
+
+    def verify_invalid_adv_data_manu_id(self, droid, exp_manu_id,
+                                        exp_manu_specific_data):
+        try:
+            droid.bleAddAdvertiseDataManufacturerId(exp_manu_id,
+                                            exp_manu_specific_data)
+            droid.bleBuildAdvertiseData()
+            self.log.debug("Set Advertise Data manu id: " + str(
+                           exp_manu_id) + ", manu specific data: " + str(
+                           exp_manu_specific_data))
+            return False
+        except SL4AAPIError:
+            self.log.debug("Set Advertise Data manu id: {},"
+                           " manu specific data: {},".
+                           format(exp_manu_id, exp_manu_specific_data))
+            return True
diff --git a/acts/tests/google/ble/api/BleScanApiTest.py b/acts/tests/google/ble/api/BleScanApiTest.py
new file mode 100644
index 0000000..afbc7e3
--- /dev/null
+++ b/acts/tests/google/ble/api/BleScanApiTest.py
@@ -0,0 +1,1296 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 exercise Ble Scan Api's. This exercises all getters and
+setters. This is important since there is a builder object that is immutable
+after you set all attributes of each object. If this test suite doesn't pass,
+then other test suites utilising Ble Scanner will also fail.
+"""
+
+from acts.controllers.android import SL4AAPIError
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import ScanSettingsCallbackType
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.BleEnum import ScanSettingsScanResultType
+from acts.test_utils.bt.BleEnum import ScanSettingsReportDelaySeconds
+from acts.test_utils.bt.BleEnum import Uuids
+
+
+class BleScanResultsError(Exception):
+
+    """Error in getting scan results"""
+
+
+class BleScanVerificationError(Exception):
+
+    """Error in comparing BleScan results"""
+
+
+class BleSetScanSettingsError(Exception):
+
+    """Error in setting Ble Scan Settings"""
+
+
+class BleSetScanFilterError(Exception):
+
+    """Error in setting Ble Scan Settings"""
+
+
+class BleScanApiTest(BluetoothBaseTest):
+    tests = None
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.tests = (
+            "test_start_ble_scan_with_default_settings",
+            "test_stop_ble_scan_default_settings",
+            "test_scan_settings_callback_type_all_matches",
+            "test_scan_settings_set_callback_type_first_match",
+            "test_scan_settings_set_callback_type_match_lost",
+            "test_scan_settings_set_invalid_callback_type",
+            "test_scan_settings_set_scan_mode_low_power",
+            "test_scan_settings_set_scan_mode_balanced",
+            "test_scan_settings_set_scan_mode_low_latency",
+            "test_scan_settings_set_invalid_scan_mode",
+            "test_scan_settings_set_report_delay_millis_min",
+            "test_scan_settings_set_report_delay_millis_min_plus_one",
+            "test_scan_settings_set_report_delay_millis_max",
+            "test_scan_settings_set_report_delay_millis_max_minus_one",
+            "test_scan_settings_set_invalid_report_delay_millis_min_minus_one",
+            "test_scan_settings_set_scan_result_type_full",
+            "test_scan_settings_set_scan_result_type_abbreviated",
+            "test_scan_settings_set_invalid_scan_result_type",
+            "test_scan_filter_set_device_name",
+            "test_scan_filter_set_device_name_blank",
+            "test_scan_filter_set_device_name_special_chars",
+            "test_scan_filter_set_device_address",
+            "test_scan_filter_set_invalid_device_address_lower_case",
+            "test_scan_filter_set_invalid_device_address_blank",
+            "test_scan_filter_set_invalid_device_address_bad_format",
+            "test_scan_filter_set_invalid_device_address_bad_address",
+            "test_scan_filter_set_manufacturer_id_data",
+            "test_scan_filter_set_manufacturer_id_data_mask",
+            "test_scan_filter_set_manufacturer_max_id",
+            "test_scan_filter_set_manufacturer_data_empty",
+            "test_scan_filter_set_manufacturer_data_mask_empty",
+            "test_scan_filter_set_invalid_manufacturer_min_id_minus_one",
+            "test_scan_filter_set_service_uuid",
+            "test_scan_filter_service_uuid_p_service",
+            "test_classic_ble_scan_with_service_uuids_p",
+            "test_classic_ble_scan_with_service_uuids_hr",
+            "test_classic_ble_scan_with_service_uuids_empty_uuid_list",
+            "test_classic_ble_scan_with_service_uuids_hr_and_p",
+        )
+
+    def _format_defaults(self, input):
+        """
+        Creates a dictionary of default ScanSetting and ScanFilter Values.
+        :return: input: dict
+        """
+        if 'ScanSettings' not in input.keys():
+            input['ScanSettings'] = (
+                ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value, 0,
+                ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+                ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        if 'ScanFilterManufacturerDataId' not in input.keys():
+            input['ScanFilterManufacturerDataId'] = -1
+        if 'ScanFilterDeviceName' not in input.keys():
+            input['ScanFilterDeviceName'] = None
+        if 'ScanFilterDeviceAddress' not in input.keys():
+            input['ScanFilterDeviceAddress'] = None
+        if 'ScanFilterManufacturerData' not in input.keys():
+            input['ScanFilterManufacturerData'] = ""
+        return input
+
+    def validate_scan_settings_helper(self, input, droid):
+        """
+        Validates each input of the scan settings object that is matches what
+        was set or not set such that it matches the defaults.
+        :return: False at any point something doesn't match. True if everything
+        matches.
+        """
+        filter_list = droid.bleGenFilterList()
+        if 'ScanSettings' in input.keys():
+            try:
+                droid.bleSetScanSettingsCallbackType(input['ScanSettings'][0])
+                droid.bleSetScanSettingsReportDelayMillis(
+                    input['ScanSettings'][1])
+                droid.bleSetScanSettingsScanMode(input['ScanSettings'][2])
+                droid.bleSetScanSettingsResultType(input['ScanSettings'][3])
+            except SL4AAPIError as error:
+                self.log.debug("Set Scan Settings failed with: ".format(error))
+                return False
+        if 'ScanFilterDeviceName' in input.keys():
+            try:
+                droid.bleSetScanFilterDeviceName(input['ScanFilterDeviceName'])
+            except SL4AAPIError as error:
+                self.log.debug("Set Scan Filter Device Name failed with: {}"
+                               .format(error))
+                return False
+        if 'ScanFilterDeviceAddress' in input.keys():
+            try:
+                droid.bleSetScanFilterDeviceAddress(
+                    input['ScanFilterDeviceAddress'])
+            except SL4AAPIError as error:
+                self.log.debug("Set Scan Filter Device Address failed with: {}"
+                               .format(error))
+                return False
+        if ('ScanFilterManufacturerDataId' in input.keys()
+                and 'ScanFilterManufacturerDataMask' in input.keys()):
+            try:
+                droid.bleSetScanFilterManufacturerData(
+                    input['ScanFilterManufacturerDataId'],
+                    input['ScanFilterManufacturerData'],
+                    input['ScanFilterManufacturerDataMask'])
+            except SL4AAPIError as error:
+                self.log.debug("Set Scan Filter Manufacturer info with data "
+                               "mask failed with: {}".format(error))
+                return False
+        if ('ScanFilterManufacturerDataId' in input.keys()
+            and 'ScanFilterManufacturerData' in input.keys()
+                and 'ScanFilterManufacturerDataMask' not in input.keys()):
+            try:
+                droid.bleSetScanFilterManufacturerData(
+                    input['ScanFilterManufacturerDataId'],
+                    input['ScanFilterManufacturerData'])
+            except SL4AAPIError as error:
+                self.log.debug("Set Scan Filter Manufacturer info failed with: "
+                               "{}".format(error))
+                return False
+        if ('ScanFilterServiceUuid' in input.keys() and 'ScanFilterServiceMask'
+            in input.keys()):
+
+            droid.bleSetScanFilterServiceUuid(input['ScanFilterServiceUuid'],
+                                              input['ScanFilterServiceMask'])
+
+        input = self._format_defaults(input)
+        scan_settings_index = droid.bleBuildScanSetting()
+        scan_settings = (
+            droid.bleGetScanSettingsCallbackType(scan_settings_index),
+            droid.bleGetScanSettingsReportDelayMillis(
+                scan_settings_index),
+            droid.bleGetScanSettingsScanMode(scan_settings_index),
+            droid.bleGetScanSettingsScanResultType(
+                scan_settings_index))
+
+        scan_filter_index = droid.bleBuildScanFilter(filter_list)
+        device_name_filter = droid.bleGetScanFilterDeviceName(
+            filter_list, scan_filter_index)
+        device_address_filter = droid.bleGetScanFilterDeviceAddress(
+            filter_list, scan_filter_index)
+        manufacturer_id = droid.bleGetScanFilterManufacturerId(
+            filter_list, scan_filter_index)
+        manufacturer_data = droid.bleGetScanFilterManufacturerData(
+            filter_list, scan_filter_index)
+
+        if scan_settings != input['ScanSettings']:
+            self.log.debug("Scan Settings did not match. expected: {}, found: "
+                           "{}".format(input['ScanSettings'], scan_settings))
+            return False
+        if device_name_filter != input['ScanFilterDeviceName']:
+            self.log.debug("Scan Filter device name did not match. expected: "
+                           "{}, found {}".format(input['ScanFilterDeviceName'],
+                                                 device_name_filter))
+            return False
+        if device_address_filter != input['ScanFilterDeviceAddress']:
+            self.log.debug("Scan Filter address name did not match. expected: "
+                           "{}, found: {}".format(
+                input['ScanFilterDeviceAddress'], device_address_filter))
+            return False
+        if manufacturer_id != input['ScanFilterManufacturerDataId']:
+            self.log.debug("Scan Filter manufacturer data id did not match. "
+                           "expected: {}, found: {}".format(
+                input['ScanFilterManufacturerDataId'], manufacturer_id))
+            return False
+        if manufacturer_data != input['ScanFilterManufacturerData']:
+            self.log.debug("Scan Filter manufacturer data did not match. "
+                           "expected: {}, found: {}".format(
+                input['ScanFilterManufacturerData'], manufacturer_data))
+            return False
+        if 'ScanFilterManufacturerDataMask' in input.keys():
+            manufacturer_data_mask = droid.bleGetScanFilterManufacturerDataMask(
+                filter_list,
+                scan_filter_index)
+            if manufacturer_data_mask != input[
+                    'ScanFilterManufacturerDataMask']:
+                self.log.debug("Manufacturer data mask did not match. expected:"
+                               " {}, found: {}".format(
+                    input['ScanFilterManufacturerDataMask'],
+                    manufacturer_data_mask))
+
+                return False
+        if ('ScanFilterServiceUuid' in input.keys() and 'ScanFilterServiceMask'
+            in input.keys()):
+
+            expected_service_uuid = input['ScanFilterServiceUuid']
+            expected_service_mask = input['ScanFilterServiceMask']
+            service_uuid = droid.bleGetScanFilterServiceUuid(filter_list,
+                                                             scan_filter_index)
+            service_mask = droid.bleGetScanFilterServiceUuidMask(filter_list,
+                                                                 scan_filter_index)
+            if service_uuid != expected_service_uuid.lower():
+                self.log.debug("Service uuid did not match. expected: {}, "
+                               "found {}".format(expected_service_uuid,service_uuid))
+                return False
+            if service_mask != expected_service_mask.lower():
+                self.log.debug("Service mask did not match. expected: {}, "
+                               "found {}".format(expected_service_mask,
+                                                 service_mask))
+                return False
+        self.scan_settings_index = scan_settings_index
+        self.filter_list = filter_list
+        self.scan_callback = droid.bleGenScanCallback()
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_start_ble_scan_with_default_settings(self):
+        """Test LE scan with default settings.
+
+        Test to validate all default scan settings values.
+
+        Steps:
+        1. Create LE scan objects.
+        2. Start LE scan.
+
+        Expected Result:
+        Scan starts successfully and matches expected settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_stop_ble_scan_default_settings(self):
+        """Test stopping an LE scan.
+
+        Test default scan settings on an actual scan. Verify it can also stop
+        the scan.
+
+        Steps:
+        1. Validate default scan settings.
+        2. Start ble scan.
+        3. Stop ble scan.
+
+        Expected Result:
+        LE scan is stopped successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 0
+        """
+        input = {}
+        test_result = self.validate_scan_settings_helper(input, self.droid)
+        if not test_result:
+            self.log.error("Could not setup ble scanner.")
+            return test_result
+        self.droid.bleStartBleScan(self.filter_list, self.scan_settings_index,
+                                   self.scan_callback)
+        try:
+            self.droid.bleStopBleScan(self.scan_callback)
+        except BleScanResultsError as error:
+            self.log.error(str(error))
+            test_result = False
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_callback_type_all_matches(self):
+        """Test LE scan settings callback type all matches.
+
+        Test scan settings callback type all matches.
+
+        Steps:
+        1. Validate the scan settings callback type with all other settings set
+        to their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value, 0,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_callback_type_first_match(self):
+        """Test LE scan settings callback type first match
+
+        Test scan settings callback type first match.
+
+        Steps:
+        1. Validate the scan settings callback type with all other settings set
+        to their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_FIRST_MATCH.value, 0,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        test_result = self.validate_scan_settings_helper(input, self.droid)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_callback_type_match_lost(self):
+        """Test LE scan settings callback type match lost.
+
+        Test scan settings callback type match lost.
+
+        Steps:
+        1. Validate the scan settings callback type with all other settings set
+        to their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_MATCH_LOST.value, 0,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        test_result = self.validate_scan_settings_helper(input, self.droid)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_invalid_callback_type(self):
+        """Test LE scan settings invalid callback type.
+
+        Test scan settings invalid callback type -1.
+
+        Steps:
+        1. Build a LE ScanSettings object with an invalid callback type.
+
+        Expected Result:
+        Api should fail to build object.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        input = {}
+        input["ScanSettings"] = (
+            -1, 0, ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        test_result = self.validate_scan_settings_helper(input, self.droid)
+        return not test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_scan_mode_low_power(self):
+        """Test LE scan settings scan mode low power mode.
+
+        Test scan settings scan mode low power.
+
+        Steps:
+        1. Validate the scan settings scan mode with all other settings set to
+        their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value, 0,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        test_result = self.validate_scan_settings_helper(input, self.droid)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_scan_mode_balanced(self):
+        """Test LE scan settings scan mode balanced.
+
+        Test scan settings scan mode balanced.
+
+        Steps:
+        1. Validate the scan settings scan mode with all other settings set to
+        their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value, 0,
+            ScanSettingsScanMode.SCAN_MODE_BALANCED.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_scan_mode_low_latency(self):
+        """Test LE scan settings scan mode low latency.
+
+        Test scan settings scan mode low latency.
+
+        Steps:
+        1. Validate the scan settings scan mode with all other settings set to
+        their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value, 0,
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_invalid_scan_mode(self):
+        """Test LE scan settings scan mode as an invalid value.
+        Test scan settings invalid scan mode -2.
+        Steps:
+        1. Set the scan settings scan mode to -2.
+
+        Expected Result:
+        Building the ScanSettings object should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value, 0, -2,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return not self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_report_delay_millis_min(self):
+        """Test scan settings report delay millis as min value
+
+        Test scan settings report delay millis min acceptable value.
+
+        Steps:
+        1. Validate the scan settings report delay millis with all other
+        settings set to their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value,
+            ScanSettingsReportDelaySeconds.MIN.value,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_report_delay_millis_min_plus_one(self):
+        """Test scan settings report delay millis as min value plus one.
+
+        Test scan settings report delay millis as min value plus one.
+
+        Steps:
+        1. Validate the scan settings report delay millis with all other
+        settings set to their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 4
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value,
+            ScanSettingsReportDelaySeconds.MIN.value + 1,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_report_delay_millis_max(self):
+        """Test scan settings report delay millis as max value.
+
+        Test scan settings report delay millis max value.
+
+        Steps:
+        1. Validate the scan settings report delay millis with all other
+        settings set to their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 3
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value,
+            ScanSettingsReportDelaySeconds.MAX.value,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_report_delay_millis_max_minus_one(self):
+        """Test scan settings report delay millis as max value minus one.
+
+        Test scan settings report delay millis max value - 1.
+
+        Steps:
+        1. Validate the scan settings report delay millis with all other
+        settings set to their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 3
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value,
+            ScanSettingsReportDelaySeconds.MAX.value - 1,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_invalid_report_delay_millis_min_minus_one(self):
+        """Test scan settings report delay millis as an invalid value.
+
+        Test scan settings invalid report delay millis min value - 1.
+
+        Steps:
+        1. Set scan settings report delay millis to min value -1.
+        2. Build scan settings object.
+
+        Expected Result:
+        Building scan settings object should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        droid = self.droid
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value,
+            ScanSettingsReportDelaySeconds.MIN.value - 1,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return not self.validate_scan_settings_helper(input, droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_scan_result_type_full(self):
+        """Test scan settings result type full.
+
+        Test scan settings result type full.
+
+        Steps:
+        1. Validate the scan settings result type with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value, 0,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_FULL.value)
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_scan_result_type_abbreviated(self):
+        """Test scan settings result type abbreviated.
+
+        Test scan settings result type abbreviated.
+
+        Steps:
+        1. Validate the scan settings result type with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value, 0,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+            ScanSettingsScanResultType.SCAN_RESULT_TYPE_ABBREVIATED.value)
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_settings_set_invalid_scan_result_type(self):
+        """Test scan settings result type as an invalid value.
+
+        Test scan settings invalid result type -1.
+
+        Steps:
+        1. Set scan settings result type as an invalid value.
+        2. Build scan settings object.
+
+        Expected Result:
+        Expected Scan settings should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        input = {}
+        input["ScanSettings"] = (
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value, 0,
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value, -1)
+        return not self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_device_name(self):
+        """Test scan filter set valid device name.
+
+        Test scan filter device name sl4atest.
+
+        Steps:
+        1. Validate the scan filter device name with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan settings.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input['ScanFilterDeviceName'] = "sl4atest"
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_device_name_blank(self):
+        """Test scan filter set blank device name.
+
+        Test scan filter device name blank.
+
+        Steps:
+        1. Validate the scan filter device name with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        droid = self.droid
+        input = {}
+        input['ScanFilterDeviceName'] = ""
+        return self.validate_scan_settings_helper(input, droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_device_name_special_chars(self):
+        """Test scan filter set device name as special chars.
+
+        Test scan filter device name special characters.
+
+        Steps:
+        1. Validate the scan filter device name with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input['ScanFilterDeviceName'] = "!@#$%^&*()\":<>/"
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_device_address(self):
+        """Test scan filter set valid device address.
+
+        Test scan filter device address valid.
+
+        Steps:
+        1. Validate the scan filter device address with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input['ScanFilterDeviceAddress'] = "01:02:03:AB:CD:EF"
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_invalid_device_address_lower_case(self):
+        """Test scan filter set invalid device address.
+
+        Test scan filter device address lower case.
+
+        Steps:
+        1. Set the scan filter address to an invalid, lowercase mac address
+
+        Expected Result:
+        Api to build scan filter should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        input = {}
+        input['ScanFilterDeviceAddress'] = "01:02:03:ab:cd:ef"
+        return not self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_invalid_device_address_blank(self):
+        """Test scan filter set invalid device address.
+
+        Test scan filter invalid device address blank.
+
+        Steps:
+        1. Set the scan filter address to an invalid, blank mac address
+
+        Expected Result:
+        Api to build scan filter should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        input = {}
+        input['ScanFilterDeviceAddress'] = ""
+        test_result = self.validate_scan_settings_helper(input, self.droid)
+        return not test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_invalid_device_address_bad_format(self):
+        """Test scan filter set badly formatted device address.
+
+        Test scan filter badly formatted device address.
+
+        Steps:
+        1. Set the scan filter address to an invalid, blank mac address
+
+        Expected Result:
+        Api to build scan filter should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        input = {}
+        input['ScanFilterDeviceAddress'] = "10.10.10.10.10"
+        return not self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_invalid_device_address_bad_address(self):
+        """Test scan filter device address as an invalid value.
+
+        Test scan filter invalid device address invalid characters.
+
+        Steps:
+        1. Set a scan filter's device address as ZZ:ZZ:ZZ:ZZ:ZZ:ZZ
+
+        Expected Result:
+        Api to build the scan filter should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        input = {}
+        input['ScanFilterDeviceAddress'] = "ZZ:ZZ:ZZ:ZZ:ZZ:ZZ"
+        return not self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_manufacturer_id_data(self):
+        """Test scan filter manufacturer data.
+
+        Test scan filter manufacturer data with a valid input.
+
+        Steps:
+        1. Validate the scan filter manufacturer id with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        expected_manufacturer_id = 0
+        expected_manufacturer_data = "1,2,1,3,4,5,6"
+        input = {}
+        input['ScanFilterManufacturerDataId'] = expected_manufacturer_id
+        input['ScanFilterManufacturerData'] = expected_manufacturer_data
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_manufacturer_id_data_mask(self):
+        """Test scan filter manufacturer data mask.
+
+        Test scan filter manufacturer data with a valid data mask.
+
+        Steps:
+        1. Validate the scan filter manufacturer id with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        expected_manufacturer_id = 1
+        expected_manufacturer_data = "1"
+        expected_manufacturer_data_mask = "1,2,1,3,4,5,6"
+        input = {}
+        input['ScanFilterManufacturerDataId'] = expected_manufacturer_id
+        input['ScanFilterManufacturerData'] = expected_manufacturer_data
+        input[
+            'ScanFilterManufacturerDataMask'] = expected_manufacturer_data_mask
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_manufacturer_max_id(self):
+        """Test scan filter manufacturer data id.
+
+        Test scan filter manufacturer data max id.
+
+        Steps:
+        1. Validate the scan filter manufacturer id with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        expected_manufacturer_id = 2147483647
+        expected_manufacturer_data = "1,2,1,3,4,5,6"
+        input = {}
+        input['ScanFilterManufacturerDataId'] = expected_manufacturer_id
+        input['ScanFilterManufacturerData'] = expected_manufacturer_data
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_manufacturer_data_empty(self):
+        """Test scan filter empty manufacturer data.
+
+        Test scan filter manufacturer data as empty but valid manufacturer data.
+
+        Steps:
+        1. Validate the scan filter manufacturer id with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        expected_manufacturer_id = 1
+        expected_manufacturer_data = ""
+        input = {}
+        input['ScanFilterManufacturerDataId'] = expected_manufacturer_id
+        input['ScanFilterManufacturerData'] = expected_manufacturer_data
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_manufacturer_data_mask_empty(self):
+        """Test scan filter empty manufacturer data mask.
+
+        Test scan filter manufacturer mask empty.
+
+        Steps:
+        1. Validate the scan filter manufacturer id with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        expected_manufacturer_id = 1
+        expected_manufacturer_data = "1,2,1,3,4,5,6"
+        expected_manufacturer_data_mask = ""
+        input = {}
+        input['ScanFilterManufacturerDataId'] = expected_manufacturer_id
+        input['ScanFilterManufacturerData'] = expected_manufacturer_data
+        input[
+            'ScanFilterManufacturerDataMask'] = expected_manufacturer_data_mask
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_invalid_manufacturer_min_id_minus_one(self):
+        """Test scan filter invalid manufacturer data.
+
+        Test scan filter invalid manufacturer id min value - 1.
+
+        Steps:
+        1. Set the scan filters manufacturer id to -1.
+        2. Build the scan filter.
+
+        Expected Result:
+        Api to build the scan filter should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        expected_manufacturer_id = -1
+        expected_manufacturer_data = "1,2,1,3,4,5,6"
+        input = {}
+        input['ScanFilterManufacturerDataId'] = expected_manufacturer_id
+        input['ScanFilterManufacturerData'] = expected_manufacturer_data
+        return not self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_set_service_uuid(self):
+        """Test scan filter set valid service uuid.
+
+        Test scan filter service uuid.
+
+        Steps:
+        1. Validate the scan filter service uuid with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        expected_service_uuid = "00000000-0000-1000-8000-00805F9B34FB"
+        expected_service_mask = "00000000-0000-1000-8000-00805F9B34FB"
+        input = {}
+        input['ScanFilterServiceUuid'] = expected_service_uuid
+        input['ScanFilterServiceMask'] = expected_service_mask
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_service_uuid_p_service(self):
+        """Test scan filter service uuid.
+
+        Test scan filter service uuid p service
+
+        Steps:
+        1. Validate the scan filter service uuid with all other settings
+        set to their respective defaults.
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 2
+        """
+        expected_service_uuid = Uuids.P_Service.value
+        expected_service_mask = "00000000-0000-1000-8000-00805F9B34FB"
+        self.log.debug("Step 1: Setup environment.")
+
+        input = {}
+        input['ScanFilterServiceUuid'] = expected_service_uuid
+        input['ScanFilterServiceMask'] = expected_service_mask
+        return self.validate_scan_settings_helper(input, self.droid)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_classic_ble_scan_with_service_uuids_p(self):
+        """Test classic LE scan with valid service uuid.
+
+        Test classic ble scan with scan filter service uuid p service uuids.
+
+        Steps:
+        1. Validate the scan filter service uuid with all other settings
+        set to their respective defaults.
+        2. Start classic ble scan.
+        3. Stop classic ble scan
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+
+        droid = self.droid
+        service_uuid_list = [Uuids.P_Service.value]
+        scan_callback = droid.bleGenLeScanCallback()
+        return self.verify_classic_ble_scan_with_service_uuids(
+            droid, scan_callback, service_uuid_list)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_classic_ble_scan_with_service_uuids_hr(self):
+        """Test classic LE scan with valid service uuid.
+
+        Test classic ble scan with scan filter service uuid hr service
+
+        Steps:
+        1. Validate the scan filter service uuid with all other settings
+        set to their respective defaults.
+        2. Start classic ble scan.
+        3. Stop classic ble scan
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        droid = self.droid
+        service_uuid_list = [Uuids.HR_SERVICE.value]
+        scan_callback = droid.bleGenLeScanCallback()
+        return self.verify_classic_ble_scan_with_service_uuids(
+            droid, scan_callback, service_uuid_list)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_classic_ble_scan_with_service_uuids_empty_uuid_list(self):
+        """Test classic LE scan with empty but valid uuid list.
+
+        Test classic ble scan with service uuids as empty list.
+
+        Steps:
+        1. Validate the scan filter service uuid with all other settings
+        set to their respective defaults.
+        2. Start classic ble scan.
+        3. Stop classic ble scan
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        droid = self.droid
+        service_uuid_list = []
+        scan_callback = droid.bleGenLeScanCallback()
+        return self.verify_classic_ble_scan_with_service_uuids(
+            droid, scan_callback, service_uuid_list)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_classic_ble_scan_with_service_uuids_hr_and_p(self):
+        """Test classic LE scan with multiple service uuids.
+
+        Test classic ble scan with service uuids a list of hr and p service.
+
+        Steps:
+        1. Validate the scan filter service uuid with all other settings
+        set to their respective defaults.
+        2. Start classic ble scan.
+        3. Stop classic ble scan
+
+        Expected Result:
+        Expected Scan filter should match found scan filter.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 1
+        """
+        droid = self.droid
+        service_uuid_list = [Uuids.HR_SERVICE.value, Uuids.P_Service.value]
+        scan_callback = droid.bleGenLeScanCallback()
+        return self.verify_classic_ble_scan_with_service_uuids(
+            droid, scan_callback, service_uuid_list)
+    # TODO: remove this when refactoring code
+
+    def verify_classic_ble_scan_with_service_uuids(
+        self, droid, scan_callback, service_uuid_list):
+
+        test_result = True
+        try:
+            test_result = droid.bleStartClassicBleScanWithServiceUuids(
+                scan_callback, service_uuid_list)
+        except BleScanResultsError as error:
+            self.log.error(str(error))
+            return False
+        droid.bleStopClassicBleScan(scan_callback)
+        if not test_result:
+            self.log.error(
+                "Start classic ble scan with service uuids return false "
+                "boolean value.")
+            return False
+        return True
diff --git a/acts/tests/google/ble/api/GattApiTest.py b/acts/tests/google/ble/api/GattApiTest.py
new file mode 100644
index 0000000..4f39602
--- /dev/null
+++ b/acts/tests/google/ble/api/GattApiTest.py
@@ -0,0 +1,128 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 exercise Gatt Apis.
+"""
+
+from acts.controllers.android import SL4AAPIError
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import log_energy_info
+from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+
+
+class GattApiTest(BluetoothBaseTest):
+    tests = None
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.tests = (
+            "test_open_gatt_server",
+            "test_open_gatt_server_on_same_callback",
+            "test_open_gatt_server_on_invalid_callback",
+        )
+
+    def setup_class(self):
+        return setup_multiple_devices_for_bt_test(self.droids, self.eds)
+
+    def setup_test(self):
+        self.log.debug(log_energy_info(self.droids, "Start"))
+        for e in self.eds:
+            e.clear_all_events()
+        return True
+
+    def teardown_test(self):
+        self.log.debug(log_energy_info(self.droids, "End"))
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_open_gatt_server(self):
+        """Test a gatt server.
+
+        Test opening a gatt server.
+
+        Steps:
+        1. Create a gatt server callback.
+        2. Open the gatt server.
+
+        Expected Result:
+        Api to open gatt server should not fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, GATT
+        Priority: 1
+        """
+        droid, ed = self.droid, self.ed
+        gatt_server_callback = droid.gattServerCreateGattServerCallback()
+        droid.gattServerOpenGattServer(gatt_server_callback)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_open_gatt_server_on_same_callback(self):
+        """Test repetitive opening of a gatt server.
+
+        Test opening a gatt server on the same callback twice in a row.
+
+        Steps:
+        1. Create a gatt server callback.
+        2. Open the gatt server.
+        3. Open the gatt server on the same callback as step 2.
+
+        Expected Result:
+        Api to open gatt server should not fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, GATT
+        Priority: 2
+        """
+        droid, ed = self.droid, self.ed
+        gatt_server_callback = droid.gattServerCreateGattServerCallback()
+        droid.gattServerOpenGattServer(gatt_server_callback)
+        droid.gattServerOpenGattServer(gatt_server_callback)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_open_gatt_server_on_invalid_callback(self):
+        """Test gatt server an an invalid callback.
+
+        Test opening a gatt server with an invalid callback.
+
+        Steps:
+        1. Open a gatt server with the gall callback set to -1.
+
+        Expected Result:
+        Api should fail to open a gatt server.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, GATT
+        Priority: 2
+        """
+        droid, ed = self.droid, self.ed
+        invalid_callback_index = -1
+        try:
+            droid.gattServerOpenGattServer(invalid_callback_index)
+        except SL4AAPIError as e:
+            self.log.info("Failed successfully with exception: {}.".format(e))
+            return True
+        return False
diff --git a/acts/tests/google/ble/beacon_tests/BeaconSwarmTest.py b/acts/tests/google/ble/beacon_tests/BeaconSwarmTest.py
new file mode 100644
index 0000000..73460c3
--- /dev/null
+++ b/acts/tests/google/ble/beacon_tests/BeaconSwarmTest.py
@@ -0,0 +1,352 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 test script exercises different testcases with a lot of ble beacon traffic.
+
+This test script was designed with this setup in mind:
+Shield box one: Android Device as DUT. 7x Sprout devices acting as 192 beacons
+"""
+
+import threading
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.bt_test_utils import adv_succ
+from acts.test_utils.bt.bt_test_utils import batch_scan_result
+from acts.test_utils.bt.bt_test_utils import scan_result
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts.test_utils.bt.bt_test_utils import log_energy_info
+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
+
+
+class BeaconSwarmTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+    beacon_swarm_count = 0
+    advertising_device_name_list = []
+    discovered_mac_address_list = []
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.tests = (
+            "test_swarm_1000_on_scan_result",
+            "test_swarm_10000_on_batch_scan_result",
+            "test_swarm_rotate_addresses",
+            "test_swarm_scan_result_filter_each_device_name",
+        )
+
+    def setup_test(self):
+        self.log.debug(log_energy_info(self.droids, "Start"))
+        self.discovered_mac_address_list = []
+        for e in self.eds:
+            e.clear_all_events()
+        return True
+
+    def teardown_test(self):
+        self.log.debug(log_energy_info(self.droids, "End"))
+        reset_bluetooth([self.scn_droid], [self.scn_ed])
+        return True
+
+    def setup_class(self):
+        if not setup_multiple_devices_for_bt_test(self.droids, self.eds):
+            return False
+        return self._start_special_advertisements()
+
+    def cleanup_class(self):
+        return reset_bluetooth(self.droids, self.eds)
+
+    def on_fail(self, test_name, begin_time):
+        take_btsnoop_logs(self.droids, self, test_name)
+        reset_bluetooth([self.scn_droid], [self.scn_ed])
+
+    def _start_advertisements_thread(self, d, e, beacon_count, restart=False):
+        if restart:
+            try:
+                reset_bluetooth([d], [e])
+            except Exception:
+                self.log.debug("Failed resetting Bluetooth, continuing...")
+                return
+        try:
+            for _ in range(beacon_count):
+                d.bleSetAdvertiseDataIncludeDeviceName(True)
+                d.bleSetAdvertiseSettingsAdvertiseMode(
+                    AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+                advertise_callback, advertise_data, advertise_settings = (
+                    generate_ble_advertise_objects(d))
+                d.bleStartBleAdvertising(
+                    advertise_callback, advertise_data, advertise_settings)
+                try:
+                    e.pop_event(
+                        adv_succ.format(advertise_callback),
+                        self.default_timeout)
+                    self.beacon_swarm_count += 1
+                    local_bt_name = d.bluetoothGetLocalName()
+                    if local_bt_name not in self.advertising_device_name_list:
+                        self.advertising_device_name_list.append(
+                            d.bluetoothGetLocalName())
+                except Exception as e:
+                    self.log.info("Advertising failed due to " + str(e))
+                self.log.info(
+                    "Beacons active: {}".format(self.beacon_swarm_count))
+        except Exception:
+            self.log.debug(
+                "Something went wrong in starting advertisements, continuing.")
+        return
+
+    def _start_special_advertisements(self):
+        self.log.info("Setting up advertisements.")
+        beacon_serials = []
+        beacon_count = 0
+        try:
+            beacon_serials = self.user_params['beacon_devices']
+            beacon_count = self.user_params['beacon_count']
+        except AttributeError:
+            self.log.info(
+                "No controllable devices connected to create beacons with."
+                " Continuing...")
+        threads = []
+        for x in range(len(self.droids)):
+            d, e = self.droids[x], self.eds[x]
+            serial_no = d.getBuildSerial()
+            if serial_no not in beacon_serials:
+                continue
+            thread = threading.Thread(target=self._start_advertisements_thread,
+                                      args=(d, e, beacon_count))
+            threads.append(thread)
+            thread.start()
+        for t in threads:
+            t.join()
+        if self.beacon_swarm_count < (beacon_count * len(beacon_serials)):
+            self.log.error(
+                "Not enough beacons advertising: {}".format(
+                    self.beacon_swarm_count))
+            return False
+        return True
+
+    def _restart_special_advertisements_thread(self):
+        beacon_serials = []
+        beacon_count = 0
+        try:
+            beacon_serials = self.user_params['beacon_devices']
+            beacon_count = self.user_params['beacon_count']
+        except AttributeError:
+            self.log.info("No controllable devices connected to create beacons"
+                          " with. Continuing...")
+        threads = []
+        while True:
+            self.log.info("Restarting advertisements.")
+            for x in range(len(self.droids)):
+                d, e = self.droids[x], self.eds[x]
+                serial_no = d.getBuildSerial()
+                if serial_no not in beacon_serials:
+                    continue
+                thread = threading.Thread(target=
+                                          self._start_advertisements_thread,
+                                          args=(d, e, beacon_count, True))
+                threads.append(thread)
+                thread.start()
+            for t in threads:
+                t.join()
+        return True
+
+    def test_swarm_1000_on_scan_result(self):
+        """Test LE scanning in a mass beacon deployment.
+
+        Test finding 1000 LE scan results in a mass beacon deployment.
+
+        Steps:
+        1. Assume that mass beacon deployment is setup.
+        2. Set LE scanning mode to low latency.
+        3. Start LE scan.
+        4. Pop scan results off the event dispatcher 1000 times.
+        5. Stop LE scanning.
+
+        Expected Result:
+        1000 scan results should be found without any exceptions.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Beacon
+        Priority: 1
+        """
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        for _ in range(1000000):
+            event_info = self.scn_ed.pop_event(
+                scan_result.format(scan_callback),
+                self.default_timeout)
+            mac_address = event_info['data']['Result']['deviceInfo']['address']
+            if mac_address not in self.discovered_mac_address_list:
+                self.discovered_mac_address_list.append(mac_address)
+                self.log.info("Discovered {} different devices.".format(
+                    len(self.discovered_mac_address_list)))
+        self.log.debug("Discovered {} different devices.".format(
+            len(self.discovered_mac_address_list)))
+        self.scn_droid.bleStopBleScan(scan_callback)
+        return True
+
+    def test_swarm_10000_on_batch_scan_result(self):
+        """Test LE batch scanning in a mass beacon deployment.
+
+        Test finding 10000 LE batch scan results in a mass beacon deployment.
+
+        Steps:
+        1. Assume that mass beacon deployment is setup.
+        2. Set LE scanning mode to low latency and report delay millis to 1
+        second.
+        3. Start LE scan.
+        4. Pop batch scan results off the event dispatcher 10000 times.
+        5. Stop LE scanning.
+
+        Expected Result:
+        1000 scan results should be found without any exceptions.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Beacon
+        Priority: 1
+        """
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.scn_droid.bleSetScanSettingsReportDelayMillis(1000)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        for _ in range(10000):
+            event_info = self.scn_ed.pop_event(
+                batch_scan_result.format(scan_callback),
+                self.default_timeout)
+            for result in event_info['data']['Results']:
+                mac_address = result['deviceInfo']['address']
+                if mac_address not in self.discovered_mac_address_list:
+                    self.discovered_mac_address_list.append(mac_address)
+        self.log.info("Discovered {} different devices.".format(
+            len(self.discovered_mac_address_list)))
+        self.scn_droid.bleStopBleScan(scan_callback)
+        return True
+
+    def test_swarm_scan_result_filter_each_device_name(self):
+        """Test basic LE scan filtering in a mass beacon deployment.
+
+        Test finding LE scan results in a mass beacon deployment. This
+        test specifically tests scan filtering of different device names and
+        that each device name is found.
+
+        Steps:
+        1. Assume that mass beacon deployment is setup with device names
+        advertising.
+        2. Set LE scanning mode to low latency.
+        3. Filter device name from one of the known advertising device names
+        4. Start LE scan.
+        5. Pop scan results matching the scan filter.
+        6. Stop LE scanning.
+        7. Repeat steps 2-6 until all advertising device names are found.
+
+        Expected Result:
+        All advertising beacons are found by their device name.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Beacon, Filtering
+        Priority: 1
+        """
+        for filter_name in self.advertising_device_name_list:
+            self.scn_droid.bleSetScanSettingsScanMode(
+                ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+            filter_list, scan_settings, scan_callback = (
+                generate_ble_scan_objects(self.scn_droid))
+            try:
+                self.scn_droid.bleSetScanFilterDeviceName(filter_name)
+                self.scn_droid.bleBuildScanFilter(filter_list)
+                self.scn_droid.bleStartBleScan(
+                    filter_list, scan_settings, scan_callback)
+                self.log.debug(self.scn_ed.pop_event(
+                    scan_result.format(scan_callback), self.default_timeout))
+            except Exception:
+                self.log.info(
+                    "Couldn't find advertiser name {}.".format(filter_name))
+                return False
+            self.scn_droid.bleStopBleScan(scan_callback)
+        return True
+
+    def test_swarm_rotate_addresses(self):
+        """Test basic LE scan filtering in a mass beacon deployment.
+
+        Test finding LE scan results in a mass beacon deployment. This test
+        rotates the mac address of the advertising devices at a consistent
+        interval in order to make the scanning device think there are
+        thousands of devices nearby.
+
+        Steps:
+        1. Assume that mass beacon deployment is setup with device names
+        advertising.
+        2. Set LE scanning mode to low latency on 28 scan instances.
+        3. Start LE scan on each of the scan instances.
+        5. Continuously Pop scan results matching the scan filter.
+        6. Rotate mac address of each advertising device sequentially.
+        7. 5-6 10,000 times.
+        8. Stop LE scanning
+
+        Expected Result:
+        The Bluetooth stack doesn't crash.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Beacon
+        Priority: 1
+        """
+        scan_callback_list = []
+        for _ in range(28):
+            self.scn_droid.bleSetScanSettingsScanMode(
+                ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+            filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+                self.scn_droid)
+            self.scn_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+            scan_callback_list.append(scan_callback)
+        thread = threading.Thread(
+            target=self._restart_special_advertisements_thread, args=())
+        thread.start()
+        n = 0
+        while n < 10000:
+            for cb in scan_callback_list:
+                event_info = self.scn_ed.pop_event(scan_result.format(
+                    cb), self.default_timeout)
+                mac_address = event_info['data']['Result']['deviceInfo']['address']
+                if mac_address not in self.discovered_mac_address_list:
+                    self.discovered_mac_address_list.append(mac_address)
+                self.log.info("Discovered {} different devices.".format(
+                    len(self.discovered_mac_address_list)))
+                n += 1
+        self.scn_droid.bleStopBleScan(scan_callback)
+        return True
diff --git a/acts/tests/google/ble/bug_testing/BugsTest.py b/acts/tests/google/ble/bug_testing/BugsTest.py
new file mode 100644
index 0000000..48193e7
--- /dev/null
+++ b/acts/tests/google/ble/bug_testing/BugsTest.py
@@ -0,0 +1,634 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 test script that acts as a sandbox for testing various bugs. Testcases here
+may eventually be made into actual testscases later.
+"""
+
+import concurrent
+import pprint
+import time
+
+from queue import Empty
+from acts.base_test import BaseTestClass
+from acts.test_utils.bt.BleEnum import *
+from acts.test_utils.bt.bt_test_utils import *
+
+
+class BugsTest(BaseTestClass):
+  tests = None
+  default_timeout = 10
+
+  def __init__(self, controllers):
+    BaseTestClass.__init__(self, controllers)
+    self.tests = (
+      "test_scan_advertise_50",
+      "test_swarm_scan",
+      "test_three_advertisers_and_three_scanners",
+      "test_dual_scans",
+      "test_multiple_advertisers_on_batch_scan_result",
+      "test_advertisement_service_uuid",
+      "test_btu_hci_advertisement_crash",
+      "test_deep_sleep_advertising",
+      "test_random_mac_address_filtering",
+      "test_advertisement",
+      "test_28_advertisers",
+    )
+
+  def setup_class(self):
+    self.droid1, self.ed1 = self.android_devices[1].get_droid()
+    self.ed1.start()
+    return setup_multiple_devices_for_bt_test(self.droids, self.eds)
+
+  def on_fail(self, test_name, begin_time):
+    take_btsnoop_logs(self.droids, self, test_name)
+    reset_bluetooth(self.droids, self.eds)
+
+  # Handler Functions Begin
+  def blescan_verify_onfailure_event_handler(self, event):
+    self.log.debug("Verifying onFailure event")
+    self.log.debug(pprint.pformat(event))
+    return event
+
+  def bleadvertise_verify_onsuccess_handler(self, event):
+    self.log.debug(pprint.pformat(event))
+    return True
+
+  def ble_scan_get_mac_address_handler(self, event):
+    self.log.info(pprint.pformat(event))
+    return event['data']['Result']['deviceInfo']['address']
+
+  def blescan_verify_onscanresult_event_handler(self, event,
+                                                expected_callbacktype=None,
+                                                system_time_nanos=None):
+    test_result = True
+    self.log.debug("Verifying onScanResult event")
+    self.log.debug(pprint.pformat(event))
+    callbacktype = event['data']['CallbackType']
+    if callbacktype != expected_callbacktype:
+      self.log.debug(" ".join(["Expected callback type:",str(expected_callbacktype),
+                               ", Found callback type: " + str(callbacktype)]))
+      test_result = False
+    return test_result
+
+  def blescan_verify_onscanresult_event_handler2(self, event,
+                                                system_time_nanos=None):
+    test_result = True
+    self.log.debug("Verifying onScanResult event")
+    self.log.debug(pprint.pformat(event))
+    return event['data']['Result']['deviceInfo']['address']
+
+  ble_device_addresses = []
+
+  def blescan_verify_onbatchscanresult_event_handler(self, event):
+    """
+    An event handler that validates the onBatchScanResult
+    :param event: dict that represents the callback onBatchScanResult
+    :return: test_result: bool
+    """
+    # Todo: Implement proper validation steps.
+    test_result = True
+    self.log.debug("Verifying onBatchScanResult event")
+    self.log.debug(pprint.pformat(event))
+    print(pprint.pformat(event))
+    for event in event['data']['Results']:
+      address = event['deviceInfo']['address']
+      if address not in self.ble_device_addresses:
+        self.ble_device_addresses.append(address)
+    return test_result
+  # Handler Functions End
+
+  def test_scan_advertise_50(self):
+    self.log.debug("Step 1: Setting up environment")
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+    advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+      AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+    self.log.debug(
+      "Step 3: Create default scan filter, scan settings, and scan callback")
+    filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+      scan_droid)
+    expected_event_name = "BleScan" + str(scan_callback) + "onScanResults"
+    advertise_callback, advertise_data, advertise_settings = generate_ble_advertise_objects(
+      advertise_droid)
+    n = 0
+    while n < 50:
+      test_result = advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data,
+                                              advertise_settings)
+      if not test_result:
+        self.log.debug("Advertising failed.")
+        return test_result
+      self.log.debug("Step 4: Start Bluetooth Le Scan on callback ID: " + str(
+        scan_callback))
+      test_result = scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+      self.log.debug(
+        "Step 5: Verify the Bluetooth Le Scan did not cause an onScanFailed event.")
+      worker = scan_event_dispatcher.handle_event(
+        self.blescan_verify_onscanresult_event_handler,
+        expected_event_name, ([1]), self.default_timeout)
+      try:
+        self.log.debug(worker.result(self.default_timeout))
+      except Empty as error:
+        test_result = False
+        self.log.debug("Test failed with Empty error: " + str(error))
+      except concurrent.futures._base.TimeoutError as error:
+        test_result = False
+        self.log.debug("Test failed with TimeoutError: " + str(error))
+      scan_droid.bleStopBleScan(scan_callback)
+      advertise_droid.bleStopBleAdvertising(advertise_callback)
+      advertise_droid.bluetoothToggleState(False)
+      advertise_droid.bluetoothToggleState(True)
+      time.sleep(12)
+      n += 1
+    return test_result
+
+  def test_swarm_scan(self):
+    self.log.debug("Step 1: Setting up environment")
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+    advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+      AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+    self.log.debug(
+      "Step 3: Create default scan filter, scan settings, and scan callback")
+    filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+      scan_droid)
+    expected_event_name = "BleScan" + str(scan_callback) + "onScanResults"
+    advertise_callback, advertise_data, advertise_settings = generate_ble_advertise_objects(
+      advertise_droid)
+    n = 0
+    while n < 10000:
+      test_result = advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+      test_result = scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+      self.log.debug(
+        "Step 5: Verify the Bluetooth Le Scan did not cause an onScanFailed event.")
+      worker = scan_event_dispatcher.handle_event(
+        self.blescan_verify_onscanresult_event_handler,
+        expected_event_name, ([1]), self.default_timeout)
+      try:
+        self.log.debug(worker.result(self.default_timeout))
+      except Empty as error:
+        test_result = False
+        self.log.debug("Test failed with Empty error: " + str(error))
+      except concurrent.futures._base.TimeoutError as error:
+        test_result = False
+        self.log.debug("Test failed with TimeoutError: " + str(error))
+      scan_droid.bleStopBleScan(scan_callback)
+      n += 1
+      advertise_droid.bleStopBleAdvertising(advertise_callback)
+    return test_result
+
+  def test_dual_scans(self):
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    scan_droid2, scan_event_dispatcher2 = self.droid1, self.ed1
+    filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+      scan_droid)
+    filter_list2, scan_settings2, scan_callback2 = generate_ble_scan_objects(
+      scan_droid2)
+    expected_event_name = "BleScan" + str(scan_callback) + "onScanResults"
+    expected_event_name2 = "BleScan" + str(scan_callback2) + "onScanResults"
+    n = 0
+    while n < 1000000:
+      test_result = scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+      test_result = scan_droid2.bluetoothStartBleScan(filter_list2,scan_settings2,scan_callback2)
+      worker = scan_event_dispatcher.handle_event(
+        self.blescan_verify_onscanresult_event_handler,
+        expected_event_name, ([1]), self.default_timeout)
+      try:
+        self.log.debug(worker.result(self.default_timeout))
+      except Empty as error:
+        test_result = False
+        self.log.debug("Test failed with Empty error: " + str(error))
+      except concurrent.futures._base.TimeoutError as error:
+        test_result = False
+        self.log.debug("Test failed with TimeoutError: " + str(error))
+      worker2 = scan_event_dispatcher2.handle_event(
+        self.blescan_verify_onscanresult_event_handler,
+        expected_event_name2, ([1]), self.default_timeout)
+      try:
+        self.log.debug(worker2.result(self.default_timeout))
+      except Empty as error:
+        test_result = False
+        self.log.debug("Test failed with Empty error: " + str(error))
+      except concurrent.futures._base.TimeoutError as error:
+        test_result = False
+        self.log.debug("Test failed with TimeoutError: " + str(error))
+      scan_droid.bleStopBleScan(scan_callback)
+      scan_droid2.bluetoothStopBleScan(scan_callback2)
+      n += 1
+    return test_result
+
+  def test_three_advertisers_and_three_scanners(self):
+    self.log.debug("Step 1: Setting up environment")
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+    advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+      AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+    self.log.debug(
+      "Step 3: Create default scan filter, scan settings, and scan callback")
+    filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+      scan_droid)
+    filter_list1, scan_settings1, scan_callback1 = generate_ble_scan_objects(
+      scan_droid)
+    filter_list2, scan_settings2, scan_callback2 = generate_ble_scan_objects(
+      scan_droid)
+
+    expected_event_name = "BleScan" + str(scan_callback) + "onScanResults"
+    advertise_callback, advertise_data, advertise_settings = (
+      generate_ble_advertise_objects(advertise_droid))
+    advertise_callback1, advertise_data1, advertise_settings1 = (
+      generate_ble_advertise_objects(advertise_droid))
+    advertise_callback2, advertise_data2, advertise_settings2 = (
+      generate_ble_advertise_objects(advertise_droid))
+    test_result = advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+    test_result = advertise_droid.bleStartBleAdvertising(advertise_callback1, advertise_data1,
+                                                      advertise_settings1)
+    test_result = advertise_droid.bleStartBleAdvertising(advertise_callback2, advertise_data2,
+                                                      advertise_settings2)
+
+    test_result = scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+    test_result = scan_droid.bleStartBleScan(filter_list1,scan_settings1,scan_callback1)
+    test_result = scan_droid.bleStartBleScan(filter_list2,scan_settings2,scan_callback2)
+    time.sleep(30)
+    self.log.debug(
+      "Step 5: Verify the Bluetooth Le Scan did not cause an onScanFailed event.")
+    worker = scan_event_dispatcher.handle_event(
+      self.blescan_verify_onscanresult_event_handler,
+      expected_event_name, ([1]), self.default_timeout)
+    try:
+      self.log.debug(worker.result(self.default_timeout))
+    except Empty as error:
+      test_result = False
+      self.log.debug("Test failed with Empty error: " + str(error))
+    except concurrent.futures._base.TimeoutError as error:
+      test_result = False
+      self.log.debug("Test failed with TimeoutError: " + str(error))
+    scan_droid.bleStopBleScan(scan_callback)
+    scan_droid.bleStopBleScan(scan_callback1)
+    scan_droid.bleStopBleScan(scan_callback2)
+    advertise_droid.bleStopBleAdvertising(advertise_callback)
+    advertise_droid.bleStopBleAdvertising(advertise_callback1)
+    advertise_droid.bleStopBleAdvertising(advertise_callback2)
+
+    return test_result
+
+  def test_multiple_advertisers_on_batch_scan_result(self):
+    """
+    Test that exercises onBatchScanResults against one device advertising its
+    max advertisements.
+    This is different from the BeaconSwarmTest:test_swarm_no_attenuation in
+    that it uses a second android device's advertisements instead of standalone
+    ble beacons.
+
+    Steps:
+    1. Setup the scanning android device
+    2. Setup max (4) advertisements on secondary device.
+    3. Verify that one hundred onBatchScanResult callback was triggered.
+    :return: test_result: bool
+    """
+    test_result = True
+    max_advertisers = 4
+    ad_callbacks = []
+    advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+    for x in range(max_advertisers):
+      advertise_callback, advertise_data, advertise_settings = generate_ble_advertise_objects(
+        advertise_droid)
+      advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+      expected_advertise_event_name = "".join(["BleAdvertise",str(advertise_callback),"onSuccess"])
+      worker = advertise_event_dispatcher.handle_event(
+        self.bleadvertise_verify_onsuccess_handler, expected_advertise_event_name, ([]),
+        self.default_timeout)
+      ad_callbacks.append(advertise_callback)
+    #self.attenuators[0].set_atten(0, 0)
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    scan_droid.bleSetScanSettingsReportDelayMillis(1000)
+    filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+      scan_droid)
+    expected_event_name = "".join(["BleScan",str(scan_callback),"onBatchScanResult"])
+    scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+    n = 0
+    while n < 100:
+      worker = scan_event_dispatcher.handle_event(
+        self.blescan_verify_onbatchscanresult_event_handler,
+        expected_event_name, ([]), self.default_timeout)
+      try:
+        self.log.debug(worker.result(self.default_timeout))
+      except Empty as error:
+        test_result = False
+        self.log.debug(" ".join(["Test failed with Empty error:",str(error)]))
+      except concurrent.futures._base.TimeoutError as error:
+        test_result = False
+        self.log.debug(" ".join(["Test failed with TimeoutError:",str(error)]))
+      n+=1
+    scan_droid.bleStopBleScan(scan_callback)
+    for x in ad_callbacks:
+      advertise_droid.bleStopBleAdvertising(x)
+    print (self.ble_device_addresses) #temporary
+    print (str(len(self.ble_device_addresses))) #temporary
+    return test_result
+
+  def test_advertisement_service_uuid(self):
+    test_result = True
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+    advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+      AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+    advertise_droid.bleSetAdvertiseDataSetServiceUuids(["00000000-0000-1000-8000-00805f9b34fb"])
+    filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+      scan_droid)
+
+    expected_event_name = "BleScan" + str(scan_callback) + "onScanResults"
+    advertise_callback, advertise_data, advertise_settings = (
+      generate_ble_advertise_objects(advertise_droid))
+    test_result = advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+
+    test_result = scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+    time.sleep(30)
+    worker = scan_event_dispatcher.handle_event(
+      self.blescan_verify_onscanresult_event_handler,
+      expected_event_name, ([1]), self.default_timeout)
+    try:
+      self.log.debug(worker.result(self.default_timeout))
+    except Empty as error:
+      test_result = False
+      self.log.debug("Test failed with Empty error: " + str(error))
+    except concurrent.futures._base.TimeoutError as error:
+      test_result = False
+      self.log.debug("Test failed with TimeoutError: " + str(error))
+    scan_droid.bleStopBleScan(scan_callback)
+    advertise_droid.bleStopBleAdvertising(advertise_callback)
+
+    return test_result
+
+
+  #{'is_connectable': True, 'mode': 0, 'tx_power_level': 2}
+  def test_btu_hci_advertisement_crash(self):
+    test_result = True
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+    x = 0
+    while x < 50:
+      advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+        AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_POWER.value)
+      advertise_droid.bleSetAdvertiseSettingsIsConnectable(True)
+      advertise_droid.bleSetAdvertiseSettingsTxPowerLevel(2)
+      filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+        scan_droid)
+
+      expected_event_name = "BleScan" + str(scan_callback) + "onScanResults"
+      advertise_callback, advertise_data, advertise_settings = (
+        generate_ble_advertise_objects(advertise_droid))
+      advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+      advertise_droid.bleStopBleAdvertising(advertise_callback)
+      x+=1
+
+    return test_result
+
+  def test_deep_sleep_advertising(self):
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+    max_advertisements = 4
+    advertisement_callback_list = []
+    advertisement_mac_addr_list = []
+    filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+      scan_droid)
+    scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+    expected_event_name = "BleScan" + str(scan_callback) + "onScanResults"
+    while len(advertisement_mac_addr_list) < max_advertisements:
+      advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+        AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+      advertise_callback, advertise_data, advertise_settings = (
+        generate_ble_advertise_objects(advertise_droid))
+      advertisement_callback_list.append(advertise_callback)
+      advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+      worker = scan_event_dispatcher.handle_event(
+        self.ble_scan_get_mac_address_handler,
+        expected_event_name, (), self.default_timeout)
+      try:
+        mac_address = worker.result(self.default_timeout)
+        print(mac_address)
+        if mac_address not in advertisement_mac_addr_list:
+          advertisement_mac_addr_list.append(mac_address)
+      except Exception:
+        self.log.info("failed to find advertisement")
+    scan_droid.bleStopBleScan(scan_callback)
+    print("putting advertise droid to sleep")
+    try:
+      print (pprint.pformat(self.ed1.pop_all(expected_event_name)))
+    except Exception:
+      print ("lol fail")
+    advertise_droid.setDeepSleep(960000) #16 minutes
+    advertise_droid.wakeUpNow()
+    scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+    advertisement_mac_addr_list2 = []
+    while len(advertisement_mac_addr_list2) < max_advertisements:
+      worker = scan_event_dispatcher.handle_event(
+        self.ble_scan_get_mac_address_handler,
+        expected_event_name, (), self.default_timeout)
+      try:
+        mac_address = worker.result(self.default_timeout)
+        print(mac_address)
+        if mac_address not in advertisement_mac_addr_list2:
+          advertisement_mac_addr_list2.append(mac_address)
+      except Exception:
+        self.log.info("failed to find advertisement")
+    scan_droid.bleStopBleScan(scan_callback)
+    diff_list = list(set(advertisement_mac_addr_list) - set(advertisement_mac_addr_list2))
+    print(pprint.pformat(advertisement_mac_addr_list))
+    print(pprint.pformat(advertisement_mac_addr_list2))
+    for callback in advertisement_callback_list:
+      advertise_droid.bleStopBleAdvertising(callback)
+    print("new callback")
+    print(pprint.pformat(diff_list))
+    print("done")
+    if len(diff_list) != max_advertisements:
+      return False
+    else:
+      return True
+
+  def test_random_mac_address_filtering(self):
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+    advertisement_callback_list = []
+    advertisement_mac_addr_list = []
+    filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+      scan_droid)
+    scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+    expected_event_name = "BleScan" + str(scan_callback) + "onScanResults"
+    advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+      AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+    advertise_callback, advertise_data, advertise_settings = (
+      generate_ble_advertise_objects(advertise_droid))
+    advertisement_callback_list.append(advertise_callback)
+    advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+    worker = scan_event_dispatcher.handle_event(
+      self.ble_scan_get_mac_address_handler,
+      expected_event_name, (), self.default_timeout)
+    mac_address = None
+    try:
+      mac_address = worker.result(self.default_timeout)
+      print(mac_address)
+      if mac_address not in advertisement_mac_addr_list:
+        advertisement_mac_addr_list.append(mac_address)
+    except Exception:
+      self.log.info("failed to find advertisement")
+    try:
+      scan_event_dispatcher.stop()
+      scan_event_dispatcher.start()
+    except Exception:
+      print("do nothing")
+    scan_droid.bleStopBleScan(scan_callback)
+    print("This mac address is being set for a filter: " + mac_address)
+    filter_list2 = scan_droid.bleGenFilterList()
+    scan_droid.bleSetScanFilterDeviceAddress(mac_address)
+    scan_droid.bleBuildScanFilter(filter_list)
+    scan_settings2 = scan_droid.bleBuildScanSetting()
+    scan_callback2 = scan_droid.bleGenScanCallback()
+
+    scan_droid.bleStartBleScan(filter_list2,scan_settings2,scan_callback2)
+    expected_event_name = "BleScan" + str(scan_callback2) + "onScanResults"
+    worker = scan_event_dispatcher.handle_event(
+      self.ble_scan_get_mac_address_handler,
+      expected_event_name, (), self.default_timeout)
+    try:
+      mac_address = worker.result(self.default_timeout)
+      print(mac_address)
+      if mac_address not in advertisement_mac_addr_list:
+        advertisement_mac_addr_list.append(mac_address)
+    except Exception:
+      self.log.info("failed to find advertisement")
+      return False
+    scan_droid.bleStopBleScan(scan_callback2)
+    advertise_droid.bleStopBleAdvertising(advertise_callback)
+    return True
+
+
+
+  def test_advertisement(self):
+    test_result = True
+    max_advertisers = 4
+    ad_callbacks = []
+    advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+    #if False:
+    #  from acts.etc.development.ide.pycharm.android_intellisense import AndroidIntellisense
+    #  assert isinstance(advertise_droid, AndroidIntellisense)
+    advertiser_local_name = advertise_droid.bluetoothGetLocalName()
+    advertise_droid.bleSetAdvertiseDataIncludeDeviceName(False)
+    advertise_callback, advertise_data, advertise_settings = generate_ble_advertise_objects(
+      advertise_droid)
+    advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+    print("Sleeping")
+    #time.sleep(30)
+    expected_advertise_event_name = "".join(["BleAdvertise",str(advertise_callback),"onSuccess"])
+    worker = advertise_event_dispatcher.handle_event(
+      self.bleadvertise_verify_onsuccess_handler, expected_advertise_event_name, ([]),
+      self.default_timeout)
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    filter_list = scan_droid.bleGenFilterList()
+    scan_droid.bleSetScanFilterDeviceName(advertiser_local_name)
+    scan_droid.bleBuildScanFilter(filter_list)
+    scan_settings = scan_droid.bleBuildScanSetting()
+    scan_callback = scan_droid.bleGenScanCallback()
+    expected_event_name = "".join(["BleScan",str(scan_callback),"onScanResult"])
+    scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+    worker = scan_event_dispatcher.handle_event(
+        self.blescan_verify_onscanresult_event_handler,
+        expected_event_name, ([1]), self.default_timeout)
+    try:
+      self.log.info(worker.result(self.default_timeout))
+    except Empty as error:
+      test_result = False
+      self.log.debug(" ".join(["Test failed with Empty error:",str(error)]))
+    except concurrent.futures._base.TimeoutError as error:
+      test_result = False
+      self.log.debug(" ".join(["Test failed with TimeoutError:",str(error)]))
+    scan_droid.bleStopBleScan(scan_callback)
+    advertise_droid.bleStopBleAdvertising(advertise_callback)
+    print("Advertiser " + advertise_droid.bluetoothGetLocalName())
+    print("Scanner" + scan_droid.bluetoothGetLocalName())
+    return test_result
+
+  def test_28_advertisers(self):
+    self.log.debug("Step 1: Setting up environment")
+    scan_droid, scan_event_dispatcher = self.droid, self.ed
+    max_advertisements = 28
+    advertise_callback_list = []
+    d_counter = 0
+    sprout_counter = 0
+    while d_counter < len(self.droids):
+      advertise_droid, advertise_event_dispatcher = self.droids[d_counter], self.eds[d_counter]
+      d_counter+=1
+      sprout_names = ["Sprout","Micromax AQ4501","4560MMX"]
+      print("Checking device model")
+      if advertise_droid.getBuildModel() not in sprout_names:
+        continue
+      n = 0
+      sprout_counter+=1
+      while n < max_advertisements:
+        advertise_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+          AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        advertise_callback, advertise_data, advertise_settings = generate_ble_advertise_objects(
+          advertise_droid)
+        test_result = advertise_droid.bleStartBleAdvertising(advertise_callback, advertise_data, advertise_settings)
+        expected_advertise_event_name = "".join(["BleAdvertise",str(advertise_callback),"onSuccess"])
+        worker = advertise_event_dispatcher.handle_event(
+        self.bleadvertise_verify_onsuccess_handler, expected_advertise_event_name, ([]),
+        self.default_timeout)
+        try:
+          worker.result()
+        except Exception as e:
+          self.log.info("Advertising failed due to " + str(e))
+          reset_bluetooth(self.droids, self.eds)
+          return False
+        print (str(n) + "th advertisement successful")
+        n+=1
+    mac_address_list = []
+    done = False
+    advertisements_to_find = sprout_counter * max_advertisements
+    min_advertisements_to_pass = int(advertisements_to_find * 0.9)
+    print("START SNIFFER")
+    time.sleep(30)
+    end_time = time.time() + 120
+    while not done and time.time() < end_time:
+      print("try again " + str(mac_address_list))
+      print(str(len(mac_address_list)))
+      filter_list = scan_droid.bleGenFilterList()
+      scan_droid.bleSetScanFilterDeviceName("Micromax AQ4501")
+      scan_droid.bleBuildScanFilter(filter_list)
+      scan_droid.bleSetScanFilterDeviceName("4560MMX")
+      scan_droid.bleBuildScanFilter(filter_list)
+      scan_settings = scan_droid.bleBuildScanSetting()
+      scan_callback = scan_droid.bleGenScanCallback()
+      scan_droid.bleStartBleScan(filter_list,scan_settings,scan_callback)
+      expected_event_name = "BleScan" + str(scan_callback) + "onScanResults"
+      worker = scan_event_dispatcher.handle_event(
+        self.blescan_verify_onscanresult_event_handler2,
+        expected_event_name, ([]), self.default_timeout)
+      try:
+        mac_address = worker.result(self.default_timeout)
+        print(mac_address)
+        if mac_address not in mac_address_list:
+          mac_address_list.append(mac_address)
+          print (str(len(mac_address_list)) + " advertisements found")
+        if len(mac_address_list) >= advertisements_to_find:
+          done = True
+      except Empty as error:
+        test_result = False
+        self.log.debug("Test failed with Empty error: " + str(error))
+      except concurrent.futures._base.TimeoutError as error:
+        self.log.debug("Test failed with TimeoutError: " + str(error))
+      scan_droid.bleStopBleScan(scan_callback)
+    return test_result
diff --git a/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py b/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
new file mode 100644
index 0000000..daff1d7
--- /dev/null
+++ b/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
@@ -0,0 +1,668 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 exercises different ways Ble Advertisements can run in
+concurrency. This test was designed to be run in a shield box.
+"""
+
+import concurrent
+import os
+import time
+
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts.test_utils.bt.BleEnum import ScanSettingsCallbackType
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.bt_test_utils import adv_succ
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import scan_result
+from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+
+
+class ConcurrentBleAdvertisingTest(BluetoothBaseTest):
+    current_path = os.path.dirname(os.path.abspath(__file__))
+    tests = None
+    default_timeout = 10
+    max_advertisements = 4
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        self.max_advertisements = self.droid_list[0]['max_advertisements']
+        if self.max_advertisements == 0:
+            self.tests = ()
+            return
+        self.tests = (
+            "test_max_advertisements_defaults",
+            "test_max_advertisements_include_device_name_and_filter_device_name",
+            "test_max_advertisements_exclude_device_name_and_filter_device_name",
+            "test_max_advertisements_with_manufacturer_data",
+            "test_max_advertisements_with_manufacturer_data_mask",
+            "test_max_advertisements_with_service_data",
+            "test_max_advertisements_with_manufacturer_data_mask_and_include_device_name",
+            "test_max_advertisements_with_service_uuids",
+            "test_max_advertisements_with_service_uuid_and_service_mask",
+            "test_max_advertisements_plus_one",
+            "test_start_two_advertisements_on_same_callback",
+            "test_toggle_advertiser_bt_state",
+            "test_restart_advertise_callback_after_bt_toggle",
+        )
+
+    def on_fail(self, test_name, begin_time):
+        self.log.debug("Test {} failed. Gathering bugreport and btsnoop logs".
+                       format(test_name))
+        take_btsnoop_logs(self.droids, self, test_name)
+        reset_bluetooth(self.droids, self.eds)
+
+    def setup_test(self):
+        return reset_bluetooth(self.droids, self.eds)
+
+    def _verify_n_advertisements(self, num_advertisements, filter_list):
+        test_result = False
+        address_list = []
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value)
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        advertise_data = self.adv_droid.bleBuildAdvertiseData()
+        advertise_settings = self.adv_droid.bleBuildAdvertiseSettings()
+        advertise_callback_list = []
+        for _ in range(num_advertisements):
+            advertise_callback = self.adv_droid.bleGenBleAdvertiseCallback()
+            advertise_callback_list.append(advertise_callback)
+            self.adv_droid.bleStartBleAdvertising(
+                advertise_callback, advertise_data,
+                advertise_settings)
+            try:
+                self.adv_ed.pop_event(
+                    adv_succ.format(advertise_callback), self.default_timeout)
+            except Empty as error:
+                self.log.debug(
+                    "Test failed with Empty error: {}".format(error))
+                return False
+            except concurrent.futures._base.TimeoutError as error:
+                self.log.debug(
+                    "Test failed, filtering callback onSuccess never occurred: "
+                    "{}".format(error))
+                return False
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        start_time = time.time()
+        while (start_time + self.default_timeout) > time.time():
+            event = None
+            try:
+                event = self.scn_ed.pop_event(
+                    scan_result.format(scan_callback), self.default_timeout)
+            except Empty as error:
+                self.log.debug("Test failed with: {}".format(error))
+                return test_result
+            except concurrent.futures._base.TimeoutError as error:
+                self.log.debug("Test failed with: {}".format(error))
+                return test_result
+            address = event['data']['Result']['deviceInfo']['address']
+            if address not in address_list:
+                address_list.append(address)
+            if len(address_list) == num_advertisements:
+                test_result = True
+                break
+        for callback in advertise_callback_list:
+            self.adv_droid.bleStopBleAdvertising(callback)
+        self.scn_droid.bleStopBleScan(scan_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_defaults(self):
+        """Testing max advertisements.
+
+        Test that a single device can have the max advertisements
+        concurrently advertising.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Start scanning on the max_advertisements as defined in the script.
+        4. Verify that all advertisements are found.
+
+        Expected Result:
+        All advertisements should start without errors.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 0
+        """
+        test_result = True
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements, filter_list)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_include_device_name_and_filter_device_name(self):
+        """Testing max advertisement variant.
+
+        Test that a single device can have the max advertisements
+        concurrently advertising. Include the device name as a part of the filter
+        and advertisement data.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Include device name in each advertisement.
+        4. Include device name filter in the scanner.
+        5. Start scanning on the max_advertisements as defined in the script.
+        6. Verify that all advertisements are found.
+
+        Expected Result:
+        All advertisements should start without errors.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 2
+        """
+        test_result = True
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements, filter_list)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_exclude_device_name_and_filter_device_name(self):
+        """Test max advertisement variant.
+
+        Test that a single device can have the max advertisements concurrently
+        advertising. Include the device name as a part of the filter but not the
+        advertisement data.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Include device name filter in the scanner.
+        4. Start scanning on the max_advertisements as defined in the script.
+        5. Verify that no advertisements are found.
+
+        Expected Result:
+        All advertisements should start without errors.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 2
+        """
+        test_result = True
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(False)
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements, filter_list)
+        return not test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_with_manufacturer_data(self):
+        """Test max advertisement variant.
+
+        Test that a single device can have the max advertisements concurrently
+        advertising. Include the manufacturer data as a part of the filter and
+        advertisement data.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Include manufacturer data in each advertisement.
+        4. Include manufacturer data filter in the scanner.
+        5. Start scanning on the max_advertisements as defined in the script.
+        6. Verify that all advertisements are found.
+
+        Expected Result:
+        All advertisements should start without errors.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 2
+        """
+        test_result = True
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterManufacturerData(1, "1")
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.adv_droid.bleAddAdvertiseDataManufacturerId(1, "1")
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements, filter_list)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_with_manufacturer_data_mask(self):
+        """Test max advertisements variant.
+
+        Test that a single device can have the max advertisements concurrently
+        advertising. Include the manufacturer data mask as a part of the filter
+        and advertisement data.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Include manufacturer data in each advertisement.
+        4. Include manufacturer data mask filter in the scanner.
+        5. Start scanning on the max_advertisements as defined in the script.
+        6. Verify that all advertisements are found.
+
+        Expected Result:
+        All advertisements should start without errors.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 2
+        """
+        test_result = True
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterManufacturerData(1, "1", "1")
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.adv_droid.bleAddAdvertiseDataManufacturerId(1, "1")
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements, filter_list)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_with_service_data(self):
+        """Test max advertisement variant.
+
+        Test that a single device can have the max advertisements concurrently
+        advertising. Include the service data as a part of the filter and
+        advertisement data.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Include service data in each advertisement.
+        4. Include service data filter in the scanner.
+        5. Start scanning on the max_advertisements as defined in the script.
+        6. Verify that all advertisements are found.
+
+        Expected Result:
+        All advertisements should start without errors.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 2
+        """
+        test_result = True
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterServiceData(
+            "0000110A-0000-1000-8000-00805F9B34FB", "11,17,80")
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.adv_droid.bleAddAdvertiseDataServiceData(
+            "0000110A-0000-1000-8000-00805F9B34FB", "11,17,80")
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements, filter_list)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_with_manufacturer_data_mask_and_include_device_name(self):
+        """Test max advertisement variant.
+
+        Test that a single device can have the max advertisements concurrently
+        advertising. Include the device name and manufacturer data as a part of
+        the filter and advertisement data.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Include device name and manufacturer data in each advertisement.
+        4. Include device name and manufacturer data filter in the scanner.
+        5. Start scanning on the max_advertisements as defined in the script.
+        6. Verify that all advertisements are found.
+
+        Expected Result:
+        All advertisements should start without errors.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 2
+        """
+        test_result = True
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleSetScanFilterManufacturerData(1, "1", "1")
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.adv_droid.bleAddAdvertiseDataManufacturerId(1, "1")
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements, filter_list)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_with_service_uuids(self):
+        """Test max advertisement variant.
+
+        Test that a single device can have the max advertisements concurrently
+        advertising. Include the service uuid as a part of the filter and
+        advertisement data.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Include service uuid in each advertisement.
+        4. Include service uuid filter in the scanner.
+        5. Start scanning on the max_advertisements as defined in the script.
+        6. Verify that all advertisements are found.
+
+        Expected Result:
+        All advertisements should start without errors.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 1
+        """
+        test_result = True
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterServiceUuid(
+            "00000000-0000-1000-8000-00805f9b34fb")
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.adv_droid.bleSetAdvertiseDataSetServiceUuids(
+            ["00000000-0000-1000-8000-00805f9b34fb"])
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements, filter_list)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_with_service_uuid_and_service_mask(self):
+        """Test max advertisements variant.
+
+        Test that a single device can have the max advertisements concurrently
+        advertising. Include the service mask as a part of the filter and
+        advertisement data.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Include service uuid in each advertisement.
+        4. Include service mask filter in the scanner.
+        5. Start scanning on the max_advertisements as defined in the script.
+        6. Verify that all advertisements are found.
+
+        Expected Result:
+        All advertisements should start without errors.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 2
+        """
+        test_result = True
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterServiceUuid(
+            "00000000-0000-1000-8000-00805f9b34fb",
+            "00000000-0000-1000-8000-00805f9b34fb")
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.adv_droid.bleSetAdvertiseDataSetServiceUuids(
+            ["00000000-0000-1000-8000-00805f9b34fb"])
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements, filter_list)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_advertisements_plus_one(self):
+        """Test max advertisements plus one.
+
+        Test that a single device can have the max advertisements concurrently
+        advertising but fail on starting the max advertisements plus one.
+        filter and advertisement data.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Start max_advertisements + 1.
+
+        Expected Result:
+        The last advertisement should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 0
+        """
+        test_result = True
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        test_result = self._verify_n_advertisements(
+            self.max_advertisements + 1, filter_list)
+        return not test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_start_two_advertisements_on_same_callback(self):
+        """Test invalid advertisement scenario.
+
+        Test that a single device cannot have two advertisements start on the
+        same callback.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Call start ble advertising on the same callback.
+
+        Expected Result:
+        The second call of start advertising on the same callback should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 1
+        """
+        test_result = True
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data,
+            advertise_settings)
+        try:
+            self.adv_ed.pop_event(
+                adv_succ.format(advertise_callback), self.default_timeout)
+        except Empty as error:
+            self.log.debug("Test failed with Empty error: {}".format(error))
+            return False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.debug(
+                "Test failed, filtering callback onSuccess never occurred: {}"
+                .format(error))
+        try:
+            self.adv_droid.bleStartBleAdvertising(
+                advertise_callback, advertise_data,
+                advertise_settings)
+            self.adv_ed.pop_event(
+                adv_succ.format(advertise_callback), self.default_timeout)
+            test_result = False
+        except Empty as error:
+            self.log.debug("Test passed with Empty error: {}".format(error))
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.debug(
+                "Test passed, filtering callback onSuccess never occurred: {}"
+                .format(error))
+
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_toggle_advertiser_bt_state(self):
+        """Test forcing stopping advertisements.
+
+        Test that a single device resets its callbacks when the bluetooth state is
+        reset. There should be no advertisements.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Call start ble advertising.
+        4. Toggle bluetooth on and off.
+        5. Scan for any advertisements.
+
+        Expected Result:
+        No advertisements should be found after toggling Bluetooth on the
+        advertising device.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 2
+        """
+        test_result = True
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data,
+            advertise_settings)
+        try:
+            self.adv_ed.pop_event(
+                adv_succ.format(advertise_callback), self.default_timeout)
+        except Empty as error:
+            self.log.debug("Test failed with Empty error: {}".format(error))
+            return False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.debug(
+                "Test failed, filtering callback onSuccess never occurred: {}".
+                format(error))
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        try:
+            self.scn_ed.pop_event(
+                scan_result.format(scan_callback), self.default_timeout)
+        except Empty as error:
+            self.log.debug("Test failed with: {}".format(error))
+            return False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.debug("Test failed with: {}".format(error))
+            return False
+        self.scn_droid.bleStopBleScan(scan_callback)
+        test_result = reset_bluetooth([self.droids[1]], [self.eds[1]])
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        if not test_result:
+            return test_result
+        try:
+            self.scn_ed.pop_event(
+                scan_result.format(scan_callback), self.default_timeout)
+            return False
+        except Empty as error:
+            self.log.debug("Test passed with: {}".format(error))
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.debug("Test passed with: {}".format(error))
+        self.scn_droid.bleStopBleScan(scan_callback)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_restart_advertise_callback_after_bt_toggle(self):
+        """Test starting an advertisement on a cleared out callback.
+
+        Test that a single device resets its callbacks when the bluetooth state
+        is reset.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android device.
+        3. Call start ble advertising.
+        4. Toggle bluetooth on and off.
+        5. Call start ble advertising on the same callback.
+
+        Expected Result:
+        Starting an advertisement on a callback id after toggling bluetooth
+        should fail.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 1
+        """
+        test_result = True
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        try:
+            self.adv_ed.pop_event(
+                adv_succ.format(advertise_callback), self.default_timeout)
+        except Empty as error:
+            self.log.debug("Test failed with Empty error: {}".format(error))
+            test_result = False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.debug(
+                "Test failed, filtering callback onSuccess never occurred: {}".
+                format(error))
+        test_result = reset_bluetooth([self.adv_droid], [self.adv_ed])
+        if not test_result:
+            return test_result
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        try:
+            self.adv_ed.pop_event(
+                adv_succ.format(advertise_callback), self.default_timeout)
+        except Empty as error:
+            self.log.debug("Test failed with Empty error: {}".format(error))
+            test_result = False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.debug(
+                "Test failed, filtering callback onSuccess never occurred: {}".
+                format(error))
+        return test_result
diff --git a/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py b/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
new file mode 100644
index 0000000..afba9b3
--- /dev/null
+++ b/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
@@ -0,0 +1,367 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 exercises Ble Scans can run in concurrency.
+This test was designed to be run in a shield box.
+"""
+
+import concurrent
+import time
+
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts.test_utils.bt.BleEnum import ScanSettingsCallbackType
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.bt_test_utils import adv_succ
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import scan_failed
+from acts.test_utils.bt.bt_test_utils import scan_result
+from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+
+
+class ConcurrentBleScanningTest(BluetoothBaseTest):
+    default_timeout = 20
+    max_concurrent_scans = 28
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        if self.droid_list[1]['max_advertisements'] == 0:
+            self.tests = (
+                "test_max_concurrent_ble_scans_plus_one",
+            )
+            return
+        self.tests = (
+            "test_max_concurrent_ble_scans",
+            "test_max_concurrent_ble_scans_then_discover_advertisement",
+            "test_max_concurrent_ble_scans_plus_one",
+            "test_max_concurrent_ble_scans_verify_scans_stop_independently",
+        )
+
+    def on_fail(self, test_name, begin_time):
+        self.log.debug(
+            "Test {} failed. Gathering bugreport and btsnoop logs."
+            .format(test_name))
+        take_btsnoop_logs(self.droids, self, test_name)
+        reset_bluetooth(self.droids, self.eds)
+
+    def setup_test(self):
+        return reset_bluetooth(self.droids, self.eds)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_concurrent_ble_scans(self):
+        """Test max LE scans.
+
+        Test that a single device can have max scans concurrently scanning.
+
+        Steps:
+        1. Initialize scanner
+        2. Initialize advertiser
+        3. Start advertising on the device from step 2
+        4. Create max ble scan callbacks
+        5. Start ble scan on each callback
+        6. Verify that each callback triggers
+        7. Stop all scans and advertisements
+
+        Expected Result:
+        All scanning instances should start without errors and the advertisement
+        should be found on each scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Concurrency
+        Priority: 0
+        """
+        test_result = True
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value)
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleSetAdvertiseSettingsIsConnectable(False)
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        try:
+            self.adv_ed.pop_event(
+                adv_succ.format(advertise_callback), self.default_timeout)
+        except Empty as error:
+            self.log.exception(
+                "Test failed with Empty error: {}".format(error))
+            test_result = False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.exception("Test failed callback onSuccess never occurred: "
+                               "{}".format(error))
+            test_result = False
+        if not test_result:
+            return test_result
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback_list = []
+        for i in range(self.max_concurrent_scans):
+            self.log.debug("Concurrent Ble Scan iteration {}".format(i + 1))
+            scan_callback = self.scn_droid.bleGenScanCallback()
+            scan_callback_list.append(scan_callback)
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+            try:
+                self.scn_ed.pop_event(
+                    scan_result.format(scan_callback), self.default_timeout)
+                self.log.info(
+                    "Found scan event successfully. Iteration {} "
+                    "successful.".format(i))
+            except Exception:
+                self.log.info(
+                    "Failed to find a scan result for callback {}"
+                    .format(scan_callback))
+                test_result = False
+                break
+        for callback in scan_callback_list:
+            self.scn_droid.bleStopBleScan(callback)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        if not test_result:
+            return test_result
+        self.log.info("Waiting for scan callbacks to stop completely.")
+        # Wait for all scan callbacks to stop. There is no confirmation
+        # otherwise.
+        time.sleep(10)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_concurrent_ble_scans_then_discover_advertisement(self):
+        """Test max LE scans variant.
+
+        Test that a single device can have max scans concurrently scanning.
+
+        Steps:
+        1. Initialize scanner
+        2. Initialize advertiser
+        3. Create max ble scan callbacks
+        4. Start ble scan on each callback
+        5. Start advertising on the device from step 2
+        6. Verify that each callback triggers
+        7. Stop all scans and advertisements
+
+        Expected Result:
+        All scanning instances should start without errors and the advertisement
+        should be found on each scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Concurrency
+        Priority: 1
+        """
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value)
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback_list = []
+        for i in range(self.max_concurrent_scans):
+            self.log.debug("Concurrent Ble Scan iteration {}".format(i + 1))
+            scan_callback = self.scn_droid.bleGenScanCallback()
+            scan_callback_list.append(scan_callback)
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        try:
+            self.adv_ed.pop_event(
+                adv_succ.format(advertise_callback), self.default_timeout)
+        except Empty as error:
+            self.log.exception("Test failed with Empty error: {}".format(error))
+            return False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.exception("Test failed, filtering callback onSuccess "
+                               "never occurred: {}".format(error))
+            return False
+        i = 0
+        for callback in scan_callback_list:
+            try:
+                self.scn_ed.pop_event(scan_result.format(scan_callback),
+                                      self.default_timeout)
+                self.log.info(
+                    "Found scan event successfully. Iteration {} successful."
+                    .format(i))
+            except Exception:
+                self.log.info(
+                    "Failed to find a scan result for callback {}"
+                    .format(scan_callback))
+                return False
+            i += 1
+        for callback in scan_callback_list:
+            self.scn_droid.bleStopBleScan(callback)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_concurrent_ble_scans_plus_one(self):
+        """Test mac LE scans variant.
+
+        Test that a single device can have max scans concurrently scanning.
+
+        Steps:
+        1. Initialize scanner
+        3. Create max ble scan callbacks plus one
+        5. Start ble scan on each callback
+        6. Verify that the n+1th scan fails.
+        7. Stop all scans
+
+        Expected Result:
+        The n+1th scan should fail to start.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Concurrency
+        Priority: 1
+        """
+        test_result = True
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value)
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback_list = []
+        for i in range(self.max_concurrent_scans):
+            self.log.debug("Concurrent Ble Scan iteration {}".format(i + 1))
+            scan_callback = self.scn_droid.bleGenScanCallback()
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+            scan_callback_list.append(scan_callback)
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        try:
+            self.scn_ed.pop_event(
+                scan_failed.format(scan_callback), self.default_timeout)
+            self.log.info(
+                "Found scan event successfully. Iteration {} successful."
+                .format(i))
+        except Exception:
+            self.log.info("Failed to find a onScanFailed event for callback {}"
+                          .format(scan_callback))
+            test_result = False
+        for callback in scan_callback_list:
+            self.scn_droid.bleStopBleScan(callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_concurrent_ble_scans_verify_scans_stop_independently(self):
+        """Test max LE scans variant.
+
+        Test that a single device can have max scans concurrently scanning.
+
+        Steps:
+        1. Initialize scanner
+        2. Initialize advertiser
+        3. Create max ble scan callbacks
+        4. Start ble scan on each callback
+        5. Start advertising on the device from step 2
+        6. Verify that the first callback triggers
+        7. Stop the scan and repeat steps 6 and 7 until all scans stopped
+
+        Expected Result:
+        All scanning instances should start without errors and the advertisement
+        should be found on each scan instance. All scanning instances should
+        stop successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Concurrency
+        Priority: 1
+        """
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_ALL_MATCHES.value)
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback_list = []
+        for i in range(self.max_concurrent_scans):
+            self.log.debug("Concurrent Ble Scan iteration {}".format(i + 1))
+            scan_callback = self.scn_droid.bleGenScanCallback()
+            scan_callback_list.append(scan_callback)
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        try:
+            self.adv_ed.pop_event(
+                adv_succ.format(advertise_callback), self.default_timeout)
+        except Empty as error:
+            self.log.exception(
+                "Test failed with Empty error: {}".format(error))
+            return False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.exception("Test failed, filtering callback onSuccess never"
+                               " occurred: {}".format(error))
+            return False
+        i = 0
+        for callback in scan_callback_list:
+            expected_scan_event_name = scan_result.format(scan_callback)
+            try:
+                self.scn_ed.pop_event(
+                    expected_scan_event_name, self.default_timeout)
+                self.log.info(
+                    "Found scan event successfully. Iteration {} successful.".
+                    format(i))
+                i += 1
+            except Exception:
+                self.log.info(
+                    "Failed to find a scan result for callback {}".
+                    format(scan_callback))
+                return False
+            self.scn_droid.bleStopBleScan(callback)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        return True
diff --git a/acts/tests/google/ble/distance_tests/BleDistanceTest.py b/acts/tests/google/ble/distance_tests/BleDistanceTest.py
new file mode 100644
index 0000000..b24b0a8
--- /dev/null
+++ b/acts/tests/google/ble/distance_tests/BleDistanceTest.py
@@ -0,0 +1,103 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 test script exercises distance based testcases.
+
+This test script was designed with this setup in mind:
+Shield box one: Android Device
+Shield box two: Android Device
+An attenuator sitting in between the two shield boxes.
+"""
+
+import pprint
+
+from queue import Empty
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+
+
+class BleDistanceTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid1, self.ed1 = self.droids[1], self.eds[1]
+        self.tests = (
+            "test_scan_default_advertisement_high_attenuation",
+        )
+
+    def cleanup_class(self):
+        self.attenuators[0].set_atten(0, 90)
+
+    def blescan_verify_onscanresult_event_handler(self, event):
+        """
+        An event handler that validates the onScanResult event.
+        :param event: dict that represents the callback onScanResult
+        :return: test_result: bool
+        """
+        # Todo: Implement proper validation steps.
+        test_result = True
+        self.log.debug("Verifying onScanResult event")
+        self.log.debug(pprint.pformat(event))
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_default_advertisement_high_attenuation(self):
+        """
+        Test that tests a large distance between the advertiser android and the
+        scanner android.
+        Steps:
+        1. Set attenuation to 90 (the highest value for the attenuator).
+        2. Setup the scanning android device
+        3. Setup the advertiser android devices.
+        3. Verify that no onScanResult callbacks were recorded.
+        :return: test_result: bool
+        """
+        test_result = True
+        self.attenuators[0].set_atten(0, 90)
+        scan_droid, scan_event_dispatcher = self.droid, self.ed
+        advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            scan_droid)
+        expected_event_name = "".join(
+            ["BleScan", str(scan_callback), "onScanResults"])
+        advertise_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        advertise_droid.bleSetAdvertiseDataIncludeTxPowerLevel(True)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(advertise_droid))
+        advertise_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        if test_result is False:
+            self.log.debug("Advertising failed.")
+            return test_result
+        scan_droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
+        worker = scan_event_dispatcher.handle_event(
+            self.blescan_verify_onscanresult_event_handler,
+            expected_event_name, ([]), self.default_timeout)
+        try:
+            event_info = scan_event_dispatcher.pop_event(expected_event_name,
+                                                         10)
+            self.log.debug("Unexpectedly found an advertiser: {}".format(
+                pprint.pformat(event_info)))
+            test_result = False
+        except Empty as error:
+            self.log.debug("No events were found as expected.")
+        scan_droid.bleStopBleScan(scan_callback)
+        advertise_droid.bleStopBleAdvertising(advertise_callback)
+        return test_result
diff --git a/acts/tests/google/ble/examples/BleExamplesTest.py b/acts/tests/google/ble/examples/BleExamplesTest.py
new file mode 100644
index 0000000..edf5ee3
--- /dev/null
+++ b/acts/tests/google/ble/examples/BleExamplesTest.py
@@ -0,0 +1,149 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 script shows simple examples of how to get started with bluetooth low energy testing in acts.
+"""
+
+import pprint
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import adv_succ
+from acts.test_utils.bt.bt_test_utils import scan_result
+from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+
+
+class BleExamplesTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+    active_scan_callback_list = []
+    active_adv_callback_list = []
+    scn_droid = None
+    adv_droid = None
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        if self.droid_list[1]['max_advertisements'] == 0:
+            self.tests = ()
+            return
+        self.tests = (
+            "test_bt_toggle",
+        )
+
+    def teardown_test(self):
+        cleanup_scanners_and_advertisers(
+            self.scn_droid, self.scn_ed, self.active_adv_callback_list,
+            self.adv_droid, self.adv_ed, self.active_adv_callback_list)
+        self.active_adv_callback_list = []
+        self.active_scan_callback_list = []
+
+    # An optional function. This overrides the default
+    # on_exception in base_test. If the test throws an
+    # unexpected exception, you can customise it.
+    def on_exception(self, test_name, begin_time):
+        self.log.debug(
+            "Test {} failed. Gathering bugreport and btsnoop logs".
+            format(test_name))
+        self.take_bug_reports(test_name, self.android_devices)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_bt_toggle(self):
+        """
+        Test that simply toggle bluetooth
+        :return:
+        """
+        return reset_bluetooth([self.droid], [self.ed])
+
+    '''
+    Start: Examples of BLE Scanning
+    '''
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_start_ble_scan(self):
+        """Test to demonstrate how to start an LE scan
+
+        Test that shows the steps to start a new ble scan.
+
+        Steps:
+        1. Create a scan filter object.
+        2. Create a scan setting object.
+        3. Create a scan callback object.
+        4. Start an LE scan using the objects created in steps 1-3.
+        5. Find an advertisement with the scanner's event dispatcher.
+
+        Expected Result:
+        A generic advertisement is found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning
+        Priority: 4
+        """
+        filter_list = self.scn_droid.bleGenFilterList()
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        event_name = scan_result.format(scan_callback)
+        try:
+            event = self.scn_ed.pop_event(event_name, self.default_timeout)
+            self.log.info(
+                "Found scan result: {}".format(pprint.pformat(event)))
+        except Exception:
+            self.log.info("Didn't find any scan results.")
+        return True
+
+    '''
+    End: Examples of BLE Scanning
+    '''
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_start_ble_advertise(self):
+        """Test to demonstrate how to start an LE advertisement
+
+        Test that shows the steps to start a new ble scan.
+
+        Steps:
+        1. Create a advertise data object
+        2. Create a advertise settings object.
+        3. Create a advertise callback object.
+        4. Start an LE advertising using the objects created in steps 1-3.
+        5. Find the onSuccess advertisement event.
+
+        Expected Result:
+        Advertisement is successfully advertising.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising
+        Priority: 4
+        """
+        advertise_data = self.adv_droid.bleBuildAdvertiseData()
+        advertise_settings = self.adv_droid.bleBuildAdvertiseSettings()
+        advertise_callback = self.adv_droid.bleGenBleAdvertiseCallback()
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.adv_ed.pop_event(adv_succ.format(advertise_callback))
+        return True
diff --git a/acts/tests/google/ble/filtering/FilteringTest.py b/acts/tests/google/ble/filtering/FilteringTest.py
new file mode 100644
index 0000000..97614d6
--- /dev/null
+++ b/acts/tests/google/ble/filtering/FilteringTest.py
@@ -0,0 +1,672 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 as it
+import pprint
+import time
+
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseTxPower
+from acts.test_utils.bt.BleEnum import JavaInteger
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.bt_test_utils import adv_fail
+from acts.test_utils.bt.bt_test_utils import adv_succ
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import scan_result
+
+
+class FilteringTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 30
+
+    valid_filter_suite = [
+        {
+            'include_tx_power_level': True
+        },
+        {
+            'filter_device_address': True
+        },
+        {
+            'manufacturer_specific_data_id': 1,
+            'manufacturer_specific_data': "1"
+        },
+        {
+            'manufacturer_specific_data_id': 1,
+            'manufacturer_specific_data': "14,0,54,0,0,0,0,0"
+        },
+        {
+            'manufacturer_specific_data_id': 1,
+            'manufacturer_specific_data': "1",
+            'manufacturer_specific_data_mask': "1"
+        },
+        {
+            'service_data_uuid': "0000110A-0000-1000-8000-00805F9B34FB",
+            'service_data': "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,26,17,18,19,"
+                            "20,21,22,23,24"
+        },
+        {
+            'service_data_uuid': "0000110B-0000-1000-8000-00805F9B34FB",
+            'service_data': "13"
+        },
+        {
+            'service_data_uuid': "0000110C-0000-1000-8000-00805F9B34FB",
+            'service_data': "11,14,50"
+        },
+        {
+            'service_data_uuid': "0000110D-0000-1000-8000-00805F9B34FB",
+            'service_data': "16,22,11"
+        },
+        {
+            'service_data_uuid': "0000110E-0000-1000-8000-00805F9B34FB",
+            'service_data': "2,9,54"
+        },
+        {
+            'service_data_uuid': "0000110F-0000-1000-8000-00805F9B34FB",
+            'service_data': "69,11,50"
+        },
+        {
+            'service_data_uuid': "00001101-0000-1000-8000-00805F9B34FB",
+            'service_data': "12,11,21"
+        },
+        {
+            'service_data_uuid': "00001102-0000-1000-8000-00805F9B34FB",
+            'service_data': "12,12,44"
+        },
+        {
+            'service_data_uuid': "00001103-0000-1000-8000-00805F9B34FB",
+            'service_data': "4,54,1"
+        },
+        {
+            'service_data_uuid': "00001104-0000-1000-8000-00805F9B34FB",
+            'service_data': "33,22,44"
+        },
+        {
+            'service_uuid': "00000000-0000-1000-8000-00805f9b34fb",
+            'service_mask': "00000000-0000-1000-8000-00805f9b34fb",
+        },
+        {
+            'service_uuid': "FFFFFFFF-0000-1000-8000-00805f9b34fb",
+            'service_mask': "00000000-0000-1000-8000-00805f9b34fb",
+        },
+        {
+            'service_uuid': "3846D7A0-69C8-11E4-BA00-0002A5D5C51B",
+            'service_mask': "00000000-0000-1000-8000-00805f9b34fb",
+        },
+        {
+            'include_device_name': True
+        },
+    ]
+
+    valid_filter_variants = {
+        'include_tx_power_level': [True, False],
+        'manufacturer_specific_data_id': [1, 2, 65535],
+        'manufacturer_specific_data': ["1", "1,2", "127"],
+        'service_data_uuid': ["00000000-0000-1000-8000-00805f9b34fb"],
+        'service_data': ["1,2,3", "1", "127"],
+        'include_device_name': [False, True],
+    }
+
+    multi_manufacturer_specific_data_suite = {
+        'manufacturer_specific_data_list': [[(1, "1"), (2, "2"),
+                                             (65535, "127")]],
+    }
+
+    settings_in_effect_variants = {
+        "mode": [
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_BALANCED.value,
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value,
+        ],
+        "tx_power_level": [
+            AdvertiseSettingsAdvertiseTxPower.ADVERTISE_TX_POWER_HIGH.value,
+            AdvertiseSettingsAdvertiseTxPower.ADVERTISE_TX_POWER_LOW.value,
+            AdvertiseSettingsAdvertiseTxPower.ADVERTISE_TX_POWER_ULTRA_LOW.value,
+            AdvertiseSettingsAdvertiseTxPower.ADVERTISE_TX_POWER_MEDIUM.value,
+        ],
+        "is_connectable": [True, False],
+        "scan_mode": [ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value,
+                      ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value,
+                      ScanSettingsScanMode.SCAN_MODE_BALANCED.value,
+                      ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value,
+                      ]
+    }
+
+    default_callback = 1
+    default_is_connectable = True
+    default_advertise_mode = 0
+    default_tx_power_level = 2
+
+    def _get_combinations(self, t):
+        varNames = sorted(t)
+        return (
+            [dict(zip(varNames, prod)) for prod
+             in it.product(*(t[varName] for varName in varNames))])
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        if self.droid_list[1]['max_advertisements'] == 0:
+            self.tests = ()
+            return
+        self.log.info(
+            "Scanner device model: {}".format(self.scn_droid.getBuildModel()))
+        self.log.info(
+            "Advertiser device model: {}".format(
+                self.adv_droid.getBuildModel()))
+        self.tests = (
+            "test_valid_filters",
+            "test_valid_filters_opportunistic_scan",
+            "test_default_advertisement",
+            "test_settings_in_effect_suite",
+            "test_filters_suite",
+            "test_filters_suite_opportunistic_scan",
+            "test_non_connectable_advertise_data",
+            # "test_multi_manufacturer_specific_data",
+        )
+
+    # Handler Functions Begin
+    def blescan_verify_onfailure_event_handler(self, event):
+        self.log.debug("Verifying {} event".format(adv_fail))
+        self.log.debug(pprint.pformat(event))
+        return event
+
+    def blescan_verify_onscanresult_event_handler(self, event, filters):
+        test_result = True
+        self.log.debug("Verifying onScanResult event: {}".format(event))
+        callback_type = event['data']['CallbackType']
+        if 'callback_type' in filters.keys():
+            if filters['callback_type'] != callback_type:
+                self.log.error("Expected callback type: {}, Found callback "
+                               "type: {}".format(filters['callback_type'],
+                                                 callback_type))
+            test_result = False
+        elif self.default_callback != callback_type:
+            self.log.error("Expected callback type: {}, Found callback type: "
+                           "{}".format(self.default_callback, callback_type))
+            test_result = False
+        if 'include_device_name' in filters.keys() and filters[
+                'include_device_name'] is not False:
+            if event['data']['Result']['deviceName'] != filters[
+                    'include_device_name']:
+                self.log.error("Expected device name: {}, Found device name: {}"
+                               .format(filters['include_device_name'],
+                                       event['data']['Result']['deviceName']))
+
+                test_result = False
+        elif 'deviceName' in event['data']['Result'].keys():
+            self.log.error(
+                "Device name was found when it wasn't meant to be included.")
+            test_result = False
+        if ('include_tx_power_level' in filters.keys() and filters[
+                'include_tx_power_level'] is not False):
+            if not event['data']['Result']['txPowerLevel']:
+                self.log.error(
+                    "Expected to find tx power level in event but found none.")
+                test_result = False
+        # TODO: (tturney) Need to investigate, possible bug here
+        # elif 'txPowerLevel' in event['data']['Result'].keys():
+        #  self.log.error("Tx power level found when it wasn't meant "
+        # "to be included.")
+        #  test_result = False
+        if not event['data']['Result']['rssi']:
+            self.log.error("Expected rssi in the advertisement, found none.")
+            test_result = False
+        if not event['data']['Result']['timestampNanos']:
+            self.log.error("Expected rssi in the advertisement, found none.")
+            test_result = False
+        return test_result
+
+    def bleadvertise_verify_onsuccess_handler(self, event, settings_in_effect):
+        self.log.debug("Verifying {} event".format(adv_succ))
+        test_result = True
+        if 'is_connectable' in settings_in_effect.keys():
+            if (event['data']['SettingsInEffect']['isConnectable'] !=
+               settings_in_effect['is_connectable']):
+                self.log.error(
+                    "Expected is connectable value: {}, Actual is "
+                    "connectable value:".format(
+                        settings_in_effect['is_connectable'],
+                        event['data']['SettingsInEffect']['isConnectable']))
+                test_result = False
+        elif (event['data']['SettingsInEffect']['isConnectable'] !=
+                self.default_is_connectable):
+            self.log.error(
+                "Default value for isConnectable did not match what was found.")
+            test_result = False
+        if 'mode' in settings_in_effect.keys():
+            if (event['data']['SettingsInEffect']['mode'] !=
+               settings_in_effect['mode']):
+                self.log.error(
+                    "Expected mode value: {}, Actual mode value: {}"
+                    .format(settings_in_effect['mode'],
+                            event['data']['SettingsInEffect']['mode']))
+                test_result = False
+        elif (event['data']['SettingsInEffect']['mode'] !=
+                self.default_advertise_mode):
+            self.log.error(
+                "Default value for filtering mode did not match what was "
+                "found.")
+            test_result = False
+        if 'tx_power_level' in settings_in_effect.keys():
+            if (event['data']['SettingsInEffect']['txPowerLevel'] ==
+               JavaInteger.MIN.value):
+                self.log.error("Expected tx power level was not meant to be: "
+                               "{}".format(JavaInteger.MIN.value))
+                test_result = False
+        elif (event['data']['SettingsInEffect']['txPowerLevel'] !=
+                self.default_tx_power_level):
+            self.log.error("Default value for tx power level did not match what"
+                           " was found.")
+            test_result = False
+        return test_result
+
+    # Handler Functions End
+
+    def _magic(self, params):
+        (filters, settings_in_effect) = params
+        test_result = True
+
+        self.log.debug("Settings in effect: {}".format(
+            pprint.pformat(settings_in_effect)))
+        self.log.debug("Filters:".format(pprint.pformat(filters)))
+        if 'is_connectable' in settings_in_effect.keys():
+            self.log.debug("Setting advertisement is_connectable to {}".format(
+                           settings_in_effect['is_connectable']))
+            self.adv_droid.bleSetAdvertiseSettingsIsConnectable(
+                settings_in_effect['is_connectable'])
+        if 'mode' in settings_in_effect.keys():
+            self.log.debug(
+                "Setting advertisement mode to {}"
+                .format(settings_in_effect['mode']))
+            self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+                settings_in_effect['mode'])
+        if 'tx_power_level' in settings_in_effect.keys():
+            self.log.debug("Setting advertisement tx_power_level to {}".format(
+                           settings_in_effect['tx_power_level']))
+            self.adv_droid.bleSetAdvertiseSettingsTxPowerLevel(
+                settings_in_effect['tx_power_level'])
+        filter_list = self.scn_droid.bleGenFilterList()
+        if ('include_device_name' in filters.keys()
+            and filters['include_device_name'] is not False):
+
+            self.log.debug("Setting advertisement include_device_name to {}"
+                           .format(filters['include_device_name']))
+            self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+            filters[
+                'include_device_name'] = self.adv_droid.bluetoothGetLocalName()
+            self.log.debug("Setting scanner include_device_name to {}".format(
+                           filters['include_device_name']))
+            self.scn_droid.bleSetScanFilterDeviceName(
+                filters['include_device_name'])
+        else:
+            self.log.debug(
+                "Setting advertisement include_device_name to False")
+            self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(False)
+        if ('include_tx_power_level' in filters.keys() and filters[
+                'include_tx_power_level'] is not False):
+            self.log.debug(
+                "Setting advertisement include_tx_power_level to True")
+            self.adv_droid.bleSetAdvertiseDataIncludeTxPowerLevel(True)
+        if 'manufacturer_specific_data_id' in filters.keys():
+            if 'manufacturer_specific_data_mask' in filters.keys():
+                self.adv_droid.bleAddAdvertiseDataManufacturerId(
+                    filters['manufacturer_specific_data_id'],
+                    filters['manufacturer_specific_data'])
+                self.scn_droid.bleSetScanFilterManufacturerData(
+                    filters['manufacturer_specific_data_id'],
+                    filters['manufacturer_specific_data'],
+                    filters['manufacturer_specific_data_mask'])
+            else:
+                self.adv_droid.bleAddAdvertiseDataManufacturerId(
+                    filters['manufacturer_specific_data_id'],
+                    filters['manufacturer_specific_data'])
+                self.scn_droid.bleSetScanFilterManufacturerData(
+                    filters['manufacturer_specific_data_id'],
+                    filters['manufacturer_specific_data'])
+        if 'service_data' in filters.keys():
+            self.adv_droid.bleAddAdvertiseDataServiceData(
+                filters['service_data_uuid'],
+                filters['service_data'])
+            self.scn_droid.bleSetScanFilterServiceData(
+                filters['service_data_uuid'],
+                filters['service_data'])
+        if 'manufacturer_specific_data_list' in filters.keys():
+            for pair in filters['manufacturer_specific_data_list']:
+                (manu_id, manu_data) = pair
+                self.adv_droid.bleAddAdvertiseDataManufacturerId(
+                    manu_id, manu_data)
+        if 'service_mask' in filters.keys():
+            self.scn_droid.bleSetScanFilterServiceUuid(
+                filters['service_uuid'].upper(),
+                filters['service_mask'])
+            self.adv_droid.bleSetAdvertiseDataSetServiceUuids(
+                [filters['service_uuid'].upper()])
+        elif 'service_uuid' in filters.keys():
+            self.scn_droid.bleSetScanFilterServiceUuid(filters['service_uuid'])
+            self.adv_droid.bleSetAdvertiseDataSetServiceUuids(
+                [filters['service_uuid']])
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        if ('scan_mode' in settings_in_effect
+            and settings_in_effect['scan_mode']
+           != ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value):
+            self.scn_droid.bleSetScanSettingsScanMode(
+                settings_in_effect['scan_mode'])
+        else:
+            self.scn_droid.bleSetScanSettingsScanMode(
+                ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        opportunistic = False
+        scan_settings2, scan_callback2 = None, None
+        if ('scan_mode' in settings_in_effect and
+                settings_in_effect['scan_mode'] ==
+                    ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value):
+            opportunistic = True
+            scan_settings2 = self.scn_droid.bleBuildScanSetting()
+            scan_callback2 = self.scn_droid.bleGenScanCallback()
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings2, scan_callback2)
+            self.scn_droid.bleSetScanSettingsScanMode(
+                ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        expected_advertise_event_name = adv_succ.format(advertise_callback)
+        self.log.debug(expected_advertise_event_name)
+        advertise_worker = self.adv_ed.handle_event(
+            self.bleadvertise_verify_onsuccess_handler,
+            expected_advertise_event_name, ([settings_in_effect]),
+            self.default_timeout)
+        try:
+            test_result = advertise_worker.result(self.default_timeout)
+        except Empty as error:
+            self.log.error("Test failed with Empty error: {}".format(error))
+            return False
+        expected_scan_event_name = scan_result.format(scan_callback)
+        worker = self.scn_ed.handle_event(
+            self.blescan_verify_onscanresult_event_handler,
+            expected_scan_event_name, ([filters]), self.default_timeout)
+        try:
+            finished = False
+            start_time = time.time()
+            while (time.time() < start_time + self.default_timeout and not
+                finished):
+
+                test_result = worker.result(self.default_timeout)
+                if test_result:
+                    finished = True
+        except Empty as error:
+            test_result = False
+            self.log.error("No scan result found: {}".format(error))
+        if opportunistic:
+            expected_scan_event_name = scan_result.format(scan_callback2)
+            worker = self.scn_ed.handle_event(
+                self.blescan_verify_onscanresult_event_handler,
+                expected_scan_event_name, ([filters]), self.default_timeout)
+            try:
+                worker.result(self.default_timeout)
+            except Empty:
+                self.log.error("Failure to find event on opportunistic scan.")
+            self.scn_droid.bleStopBleScan(scan_callback2)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        self.scn_droid.bleStopBleScan(scan_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_default_advertisement(self):
+        """Test a default advertisement.
+
+        Test that a default advertisement is found and matches corresponding
+        settings.
+
+        Steps:
+        1. Create a advertise data object
+        2. Create a advertise settings object.
+        3. Create a advertise callback object.
+        4. Start an LE advertising using the objects created in steps 1-3.
+        5. Find the onSuccess advertisement event.
+
+        Expected Result:
+        Advertisement is successfully advertising.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 2
+        """
+        filters = {}
+        settings_in_effect = {}
+        params = (filters, settings_in_effect)
+        return self._magic(params)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_settings_in_effect_suite(self):
+        """Test combinations of settings with scanning and advertising.
+
+        Test combinations of valid advertising modes, tx power, is connectable,
+        and scan modes.
+
+        Steps:
+        1. Generate testcases of the combination of settings_in_effect_variants
+        dictionary. This involves setting scan settings and advertising
+        settings.
+
+        Expected Result:
+        Scan filters match advertising settings and advertisements are found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 1
+        """
+        settings = self._get_combinations(
+            self.settings_in_effect_variants)
+        filters = [{"include_device_name": True}]
+        params = list(it.product(filters, settings))
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="settings_in_effect_suite")
+        if failed:
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_filters_suite(self):
+        """Test combinations of settings with scanning and advertising.
+
+        Test combinations of valid advertisement data and scan settings.
+
+        Steps:
+        1. Generate testcases of the combination of valid_filter_variants and
+        settings dictionaries. This involves setting scan settings and
+        advertising settings.
+
+        Expected Result:
+        Scan filters match advertising settings and advertisements are found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 1
+        """
+        valid_filter_suit = self._get_combinations(self.valid_filter_variants)
+        settings = [
+            {'mode':AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value}]
+        params = list(it.product(valid_filter_suit, settings))
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="filters_suite")
+        if failed:
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_filters_suite_opportunistic_scan(self):
+        """Test combinations of settings with opportunistic scanning.
+
+        Test combinations of valid advertisement data and scan settings. This
+        emphasises scan mode opportunistic.
+
+        Steps:
+        1. Generate testcases of the combination of valid_filter_suite and
+        settings dictionaries. This involves setting scan settings and
+        advertising settings.
+
+        Expected Result:
+        Scan filters match advertising settings and advertisements are found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, Opportunistic Scan
+        Priority: 1
+        """
+        reset_bluetooth(self.droids, self.eds)
+        valid_filter_suit = self._get_combinations(self.valid_filter_variants)
+        settings = [
+            {'mode': AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value,
+             'scan_mode': ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value}]
+        params = list(it.product(valid_filter_suit, settings))
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="filters_suite")
+        if failed:
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_valid_filters(self):
+        """Test combinations of settings with scanning and advertising.
+
+        Test combinations of valid advertisement data and scan settings.
+
+        Steps:
+        1. Generate testcases of the combination of valid_filters and
+        settings dictionaries. This involves setting scan settings and
+        advertising settings.
+
+        Expected Result:
+        Scan filters match advertising settings and advertisements are found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 1
+        """
+        reset_bluetooth(self.droids, self.eds)
+        settings = [
+            {'mode': AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value}]
+        params = list(it.product(self.valid_filter_suite, settings))
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="valid_filters")
+        if failed:
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_valid_filters_opportunistic_scan(self):
+        """Test combinations of settings with opportunistic scanning.
+
+        Test combinations of valid advertisement data and scan settings. This
+        emphasises scan mode opportunistic.
+
+        Steps:
+        1. Generate testcases of the combination of valid_filter_suite and
+        settings dictionaries. This involves setting scan settings and
+        advertising settings.
+
+        Expected Result:
+        Scan filters match advertising settings and advertisements are found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, Opportunistic Scan
+        Priority: 1
+        """
+        settings = [
+            {'mode': AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value,
+             'scan_mode': ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value}]
+        params = list(it.product(self.valid_filter_suite, settings))
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="valid_filters")
+        if failed:
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_multi_manufacturer_specific_data(self):
+
+        settings = [
+            {'mode': AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value}]
+        multi = self._get_combinations(
+            self.multi_manufacturer_specific_data_suite)
+        params = list(it.product(multi, settings))
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="multi_manu_data")
+        if failed:
+            return True
+        return False
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_non_connectable_advertise_data(self):
+        """Test non connectable advertisement data.
+
+        Non-connectable advertisement data does not include the AD flags in
+        the advertisement giving back more data to the overall advertisement
+        data size.
+
+        Steps:
+        1. Create a large advertisement data object.
+        2. Set isConnectable to false.
+        3. Build advertising objects.
+        4. Start scanning.
+        5. Start advertising.
+        6. Find advertisement and verify data.
+
+        Expected Result:
+        Scan filters match advertising settings and advertisements are found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 1
+        """
+        settings = {'is_connectable': False}
+        filters = {
+            'service_data_uuid': "0000110A-0000-1000-8000-00805F9B34FB",
+            'service_data': "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,26,17,18,19,"
+                            "20,21,22,23,24,25,26,27",
+        }
+        params = (filters, settings)
+        return self._magic(params)
diff --git a/acts/tests/google/ble/filtering/UniqueFilteringTest.py b/acts/tests/google/ble/filtering/UniqueFilteringTest.py
new file mode 100644
index 0000000..6f09d97
--- /dev/null
+++ b/acts/tests/google/ble/filtering/UniqueFilteringTest.py
@@ -0,0 +1,490 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 test script exercises different filters and outcomes not exercised in
+FilteringTest.
+"""
+
+import concurrent
+import pprint
+import time
+
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import batch_scan_result
+from acts.test_utils.bt.bt_test_utils import scan_result
+
+
+class UniqueFilteringTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        if self.droid_list[1]['max_advertisements'] == 0:
+            self.tests = ()
+            return
+        self.tests = (
+            "test_scan_flush_pending_scan_results",
+            "test_scan_non_existent_name_filter",
+            "test_scan_advertisement_with_device_service_uuid_filter_expect_no_events",
+            "test_scan_filter_device_address",
+        )
+        if self.droid_list[1]['max_advertisements'] > 1:
+            self.tests = self.tests + \
+                ("test_scan_filtering_multiple_advertisements_manufacturer_data",
+                 )
+        if self.droid_list[0]['batch_scan_supported']:
+            self.tests = self.tests + (
+                "test_scan_flush_results_without_on_batch_scan_results_triggered",
+                "test_scan_trigger_on_batch_scan_results",)
+
+    def blescan_verify_onfailure_event_handler(self, event):
+        self.log.debug("Verifying onFailure event")
+        self.log.debug(pprint.pformat(event))
+        return event
+
+    def blescan_verify_onscanresult_event_handler(self, event,
+                                                  expected_callbacktype=None,
+                                                  system_time_nanos=None):
+        test_result = True
+        self.log.debug("Verifying onScanResult event")
+        self.log.debug(pprint.pformat(event))
+        callbacktype = event['data']['CallbackType']
+        if callbacktype != expected_callbacktype:
+            self.log.debug("Expected callback type: {}, Found callback type: {}".format(
+                expected_callbacktype, callbacktype))
+            test_result = False
+        return test_result
+
+    def blescan_get_mac_address_event_handler(self, event):
+        return event['data']['Result']['deviceInfo']['address']
+
+    def blescan_verify_onbatchscanresult_event_handler(self, event,
+                                                       system_time_nanos=None,
+                                                       report_delay_nanos=None):
+        test_result = True
+        self.log.debug("Verifying onBatchScanResult event")
+        self.log.debug(pprint.pformat(event))
+        for result in event['data']['Results']:
+            timestamp_nanos = result['timestampNanos']
+            length_of_time = timestamp_nanos - system_time_nanos
+            self.log.debug("Difference in time in between scan start and "
+                           "onBatchScanResult: {}".format(length_of_time))
+            buffer = 1000000000  # 1 second
+            if length_of_time > (report_delay_nanos + buffer):
+                self.log.debug(
+                    "Difference was greater than the allowable difference.")
+                test_result = False
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_flush_pending_scan_results(self):
+        """Test LE scan api flush pending results.
+
+        Test that flush pending scan results doesn't affect onScanResults from
+        triggering.
+
+        Steps:
+        1. Setup the scanning android device.
+        2. Setup the advertiser android devices.
+        3. Trigger bluetoothFlushPendingScanResults on the scanning droid.
+        4. Verify that only one onScanResults callback was triggered.
+
+        Expected Result:
+        After flushing pending scan results, make sure only one onScanResult
+        callback was triggered.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 1
+        """
+        test_result = True
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        expected_event_name = scan_result.format(scan_callback)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.scn_droid.bleFlushPendingScanResults(scan_callback)
+        worker = self.scn_ed.handle_event(
+            self.blescan_verify_onscanresult_event_handler,
+            expected_event_name, ([1]), self.default_timeout)
+        try:
+            self.log.debug(worker.result(self.default_timeout))
+        except Empty as error:
+            test_result = False
+            self.log.debug("Test failed with Empty error: {}".format(error))
+        except concurrent.futures._base.TimeoutError as error:
+            test_result = False
+            self.log.debug("Test failed with TimeoutError: {}".format(error))
+        self.scn_droid.bleStopBleScan(scan_callback)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_trigger_on_batch_scan_results(self):
+        """Test triggering batch scan results.
+
+        Test that triggers onBatchScanResults and verifies the time to trigger
+        within one second leeway.
+
+        Steps:
+        1. Setup the scanning android device with report delay seconds set to
+        5000.
+        2. Setup the advertiser android devices.
+        3. Verify that only one onBatchScanResult callback was triggered.
+        4. Compare the system time that the scan was started with the elapsed
+        time that is in the callback.
+
+        Expected Result:
+        The scan event dispatcher should find an onBatchScanResult event.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, Batch Scanning
+        Priority: 2
+        """
+        test_result = True
+        self.scn_droid.bleSetScanSettingsReportDelayMillis(5000)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        expected_event_name = batch_scan_result.format(scan_callback)
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.adv_droid.bleSetAdvertiseDataIncludeTxPowerLevel(True)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        system_time_nanos = self.scn_droid.getSystemElapsedRealtimeNanos()
+        self.log.debug("Current system time: {}".format(system_time_nanos))
+        worker = self.scn_ed.handle_event(
+            self.blescan_verify_onbatchscanresult_event_handler,
+            expected_event_name, ([system_time_nanos, 5000000000]),
+            self.default_timeout)
+        try:
+            self.log.debug(worker.result(self.default_timeout))
+        except Empty as error:
+            test_result = False
+            self.log.debug("Test failed with: {}".format(error))
+        except concurrent.futures._base.TimeoutError as error:
+            test_result = False
+            self.log.debug("Test failed with: {}".format(error))
+        self.scn_droid.bleStopBleScan(scan_callback)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_flush_results_without_on_batch_scan_results_triggered(self):
+        """Test that doesn't expect a batch scan result.
+
+        Test flush pending scan results with a report delay seconds set to 0.
+        No onBatchScanResults callback should be triggered.
+
+        Steps:
+        1. Setup the scanning android device with report delay seconds set to 0
+        (or just use default).
+        2. Setup the advertiser android devices.
+
+        Expected Result:
+        Verify that no onBatchScanResults were triggered.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, Batch Scanning
+        Priority: 2
+        """
+        test_result = True
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        expected_event_name = batch_scan_result.format(scan_callback)
+        advertise_callback, advertise_data, advertise_settings =(
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        worker = self.scn_ed.handle_event(
+            self.blescan_verify_onbatchscanresult_event_handler,
+            expected_event_name, ([]), self.default_timeout)
+        self.scn_droid.bleFlushPendingScanResults(scan_callback)
+        try:
+            event_info = self.scn_ed.pop_event(expected_event_name,
+                                               10)
+            self.log.debug(
+                "Unexpectedly found an advertiser: {}".format(event_info))
+            test_result = False
+        except Empty:
+            self.log.debug("No {} events were found as expected.".format(
+                batch_scan_result))
+        self.scn_droid.bleStopBleScan(scan_callback)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_non_existent_name_filter(self):
+        """Test non-existent name filter.
+
+        Test scan filter on non-existent device name.
+
+        Steps:
+        1. Setup the scanning android device with scan filter for device name
+        set to an unexpected value.
+        2. Setup the advertiser android devices.
+        3. Verify that no onScanResults were triggered.
+
+        Expected Result:
+        No advertisements were found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 2
+        """
+        test_result = True
+        filter_name = "{}_probably_wont_find".format(
+            self.adv_droid.bluetoothGetLocalName())
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.scn_droid.bleSetScanFilterDeviceName(filter_name)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        expected_event_name = scan_result.format(scan_callback)
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.adv_droid.bleSetAdvertiseDataIncludeTxPowerLevel(True)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        try:
+            event_info = self.scn_ed.pop_event(
+                expected_event_name, self.default_timeout)
+            self.log.error(
+                "Unexpectedly found an advertiser: {}".format(event_info))
+            test_result = False
+        except Empty:
+            self.log.debug("No events were found as expected.")
+        self.scn_droid.bleStopBleScan(scan_callback)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_advertisement_with_device_service_uuid_filter_expect_no_events(
+            self):
+        """Test scan filtering against an advertisement with no data.
+
+        Test that exercises a service uuid filter on the scanner but no server
+        uuid added to the advertisement.
+
+        Steps:
+        1. Setup the scanning android device with scan filter including a
+        service uuid and mask.
+        2. Setup the advertiser android devices.
+        3. Verify that no onScanResults were triggered.
+
+        Expected Result:
+        Verify no advertisements found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 1
+        """
+        test_result = True
+        service_uuid = "00000000-0000-1000-8000-00805F9B34FB"
+        service_mask = "00000000-0000-1000-8000-00805F9B34FA"
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.scn_droid.bleSetScanFilterServiceUuid(service_uuid, service_mask)
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        expected_event_name = scan_result.format(scan_callback)
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.adv_droid.bleSetAdvertiseDataIncludeTxPowerLevel(True)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        worker = self.scn_ed.handle_event(
+            self.blescan_verify_onscanresult_event_handler,
+            expected_event_name, ([1]), self.default_timeout)
+        try:
+            event_info = self.scn_ed.pop_event(expected_event_name,
+                                               self.default_timeout)
+            self.log.error(
+                "Unexpectedly found an advertiser:".format(event_info))
+            test_result = False
+        except Empty as error:
+            self.log.debug("No events were found as expected.")
+        self.scn_droid.bleStopBleScan(scan_callback)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filtering_multiple_advertisements_manufacturer_data(self):
+        """Test scan filtering against multiple varying advertisements.
+
+        Test scan filtering against multiple varying advertisements. The first
+        advertisement will have partial manufacturer data that matches the
+        the full manufacturer data in the second advertisement.
+
+        Steps:
+        1. Setup up an advertisement with manufacturer data "1,2,3".
+        2. Setup a second advertisement with manufacturer data
+        "1,2,3,4,5,6,7,8".
+        3. Start advertising on each advertisement.
+        4. Create a scan filter that includes manufacturer data "1,2,3".
+
+        Expected Result:
+        TBD. Right now Shamu finds only the first advertisement with
+        manufacturer data "1,2,3".
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 2
+        """
+        # TODO: (tturney) waiting on input from team on how we handle this
+        # situation.
+        test_result = True
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        self.adv_droid.bleAddAdvertiseDataManufacturerId(117, "1,2,3")
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        self.adv_droid.bleAddAdvertiseDataManufacturerId(
+            117, "1,2,3,4,5,6,7,8")
+        advertise_callback1, advertise_data1, advertise_settings1 = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback1, advertise_data1,
+            advertise_settings1)
+
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleSetScanFilterManufacturerData(
+            117, "1,2,3", "127,127,127")
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_filter_device_address(self):
+        """Test scan filtering of a device address.
+
+        This test will have to create two scanning instances. The first will
+        have no filters and will find the generic advertisement's mac address.
+        The second will have a filter of the found mac address.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a new scanner with scan filter with a mac address filter of
+        what was found in step 3.
+        6. Start the scanner.
+
+        Expected Result:
+        Verify that the advertisement was found in the second scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning
+        Priority: 1
+        """
+        test_result = True
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        expected_event_name = scan_result.format(scan_callback)
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(self.adv_droid))
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        event_info = self.scn_ed.pop_event(
+            expected_event_name, self.default_timeout)
+        mac_address = event_info['data']['Result']['deviceInfo']['address']
+        self.log.info(
+            "Filter advertisement with address {}".format(mac_address))
+        self.scn_droid.bleStopBleScan(scan_callback)
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.scn_droid.bleSetScanFilterDeviceAddress(mac_address)
+        filter_list2, scan_settings2, scan_callback2 = (
+            generate_ble_scan_objects(self.scn_droid))
+
+        self.scn_droid.bleBuildScanFilter(filter_list2)
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        expected_event_name = scan_result.format(scan_callback2)
+        found_event = self.scn_ed.pop_event(
+            expected_event_name, self.default_timeout)
+        if (found_event['data']['Result']['deviceInfo']['address'] !=
+                mac_address):
+            test_result = False
+        self.scn_droid.bleStopBleScan(scan_callback2)
+        self.adv_droid.bleStopBleAdvertising(advertise_callback)
+        return test_result
diff --git a/acts/tests/google/ble/gatt/GattConnectTest.py b/acts/tests/google/ble/gatt/GattConnectTest.py
new file mode 100644
index 0000000..4e9c4c6
--- /dev/null
+++ b/acts/tests/google/ble/gatt/GattConnectTest.py
@@ -0,0 +1,969 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 test script exercises different GATT connection tests.
+"""
+
+import pprint
+from queue import Empty
+import time
+from contextlib import suppress
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import BluetoothGattCharacteristic
+from acts.test_utils.bt.BleEnum import BluetoothGattDescriptor
+from acts.test_utils.bt.BleEnum import BluetoothGattService
+from acts.test_utils.bt.BleEnum import BluetoothMtuSize
+from acts.test_utils.bt.bt_test_utils import characteristic_write_request
+from acts.test_utils.bt.bt_test_utils import characteristic_write
+from acts.test_utils.bt.bt_test_utils import descriptor_write
+from acts.test_utils.bt.bt_test_utils import descriptor_write_request
+from acts.test_utils.bt.bt_test_utils import disconnect_gatt_connection
+from acts.test_utils.bt.bt_test_utils import gatt_services_discovered
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
+from acts.test_utils.bt.bt_test_utils import mtu_changed
+from acts.test_utils.bt.bt_test_utils import orchestrate_gatt_connection
+from acts.test_utils.bt.bt_test_utils import read_remote_rssi
+from acts.test_utils.bt.bt_test_utils import service_added
+from acts.test_utils.bt.bt_test_utils import setup_gatt_characteristics
+from acts.test_utils.bt.bt_test_utils import setup_gatt_connection
+from acts.test_utils.bt.bt_test_utils import setup_gatt_descriptors
+from acts.test_utils.bt.bt_test_utils import log_energy_info
+
+
+class GattConnectTest(BluetoothBaseTest):
+    tests = None
+    adv_instances = []
+    default_timeout = 10
+    default_discovery_timeout = 3
+    droid_list = ()
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.cen_droid, self.cen_ed = self.droids[0], self.eds[0]
+        self.per_droid, self.per_ed = self.droids[1], self.eds[1]
+        if self.droid_list[1]['max_advertisements'] == 0:
+            self.tests = ()
+            return
+        self.tests = (
+            "test_gatt_connect",
+            "test_gatt_request_min_mtu",
+            "test_gatt_request_max_mtu",
+            "test_gatt_request_out_of_bounds_mtu",
+            "test_gatt_connect_trigger_on_read_rssi",
+            "test_gatt_connect_trigger_on_services_discovered",
+            "test_gatt_connect_trigger_on_services_discovered_iterate_attributes",
+            "test_gatt_connect_with_service_uuid_variations",
+            "test_gatt_connect_in_quick_succession",
+            "test_write_descriptor_stress",
+            "test_write_characteristic_stress",
+        )
+
+    def teardown_test(self):
+        for adv in self.adv_instances:
+            self.per_droid.bleStopBleAdvertising(adv)
+        self.log.debug(log_energy_info(self.droids, "End"))
+        return True
+
+    def _setup_characteristics_and_descriptors(self, droid):
+        characteristic_input = [
+            {
+                'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+                'property': BluetoothGattCharacteristic.PROPERTY_WRITE.value |
+                        BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE.value,
+                'permission': BluetoothGattCharacteristic.PROPERTY_WRITE.value
+            },
+            {
+                'uuid': "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
+                'property': BluetoothGattCharacteristic.PROPERTY_NOTIFY.value |
+                        BluetoothGattCharacteristic.PROPERTY_READ.value,
+                'permission': BluetoothGattCharacteristic.PERMISSION_READ.value
+            },
+            {
+                'uuid': "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
+                'property': BluetoothGattCharacteristic.PROPERTY_NOTIFY.value |
+                        BluetoothGattCharacteristic.PROPERTY_READ.value,
+                'permission': BluetoothGattCharacteristic.PERMISSION_READ.value
+            },
+        ]
+        descriptor_input = [
+            {
+                'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+                'property': BluetoothGattDescriptor.PERMISSION_READ.value |
+                        BluetoothGattDescriptor.PERMISSION_WRITE.value,
+            },
+            {
+                'uuid': "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
+                'property': BluetoothGattDescriptor.PERMISSION_READ.value |
+                        BluetoothGattCharacteristic.PERMISSION_WRITE.value,
+            }
+        ]
+        characteristic_list = setup_gatt_characteristics(
+            droid, characteristic_input)
+        descriptor_list = setup_gatt_descriptors(droid, descriptor_input)
+        return characteristic_list, descriptor_list
+
+    def _orchestrate_gatt_disconnection(self, bluetooth_gatt, gatt_callback):
+        self.log.info("Disconnecting from peripheral device.")
+        test_result = disconnect_gatt_connection(
+            self.cen_droid, self.cen_ed, bluetooth_gatt, gatt_callback)
+        if not test_result:
+            self.log.info("Failed to disconnect from peripheral device.")
+            return False
+        return True
+
+    def _iterate_attributes(self, discovered_services_index):
+        services_count = self.cen_droid.gattClientGetDiscoveredServicesCount(
+            discovered_services_index)
+        for i in range(services_count):
+            service = self.cen_droid.gattClientGetDiscoveredServiceUuid(
+                discovered_services_index, i)
+            self.log.info("Discovered service uuid {}".format(service))
+            characteristic_uuids = (
+                self.cen_droid.gattClientGetDiscoveredCharacteristicUuids(
+                    discovered_services_index, i))
+            for characteristic in characteristic_uuids:
+                self.log.info(
+                    "Discovered characteristic uuid {}".format(characteristic))
+                descriptor_uuids = (
+                    self.cen_droid.gattClientGetDiscoveredDescriptorUuids(
+                        discovered_services_index, i, characteristic))
+                for descriptor in descriptor_uuids:
+                    self.log.info(
+                        "Discovered descriptor uuid {}".format(descriptor))
+
+    def _find_service_added_event(self, gatt_server_callback, uuid):
+        event = self.per_ed.pop_event(
+            service_added.format(gatt_server_callback),
+            self.default_timeout)
+        if event['data']['serviceUuid'].lower() != uuid.lower():
+            self.log.info(
+                "Uuid mismatch. Found: {}, Expected {}.".format(
+                    event['data']['serviceUuid'],
+                    uuid))
+            return False
+        return True
+
+    def _setup_multiple_services(self):
+        gatt_server_callback = (
+            self.per_droid.gattServerCreateGattServerCallback())
+        gatt_server = self.per_droid.gattServerOpenGattServer(
+            gatt_server_callback)
+        characteristic_list, descriptor_list = (
+            self._setup_characteristics_and_descriptors(self.per_droid))
+        self.per_droid.gattServerCharacteristicAddDescriptor(
+            characteristic_list[1], descriptor_list[0])
+        self.per_droid.gattServerCharacteristicAddDescriptor(
+            characteristic_list[2], descriptor_list[1])
+        gatt_service = self.per_droid.gattServerCreateService(
+            "00000000-0000-1000-8000-00805f9b34fb",
+            BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+        gatt_service2 = self.per_droid.gattServerCreateService(
+            "FFFFFFFF-0000-1000-8000-00805f9b34fb",
+            BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+        gatt_service3 = self.per_droid.gattServerCreateService(
+            "3846D7A0-69C8-11E4-BA00-0002A5D5C51B",
+            BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+        for characteristic in characteristic_list:
+            self.per_droid.gattServerAddCharacteristicToService(gatt_service,
+                                                                characteristic)
+        self.per_droid.gattServerAddService(gatt_server, gatt_service)
+        result = self._find_service_added_event(
+            gatt_server_callback, "00000000-0000-1000-8000-00805f9b34fb")
+        if not result:
+            return False
+        for characteristic in characteristic_list:
+            self.per_droid.gattServerAddCharacteristicToService(gatt_service2,
+                                                                characteristic)
+        self.per_droid.gattServerAddService(gatt_server, gatt_service2)
+        result = self._find_service_added_event(
+            gatt_server_callback, "FFFFFFFF-0000-1000-8000-00805f9b34fb")
+        if not result:
+            return False
+        for characteristic in characteristic_list:
+            self.per_droid.gattServerAddCharacteristicToService(gatt_service3,
+                                                                characteristic)
+        self.per_droid.gattServerAddService(gatt_server, gatt_service3)
+        result = self._find_service_added_event(
+            gatt_server_callback, "3846D7A0-69C8-11E4-BA00-0002A5D5C51B")
+        if not result:
+            return False, False
+        return gatt_server_callback, gatt_server
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_connect(self):
+        """Test GATT connection over LE.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT
+        Priority: 0
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_request_min_mtu(self):
+        """Test GATT connection over LE and exercise MTU sizes.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client. Request an MTU size that matches the correct minimum size.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner (client) request MTU size change to the
+        minimum value.
+        7. Find the MTU changed event on the client.
+        8. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and the MTU value found
+        matches the expected MTU value.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, MTU
+        Priority: 0
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        self.cen_droid.gattClientRequestMtu(bluetooth_gatt,
+                                            BluetoothMtuSize.MIN.value)
+        mtu_event = self.cen_ed.pop_event(mtu_changed.format(bluetooth_gatt))
+        if mtu_event['data']['MTU'] != BluetoothMtuSize.MIN.value:
+            return False
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_request_max_mtu(self):
+        """Test GATT connection over LE and exercise MTU sizes.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client. Request an MTU size that matches the correct maximum size.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner (client) request MTU size change to the
+        maximum value.
+        7. Find the MTU changed event on the client.
+        8. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and the MTU value found
+        matches the expected MTU value.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, MTU
+        Priority: 0
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        self.cen_droid.gattClientRequestMtu(bluetooth_gatt,
+            BluetoothMtuSize.MAX.value)
+        mtu_event = self.cen_ed.pop_event(mtu_changed.format(bluetooth_gatt))
+        if mtu_event['data']['MTU'] != BluetoothMtuSize.MAX.value:
+            return False
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_request_out_of_bounds_mtu(self):
+        """Test GATT connection over LE and exercise an out of bound MTU size.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client. Request an MTU size that is the MIN value minus 1.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner (client) request MTU size change to the
+        minimum value minus one.
+        7. Find the MTU changed event on the client.
+        8. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that an MTU changed event was not discovered and that
+        it didn't cause an exception when requesting an out of bounds
+        MTU.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, MTU
+        Priority: 0
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        self.cen_droid.gattClientRequestMtu(bluetooth_gatt,
+            BluetoothMtuSize.MIN.value - 1)
+        try:
+            self.cen_ed.pop_event(mtu_changed.format(bluetooth_gatt),
+                    self.default_timeout)
+            self.log.error("Found {} event when it wasn't expected".
+                    format(mtu_changed.format(bluetooth_gatt)))
+            return False
+        except Exception:
+            self.log.debug("Successfully didn't find {} event"
+                           .format(mtu_changed.format(bluetooth_gatt)))
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_connect_trigger_on_read_rssi(self):
+        """Test GATT connection over LE read RSSI.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client then read the RSSI.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner, request to read the RSSI of the advertiser.
+        7. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully. Verify that the RSSI was ready correctly.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, RSSI
+        Priority: 1
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        if self.cen_droid.gattClientReadRSSI(bluetooth_gatt):
+            self.cen_ed.pop_event(
+                read_remote_rssi.format(gatt_callback), self.default_timeout)
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_connect_trigger_on_services_discovered(self):
+        """Test GATT connection and discover services of peripheral.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client the discover all services from the connected device.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner (central device), discover services.
+        7. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully. Verify that the service were discovered.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, Services
+        Priority: 1
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_connect_trigger_on_services_discovered_iterate_attributes(self):
+        """Test GATT connection and iterate peripherals attributes.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client and iterate over all the characteristics and descriptors of the
+        discovered services.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner (central device), discover services.
+        7. Iterate over all the characteristics and descriptors of the
+        discovered features.
+        8. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully. Verify that the services, characteristics, and descriptors
+        were discovered.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, Services
+        Characteristics, Descriptors
+        Priority: 1
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+            self._iterate_attributes(discovered_services_index)
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_connect_with_service_uuid_variations(self):
+        """Test GATT connection with multiple service uuids.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client with multiple service uuid variations.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner (central device), discover services.
+        7. Verify that all the service uuid variations are found.
+        8. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully. Verify that the service uuid variations are found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, Services
+        Priority: 2
+        """
+        gatt_server_callback, gatt_server = self._setup_multiple_services()
+        if not gatt_server_callback or not gatt_server:
+            return False
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+            self._iterate_attributes(discovered_services_index)
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_connect_in_quick_succession(self):
+        """Test GATT connections multiple times.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client with multiple iterations.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. Disconnect the GATT connection.
+        7. Repeat steps 5 and 6 twenty times.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully twenty times.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, Stress
+        Priority: 1
+        """
+        mac_address, adv_callback = get_mac_address_of_generic_advertisement(
+            self.cen_droid,
+            self.cen_ed,
+            self.per_droid,
+            self.per_ed)
+        autoconnect = False
+        for i in range(20):
+            test_result, bluetooth_gatt, gatt_callback = setup_gatt_connection(
+                self.cen_droid, self.cen_ed, mac_address, autoconnect)
+            if not test_result:
+                self.log.info("Could not connect to peripheral.")
+                return False
+            test_result = self._orchestrate_gatt_disconnection(
+                bluetooth_gatt, gatt_callback)
+            # Temporary fix
+            time.sleep(3)
+            if not test_result:
+                self.log.info("Failed to disconnect from peripheral device.")
+                return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_write_descriptor_stress(self):
+        """Test GATT connection writing and reading descriptors.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client with multiple service uuid variations.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. Discover services.
+        7. Write data to the descriptors of each characteristic 100 times.
+        8. Read the data sent to the descriptors.
+        9. Disconnect the GATT connection.
+
+        Expected Result:
+        Each descriptor in each characteristic is written and read 100 times.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, Stress,
+        Characteristics, Descriptors
+        Priority: 1
+        """
+        gatt_server_callback, gatt_server = self._setup_multiple_services()
+        if not gatt_server_callback or not gatt_server:
+            return False
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+        else:
+            self.log.info("Failed to discover services.")
+            return False
+        services_count = self.cen_droid.gattClientGetDiscoveredServicesCount(
+            discovered_services_index)
+
+        connected_device_list = self.per_droid.gattServerGetConnectedDevices(
+            gatt_server)
+        if len(connected_device_list) == 0:
+            self.log.info("No devices connected from peripheral.")
+            return False
+        bt_device_id = 0
+        status = 1
+        offset = 1
+        test_value = "1,2,3,4,5,6,7"
+        test_value_return = "1,2,3"
+        for i in range(services_count):
+            characteristic_uuids = (
+                self.cen_droid.gattClientGetDiscoveredCharacteristicUuids(
+                    discovered_services_index, i))
+            for characteristic in characteristic_uuids:
+                descriptor_uuids = (
+                    self.cen_droid.gattClientGetDiscoveredDescriptorUuids(
+                        discovered_services_index, i, characteristic))
+                for x in range(100):
+                    for descriptor in descriptor_uuids:
+                        self.cen_droid.gattClientDescriptorSetValue(
+                            bluetooth_gatt, discovered_services_index, i,
+                            characteristic, descriptor, test_value)
+                        self.cen_droid.gattClientWriteDescriptor(
+                            bluetooth_gatt, discovered_services_index, i,
+                            characteristic, descriptor)
+                        event = self.per_ed.pop_event(
+                            descriptor_write_request.format(
+                                gatt_server_callback), self.default_timeout)
+                        self.log.info("{} event found: {}".format(
+                                      descriptor_write_request.format(
+                                          gatt_callback), event))
+                        request_id = event['data']['requestId']
+                        found_value = event['data']['value']
+                        if found_value != test_value:
+                            self.log.info("Values didn't match. Found: {}, "
+                                          "Expected: {}".format(found_value,
+                                                                test_value))
+                            return False
+                        self.per_droid.gattServerSendResponse(
+                            gatt_server, bt_device_id, request_id, status,
+                            offset, test_value_return)
+                        self.log.info("onDescriptorWrite event found: {}"
+                                      .format(
+                            self.cen_ed.pop_event(
+                                descriptor_write.format(gatt_callback),
+                                self.default_timeout)))
+        return True
+
+    #TODO (tturney): Refactor to make this easier to run.
+    @BluetoothBaseTest.bt_test_wrap
+    def test_write_characteristic(self):
+        """Test GATT connection writing characteristics.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client and exercise writing a characteristic.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. Discover services.
+        7. Set discovered characteristic notification to True
+        8. Write data to the characteristic.
+        9. Send a response from the peripheral to the central.
+        10. Disconnect the GATT connection.
+
+        Expected Result:
+        The characteristic data should be written successfully
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Filtering, Scanning, GATT, Stress,
+        Characteristics, Descriptors
+        Priority: 1
+        """
+        gatt_server_callback = self.per_droid.gattServerCreateGattServerCallback(
+        )
+        gatt_server = self.per_droid.gattServerOpenGattServer(
+            gatt_server_callback)
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+
+        service_uuid = "3846D7A0-69C8-11E4-BA00-0002A5D5C51B"
+        characteristic_uuid = "aa7edd5a-4d1d-4f0e-883a-d145616a1630"
+        descriptor_uuid = "aa7edd5a-4d1d-4f0e-883a-d145616a1630"
+
+        characteristic = (
+            self.per_droid.gattServerCreateBluetoothGattCharacteristic(
+                characteristic_uuid,
+                BluetoothGattCharacteristic.PROPERTY_WRITE.value,
+                BluetoothGattCharacteristic.PERMISSION_WRITE.value))
+
+        descriptor = self.per_droid.gattServerCreateBluetoothGattDescriptor(
+            descriptor_uuid,
+            BluetoothGattDescriptor.PERMISSION_READ.value |
+            BluetoothGattDescriptor.PERMISSION_WRITE.value,
+        )
+        self.per_droid.gattServerCharacteristicAddDescriptor(
+            characteristic, descriptor)
+
+        gatt_service = self.per_droid.gattServerCreateService(
+            service_uuid,
+            BluetoothGattService.SERVICE_TYPE_PRIMARY.value
+        )
+
+        self.per_droid.gattServerAddCharacteristicToService(gatt_service,
+                                                            characteristic)
+
+        self.per_droid.gattServerAddService(gatt_server, gatt_service)
+        result = self._find_service_added_event(
+            gatt_server_callback, service_uuid)
+        if not result:
+            return False
+
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+        services_count = self.cen_droid.gattClientGetDiscoveredServicesCount(
+            discovered_services_index)
+        disc_service_index = 0
+        for i in range(services_count):
+            disc_service_uuid = (
+                self.cen_droid.gattClientGetDiscoveredServiceUuid(
+                    discovered_services_index, i).upper())
+            if disc_service_uuid == service_uuid:
+                disc_service_index = i
+                break
+
+        self.cen_droid.gattClientSetCharacteristicNotification(
+            gatt_callback,
+            discovered_services_index,
+            disc_service_index,
+            characteristic_uuid,
+            True
+        )
+
+        test_value = "1,2,3,4,5,6,7"
+        self.cen_droid.gattClientCharacteristicSetValue(
+            bluetooth_gatt,
+            discovered_services_index,
+            disc_service_index,
+            characteristic_uuid,
+            test_value
+        )
+
+        self.cen_droid.gattClientWriteCharacteristic(
+            bluetooth_gatt,
+            discovered_services_index,
+            disc_service_index,
+            characteristic_uuid
+        )
+
+        event = self.per_ed.pop_event(
+            characteristic_write_request.format(gatt_server_callback),
+            self.default_timeout
+        )
+
+        request_id = event['data']['requestId']
+        bt_device_id = 0
+        status = 1
+        offset = 1
+        test_value_return = "1,2,3"
+        self.per_droid.gattServerGetConnectedDevices(gatt_server)
+        self.per_droid.gattServerSendResponse(
+            gatt_server,
+            bt_device_id,
+            request_id,
+            status,
+            offset,
+            test_value_return
+        )
+
+        self.cen_ed.pop_event(
+            characteristic_write.format(bluetooth_gatt),
+            self.default_timeout
+        )
+        return True
+
+    # TODO: (tturney) fix this, documentation
+    @BluetoothBaseTest.bt_test_wrap
+    def test_write_characteristic_stress(self):
+        gatt_server_callback, gatt_server = self._setup_multiple_services()
+        if not gatt_server_callback or not gatt_server:
+            return False
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+        else:
+            self.log.info("Failed to discover services.")
+            return False
+        services_count = self.cen_droid.gattClientGetDiscoveredServicesCount(
+            discovered_services_index)
+
+        connected_device_list = self.per_droid.gattServerGetConnectedDevices(
+            gatt_server)
+        if len(connected_device_list) == 0:
+            self.log.info("No devices connected from peripheral.")
+            return False
+        bt_device_id = 0
+        status = 1
+        offset = 1
+        test_value = "1,2,3,4,5,6,7"
+        test_value_return = "1,2,3"
+        for i in range(services_count):
+            characteristic_uuids = (
+                self.cen_droid.gattClientGetDiscoveredCharacteristicUuids(
+                    discovered_services_index, i))
+            for i in range(100):
+                for characteristic in characteristic_uuids:
+                    self.cen_droid.gattClientCharacteristicSetValue(
+                        bluetooth_gatt, discovered_services_index, i,
+                        characteristic, test_value)
+                    self.cen_droid.gattClientWriteCharacteristic(
+                        bluetooth_gatt, discovered_services_index, i,
+                        characteristic)
+                    self.cen_droid.gattClientWriteCharacteristic(
+                        bluetooth_gatt, discovered_services_index, i,
+                        characteristic)
+                    self.cen_droid.gattClientWriteCharacteristic(
+                        bluetooth_gatt, discovered_services_index, i,
+                        characteristic)
+                    self.cen_droid.gattClientWriteCharacteristic(
+                        bluetooth_gatt, discovered_services_index, i,
+                        characteristic)
+                    time.sleep(8)
+                    event = self.per_ed.pop_event(
+                        characteristic_write_request.format(
+                            gatt_server_callback), self.default_timeout)
+                    self.log.info("{} event found: {}".format(
+                                  characteristic_write_request.format(
+                                      gatt_server_callback), event))
+                    request_id = event['data']['requestId']
+                    found_value = event['data']['value']
+                    if found_value != test_value:
+                        self.log.info("Values didn't match. Found: {}, "
+                                      "Expected: {}".format(found_value,
+                                                            test_value))
+                        return False
+                    self.per_droid.gattServerSendResponse(
+                        gatt_server, bt_device_id, request_id, status, offset,
+                        test_value_return)
+                    self.log.info(
+                        "onCharacteristicWrite event found: {}".format(
+                            characteristic_write_request.format(
+                                gatt_server_callback),
+                            self.cen_ed.pop_event(
+                                characteristic_write.format(bluetooth_gatt),
+                                self.default_timeout)))
+        return True
+
+    # TODO: (tturney) Work in progress...
+    @BluetoothBaseTest.bt_test_wrap
+    def test_cross_key_pairing(self):
+        # gatt_server_callback, gatt_server = self._setup_multiple_services()
+        gatt_server_callback = (
+            self.per_droid.gattServerCreateGattServerCallback())
+        gatt_server = self.per_droid.gattServerOpenGattServer(
+            gatt_server_callback)
+        characteristic_input = [
+            {
+                'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+                'property': BluetoothGattCharacteristic.PROPERTY_WRITE.value |
+                            BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE.value,
+                'permission': BluetoothGattCharacteristic.PROPERTY_WRITE.value
+            },
+        ]
+        characteristic_list = setup_gatt_characteristics(
+            self.per_droid, characteristic_input)
+        gatt_service = self.per_droid.gattServerCreateService(
+            "3846d7a0-69c8-11e4-ba00-0002a5d5c51b",
+            BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+        self.per_droid.gatt_serviceAddCharacteristicToService(
+            gatt_service, characteristic_list[0])
+        self.per_droid.gattServerAddService(gatt_server, gatt_service)
+        self.per_droid.gattServerAddService(gatt_server, gatt_service)
+        self.per_droid.gattServerAddService(gatt_server, gatt_service)
+        result = self._find_service_added_event(
+            gatt_server_callback,
+            "3846d7a0-69c8-11e4-ba00-0002a5d5c51b")
+        if not result:
+            return False
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed))
+        self.adv_instances.append(adv_callback)
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+        else:
+            self.log.info("Failed to discover services.")
+            return False
+        services_count = self.cen_droid.gattClientGetDiscoveredServicesCount(
+            discovered_services_index)
+        print("services count {}".format(services_count))
+        connected_device_list = self.per_droid.gattServerGetConnectedDevices(
+            gatt_server)
+        if len(connected_device_list) == 0:
+            self.log.info("No devices connected from peripheral.")
+            return False
+        bt_device_id = 0
+        status = 1
+        offset = 1
+        test_value = "1,2,3,4,5,6,7"
+        test_value_return = "1,2,3"
+        for i in range(services_count):
+            characteristic_uuids = (
+                self.cen_droid.gattClientGetDiscoveredCharacteristicUuids(
+                    discovered_services_index, i))
+            print(characteristic_uuids)
+            for characteristic in characteristic_uuids:
+                self.cen_droid.gattClientCharacteristicSetValue(
+                    bluetooth_gatt, discovered_services_index, i,
+                    characteristic, test_value)
+                print (self.cen_droid.gattClientWriteCharacteristic(
+                       bluetooth_gatt, discovered_services_index, i,
+                       characteristic))
+                self.cen_droid.gattClientReadCharacteristic(
+                    bluetooth_gatt, discovered_services_index, i,
+                    characteristic)
+        return True
diff --git a/acts/tests/google/ble/scan/BatchingTest.py b/acts/tests/google/ble/scan/BatchingTest.py
new file mode 100644
index 0000000..8784e6b
--- /dev/null
+++ b/acts/tests/google/ble/scan/BatchingTest.py
@@ -0,0 +1,80 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 test script exercises batch scanning scenarios.
+"""
+
+import pprint
+from queue import Empty
+import time
+from contextlib import suppress
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import scan_result
+
+# TODO: (tturney) finish separating out testcases in various suits out to here.
+
+
+class BatchingTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid1, self.ed1 = self.droids[1], self.eds[1]
+        self.tests = (
+            "test_automatic_clearing_of_batch_data",
+        )
+
+    #TODO: (tturney) finish testcase.
+    @BluetoothBaseTest.bt_test_wrap
+    def test_automatic_clearing_of_batch_data(self):
+        """Test automatic clearing of batch data.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client.
+
+        Steps:
+        1.
+
+        Expected Result:
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Batch Scanning
+        Priority: 3
+        """
+        scan_droid, scan_event_dispatcher = self.droid, self.ed
+        advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+        ad_callback, ad_data, ad_settings = generate_ble_advertise_objects(
+            advertise_droid)
+        advertise_droid.bleStartBleAdvertising(
+            ad_data, ad_settings, ad_callback)
+
+        scan_filter_list = scan_droid.bleGenFilterList()
+        scan_droid.bleBuildScanFilter(scan_filter_list)
+        scan_droid.bleSetScanSettingsReportDelayMillis(1000)
+        scan_settings = scan_droid.bleBuildScanSetting()
+        scan_callback = scan_droid.bleGenScanCallback()
+        system_time_nanos = scan_droid.getSystemElapsedRealtimeNanos()
+        scan_droid.bleStartBleScan(
+            scan_filter_list, scan_settings, scan_callback)
+        expected_event = scan_result.format(scan_callback)
+        scan_droid.pop_event(expected_event, self.default_timeout)
+        return True
diff --git a/acts/tests/google/ble/scan/BleBackgroundScanTest.py b/acts/tests/google/ble/scan/BleBackgroundScanTest.py
new file mode 100644
index 0000000..05d3ea8
--- /dev/null
+++ b/acts/tests/google/ble/scan/BleBackgroundScanTest.py
@@ -0,0 +1,177 @@
+# python3.4
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 test script exercises background scan test scenarios.
+"""
+
+from queue import Empty
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import BluetoothAdapterState
+from acts.test_utils.bt.bt_test_utils import bluetooth_off
+from acts.test_utils.bt.bt_test_utils import bluetooth_on
+from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts.test_utils.bt.bt_test_utils import log_energy_info
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import scan_result
+
+
+class BleBackgroundScanTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+    max_scan_instances = 28
+    report_delay = 2000
+    scan_callbacks = []
+    adv_callbacks = []
+    active_scan_callback_list = []
+    active_adv_callback_list = []
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        if self.droid_list[1]['max_advertisements'] == 0:
+            self.tests = ()
+            return
+        self.tests = (
+            "test_background_scan",
+            "test_background_scan_ble_disabled",
+        )
+
+    def setup_test(self):
+        self.log.debug(log_energy_info(self.droids, "Start"))
+        if (self.scn_droid.bluetoothGetLeState() ==
+                BluetoothAdapterState.STATE_OFF.value):
+            self.scn_droid.bluetoothEnableBLE()
+            self.scn_ed.pop_event("BleStateChangedOn")
+        for e in self.eds:
+            e.clear_all_events()
+        return True
+
+    def teardown_test(self):
+        self.log.debug(log_energy_info(self.droids, "End"))
+        cleanup_scanners_and_advertisers(
+            self.scn_droid, self.scn_ed, self.active_adv_callback_list,
+            self.adv_droid, self.adv_ed, self.active_adv_callback_list)
+        self.active_adv_callback_list = []
+        self.active_scan_callback_list = []
+
+    def _setup_generic_advertisement(self):
+        adv_callback, adv_data, adv_settings = generate_ble_advertise_objects(
+            self.adv_droid)
+        self.adv_droid.bleStartBleAdvertising(
+            adv_callback, adv_data, adv_settings)
+        self.active_adv_callback_list.append(adv_callback)
+
+    def _verify_no_events_found(self, event_name):
+        try:
+            self.scn_ed.pop_event(event_name, self.default_timeout)
+            self.log.error("Found an event when none was expected.")
+            return False
+        except Empty:
+            self.log.info("No scan result found as expected.")
+            return True
+
+    def _delete_me(self):
+        import time
+        time.sleep(5)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_background_scan(self):
+        """Test generic background scan.
+
+        Tests LE background scan. The goal is to find scan results even though
+        Bluetooth is turned off.
+
+        Steps:
+        1. Setup an advertisement on dut1
+        2. Enable LE on the Bluetooth Adapter on dut0
+        3. Toggle BT off on dut1
+        4. Start a LE scan on dut0
+        5. Find the advertisement from dut1
+
+        Expected Result:
+        Find a advertisement from the scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Background Scanning
+        Priority: 0
+        """
+        import time
+        self._setup_generic_advertisement()
+        self.scn_droid.bluetoothToggleState(False)
+        self.scn_ed.pop_event(bluetooth_off, self.default_timeout)
+        self.scn_droid.bluetoothDisableBLE()
+        self.scn_ed.pop_event(bluetooth_off, self.default_timeout)
+        self.scn_droid.bluetoothEnableBLE()
+        self._delete_me()
+        self.scn_ed.pop_event(bluetooth_on, self.default_timeout * 2)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_background_scan_ble_disabled(self):
+        """Test background LE scanning with LE disabled.
+
+        Tests LE background scan. The goal is to find scan results even though
+        Bluetooth is turned off.
+
+        Steps:
+        1. Setup an advertisement on dut1
+        2. Enable LE on the Bluetooth Adapter on dut0
+        3. Toggle BT off on dut1
+        4. Start a LE scan on dut0
+        5. Find the advertisement from dut1
+
+        Expected Result:
+        Find a advertisement from the scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Background Scanning
+        Priority: 0
+        """
+        self._setup_generic_advertisement()
+        self.scn_droid.bluetoothEnableBLE()
+        self.scn_droid.bluetoothToggleState(False)
+        self.scn_ed.pop_event(bluetooth_off, self.default_timeout)
+        self._delete_me()
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        try:
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+            self.scn_ed.pop_event(scan_result.format(scan_callback))
+            self.log.info("Was able to start background scan even though ble "
+                          "was disabled.")
+            return False
+        except Exception:
+            self.log.info(
+                "Was not able to start a background scan as expected.")
+        return True
diff --git a/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py b/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py
new file mode 100644
index 0000000..fcaf01d
--- /dev/null
+++ b/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py
@@ -0,0 +1,277 @@
+# python3.4
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 test script exercises different onLost/onFound scenarios.
+"""
+
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts.test_utils.bt.BleEnum import ScanSettingsCallbackType
+from acts.test_utils.bt.BleEnum import ScanSettingsMatchMode
+from acts.test_utils.bt.BleEnum import ScanSettingsMatchNum
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.bt_test_utils import adv_succ
+from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import log_energy_info
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import scan_result
+
+
+class BleOnLostOnFoundTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+    max_scan_instances = 28
+    active_scan_callback_list = []
+    active_adv_callback_list = []
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        if self.droid_list[1]['max_advertisements'] == 0:
+            self.tests = ()
+            return
+        self.tests = (
+            "test_onlost_onfound_defaults",
+            "test_onlost_onfound_match_mode_sticky",
+            "test_onlost_onfound_match_num_few",
+        )
+
+    def teardown_test(self):
+        self.log.info(log_energy_info(self.droids, "End"))
+        cleanup_scanners_and_advertisers(
+            self.scn_droid, self.scn_ed, self.active_adv_callback_list,
+            self.adv_droid, self.adv_ed, self.active_adv_callback_list)
+        self.active_adv_callback_list = []
+        self.active_scan_callback_list = []
+
+    def on_exception(self, test_name, begin_time):
+        reset_bluetooth(self.droids, self.eds)
+
+    def _start_generic_advertisement_include_device_name(self):
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        advertise_data = self.adv_droid.bleBuildAdvertiseData()
+        advertise_settings = self.adv_droid.bleBuildAdvertiseSettings()
+        advertise_callback = self.adv_droid.bleGenBleAdvertiseCallback()
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.adv_ed.pop_event(adv_succ.format(advertise_callback),
+                              self.default_timeout)
+        self.active_adv_callback_list.append(advertise_callback)
+        return advertise_callback
+
+    def _verify_no_events_found(self, event_name):
+        try:
+            self.scn_ed.pop_event(event_name, self.default_timeout)
+            self.log.error("Found an event when none was expected.")
+            return False
+        except Empty:
+            self.log.info("No scan result found as expected.")
+            return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_onlost_onfound_defaults(self):
+        """Test generic onlost/onfound defaults.
+
+        Tests basic onFound/onLost functionality.
+
+        Steps:
+        1. Setup dut0 scanner and start scan with this setup:
+          Scan Mode: SCAN_MODE_LOW_LATENCY
+          Callback Type: CALLBACK_TYPE_FOUND_AND_LOST
+          Match Mode: AGGRESSIVE
+          Num of Matches: MATCH_NUM_ONE_ADVERTISEMENT
+          Filter: Device name of dut1
+        2. Start an advertisement on dut1, include device name
+        3. Find an onFound event
+        4. Stop the advertisement on dut1
+        5. Find an onLost event
+
+        Expected Result:
+        Find an onLost and an onFound event successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, onLost, onFound
+        Priority: 0
+        """
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_FOUND_AND_LOST.value)
+        self.scn_droid.bleSetScanSettingsMatchMode(
+            ScanSettingsMatchMode.AGGRESIVE.value)
+        self.scn_droid.bleSetScanSettingsNumOfMatches(
+            ScanSettingsMatchNum.MATCH_NUM_ONE_ADVERTISEMENT.value)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        adv_callback = self._start_generic_advertisement_include_device_name()
+        event = self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout * 3)
+        found_callback_type = event['data']['CallbackType']
+        if event['data']['CallbackType'] != ScanSettingsCallbackType.CALLBACK_TYPE_FIRST_MATCH.value:
+            self.log.info("Found Callbacreset_bluetoothkType:{}, Expected CallbackType:{}".format(
+                found_callback_type, ScanSettingsCallbackType.CALLBACK_TYPE_FIRST_MATCH.value))
+            return False
+        self.adv_droid.bleStopBleAdvertising(adv_callback)
+        event = self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout * 4)
+        found_callback_type = event['data']['CallbackType']
+        if found_callback_type != ScanSettingsCallbackType.CALLBACK_TYPE_MATCH_LOST.value:
+            self.log.info("Found CallbackType:{}, Expected CallbackType:{}".format(
+                found_callback_type, ScanSettingsCallbackType.CALLBACK_TYPE_MATCH_LOST.value))
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_onlost_onfound_match_mode_sticky(self):
+        """Test generic onlost/onfound in sticky mode.
+
+        Tests basic onFound/onLost functionality.
+
+        Steps:
+        1. Setup dut0 scanner and start scan with this setup:
+          Scan Mode: SCAN_MODE_LOW_LATENCY
+          Callback Type: CALLBACK_TYPE_FOUND_AND_LOST
+          Match Mode: STICKY
+          Num of Matches: MATCH_NUM_ONE_ADVERTISEMENT
+          Filter: Device name of dut1
+        2. Start an advertisement on dut1, include device name
+        3. Find an onFound event
+        4. Stop the advertisement on dut1
+        5. Find an onLost event
+
+        Expected Result:
+        Find an onLost and an onFound event successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, onLost, onFound
+        Priority: 1
+        """
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_FOUND_AND_LOST.value)
+        self.scn_droid.bleSetScanSettingsMatchMode(
+            ScanSettingsMatchMode.STICKY.value)
+        self.scn_droid.bleSetScanSettingsNumOfMatches(
+            ScanSettingsMatchNum.MATCH_NUM_ONE_ADVERTISEMENT.value)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        adv_callback = self._start_generic_advertisement_include_device_name()
+        event = self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout * 3)
+        found_callback_type = event['data']['CallbackType']
+        if event['data']['CallbackType'] != ScanSettingsCallbackType.CALLBACK_TYPE_FIRST_MATCH.value:
+            self.log.info("Found CallbackType:{}, Expected CallbackType:{}".format(
+                found_callback_type, ScanSettingsCallbackType.CALLBACK_TYPE_FIRST_MATCH.value))
+            return False
+        self.adv_droid.bleStopBleAdvertising(adv_callback)
+        event = self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout * 4)
+        found_callback_type = event['data']['CallbackType']
+        if found_callback_type != ScanSettingsCallbackType.CALLBACK_TYPE_MATCH_LOST.value:
+            self.log.info("Found CallbackType:{}, Expected CallbackType:{}".format(
+                found_callback_type, ScanSettingsCallbackType.CALLBACK_TYPE_MATCH_LOST.value))
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_onlost_onfound_match_num_few(self):
+        """Test generic onlost/onfound num few.
+
+        Tests basic onFound/onLost functionality.
+
+        Steps:
+        1. Setup dut0 scanner and start scan with this setup:
+          Scan Mode: SCAN_MODE_LOW_LATENCY
+          Callback Type: CALLBACK_TYPE_FOUND_AND_LOST
+          Match Mode: AGGRESSIVE
+          Num of Matches: MATCH_NUM_FEW_ADVERTISEMENT
+          Filter: Device name of dut1
+        2. Start an advertisement on dut1, include device name
+        3. Find an onFound event
+        4. Stop the advertisement on dut1
+        5. Find an onLost event
+
+        Expected Result:
+        Find an onLost and an onFound event successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, onLost, onFound
+        Priority: 1
+        """
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_FOUND_AND_LOST.value)
+        self.scn_droid.bleSetScanSettingsMatchMode(
+            ScanSettingsMatchMode.AGGRESIVE.value)
+        self.scn_droid.bleSetScanSettingsNumOfMatches(
+            ScanSettingsMatchNum.MATCH_NUM_FEW_ADVERTISEMENT.value)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        adv_callback = self._start_generic_advertisement_include_device_name()
+        event = self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout * 3)
+        found_callback_type = event['data']['CallbackType']
+        if event['data']['CallbackType'] != ScanSettingsCallbackType.CALLBACK_TYPE_FIRST_MATCH.value:
+            self.log.info("Found CallbackType:{}, Expected CallbackType:{}".format(
+                found_callback_type, ScanSettingsCallbackType.CALLBACK_TYPE_FIRST_MATCH.value))
+            return False
+        self.adv_droid.bleStopBleAdvertising(adv_callback)
+        event = self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout * 4)
+        found_callback_type = event['data']['CallbackType']
+        if found_callback_type != ScanSettingsCallbackType.CALLBACK_TYPE_MATCH_LOST.value:
+            self.log.info("Found CallbackType:{}, Expected CallbackType:{}".format(
+                found_callback_type, ScanSettingsCallbackType.CALLBACK_TYPE_MATCH_LOST.value))
+            return False
+        return True
diff --git a/acts/tests/google/ble/scan/BleOpportunisticScanTest.py b/acts/tests/google/ble/scan/BleOpportunisticScanTest.py
new file mode 100644
index 0000000..a99c21d
--- /dev/null
+++ b/acts/tests/google/ble/scan/BleOpportunisticScanTest.py
@@ -0,0 +1,660 @@
+# python3.4
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 test script exercises different opportunistic scan scenarios.
+It is expected that the second AndroidDevice is able to advertise.
+
+This test script was designed with this setup in mind:
+Shield box one: Android Device, Android Device
+"""
+
+from queue import Empty
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.bt_test_utils import batch_scan_result
+from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import scan_result
+
+
+class BleOpportunisticScanTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+    max_scan_instances = 28
+    report_delay = 2000
+    scan_callbacks = []
+    adv_callbacks = []
+    active_scan_callback_list = []
+    active_adv_callback_list = []
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        if self.droid_list[1]['max_advertisements'] == 0:
+            self.tests = ()
+            return
+        self.tests = (
+            "test_scan_result_no_advertisement",
+            "test_scan_result_no_advertisement",
+            "test_scan_result",
+            "test_batch_scan_result_not_expected",
+            "test_scan_result_not_expected",
+            "test_max_opportunistic_scan_instances",
+            "test_discover_opportunistic_scan_result_off_secondary_scan_filter",
+            "test_negative_opportunistic_scan_filter_result_off_secondary_scan_result",
+            "test_opportunistic_scan_filter_result_off_secondary_scan_result",
+        )
+        if self.droid_list[0]['batch_scan_supported']:
+            self.tests = self.tests + (
+                    "test_batch_scan_result",
+                    "test_max_opportunistic_batch_scan_instances",
+                    )
+
+    def teardown_test(self):
+        cleanup_scanners_and_advertisers(
+            self.scn_droid, self.scn_ed, self.active_adv_callback_list,
+            self.adv_droid, self.adv_ed, self.active_adv_callback_list)
+        self.active_adv_callback_list = []
+        self.active_scan_callback_list = []
+
+    def on_exception(self, test_name, begin_time):
+        reset_bluetooth(self.droids, self.eds)
+
+    def _setup_generic_advertisement(self):
+        adv_callback, adv_data, adv_settings = generate_ble_advertise_objects(
+            self.adv_droid)
+        self.adv_droid.bleStartBleAdvertising(
+            adv_callback, adv_data, adv_settings)
+        self.active_adv_callback_list.append(adv_callback)
+
+    def _verify_no_events_found(self, event_name):
+        try:
+            event = self.scn_ed.pop_event(event_name, self.default_timeout)
+            self.log.error(
+                "Found an event when none was expected: {}".format(event))
+            return False
+        except Empty:
+            self.log.info("No scan result found as expected.")
+            return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_result_no_advertisement(self):
+        """Test opportunistic scan with no advertisement.
+
+        Tests opportunistic scan where there are no advertisements. This should
+        not find any onScanResults.
+
+        Steps:
+        1. Initialize scanner with scan mode set to opportunistic mode.
+        2. Start scanning on dut 0
+        3. Pop onScanResults event on the scanner
+
+        Expected Result:
+        Find no advertisements with the opportunistic scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan
+        Priority: 1
+        """
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        if not self._verify_no_events_found(scan_result.format(scan_callback)):
+            return False
+        self.scn_droid.bleStopBleScan(scan_callback)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_batch_scan_result_no_advertisement(self):
+        """Test batch opportunistic scan without an advertisement.
+
+        Tests opportunistic scan where there are no advertisements. This should
+        not find any onBatchScanResult.
+
+        Steps:
+        1. Initialize scanner with scan mode set to opportunistic mode.
+        2. Set report delay seconds such that onBatchScanResult events are
+        expected
+        2. Start scanning on dut 0
+        3. Pop onBatchScanResult event on the scanner
+
+        Expected Result:
+        Find no advertisements with the opportunistic scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan, Batch Scanning
+        Priority: 1
+        """
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        self.scn_droid.bleSetScanSettingsReportDelayMillis(self.report_delay)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        if not self._verify_no_events_found(batch_scan_result.format(
+            scan_callback)):
+            return False
+        self.scn_droid.bleStopBleScan(scan_callback)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_result(self):
+        """Test opportunistic scan with an advertisement.
+
+        Tests opportunistic scan where it will only report scan results when
+        other registered scanners find results.
+
+        Steps:
+        1. Initialize advertiser and start advertisement on dut1
+        2. Initialize scanner with scan mode set to opportunistic mode on dut0
+        and start scanning
+        3. Try to find an event, expect none.
+        4. Start a second scanner on dut0, with any other mode set
+        5. Pop onScanResults event on the second scanner
+        6. Pop onScanResults event on the first scanner
+
+        Expected Result:
+        Scan result is found on the opportunistic scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan
+        Priority: 1
+        """
+        self._setup_generic_advertisement()
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        if not self._verify_no_events_found(scan_result.format(scan_callback)):
+            return False
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list2, scan_settings2, scan_callback2 = (
+            generate_ble_scan_objects(self.scn_droid))
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        self.active_scan_callback_list.append(scan_callback2)
+        self.scn_ed.pop_event(
+            scan_result.format(scan_callback2), self.default_timeout)
+        self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_batch_scan_result(self):
+        """Test batch opportunistic scan with advertisement.
+
+        Tests opportunistic scan where it will only report scan results when
+        other registered scanners find results. Set the report delay millis such
+        that an onBatchScanResult is expected.
+
+        Steps:
+        1. Initialize advertiser and start advertisement on dut1
+        2. Initialize scanner with scan mode set to opportunistic mode and
+        set scan settings report delay seconds such that a batch scan is
+        expected
+        3. Start scanning on dut 0
+        4. Try to find an event, expect none.
+        5. Start a second scanner on dut0, with any other mode set and set scan
+        settings report delay millis such that an onBatchScanResult is expected
+        6. Pop onBatchScanResult event on the second scanner
+        7. Pop onBatchScanResult event on the first scanner
+
+        Expected Result:
+        Find a batch scan result on both opportunistic scan instances.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan, Batch Scanning
+        Priority: 1
+        """
+        self._setup_generic_advertisement()
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        self.scn_droid.bleSetScanSettingsReportDelayMillis(self.report_delay)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        if not self._verify_no_events_found(batch_scan_result.format(
+            scan_callback)):
+            return False
+        self.scn_droid.bleSetScanSettingsReportDelayMillis(self.report_delay)
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list2 = self.scn_droid.bleGenFilterList()
+        scan_settings2 = self.scn_droid.bleBuildScanSetting()
+        scan_callback2 = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        self.active_scan_callback_list.append(scan_callback2)
+        self.scn_ed.pop_event(
+            batch_scan_result.format(scan_callback2), self.default_timeout)
+        self.scn_ed.pop_event(
+            batch_scan_result.format(scan_callback), self.default_timeout)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_batch_scan_result_not_expected(self):
+        """Test opportunistic batch scan without expecting an event.
+
+        Tests opportunistic scan where it will only report scan results when
+        other registered scanners find results. Set the report delay millis such
+        that a batch scan is not expected.
+
+        Steps:
+        1. Initialize advertiser and start advertisement on dut1
+        2. Initialize scanner with scan mode set to opportunistic mode and
+        set scan settings report delay seconds such that a batch scan is
+        expected.
+        3. Start scanning on dut 0
+        4. Try to find an event, expect none.
+        5. Start a second scanner on dut0, with any other mode set and set scan
+        settings report delay millis to 0 such that an onBatchScanResult is not
+        expected.
+        6. Pop onScanResults event on the second scanner
+        7. Pop onBatchScanResult event on the first scanner
+
+        Expected Result:
+        Batch scan result is not expected on opportunistic scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan, Batch Scanning
+        Priority: 1
+        """
+        self._setup_generic_advertisement()
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        self.scn_droid.bleSetScanSettingsReportDelayMillis(self.report_delay)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        if not self._verify_no_events_found(batch_scan_result.format(
+            scan_callback)):
+
+            return False
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list2, scan_settings2, scan_callback2 = (
+            generate_ble_scan_objects(self.scn_droid))
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        self.active_scan_callback_list.append(scan_callback2)
+        self.scn_ed.pop_event(
+            scan_result.format(scan_callback2), self.default_timeout)
+        return self._verify_no_events_found(batch_scan_result.format(
+            scan_callback))
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_result_not_expected(self):
+        """Test opportunistic scan without expecting an event.
+
+        Tests opportunistic scan where it will only report batch scan results
+        when other registered scanners find results.
+
+        Steps:
+        1. Initialize advertiser and start advertisement on dut1
+        2. Initialize scanner with scan mode set to opportunistic mode.
+        3. Start scanning on dut 0
+        4. Try to find an event, expect none.
+        5. Start a second scanner on dut0, with any other mode set and set scan
+        settings
+        report delay millis such that an onBatchScanResult is expected
+        6. Pop onBatchScanResult event on the second scanner
+        7. Pop onScanResults event on the first scanner
+
+        Expected Result:
+        Scan result is not expected on opportunistic scan instance.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan
+        Priority: 1
+        """
+        self._setup_generic_advertisement()
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        filter_list = self.scn_droid.bleGenFilterList()
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        if not self._verify_no_events_found(scan_result.format(scan_callback)):
+            return False
+        self.scn_droid.bleSetScanSettingsReportDelayMillis(self.report_delay)
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list2, scan_settings2, scan_callback2 = (
+            generate_ble_scan_objects(self.scn_droid))
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        self.active_scan_callback_list.append(scan_callback2)
+        self.scn_ed.pop_event(
+            batch_scan_result.format(scan_callback2), self.default_timeout)
+        return self._verify_no_events_found(scan_result.format(scan_callback))
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_opportunistic_scan_instances(self):
+        """Test max number of opportunistic scan instances.
+
+        Tests max instances of opportunistic scans. Each instances should
+        find an onScanResults event.
+
+        Steps:
+        1. Initialize advertiser and start advertisement on dut1
+        2. Set scan settings to opportunistic scan on dut0 scan instance
+        3. Start scan scan from step 2
+        4. Repeat step two and three until there are max_scan_instances-1 scan
+        instances
+        5. Start a regular ble scan on dut0 with the last available scan
+        instance
+        6. Pop onScanResults event on all scan instances
+
+        Expected Result:
+        Each opportunistic scan instance finds a advertisement.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan
+        Priority: 1
+        """
+        self._setup_generic_advertisement()
+        for _ in range(self.max_scan_instances - 1):
+            self.scn_droid.bleSetScanSettingsScanMode(
+                ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+            filter_list = self.scn_droid.bleGenFilterList()
+            scan_settings = self.scn_droid.bleBuildScanSetting()
+            scan_callback = self.scn_droid.bleGenScanCallback()
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+            self.active_scan_callback_list.append(scan_callback)
+
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list2, scan_settings2, scan_callback2 = (
+            generate_ble_scan_objects(self.scn_droid))
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        self.active_scan_callback_list.append(scan_callback2)
+
+        for callback in self.active_scan_callback_list:
+            self.scn_ed.pop_event(
+                scan_result.format(callback), self.default_timeout)
+
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_max_opportunistic_batch_scan_instances(self):
+        """Test max opportunistic batch scan instances.
+
+        Tests max instances of opportunistic batch scans. Each instances should
+        find an onBatchScanResult event.
+
+        Steps:
+        1. Initialize advertiser and start advertisement on dut1
+        2. Set scan settings to opportunistic scan on dut0 scan instance and
+        set report delay seconds such that an onBatchScanResult is expected
+        3. Start scan scan from step 2
+        4. Repeat step two and three until there are max_scan_instances-1 scan
+        instances
+        5. Start a regular ble scan on dut0 with the last available scan
+        instance
+        6. Pop onBatchScanResult event on all scan instances
+
+        Expected Result:
+        Each opportunistic scan instance finds an advertisement.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan, Batch Scanning
+        Priority: 1
+        """
+        self._setup_generic_advertisement()
+        for _ in range(self.max_scan_instances - 1):
+            self.scn_droid.bleSetScanSettingsScanMode(
+                ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+            self.scn_droid.bleSetScanSettingsReportDelayMillis(
+                self.report_delay)
+            filter_list = self.scn_droid.bleGenFilterList()
+            scan_settings = self.scn_droid.bleBuildScanSetting()
+            scan_callback = self.scn_droid.bleGenScanCallback()
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+            self.active_scan_callback_list.append(scan_callback)
+
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.scn_droid.bleSetScanSettingsReportDelayMillis(self.report_delay)
+        filter_list2, scan_settings2, scan_callback2 = (
+            generate_ble_scan_objects(self.scn_droid))
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        self.active_scan_callback_list.append(scan_callback2)
+
+        for callback in self.active_scan_callback_list:
+            self.scn_ed.pop_event(
+                batch_scan_result.format(callback), self.default_timeout)
+
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_discover_opportunistic_scan_result_off_secondary_scan_filter(self):
+        """Test opportunistic scan result from secondary scan filter.
+
+        Tests opportunistic scan where the secondary scan instance does not find
+        an advertisement but the scan instance with scan mode set to
+        opportunistic scan will find an advertisement.
+
+        Steps:
+        1. Initialize advertiser and start advertisement on dut1 (make sure the
+        advertisement is not advertising the device name)
+        2. Set scan settings to opportunistic scan on dut0 scan instance
+        3. Start scan scan from step 2
+        4. Try to find an event, expect none
+        5. Start a second scanner on dut0, with any other mode set and set the
+        scan filter device name to "opp_test"
+        6. Pop onScanResults from the second scanner
+        7. Expect no events
+        8. Pop onScanResults from the first scanner
+
+        Expected Result:
+        Opportunistic scan instance finds an advertisement.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan
+        Priority: 1
+        """
+        self._setup_generic_advertisement()
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        if not self._verify_no_events_found(scan_result.format(scan_callback)):
+            return False
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list2, scan_settings2, scan_callback2 = (
+            generate_ble_scan_objects(self.scn_droid))
+        self.scn_droid.bleSetScanFilterDeviceName("opp_test")
+        self.scn_droid.bleBuildScanFilter(filter_list2)
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        self.active_scan_callback_list.append(scan_callback2)
+        if not self._verify_no_events_found(scan_result.format(scan_callback2)):
+            return False
+        self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_negative_opportunistic_scan_filter_result_off_secondary_scan_result(self):
+        """Test opportunistic scan not found scenario.
+
+        Tests opportunistic scan where the secondary scan instance does find an
+        advertisement but the scan instance with scan mode set to opportunistic
+        scan does not find an advertisement due to mismatched scan filters.
+
+        Steps:
+        1. Initialize advertiser and start advertisement on dut1 (make sure the
+        advertisement is not advertising the device name)
+        2. Set scan settings to opportunistic scan on dut0 scan instance and set
+        the scan filter device name to "opp_test"
+        3. Start scan scan from step 2
+        4. Try to find an event, expect none
+        5. Start a second scanner on dut0, with any other mode set
+        6. Pop onScanResults from the second scanner
+        7. Pop onScanResults from the first scanner
+        8. Expect no events
+
+        Expected Result:
+        Opportunistic scan instance doesn't find any advertisements.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan
+        Priority: 1
+        """
+        self._setup_generic_advertisement()
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleSetScanFilterDeviceName("opp_test")
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        if not self._verify_no_events_found(scan_result.format(scan_callback)):
+            return False
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list2, scan_settings2, scan_callback2 = (
+            generate_ble_scan_objects(self.scn_droid))
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        self.active_scan_callback_list.append(scan_callback2)
+        self.scn_ed.pop_event(
+            scan_result.format(scan_callback2), self.default_timeout)
+        return self._verify_no_events_found(scan_result.format(scan_callback))
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_opportunistic_scan_filter_result_off_secondary_scan_result(self):
+        """Test opportunistic scan from a secondary scan result.
+
+        Tests opportunistic scan where the scan filters are the same between the
+        first scan instance with opportunistic scan set and the second instance
+        with any other mode set.
+
+        Steps:
+        1. Initialize advertiser and start advertisement on dut1 (make sure the
+        advertisement is not advertising the device name)
+        2. Set scan settings to opportunistic scan on dut0 scan instance and set
+        the scan filter device name to the advertiser's device name
+        3. Start scan scan from step 2
+        4. Try to find an event, expect none
+        5. Start a second scanner on dut0, with any other mode set and set the
+        scan filter device name to the advertiser's device name
+        6. Pop onScanResults from the second scanner
+        7. Pop onScanResults from the first scanner
+
+        Expected Result:
+        Opportunistic scan instance finds a advertisement.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Scanning, Opportunistic Scan
+        Priority: 1
+        """
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self._setup_generic_advertisement()
+        adv_device_name = self.adv_droid.bluetoothGetLocalName()
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_droid)
+        self.scn_droid.bleSetScanFilterDeviceName(adv_device_name)
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        if not self._verify_no_events_found(scan_result.format(scan_callback)):
+            return False
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        filter_list2, scan_settings2, scan_callback2 = (
+            generate_ble_scan_objects(self.scn_droid))
+        self.scn_droid.bleSetScanFilterDeviceName(adv_device_name)
+        self.scn_droid.bleBuildScanFilter(filter_list2)
+        self.scn_droid.bleStartBleScan(
+            filter_list2, scan_settings2, scan_callback2)
+        self.active_scan_callback_list.append(scan_callback2)
+        self.scn_ed.pop_event(
+            scan_result.format(scan_callback2), self.default_timeout)
+        self.scn_ed.pop_event(
+            scan_result.format(scan_callback), self.default_timeout)
+        return True
diff --git a/acts/tests/google/ble/scan/DeathToBluetoothTest.py b/acts/tests/google/ble/scan/DeathToBluetoothTest.py
new file mode 100644
index 0000000..27f19e4
--- /dev/null
+++ b/acts/tests/google/ble/scan/DeathToBluetoothTest.py
@@ -0,0 +1,93 @@
+# python3.4
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 queue import Empty
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import *
+from acts.test_utils.bt.bt_test_utils import *
+
+
+class DeathToBluetoothTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+    max_scan_instances = 28
+    report_delay = 2000
+    scan_callbacks = []
+    adv_callbacks = []
+    active_scan_callback_list = []
+    active_adv_callback_list = []
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.tests = (
+            "test_death",
+        )
+
+    def teardown_test(self):
+#    cleanup_scanners_and_advertisers(self.scn_droid, self.scn_ed, self.active_adv_callback_list,
+# self.adv_droid, self.adv_ed, self.active_adv_callback_list)
+        self.active_adv_callback_list = []
+        self.active_scan_callback_list = []
+
+    def on_exception(self, test_name, begin_time):
+        reset_bluetooth(self.droids, self.eds)
+
+    def on_fail(self, test_name, begin_time):
+        reset_bluetooth(self.droids, self.eds)
+
+    def _setup_generic_advertisement(self):
+        adv_callback, adv_data, adv_settings = generate_ble_advertise_objects(
+            self.adv_droid)
+        self.adv_droid.bleStartBleAdvertising(
+            adv_callback, adv_data, adv_settings)
+        self.active_adv_callback_list.append(adv_callback)
+
+    def _verify_no_events_found(self, event_name):
+        try:
+            self.scn_ed.pop_event(event_name, self.default_timeout)
+            self.log.error("Found an event when none was expected.")
+            return False
+        except Empty:
+            self.log.info("No scan result found as expected.")
+            return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_death(self):
+        """
+        Tests ...
+        Steps
+        1: ...
+        :return: boolean
+        """
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.scn_droid.bleSetScanSettingsCallbackType(6)
+        # self.scn_droid.bleSetScanSettingsMatchMode(2) #sticky
+        self.scn_droid.bleSetScanSettingsMatchMode(1)  # aggresive
+        self.scn_droid.bleSetScanSettingsNumOfMatches(1)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        for _ in range(10000):
+            self.scn_ed.pop_event(scan_result.format(scan_callback))
+        return True
diff --git a/acts/tests/google/ble/system_tests/BleLongevityTest.py b/acts/tests/google/ble/system_tests/BleLongevityTest.py
new file mode 100644
index 0000000..1a73f76
--- /dev/null
+++ b/acts/tests/google/ble/system_tests/BleLongevityTest.py
@@ -0,0 +1,233 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 concurrent
+import pprint
+import time
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.event_dispatcher import IllegalStateError
+from queue import Empty
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+
+
+class BleLongevityTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid1, self.ed1 = self.droids[1], self.eds[1]
+        self.tests = (
+            "test_b17040164",
+            # "test_long_advertising_same_callback",
+        )
+
+    def blescan_verify_onscanresult_event_handler(self, event,
+                                                  expected_callbacktype=None,
+                                                  system_time_nanos=None):
+        test_result = True
+        self.log.debug("Verifying onScanResult event")
+        self.log.debug(pprint.pformat(event))
+        callbacktype = event['data']['CallbackType']
+        if callbacktype != expected_callbacktype:
+            self.log.debug(
+                " ".join(["Expected callback type:", str(expected_callbacktype),
+                          ", Found callback type:", str(callbacktype)]))
+            test_result = False
+        return test_result
+
+    def bleadvertise_verify_onsuccess_event_handler(self, event):
+        test_result = True
+        self.log.debug("Verifying onSuccess event")
+        self.log.debug(pprint.pformat(event))
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_long_advertising_same_callback(self):
+        scan_droid, scan_event_dispatcher = self.droid, self.ed
+        advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+        advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            scan_droid)
+        expected_event_name = "".join(
+            ["BleScan", str(scan_callback), "onScanResults"])
+        advertise_callback, advertise_data, advertise_settings = (
+            generate_ble_advertise_objects(advertise_droid))
+        looperCount = 100000
+        expected_advertise_event = "".join(
+            ["BleAdvertise", str(advertise_callback), "onSuccess"])
+        while looperCount != 0:
+            start = time.time()
+            self.droid.eventClearBuffer()
+            self.droid1.eventClearBuffer()
+            test_result = advertise_droid.bleStartBleAdvertising(
+                advertise_callback, advertise_data, advertise_settings)
+
+            if not test_result:
+                self.log.debug("Advertising failed.")
+                return test_result
+            self.log.debug(
+                " ".join(["Start Bluetooth Le Scan on callback ID:",
+                          str(scan_callback)]))
+
+            worker = advertise_event_dispatcher.handle_event(
+                self.bleadvertise_verify_onsuccess_event_handler,
+                expected_advertise_event, (), 20)
+            try:
+                self.log.debug(worker.result(self.default_timeout))
+            except Empty as error:
+                test_result = False
+                self.log.debug(
+                    " ".join(["Test failed with Empty error:", str(error)]))
+            except concurrent.futures._base.TimeoutError as error:
+                test_result = False
+                self.log.debug(
+                    " ".join(["Test failed with TimeoutError:", str(error)]))
+
+            scan_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+            worker = scan_event_dispatcher.handle_event(
+                self.blescan_verify_onscanresult_event_handler,
+                expected_event_name, ([1]), 20)
+
+            try:
+                self.log.debug(worker.result(self.default_timeout))
+            except Empty as error:
+                test_result = False
+                self.log.debug(
+                    " ".join(["Test failed with Empty error:", str(error)]))
+            except concurrent.futures._base.TimeoutError as error:
+                test_result = False
+                self.log.debug(
+                    " ".join(["Test failed with TimeoutError:", str(error)]))
+            scan_droid.bleStopBleScan(scan_callback)
+            advertise_droid.bleStopBleAdvertising(advertise_callback)
+            try:
+                self.ed1.pop_all(expected_advertise_event)
+            except IllegalStateError as error:
+                self.log.debug(
+                    " ".join(["Device in an illigal state:", str(error)]))
+            looperCount -= 1
+            self.log.debug(
+                " ".join(["Total time taken for this loop:", str(time.time() - start)]))
+            time.sleep(2)
+            start += 2
+        self.log.debug(
+            "Step 5: Verify the Bluetooth Le Scan did not cause an onScanFailed event.")
+
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_long_advertising_different_callback(self):
+        scan_droid, scan_event_dispatcher = self.droid, self.ed
+        advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+        advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            scan_droid)
+        expected_event_name = "".join(
+            ["BleScan", str(scan_callback), "onScanResults"])
+        looperCount = 100000
+
+        while looperCount != 0:
+            start = time.time()
+            advertise_callback, advertise_data, advertise_settings = generate_ble_advertise_objects(
+                advertise_droid)
+            test_result = advertise_droid.bleStartBleAdvertising(
+                advertise_callback, advertise_data, advertise_settings)
+            expected_advertise_event = "".join(
+                ["BleAdvertise", str(advertise_callback), "onSuccess"])
+
+            if not test_result:
+                self.log.debug("Advertising failed.")
+                return test_result
+
+            worker = advertise_event_dispatcher.handle_event(
+                self.bleadvertise_verify_onsuccess_event_handler,
+                expected_advertise_event, ())
+            try:
+                self.log.debug(worker.result(self.default_timeout))
+            except Empty as error:
+                test_result = False
+                self.log.debug(
+                    " ".join(["Test failed with Empty error:", str(error)]))
+            except concurrent.futures._base.TimeoutError as error:
+                test_result = False
+                self.log.debug(
+                    " ".join(["Test failed with TimeoutError: ", str(error)]))
+            scan_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+            worker = scan_event_dispatcher.handle_event(
+                self.blescan_verify_onscanresult_event_handler,
+                expected_event_name, ([1]))
+
+            try:
+                self.log.debug(worker.result(self.default_timeout))
+            except Empty as error:
+                test_result = False
+                self.log.debug(
+                    " ".join(["Test failed with Empty error:", str(error)]))
+            except concurrent.futures._base.TimeoutError as error:
+                test_result = False
+                self.log.debug(
+                    " ".join(["Test failed with TimeoutError: ", str(error)])).bluetoothStopBleScan(scan_callback)
+            advertise_droid.bleStopBleAdvertising(advertise_callback)
+            looperCount -= 1
+            self.log.debug(
+                " ".join(["Total time taken for this loop:", str(time.time() - start)]))
+            time.sleep(2)
+            start += 2
+        self.log.debug(
+            "Step 5: Verify the Bluetooth Le Scan did not cause an onScanFailed event.")
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_b17040164(self):
+        test_result = True
+        scan_droid, scan_event_dispatcher = self.droid, self.ed
+        advertise_droid, advertise_event_dispatcher = self.droid1, self.ed1
+        advertise_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            scan_droid)
+        expected_event_name = "".join(
+            ["BleScan", str(scan_callback), "onScanResults"])
+        advertise_callback, advertise_data, advertise_settings = generate_ble_advertise_objects(
+            advertise_droid)
+        looperCount = 1000
+        expected_advertise_event = "".join(
+            ["BleAdvertise", str(advertise_callback), "onSuccess"])
+        while looperCount != 0:
+            advertise_droid.eventClearBuffer()
+            self.ed1.start()
+            advertise_droid.bluetoothToggleState(True)
+            time.sleep(10)
+            advertise_droid.eventClearBuffer()
+            test_result = advertise_droid.bleStartBleAdvertising(
+                advertise_callback, advertise_data, advertise_settings)
+            time.sleep(5)
+            scan_droid.bleStopBleScan(scan_callback)
+            time.sleep(5)
+            advertise_droid.bleStopBleAdvertising(advertise_callback)
+            looperCount -= 1
+            self.ed1.stop()
+            advertise_droid.bluetoothToggleState(False)
+            time.sleep(5)
+            self.log.debug(" ".join(["Done with iteration", str(looperCount)]))
+        return test_result
diff --git a/acts/tests/google/ble/system_tests/BleOnLostOnFoundStressTest.py b/acts/tests/google/ble/system_tests/BleOnLostOnFoundStressTest.py
new file mode 100644
index 0000000..987b8bb
--- /dev/null
+++ b/acts/tests/google/ble/system_tests/BleOnLostOnFoundStressTest.py
@@ -0,0 +1,226 @@
+# python3.4
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""
+OnLost onFound Stress Test.
+"""
+
+import threading
+import time
+
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
+from acts.test_utils.bt.BleEnum import ScanSettingsCallbackType
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.BleEnum import ScanSettingsMatchMode
+from acts.test_utils.bt.BleEnum import ScanSettingsMatchNum
+from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import orchestrate_gatt_connection
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import run_continuous_write_descriptor
+from acts.test_utils.bt.bt_test_utils import setup_multiple_services
+
+
+class BleOnLostOnFoundStressTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+    max_scan_instances = 28
+    report_delay = 2000
+    active_scan_callback_list = []
+    active_adv_callback_list = []
+    scan_result = "BleScan{}onScanResults"
+    batch_scan_result = "BleScan{}onBatchScanResult"
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        if self.droid_list[1]['max_advertisements'] == 0:
+            self.tests = ()
+            return
+        self.tests = (
+            "test_on_star_while_polling_energy_stats",
+            "test_more_stress_test",
+        )
+
+    def teardown_test(self):
+        cleanup_scanners_and_advertisers(
+            self.scn_droid, self.scn_ed, self.active_adv_callback_list,
+            self.adv_droid, self.adv_ed, self.active_adv_callback_list)
+        self.active_adv_callback_list = []
+        self.active_scan_callback_list = []
+
+    def on_exception(self, test_name, begin_time):
+        reset_bluetooth(self.droids, self.eds)
+
+    def _start_generic_advertisement_include_device_name(self):
+        self.adv_droid.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.adv_droid.bleSetAdvertiseSettingsAdvertiseMode(
+            AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
+        advertise_data = self.adv_droid.bleBuildAdvertiseData()
+        advertise_settings = self.adv_droid.bleBuildAdvertiseSettings()
+        advertise_callback = self.adv_droid.bleGenBleAdvertiseCallback()
+        self.adv_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        self.adv_ed.pop_event(
+            "BleAdvertise{}onSuccess".format(advertise_callback),
+            self.default_timeout)
+        self.active_adv_callback_list.append(advertise_callback)
+        return advertise_callback
+
+    def _verify_no_events_found(self, event_name):
+        try:
+            self.scn_ed.pop_event(event_name, self.default_timeout)
+            self.log.error("Found an event when none was expected.")
+            return False
+        except Empty:
+            self.log.info("No scan result found as expected.")
+            return True
+
+    def _poll_energy(self):
+        import random
+        while True:
+            self.log.debug(
+                self.scn_droid.bluetoothGetControllerActivityEnergyInfo(1))
+            time.sleep(2)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_on_star_while_polling_energy_stats(self):
+        """
+        Tests ...
+        Steps
+        1: ...
+        :return: boolean
+        """
+        thread = threading.Thread(target=self._poll_energy)
+        thread.start()
+
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_FOUND_AND_LOST.value)
+        self.scn_droid.bleSetScanSettingsMatchMode(
+            ScanSettingsMatchMode.AGGRESIVE.value)
+        self.scn_droid.bleSetScanSettingsNumOfMatches(
+            ScanSettingsMatchNum.MATCH_NUM_ONE_ADVERTISEMENT.value)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        on_found_count = 0
+        on_lost_count = 0
+        from contextlib import suppress
+        for x in range(100000):
+            adv_callback = (
+                self._start_generic_advertisement_include_device_name())
+            with suppress(Exception):
+                event = self.scn_ed.pop_event(
+                    self.scan_result.format(scan_callback),
+                    self.default_timeout * 3)
+                if event['data']['CallbackType'] == 2:
+                    on_found_count += 1
+                elif event['data']['CallbackType'] == 4:
+                    on_lost_count += 1
+            self.adv_droid.bleStopBleAdvertising(adv_callback)
+            with suppress(Exception):
+                event2 = self.scn_ed.pop_event(
+                    self.scan_result.format(scan_callback),
+                    self.default_timeout * 4)
+                if event2['data']['CallbackType'] == 2:
+                    on_found_count += 1
+                elif event2['data']['CallbackType'] == 4:
+                    on_lost_count += 1
+        thread.join()
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_more_stress_test(self):
+        gatt_server_callback, gatt_server = setup_multiple_services(
+            self.adv_droid, self.adv_ed)
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.scn_droid, self.scn_ed,
+                                        self.adv_droid, self.adv_ed))
+        self.active_scan_callback_list.append(adv_callback)
+        if self.scn_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.scn_ed.pop_event(
+                "GattConnect{}onServicesDiscovered".format(bluetooth_gatt),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+        else:
+            self.log.info("Failed to discover services.")
+            return False
+        services_count = self.scn_droid.gattClientGetDiscoveredServicesCount(
+            discovered_services_index)
+        thread = threading.Thread(
+            target=run_continuous_write_descriptor, args=(
+                self.scn_droid, self.scn_ed, self.adv_droid, self.adv_ed,
+                gatt_server, gatt_server_callback, bluetooth_gatt,
+                services_count, discovered_services_index))
+        thread.start()
+        thread2 = threading.Thread(target=self._poll_energy)
+        thread2.start()
+
+        filter_list = self.scn_droid.bleGenFilterList()
+        self.scn_droid.bleSetScanFilterDeviceName(
+            self.adv_droid.bluetoothGetLocalName())
+        self.scn_droid.bleSetScanSettingsScanMode(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self.scn_droid.bleSetScanSettingsCallbackType(
+            ScanSettingsCallbackType.CALLBACK_TYPE_FOUND_AND_LOST.value)
+        self.scn_droid.bleSetScanSettingsMatchMode(
+            ScanSettingsMatchMode.AGGRESIVE.value)
+        self.scn_droid.bleSetScanSettingsNumOfMatches(
+            ScanSettingsMatchNum.MATCH_NUM_ONE_ADVERTISEMENT.value)
+        scan_settings = self.scn_droid.bleBuildScanSetting()
+        scan_callback = self.scn_droid.bleGenScanCallback()
+        self.scn_droid.bleBuildScanFilter(filter_list)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+        on_found_count = 0
+        on_lost_count = 0
+        time.sleep(60)
+        from contextlib import suppress
+        for x in range(100000):
+            adv_callback = self._start_generic_advertisement_include_device_name(
+            )
+            with suppress(Exception):
+                event = self.scn_ed.pop_event(
+                    self.scan_result.format(scan_callback),
+                    self.default_timeout * 3)
+                if event['data']['CallbackType'] == 2:
+                    on_found_count += 1
+                elif event['data']['CallbackType'] == 4:
+                    on_lost_count += 1
+            self.adv_droid.bleStopBleAdvertising(adv_callback)
+            with suppress(Exception):
+                event2 = self.scn_ed.pop_event(
+                    self.scan_result.format(scan_callback),
+                    self.default_timeout * 4)
+                if event2['data']['CallbackType'] == 2:
+                    on_found_count += 1
+                elif event2['data']['CallbackType'] == 4:
+                    on_lost_count += 1
+        thread.join()
+        thread2.join()
+        return True
diff --git a/acts/tests/google/ble/system_tests/BleStressTest.py b/acts/tests/google/ble/system_tests/BleStressTest.py
new file mode 100644
index 0000000..54747d2
--- /dev/null
+++ b/acts/tests/google/ble/system_tests/BleStressTest.py
@@ -0,0 +1,281 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""
+Basic LE Stress tests.
+"""
+
+import concurrent
+import pprint
+import time
+
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import scan_result
+
+class BleStressTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.scn_droid, self.scn_ed = self.droids[0], self.eds[0]
+        self.adv_droid, self.adv_ed = self.droids[1], self.eds[1]
+        self.tests = (
+            "test_loop_scanning_1000",
+            "test_restart_scan_callback_after_bt_toggle",
+            "test_loop_scanning_100_verify_no_hci_timeout",
+            "test_start_le_scan_while_toggling_bt",
+        )
+        if self.droid_list[0]['max_advertisements'] > 0:
+            self.tests = self.tests + ("test_loop_advertising_100",
+                                       "test_restart_advertise_callback_after_bt_toggle",)
+
+    def bleadvertise_verify_onsuccess_handler(self, event):
+        test_result = True
+        self.log.debug("Verifying onSuccess event")
+        self.log.debug(pprint.pformat(event))
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_loop_scanning_1000(self):
+        """Stress start/stop scan instances.
+
+        This test will start and stop scan instances as fast as possible. This
+        will guarantee that the scan instances are properly being cleaned up
+        when the scan is stopped.
+
+        Steps:
+        1. Start a scan instance.
+        2. Stop the scan instance.
+        3. Repeat steps 1-2 1000 times.
+
+        Expected Result:
+        Neither starting or stopping scan instances causes any failures.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Stress
+        Priority: 1
+        """
+        scan_droid, scan_event_dispatcher = self.droid, self.ed
+        test_result = True
+        for _ in range(1000):
+            filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+                scan_droid)
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+            self.scn_droid.bleStopBleScan(scan_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_loop_scanning_100_verify_no_hci_timeout(self):
+        """Stress start/stop scan instances variant.
+
+        This test will start and stop scan instances with a one second timeout
+        in between each iteration. This testcase was added because the specific
+        timing combination caused hci timeouts.
+
+        Steps:
+        1. Start a scan instance.
+        2. Stop the scan instance.
+        3. Sleep for 1 second.
+        4. Repeat steps 1-3 100 times.
+
+        Expected Result:
+        Neither starting or stopping scan instances causes any failures.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Stress
+        Priority: 1
+        """
+        adv_callback, adv_data, adv_settings = generate_ble_advertise_objects(
+            self.adv_droid)
+        self.adv_droid.bleStartBleAdvertising(
+            adv_callback, adv_data, adv_settings)
+        test_result = True
+        for _ in range(100):
+            filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+                self.scn_droid)
+            self.scn_droid.bleStartBleScan(
+                filter_list, scan_settings, scan_callback)
+            self.log.info(self.scn_ed.pop_event(
+                scan_result.format(scan_callback)))
+            self.scn_droid.bleStopBleScan(scan_callback)
+            time.sleep(1)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_loop_advertising_100(self):
+        """Stress start/stop advertising instances.
+
+        This test will start and stop advertising instances as fast as possible.
+
+        Steps:
+        1. Start a advertising instance.
+        2. Find that an onSuccess callback is triggered.
+        3. Stop the advertising instance.
+        4. Repeat steps 1-3 100 times.
+
+        Expected Result:
+        Neither starting or stopping advertising instances causes any failures.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Stress
+        Priority: 1
+        """
+        advertise_droid, advertise_event_dispatcher = self.droid, self.ed
+        test_result = True
+        for _ in range(100):
+            advertise_callback, advertise_data, advertise_settings = generate_ble_advertise_objects(
+                advertise_droid)
+            advertise_droid.bleStartBleAdvertising(
+                advertise_callback, advertise_data, advertise_settings)
+            expected_advertise_event_name = "".join(
+                ["BleAdvertise", str(advertise_callback), "onSuccess"])
+            worker = advertise_event_dispatcher.handle_event(
+                self.bleadvertise_verify_onsuccess_handler, expected_advertise_event_name, (
+                    []),
+                self.default_timeout)
+            try:
+                self.log.debug(worker.result(self.default_timeout))
+            except Empty as error:
+                self.log.debug(
+                    " ".join(["Test failed with Empty error:", str(error)]))
+                test_result = False
+            except concurrent.futures._base.TimeoutError as error:
+                self.log.debug(
+                    " ".join(["Test failed, filtering callback onSuccess never occurred:",
+                              str(error)]))
+                test_result = False
+            advertise_droid.bleStopBleAdvertising(advertise_callback)
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_restart_advertise_callback_after_bt_toggle(self):
+        """Test to reuse an advertise callback.
+
+        This will verify if advertising objects can be reused after a bluetooth
+        toggle.
+
+        Steps:
+        1. Start a advertising instance.
+        2. Find that an onSuccess callback is triggered.
+        3. Stop the advertising instance.
+        4. Toggle bluetooth off and on.
+        5. Start an advertising instance on the same objects used in step 1.
+        6. Find that an onSuccess callback is triggered.
+
+        Expected Result:
+        Advertisement should start successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Stress
+        Priority: 1
+        """
+        test_result = True
+        advertise_droid, advertise_event_dispatcher = self.droid, self.ed
+        advertise_callback, advertise_data, advertise_settings = generate_ble_advertise_objects(
+            advertise_droid)
+        advertise_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        expected_advertise_event_name = "".join(
+            ["BleAdvertise", str(advertise_callback), "onSuccess"])
+        worker = advertise_event_dispatcher.handle_event(
+            self.bleadvertise_verify_onsuccess_handler, expected_advertise_event_name, (
+                []),
+            self.default_timeout)
+        try:
+            self.log.debug(worker.result(self.default_timeout))
+        except Empty as error:
+            self.log.debug(
+                " ".join(["Test failed with Empty error:", str(error)]))
+            test_result = False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.debug(
+                " ".join(["Test failed, filtering callback onSuccess never occurred:",
+                          str(error)]))
+        test_result = reset_bluetooth([self.droids[0]], [self.eds[0]])
+        if not test_result:
+            return test_result
+        time.sleep(5)
+        advertise_droid.bleStartBleAdvertising(
+            advertise_callback, advertise_data, advertise_settings)
+        worker = advertise_event_dispatcher.handle_event(
+            self.bleadvertise_verify_onsuccess_handler, expected_advertise_event_name, (
+                []),
+            self.default_timeout)
+        try:
+            self.log.debug(worker.result(self.default_timeout))
+        except Empty as error:
+            self.log.debug(
+                " ".join(["Test failed with Empty error:", str(error)]))
+            test_result = False
+        except concurrent.futures._base.TimeoutError as error:
+            self.log.debug(
+                " ".join(["Test failed, filtering callback onSuccess never occurred:",
+                          str(error)]))
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_restart_scan_callback_after_bt_toggle(self):
+        """Test to reuse an scan callback.
+
+        This will verify if scan objects can be reused after a bluetooth
+        toggle.
+
+        Steps:
+        1. Start a scanning instance.
+        3. Stop the scanning instance.
+        4. Toggle bluetooth off and on.
+        5. Start an scanning instance on the same objects used in step 1.
+
+        Expected Result:
+        Scanner should start successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Scanning, Stress
+        Priority: 1
+        """
+        test_result = True
+        scan_droid, scan_event_dispatcher = self.droid, self.ed
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            scan_droid)
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+        reset_bluetooth([scan_droid], [scan_event_dispatcher])
+        self.scn_droid.bleStartBleScan(
+            filter_list, scan_settings, scan_callback)
+
+        return test_result
diff --git a/acts/tests/google/bt/BtBasicFunctionalityTest.py b/acts/tests/google/bt/BtBasicFunctionalityTest.py
new file mode 100644
index 0000000..609d2b1
--- /dev/null
+++ b/acts/tests/google/bt/BtBasicFunctionalityTest.py
@@ -0,0 +1,419 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 basic functionality test cases.
+This test was designed to be run in a shield box.
+"""
+
+import time
+
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BtEnum import BluetoothScanModeType
+from acts.test_utils.bt.bt_test_utils import check_device_supported_profiles
+from acts.test_utils.bt.bt_test_utils import log_energy_info
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import set_device_name
+from acts.test_utils.bt.bt_test_utils import set_bt_scan_mode
+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
+
+
+class BtBasicFunctionalityTest(BluetoothBaseTest):
+    default_timeout = 10
+    scan_discovery_time = 5
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.tests = (
+            "test_bluetooth_reset",
+            "test_make_device_discoverable",
+            "test_make_device_undiscoverable",
+            "test_set_device_name",
+            "test_scan_mode_off",
+            "test_scan_mode_none",
+            "test_scan_mode_connectable",
+            "test_scan_mode_connectable_discoverable",
+            #"test_if_support_hid_profile",
+            #"test_if_support_hsp_profile",
+            #"test_if_support_a2dp_profile",
+            #"test_if_support_avrcp_profile",
+        )
+
+    def setup_class(self):
+        return setup_multiple_devices_for_bt_test(self.droids, self.eds)
+
+    def setup_test(self):
+        self.log.debug(log_energy_info(self.droids, "Start"))
+        for e in self.eds:
+            e.clear_all_events()
+        return True
+
+    def teardown_test(self):
+        self.log.debug(log_energy_info(self.droids, "End"))
+        return True
+
+    def on_fail(self, test_name, begin_time):
+        take_btsnoop_logs(self.droids, self, test_name)
+        reset_bluetooth(self.droids, self.eds)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_bluetooth_reset(self):
+        """Test resetting bluetooth.
+
+        Test the integrity of resetting bluetooth on Android.
+
+        Steps:
+        1. Toggle bluetooth off.
+        2. Toggle bluetooth on.
+
+        Expected Result:
+        Bluetooth should toggle on and off without failing.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 1
+        """
+        return reset_bluetooth(self.droids, self.eds)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_make_device_discoverable(self):
+        """Test device discoverablity.
+
+        Test that verifies devices is discoverable.
+
+        Steps:
+        1. Initialize two android devices
+        2. Make device1 discoverable
+        3. Check discoverable device1 scan mode
+        4. Make device2 start discovery
+        5. Use device2 get all discovered bluetooth devices list
+        6. Verify device1 is in the list
+
+        Expected Result:
+        Device1 is in the discovered devices list.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 1
+        """
+        droid1 = self.droids[0]
+        droid2 = self.droids[1]
+        droid1.bluetoothMakeDiscoverable()
+        scan_mode = droid1.bluetoothGetScanMode()
+        if (scan_mode ==
+                BluetoothScanModeType.SCAN_MODE_CONNECTABLE_DISCOVERABLE.value):
+            self.log.debug("Android device1 scan mode is "
+                           "SCAN_MODE_CONNECTABLE_DISCOVERABLE")
+        else:
+            self.log.debug("Android device1 scan mode is not "
+                           "SCAN_MODE_CONNECTABLE_DISCOVERABLE")
+            return False
+        if droid2.bluetoothStartDiscovery():
+            self.log.debug("Android device2 start discovery process success")
+            # Give Bluetooth time to discover advertising devices
+            time.sleep(self.scan_discovery_time)
+            droid1_name = droid1.bluetoothGetLocalName()
+            get_all_discovered_devices = droid2.bluetoothGetDiscoveredDevices()
+            find_flag = False
+            if get_all_discovered_devices:
+                self.log.debug("Android device2 get all the discovered devices "
+                               "list {}".format(get_all_discovered_devices))
+                for i in get_all_discovered_devices:
+                    if 'name' in i and i['name'] == droid1_name:
+                        self.log.debug("Android device1 is in the discovery "
+                                       "list of device2")
+                        find_flag = True
+                        break
+            else:
+                self.log.debug("Android device2 get all the discovered devices "
+                               "list is empty")
+                return False
+        else:
+            self.log.debug("Android device2 start discovery process error")
+            return False
+        if not find_flag:
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_make_device_undiscoverable(self):
+        """Test device un-discoverability.
+
+        Test that verifies device is un-discoverable.
+
+        Steps:
+        1. Initialize two android devices
+        2. Make device1 un-discoverable
+        3. Check un-discoverable device1 scan mode
+        4. Make device2 start discovery
+        5. Use device2 get all discovered bluetooth devices list
+        6. Verify device1 is not in the list
+
+        Expected Result:
+        Device1 should not be in the discovered devices list.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 1
+        """
+        droid1 = self.droids[0]
+        droid2 = self.droids[1]
+        droid1.bluetoothMakeUndiscoverable()
+        scan_mode = droid1.bluetoothGetScanMode()
+        if scan_mode == BluetoothScanModeType.SCAN_MODE_NONE.value:
+            self.log.debug("Android device1 scan mode is SCAN_MODE_NONE")
+        else:
+            self.log.debug("Android device1 scan mode is not SCAN_MODE_NONE")
+            return False
+        if droid2.bluetoothStartDiscovery():
+            self.log.debug("Android device2 start discovery process success")
+            # Give Bluetooth time to discover advertising devices
+            time.sleep(self.scan_discovery_time)
+            droid1_name = droid1.bluetoothGetLocalName()
+            get_all_discovered_devices = droid2.bluetoothGetDiscoveredDevices()
+            find_flag = False
+            if get_all_discovered_devices:
+                self.log.debug("Android device2 get all the discovered devices "
+                               "list {}".format(get_all_discovered_devices))
+                for i in get_all_discovered_devices:
+                    if 'name' in i and i['name'] == droid1_name:
+                        self.log.debug(
+                            "Android device1 is in the discovery list of "
+                            "device2")
+                        find_flag = True
+                        break
+            else:
+                self.log.debug("Android device2 found no devices.")
+                return True
+        else:
+            self.log.debug("Android device2 start discovery process error")
+            return False
+        if find_flag:
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_set_device_name(self):
+        """Test bluetooth device name.
+
+        Test that a single device can be set device name.
+
+        Steps:
+        1. Initialize one android devices
+        2. Set device name
+        3. Return true is set device name success
+
+        Expected Result:
+        Bluetooth device name is set correctly.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 1
+        """
+        droid1 = self.droids[0]
+        name = "SetDeviceName"
+        return set_device_name(droid1, name)
+
+    def test_scan_mode_off(self):
+        """Test disabling bluetooth scanning.
+
+        Test that changes scan mode to off.
+
+        Steps:
+        1. Initialize android device.
+        2. Set scan mode STATE_OFF by disabling bluetooth.
+        3. Verify scan state.
+
+        Expected Result:
+        Verify scan state is off.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 1
+        """
+        droid1, event_dispatcher1 = self.droids[0], self.eds[0]
+        self.log.debug("Test scan mode STATE_OFF.")
+        return set_bt_scan_mode(droid1, event_dispatcher1,
+                                BluetoothScanModeType.STATE_OFF.value)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_mode_none(self):
+        """Test bluetooth scan mode none.
+
+        Test that changes scan mode to none.
+
+        Steps:
+        1. Initialize android device.
+        2. Set scan mode SCAN_MODE_NONE by disabling bluetooth.
+        3. Verify scan state.
+
+        Expected Result:
+        Verify that scan mode is set to none.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 1
+        """
+        droid1, event_dispatcher1 = self.droids[0], self.eds[0]
+        self.log.debug("Test scan mode SCAN_MODE_NONE.")
+        return set_bt_scan_mode(droid1, event_dispatcher1,
+                                BluetoothScanModeType.SCAN_MODE_NONE.value)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_mode_connectable(self):
+        """Test bluetooth scan mode connectable.
+
+        Test that changes scan mode to connectable.
+
+        Steps:
+        1. Initialize android device.
+        2. Set scan mode SCAN_MODE_CONNECTABLE.
+        3. Verify scan state.
+
+        Expected Result:
+        Verify that scan mode is set to connectable.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 2
+        """
+        droid1, event_dispatcher1 = self.droids[0], self.eds[0]
+        self.log.debug("Test scan mode SCAN_MODE_CONNECTABLE.")
+        return set_bt_scan_mode(
+            droid1, event_dispatcher1,
+            BluetoothScanModeType.SCAN_MODE_CONNECTABLE.value)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_scan_mode_connectable_discoverable(self):
+        """Test bluetooth scan mode connectable.
+
+        Test that changes scan mode to connectable.
+
+        Steps:
+        1. Initialize android device.
+        2. Set scan mode SCAN_MODE_DISCOVERABLE.
+        3. Verify scan state.
+
+        Expected Result:
+        Verify that scan mode is set to discoverable.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 2
+        """
+        droid1, event_dispatcher1 = self.droids[0], self.eds[0]
+        self.log.debug("Test scan mode SCAN_MODE_CONNECTABLE_DISCOVERABLE.")
+        return set_bt_scan_mode(
+            droid1, event_dispatcher1,
+            BluetoothScanModeType.SCAN_MODE_CONNECTABLE_DISCOVERABLE.value)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_if_support_hid_profile(self):
+        """ Test that a single device can support HID profile.
+        Steps
+        1. Initialize one android devices
+        2. Check devices support profiles and return a dictionary
+        3. Check the value of key 'hid'
+
+        Expected Result:
+        Device1 is in the discovered devices list.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 1
+        """
+        droid1 = self.droids[0]
+        profiles = check_device_supported_profiles(droid1)
+        if not profiles['hid']:
+            self.log.debug("Android device do not support HID profile.")
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_if_support_hsp_profile(self):
+        """ Test that a single device can support HSP profile.
+        Steps
+        1. Initialize one android devices
+        2. Check devices support profiles and return a dictionary
+        3. Check the value of key 'hsp'
+        :return: test_result: bool
+        """
+        droid1 = self.droids[0]
+        profiles = check_device_supported_profiles(droid1)
+        if not profiles['hsp']:
+            self.log.debug("Android device do not support HSP profile.")
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_if_support_a2dp_profile(self):
+        """ Test that a single device can support A2DP profile.
+        Steps
+        1. Initialize one android devices
+        2. Check devices support profiles and return a dictionary
+        3. Check the value of key 'a2dp'
+        :return: test_result: bool
+        """
+        droid1 = self.droids[0]
+        profiles = check_device_supported_profiles(droid1)
+        if not profiles['a2dp']:
+            self.log.debug("Android device do not support A2DP profile.")
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_if_support_avrcp_profile(self):
+        """ Test that a single device can support AVRCP profile.
+        Steps
+        1. Initialize one android devices
+        2. Check devices support profiles and return a dictionary
+        3. Check the value of key 'avrcp'
+        :return: test_result: bool
+        """
+        droid1 = self.droids[0]
+        profiles = check_device_supported_profiles(droid1)
+        if not profiles['avrcp']:
+            self.log.debug("Android device do not support AVRCP profile.")
+            return False
+        return True
diff --git a/acts/tests/google/bt/SppTest.py b/acts/tests/google/bt/SppTest.py
new file mode 100644
index 0000000..c55247f
--- /dev/null
+++ b/acts/tests/google/bt/SppTest.py
@@ -0,0 +1,132 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 basic 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_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import get_bt_mac_address
+from acts.test_utils.bt.bt_test_utils import log_energy_info
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import rfcomm_accept
+from acts.test_utils.bt.bt_test_utils import rfcomm_connect
+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
+
+
+class SppTest(BluetoothBaseTest):
+    default_timeout = 10
+    scan_discovery_time = 5
+    thread_list = []
+    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.server_droid, self.server_ed = self.droids[0], self.eds[0]
+        self.client_droid, self.client_ed = self.droids[1], self.eds[1]
+        self.tests = (
+            "test_spp_connection",
+        )
+
+    def _clear_bonded_devices(self):
+        for d in self.droids:
+            bonded_device_list = d.bluetoothGetBondedDevices()
+            for device in bonded_device_list:
+                d.bluetoothUnbond(device['address'])
+
+    def setup_class(self):
+        return setup_multiple_devices_for_bt_test(self.droids, self.eds)
+
+    def setup_test(self):
+        self._clear_bonded_devices()
+        self.log.debug(log_energy_info(self.droids, "Start"))
+        for e in self.eds:
+            e.clear_all_events()
+        return True
+
+    def teardown_test(self):
+        self.log.debug(log_energy_info(self.droids, "End"))
+        return True
+
+    def on_fail(self, test_name, begin_time):
+        take_btsnoop_logs(self.droids, self, test_name)
+        reset_bluetooth(self.droids, self.eds)
+
+    def teardown_test(self):
+        for thread in self.thread_list:
+            thread.join()
+
+    def orchestrate_rfcomm_connect(self, server_mac):
+        accept_thread = threading.Thread(
+            target=rfcomm_accept, args=(self.server_droid,))
+        self.thread_list.append(accept_thread)
+        accept_thread.start()
+        connect_thread = threading.Thread(
+            target=rfcomm_connect, args=(self.client_droid, server_mac))
+        self.thread_list.append(connect_thread)
+        connect_thread.start()
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_spp_connection(self):
+        """Test bluetooth SPP profile.
+
+        Test SPP profile though establishing an RFCOMM connection.
+
+        Steps:
+        1. Get the mac address of the server device.
+        2. Establish an RFCOMM connection from the client to the server AD.
+        3. Verify that the RFCOMM connection is active from both the client and
+        server.
+        4. Disconnect the RFCOMM connection.
+
+        Expected Result:
+        RFCOMM connection is established then disconnected succcessfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic, SPP, RFCOMM
+        Priority: 1
+        """
+        server_mac = get_bt_mac_address(self.client_droid, self.server_droid)
+        # temporary workaround. Need to find out why I can't connect after I do
+        # a device discovery from get_bt_mac_address
+        reset_bluetooth([self.server_droid], [self.server_ed])
+        self.orchestrate_rfcomm_connect(server_mac)
+        self.log.info("Write message.")
+        self.client_droid.bluetoothRfcommWrite(self.message)
+        self.log.info("Read message.")
+        read_msg = self.server_droid.bluetoothRfcommRead()
+        self.log.info("Verify message.")
+        assert self.message == read_msg, "Mismatch! Read {}".format(read_msg)
+        if len(self.server_droid.bluetoothRfcommActiveConnections()) == 0:
+            self.log.info("No rfcomm connections found on server.")
+            return False
+        if len(self.client_droid.bluetoothRfcommActiveConnections()) == 0:
+            self.log.info("no rfcomm connections found on client.")
+            return False
+        self.client_droid.bluetoothRfcommStop()
+        self.server_droid.bluetoothRfcommStop()
+        return True
diff --git a/acts/tests/google/bt/gatt/GattOverBrEdrTest.py b/acts/tests/google/bt/gatt/GattOverBrEdrTest.py
new file mode 100644
index 0000000..d05eed0
--- /dev/null
+++ b/acts/tests/google/bt/gatt/GattOverBrEdrTest.py
@@ -0,0 +1,541 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 suite for GATT over BR/EDR.
+"""
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import BluetoothGattCharacteristic
+from acts.test_utils.bt.BleEnum import BluetoothGattDescriptor
+from acts.test_utils.bt.BleEnum import BluetoothGattService
+from acts.test_utils.bt.bt_test_utils import descriptor_write
+from acts.test_utils.bt.bt_test_utils import descriptor_write_request
+from acts.test_utils.bt.bt_test_utils import disconnect_gatt_connection
+from acts.test_utils.bt.bt_test_utils import gatt_services_discovered
+from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
+from acts.test_utils.bt.bt_test_utils import get_bt_mac_address
+from acts.test_utils.bt.bt_test_utils import orchestrate_gatt_connection
+from acts.test_utils.bt.bt_test_utils import read_remote_rssi
+from acts.test_utils.bt.bt_test_utils import service_added
+from acts.test_utils.bt.bt_test_utils import setup_gatt_characteristics
+from acts.test_utils.bt.bt_test_utils import setup_gatt_descriptors
+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
+
+class GattOverBrEdrTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+    default_discovery_timeout = 3
+    droid_list = ()
+    per_droid_mac_address = None
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.droid_list = get_advanced_droid_list(self.droids, self.eds)
+        self.cen_droid, self.cen_ed = self.droids[0], self.eds[0]
+        self.per_droid, self.per_ed = self.droids[1], self.eds[1]
+        self.tests = (
+            "test_gatt_bredr_connect",
+            "test_gatt_bredr_connect_trigger_on_read_rssi",
+            "test_gatt_bredr_connect_trigger_on_services_discovered",
+            "test_gatt_bredr_connect_trigger_on_services_discovered_iterate_attributes",
+            "test_gatt_bredr_connect_with_service_uuid_variations",
+            "test_gatt_bredr_connect_multiple_iterations",
+            "test_bredr_write_descriptor_stress",
+        )
+
+    def setup_class(self):
+        self.log.info("Setting up devices for bluetooth testing.")
+        if not setup_multiple_devices_for_bt_test(self.droids, self.eds):
+            return False
+        self.per_droid_mac_address = get_bt_mac_address(
+            self.cen_droid, self.per_droid, False)
+        if not self.per_droid_mac_address:
+            return False
+        return True
+
+    def on_fail(self, test_name, begin_time):
+        take_btsnoop_logs(self.droids, self, test_name)
+        reset_bluetooth(self.droids, self.eds)
+
+    def _setup_characteristics_and_descriptors(self, droid):
+        characteristic_input = [
+            {
+                'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+                'property': BluetoothGattCharacteristic.PROPERTY_WRITE.value |
+                        BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE.value,
+                'permission': BluetoothGattCharacteristic.PROPERTY_WRITE.value
+            },
+            {
+                'uuid': "21c0a0bf-ad51-4a2d-8124-b74003e4e8c8",
+                'property': BluetoothGattCharacteristic.PROPERTY_NOTIFY.value |
+                        BluetoothGattCharacteristic.PROPERTY_READ.value,
+                'permission': BluetoothGattCharacteristic.PERMISSION_READ.value
+            },
+            {
+                'uuid': "6774191f-6ec3-4aa2-b8a8-cf830e41fda6",
+                'property': BluetoothGattCharacteristic.PROPERTY_NOTIFY.value |
+                        BluetoothGattCharacteristic.PROPERTY_READ.value,
+                'permission': BluetoothGattCharacteristic.PERMISSION_READ.value
+            },
+        ]
+        descriptor_input = [
+            {
+                'uuid': "aa7edd5a-4d1d-4f0e-883a-d145616a1630",
+                'property': BluetoothGattDescriptor.PERMISSION_READ.value |
+                        BluetoothGattDescriptor.PERMISSION_WRITE.value,
+            },
+            {
+                'uuid': "76d5ed92-ca81-4edb-bb6b-9f019665fb32",
+                'property': BluetoothGattDescriptor.PERMISSION_READ.value |
+                        BluetoothGattCharacteristic.PERMISSION_WRITE.value,
+            }
+        ]
+        characteristic_list = setup_gatt_characteristics(
+            droid, characteristic_input)
+        descriptor_list = setup_gatt_descriptors(droid, descriptor_input)
+        return characteristic_list, descriptor_list
+
+    def _orchestrate_gatt_disconnection(self, bluetooth_gatt, gatt_callback):
+        self.log.info("Disconnecting from peripheral device.")
+        test_result = disconnect_gatt_connection(
+            self.cen_droid, self.cen_ed, bluetooth_gatt,
+            gatt_callback)
+        if not test_result:
+            self.log.info("Failed to disconnect from peripheral device.")
+            return False
+        return True
+
+    def _iterate_attributes(self, discovered_services_index):
+        services_count = self.cen_droid.gattClientGetDiscoveredServicesCount(
+            discovered_services_index)
+        for i in range(services_count):
+            service = self.cen_droid.gattClientGetDiscoveredServiceUuid(
+                discovered_services_index, i)
+            self.log.info("Discovered service uuid {}".format(service))
+            characteristic_uuids = (
+                self.cen_droid.gattClientGetDiscoveredCharacteristicUuids(
+                    discovered_services_index, i))
+            for characteristic in characteristic_uuids:
+                self.log.info(
+                    "Discovered characteristic uuid {}".format(characteristic))
+                descriptor_uuids = (
+                    self.cen_droid.gattClientGetDiscoveredDescriptorUuids(
+                        discovered_services_index, i, characteristic))
+                for descriptor in descriptor_uuids:
+                    self.log.info(
+                        "Discovered descriptor uuid {}".format(descriptor))
+
+    def _find_service_added_event(self, gatt_server_callback, uuid):
+        event = self.per_ed.pop_event(
+            service_added.format(gatt_server_callback),
+            self.default_timeout)
+        if event['data']['serviceUuid'].lower() != uuid.lower():
+            self.log.info(
+                "Uuid mismatch. Found: {}, Expected {}.".format(
+                    event['data']['serviceUuid'],
+                    uuid))
+            return False
+        return True
+
+    def _setup_multiple_services(self):
+        gatt_server_callback = (
+            self.per_droid.gattServerCreateGattServerCallback())
+        gatt_server = self.per_droid.gattServerOpenGattServer(
+            gatt_server_callback)
+        characteristic_list, descriptor_list = (
+            self._setup_characteristics_and_descriptors(self.per_droid))
+        self.per_droid.gattServerCharacteristicAddDescriptor(
+            characteristic_list[1], descriptor_list[0])
+        self.per_droid.gattServerCharacteristicAddDescriptor(
+            characteristic_list[2], descriptor_list[1])
+        gatt_service = self.per_droid.gattServerCreateService(
+            "00000000-0000-1000-8000-00805f9b34fb",
+            BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+        gatt_service2 = self.per_droid.gattServerCreateService(
+            "FFFFFFFF-0000-1000-8000-00805f9b34fb",
+            BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+        gatt_service3 = self.per_droid.gattServerCreateService(
+            "3846D7A0-69C8-11E4-BA00-0002A5D5C51B",
+            BluetoothGattService.SERVICE_TYPE_PRIMARY.value)
+        for characteristic in characteristic_list:
+            self.per_droid.gattServerAddCharacteristicToService(gatt_service,
+                                                                characteristic)
+        self.per_droid.gattServerAddService(gatt_server, gatt_service)
+        result = self._find_service_added_event(
+            gatt_server_callback,
+            "00000000-0000-1000-8000-00805f9b34fb")
+        if not result:
+            return False
+        for characteristic in characteristic_list:
+            self.per_droid.gattServerAddCharacteristicToService(gatt_service2,
+                                                                characteristic)
+        self.per_droid.gattServerAddService(gatt_server, gatt_service2)
+        result = self._find_service_added_event(
+            gatt_server_callback,
+            "FFFFFFFF-0000-1000-8000-00805f9b34fb")
+        if not result:
+            return False
+        for characteristic in characteristic_list:
+            self.per_droid.gattServerAddCharacteristicToService(gatt_service3,
+                                                                characteristic)
+        self.per_droid.gattServerAddService(gatt_server, gatt_service3)
+        result = self._find_service_added_event(
+            gatt_server_callback,
+            "3846D7A0-69C8-11E4-BA00-0002A5D5C51B")
+        if not result:
+            return False, False
+        return gatt_server_callback, gatt_server
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_bredr_connect(self):
+        """Test GATT connection over BR/EDR.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: BR/EDR, Filtering, GATT, Scanning
+        Priority: 0
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed, False,
+                                        self.per_droid_mac_address))
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_bredr_connect_trigger_on_read_rssi(self):
+        """Test GATT connection over BR/EDR read RSSI.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client then read the RSSI.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner, request to read the RSSI of the advertiser.
+        7. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully. Verify that the RSSI was ready correctly.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: BR/EDR, Scanning, GATT, RSSI
+        Priority: 1
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed, False,
+                                        self.per_droid_mac_address))
+        if self.cen_droid.gattClientReadRSSI(bluetooth_gatt):
+            self.cen_ed.pop_event(
+                read_remote_rssi.format(gatt_callback), self.default_timeout)
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_bredr_connect_trigger_on_services_discovered(self):
+        """Test GATT connection and discover services of peripheral.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client the discover all services from the connected device.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner (central device), discover services.
+        7. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully. Verify that the service were discovered.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: BR/EDR, Scanning, GATT, Services
+        Priority: 1
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed, False,
+                                        self.per_droid_mac_address))
+        discovered_services_index = -1
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_bredr_connect_trigger_on_services_discovered_iterate_attributes(self):
+        """Test GATT connection and iterate peripherals attributes.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client and iterate over all the characteristics and descriptors of the
+        discovered services.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner (central device), discover services.
+        7. Iterate over all the characteristics and descriptors of the
+        discovered features.
+        8. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully. Verify that the services, characteristics, and descriptors
+        were discovered.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: BR/EDR, Scanning, GATT, Services
+        Characteristics, Descriptors
+        Priority: 1
+        """
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed, False,
+                                        self.per_droid_mac_address))
+        discovered_services_index = -1
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+            self._iterate_attributes(discovered_services_index)
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_bredr_connect_with_service_uuid_variations(self):
+        """Test GATT connection with multiple service uuids.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client with multiple service uuid variations.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. From the scanner (central device), discover services.
+        7. Verify that all the service uuid variations are found.
+        8. Disconnect the GATT connection.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully. Verify that the service uuid variations are found.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: BR/EDR, Scanning, GATT, Services
+        Priority: 2
+        """
+        gatt_server_callback, gatt_server = self._setup_multiple_services()
+        if not gatt_server_callback or not gatt_server:
+            return False
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed, False,
+                                        self.per_droid_mac_address))
+        discovered_services_index = -1
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+            self._iterate_attributes(discovered_services_index)
+        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
+                                                    gatt_callback)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_gatt_bredr_connect_multiple_iterations(self):
+        """Test GATT connections multiple times.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client with multiple iterations.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. Disconnect the GATT connection.
+        7. Repeat steps 5 and 6 twenty times.
+
+        Expected Result:
+        Verify that a connection was established and then disconnected
+        successfully twenty times.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: BR/EDR, Scanning, GATT, Stress
+        Priority: 1
+        """
+        autoconnect = False
+        mac_address = get_bt_mac_address(self.cen_droid, self.per_droid)
+        for i in range(20):
+            bluetooth_gatt, gatt_callback, adv_callback = (
+                orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                            self.per_droid, self.per_ed, False,
+                                            self.per_droid_mac_address))
+            self.log.info("Disconnecting from peripheral device.")
+            test_result = self._orchestrate_gatt_disconnection(
+                bluetooth_gatt, gatt_callback)
+            if not test_result:
+                self.log.info("Failed to disconnect from peripheral device.")
+                return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_bredr_write_descriptor_stress(self):
+        """Test GATT connection writing and reading descriptors.
+
+        Test establishing a gatt connection between a GATT server and GATT
+        client with multiple service uuid variations.
+
+        Steps:
+        1. Start a generic advertisement.
+        2. Start a generic scanner.
+        3. Find the advertisement and extract the mac address.
+        4. Stop the first scanner.
+        5. Create a GATT connection between the scanner and advertiser.
+        6. Discover services.
+        7. Write data to the descriptors of each characteristic 100 times.
+        8. Read the data sent to the descriptors.
+        9. Disconnect the GATT connection.
+
+        Expected Result:
+        Each descriptor in each characteristic is written and read 100 times.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: BR/EDR, Scanning, GATT, Stress, Characteristics, Descriptors
+        Priority: 1
+        """
+        gatt_server_callback, gatt_server = self._setup_multiple_services()
+        if not gatt_server_callback or not gatt_server:
+            return False
+        bluetooth_gatt, gatt_callback, adv_callback = (
+            orchestrate_gatt_connection(self.cen_droid, self.cen_ed,
+                                        self.per_droid, self.per_ed, False,
+                                        self.per_droid_mac_address))
+        if self.cen_droid.gattClientDiscoverServices(bluetooth_gatt):
+            event = self.cen_ed.pop_event(
+                gatt_services_discovered.format(gatt_callback),
+                self.default_timeout)
+            discovered_services_index = event['data']['ServicesIndex']
+        else:
+            self.log.info("Failed to discover services.")
+            return False
+        services_count = self.cen_droid.gattClientGetDiscoveredServicesCount(
+            discovered_services_index)
+
+        connected_device_list = self.per_droid.gattServerGetConnectedDevices(
+            gatt_server)
+        if len(connected_device_list) == 0:
+            self.log.info("No devices connected from peripheral.")
+            return False
+        bt_device_id = 0
+        status = 1
+        offset = 1
+        test_value = "1,2,3,4,5,6,7"
+        test_value_return = "1,2,3"
+        for i in range(services_count):
+            characteristic_uuids = (
+                self.cen_droid.gattClientGetDiscoveredCharacteristicUuids(
+                    discovered_services_index, i))
+            for characteristic in characteristic_uuids:
+                descriptor_uuids = (
+                    self.cen_droid.gattClientGetDiscoveredDescriptorUuids(
+                        discovered_services_index, i, characteristic))
+                for _ in range(100):
+                    for descriptor in descriptor_uuids:
+                        self.cen_droid.gattClientDescriptorSetValue(
+                            bluetooth_gatt, discovered_services_index, i,
+                            characteristic, descriptor, test_value)
+                        self.cen_droid.gattClientWriteDescriptor(
+                            bluetooth_gatt, discovered_services_index, i,
+                            characteristic, descriptor)
+                        event = self.per_ed.pop_event(
+                            descriptor_write_request.format(gatt_callback),
+                            self.default_timeout)
+                        self.log.info(
+                            "onDescriptorWriteRequest event found: {}".format(
+                                event))
+                        request_id = event['data']['requestId']
+                        found_value = event['data']['value']
+                        if found_value != test_value:
+                            self.log.info("Values didn't match. Found: {}, "
+                                          "Expected: {}".format(found_value,
+                                                                test_value))
+                            return False
+                        self.per_droid.gattServerSendResponse(
+                            gatt_server, bt_device_id, request_id, status,
+                            offset, test_value_return)
+                        self.log.info(
+                            "onDescriptorWrite event found: {}".format(
+                                self.cen_ed.pop_event(
+                                descriptor_write.format(bluetooth_gatt),
+                                self.default_timeout)))
+        return True
diff --git a/acts/tests/google/bt/setup/BtPreFlightTest.py b/acts/tests/google/bt/setup/BtPreFlightTest.py
new file mode 100644
index 0000000..cba93fc
--- /dev/null
+++ b/acts/tests/google/bt/setup/BtPreFlightTest.py
@@ -0,0 +1,70 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See thea
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""
+Bluetooth Pre-Flight Test.
+"""
+
+from acts.base_test import BaseTestClass
+import os
+import pprint
+
+
+class BtPreFlightTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_setup_logging",
+        )
+
+    def setup_class(self):
+        for d in self.droids:
+            serial = d.getBuildSerial()
+            self.log.debug("****START: {} DEVICE INFO****".format(serial))
+            self.log.debug(
+                "BOOTLOADER VERSION {}".format(d.getBuildBootloader()))
+            self.log.debug("BUILD HARDWARE {}".format(d.getBuildHardware()))
+            self.log.debug("BUILD PRODUCT {}".format(d.getBuildProduct()))
+            self.log.debug("*ENVIRONMENT DETAILS*")
+            self.log.debug(pprint.pformat(d.environment()))
+            self.log.debug("****END: {} DEVICE INFO****".format(serial))
+        return True
+
+    def test_setup_logging(self):
+        conf_path = "{}/bt_stack.conf".format(
+            os.path.dirname(os.path.realpath(__file__)))
+        log_level_check = "TRC_BTM=5"
+        remount_check = "remount succeeded"
+        for ad in self.android_devices:
+            remount_result = ad.adb.remount()
+            if remount_check not in str(remount_result):
+                # Test for devices that have disable_verity as not all do
+                try:
+                    ad.adb.disable_verity()
+                    ad.reboot()
+                    remount_result = ad.adb.remount()
+                    if remount_check not in str(remount_result):
+                        self.abort_all("Unable to remount device")
+                except Exception as e:
+                    self.abort_all("Exception in BT pre-flight test: {}"
+                                   .format(e))
+            ad.adb.push(
+                "{} /system/etc/bluetooth/bt_stack.conf".format(conf_path))
+            result = ad.adb.shell("cat /system/etc/bluetooth/bt_stack.conf")
+            # Verify that the log levels have been raised
+            if log_level_check not in str(result):
+                self.abort_all("BT log levels not set")
+        return True
diff --git a/acts/tests/google/bt/setup/bt_stack.conf b/acts/tests/google/bt/setup/bt_stack.conf
new file mode 100644
index 0000000..b56fd13
--- /dev/null
+++ b/acts/tests/google/bt/setup/bt_stack.conf
@@ -0,0 +1,38 @@
+# Enable BtSnoop logging function
+# valid value : true, false
+BtSnoopLogOutput=true
+
+# BtSnoop log output file
+BtSnoopFileName=/sdcard/btsnoop_hci.log
+
+# Preserve existing BtSnoop log before overwriting
+BtSnoopSaveLog=true
+
+# Enable trace level reconfiguration function
+# Must be present before any TRC_ trace level settings
+TraceConf=true
+
+# Trace level configuration
+#   BT_TRACE_LEVEL_NONE    0    ( No trace messages to be generated )
+#   BT_TRACE_LEVEL_ERROR   1    ( Error condition trace messages )
+#   BT_TRACE_LEVEL_WARNING 2    ( Warning condition trace messages )
+#   BT_TRACE_LEVEL_API     3    ( API traces )
+#   BT_TRACE_LEVEL_EVENT   4    ( Debug messages for events )
+#   BT_TRACE_LEVEL_DEBUG   5    ( Full debug messages )
+#   BT_TRACE_LEVEL_VERBOSE 6    ( Verbose messages ) - Currently supported for TRC_BTAPP only.
+TRC_BTM=5
+TRC_HCI=5
+TRC_L5CAP=5
+TRC_RFCOMM=5
+TRC_OBEX=5
+TRC_AVCT=5
+TRC_AVDT=5
+TRC_AVRC=5
+TRC_AVDT_SCB=5
+TRC_AVDT_CCB=5
+TRC_A5D=5
+TRC_SDP=5
+TRC_GATT=5
+TRC_SMP=5
+TRC_BTAPP=5
+TRC_BTIF=5
\ No newline at end of file
diff --git a/acts/tests/google/bt/system_tests/BtStressTest.py b/acts/tests/google/bt/system_tests/BtStressTest.py
new file mode 100644
index 0000000..847ea57
--- /dev/null
+++ b/acts/tests/google/bt/system_tests/BtStressTest.py
@@ -0,0 +1,86 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""
+Basic Bluetooth Classic stress tests.
+"""
+
+from acts.base_test import BaseTestClass
+from acts.test_utils.bt.bt_test_utils import log_energy_info
+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
+
+
+class BtStressTest(BaseTestClass):
+    tests = None
+    default_timeout = 10
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_toggle_bluetooth",
+        )
+
+    def setup_class(self):
+        self.droid1, self.ed1 = self.droids[1], self.eds[1]
+        return setup_multiple_devices_for_bt_test(self.droids, self.eds)
+
+    def setup_test(self):
+        return reset_bluetooth(self.droids, self.eds)
+
+    def setup_test(self):
+        setup_result = reset_bluetooth(self.droids, self.eds)
+        self.log.debug(log_energy_info(self.droids, "Start"))
+        for e in self.eds:
+            e.clear_all_events()
+        return setup_result
+
+    def teardown_test(self):
+        self.log.debug(log_energy_info(self.droids, "End"))
+        return True
+
+    def test_toggle_bluetooth(self):
+        """Stress test toggling bluetooth on and off.
+
+        Test the integrity of toggling bluetooth on and off.
+
+        Steps:
+        1. Toggle bluetooth off.
+        2. Toggle bluetooth on.
+        3. Repeat steps 1 and 2 one-hundred times.
+
+        Expected Result:
+        Each iteration of toggling bluetooth on and off should not cause an
+        exception.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic, Stress
+        Priority: 1
+        """
+        droid, ed = self.droid, self.ed
+        n = 0
+        test_result = True
+        test_result_list = []
+        while n < 100:
+            self.log.info("Toggling bluetooth iteration {}.".format(n))
+            test_result = reset_bluetooth([droid], [ed])
+            test_result_list.append(test_result)
+            n += 1
+        if False in test_result_list:
+            return False
+        return test_result
diff --git a/acts/tests/google/bt/system_tests/SppStressTest.py b/acts/tests/google/bt/system_tests/SppStressTest.py
new file mode 100644
index 0000000..1e46608
--- /dev/null
+++ b/acts/tests/google/bt/system_tests/SppStressTest.py
@@ -0,0 +1,123 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 basic functionality test cases.
+This test was designed to be run in a shield box.
+"""
+
+import threading
+import time
+from contextlib import suppress
+
+
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import get_bt_mac_address
+from acts.test_utils.bt.bt_test_utils import rfcomm_accept
+from acts.test_utils.bt.bt_test_utils import rfcomm_connect
+from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+
+
+class SppStressTest(BluetoothBaseTest):
+    default_timeout = 10
+    scan_discovery_time = 5
+    thread_list = []
+    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.server_droid, self.server_ed = self.droids[0], self.eds[0]
+        self.client_droid, self.client_ed = self.droids[1], self.eds[1]
+        self.tests = (
+            "test_rfcomm_connection_stress",
+        )
+
+    def _clear_bonded_devices(self):
+        for d in self.droids:
+            bonded_device_list = d.bluetoothGetConnectedDevices()
+            for device in bonded_device_list:
+                d.bluetoothUnbond(device)
+
+
+    def on_fail(self, test_name, begin_time):
+        take_btsnoop_logs(self.droids, self, test_name)
+        reset_bluetooth(self.droids, self.eds)
+
+    def teardown_test(self):
+        with suppress(Exception):
+            for thread in self.thread_list:
+                thread.join()
+
+    def orchestrate_rfcomm_connect(self, server_mac):
+        accept_thread = threading.Thread(
+            target=rfcomm_accept, args=(self.server_droid,))
+        self.thread_list.append(accept_thread)
+        accept_thread.start()
+        connect_thread = threading.Thread(
+            target=rfcomm_connect, args=(self.client_droid, server_mac))
+        self.thread_list.append(connect_thread)
+        connect_thread.start()
+
+    def test_rfcomm_connection_stress(self):
+        """Stress test an RFCOMM connection
+
+        Test the integrity of RFCOMM. Verify that file descriptors are cleared
+        out properly.
+
+        Steps:
+        1. Establish a bonding between two Android devices.
+        2. Write data to RFCOMM from the client droid.
+        3. Read data from RFCOMM from the server droid.
+        4. Stop the RFCOMM connection.
+        5. Repeat steps 2-4 1000 times.
+
+        Expected Result:
+        Each iteration should read and write to the RFCOMM connection
+        successfully.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic, Stress, RFCOMM, SPP
+        Priority: 1
+        """
+        server_mac = get_bt_mac_address(self.client_droid, self.server_droid)
+        self._clear_bonded_devices()
+        # temporary workaround. Need to find out why I can't connect after
+        # I do a device discovery from get_bt_mac_address.
+        reset_bluetooth([self.server_droid], [self.server_ed])
+        for n in range(1000):
+            self.orchestrate_rfcomm_connect(server_mac)
+            self.log.info("Write message.")
+            self.client_droid.bluetoothRfcommWrite(self.message)
+            self.log.info("Read message.")
+            read_msg = self.server_droid.bluetoothRfcommRead()
+            self.log.info("Verify message.")
+            assert self.message == read_msg, "Mismatch! Read {}".format(
+                read_msg)
+            self.client_droid.bluetoothRfcommStop()
+            self.server_droid.bluetoothRfcommStop()
+            for t in self.thread_list:
+                t.join()
+            self.thread_list.clear()
+            self.log.info("Iteration {} completed".format(n))
+        return True
diff --git a/acts/tests/google/bt/test_tools/BtReconnectTest.py b/acts/tests/google/bt/test_tools/BtReconnectTest.py
new file mode 100644
index 0000000..3875862
--- /dev/null
+++ b/acts/tests/google/bt/test_tools/BtReconnectTest.py
@@ -0,0 +1,81 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import *
+
+
+class BtReconnectTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.tests = (
+            "test_tool_reconnect",
+        )
+
+    def setup_class(self):
+        return setup_multiple_devices_for_bt_test(self.droids, self.eds)
+
+    def setup_test(self):
+        return reset_bluetooth(self.droids, self.eds)
+
+    def setup_test(self):
+        setup_result = reset_bluetooth(self.droids, self.eds)
+        self.log.debug(log_energy_info(self.droids, "Start"))
+        return setup_result
+
+    def teardown_test(self):
+        self.log.debug(log_energy_info(self.droids, "End"))
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_tool_reconnect(self):
+        droid, ed = self.droid, self.ed
+        n = 0
+        test_result = True
+        test_result_list = []
+        sleep_time = input(
+            "Assumption: Android Device is already paired.\nEnter sleep time before toggling bluetooth back on in milliseconds:")
+        sleep_time_ms = int(sleep_time) / 1000
+        iteration_count = input("Enter number of iterations:")
+        while n < int(iteration_count):
+            self.log.info("Test iteration {}.".format(n))
+            test_result = True
+            self.log.info("Toggling BT state off...")
+            droid.bluetoothToggleState(False)
+            self.log.info("Sleeping {} milliseconds".format(sleep_time))
+            time.sleep(sleep_time_ms)
+            self.log.info("Toggling BT state on...")
+            droid.bluetoothToggleState(True)
+            start_time = time.time()
+            connected_devices = droid.bluetoothGetConnectedDevices()
+            self.log.info(
+                "Waiting up to 10 seconds for device to reconnect...")
+            while time.time() < start_time + 10 and len(connected_devices) != 1:
+                connected_devices = droid.bluetoothGetConnectedDevices()
+                if len(connected_devices) > 0:
+                    break
+            if len(connected_devices) != 1:
+                print (
+                    "Failed to reconnect at iteration {}... continuing".format(n))
+            test_result_list.append(test_result)
+            n += 1
+        if False in test_result_list:
+            return False
+        return test_result
diff --git a/acts/tests/google/bt/test_tools/EnergyTest.py b/acts/tests/google/bt/test_tools/EnergyTest.py
new file mode 100644
index 0000000..101b3ed
--- /dev/null
+++ b/acts/tests/google/bt/test_tools/EnergyTest.py
@@ -0,0 +1,39 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+"""
+Continuously poll for energy info for a single Android Device
+"""
+
+from contextlib import suppress
+from queue import Empty
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+
+
+class EnergyTest(BluetoothBaseTest):
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.tests = (
+            "test_continuous_energy_report",
+        )
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_continuous_energy_report(self):
+        while (True):
+            with suppress(Exception):
+                self.log.info(
+                    self.droids[0].bluetoothGetControllerActivityEnergyInfo(1))
+        return True
diff --git a/acts/tests/google/bt/test_tools/ToolsTest.py b/acts/tests/google/bt/test_tools/ToolsTest.py
new file mode 100644
index 0000000..4cff88e
--- /dev/null
+++ b/acts/tests/google/bt/test_tools/ToolsTest.py
@@ -0,0 +1,122 @@
+# python3.4
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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_test_utils import *
+
+import time
+import pprint
+
+
+class ToolsTest(BluetoothBaseTest):
+    tests = None
+    default_timeout = 10
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.tests = (
+            "test_toggle_bluetooth",
+            "test_toggle_airplane_mode",
+            "test_create_10_sms",
+            "test_continuously_log_battery_stats",
+
+        )
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_toggle_bluetooth(self):
+        """
+        Test the integrity of toggling bluetooth on and off.
+        Steps:
+        1. Toggle bluetooth off.
+        2. Toggle bluetooth on.
+        3. Repeat steps 1 and 2 one-hundred times.
+        :return: boolean test_result
+        """
+        droid, ed = self.droid, self.ed
+        n = 0
+        test_result = True
+        test_result_list = []
+        while n < 100:
+            self.log.info("Toggling bluetooth iteration {}.".format(n))
+            test_result = reset_bluetooth([droid], [ed])
+            start_time = time.time()
+            connected_devices = droid.bluetoothGetConnectedDevices()
+            print (pprint.pformat(connected_devices))
+            while time.time() < start_time + 10 and len(connected_devices) != 1:
+                time.sleep(1)
+                connected_devices = droid.bluetoothGetConnectedDevices()
+                print (pprint.pformat(connected_devices))
+            if len(connected_devices) != 1:
+                print ("died at iteration {}".format(n))
+                return False
+            test_result_list.append(test_result)
+            n += 1
+        if False in test_result_list:
+            return False
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_toggle_airplane_mode(self):
+        """
+        Test the integrity of toggling airplane mode on and off.
+        Steps:
+        1. Toggle airplane off.
+        2. Toggle airplane on.
+        3. Repeat steps 1 and 2 one-hundred times.
+        :return: boolean test_result
+        """
+        droid, ed = self.droid, self.ed
+        n = 0
+        test_result = True
+        test_result_list = []
+        while n < 100:
+            self.log.info("Toggling bluetooth iteration {}.".format(n))
+            droid.toggleAirplaneMode(True)
+            time.sleep(6)
+            droid.toggleAirplaneMode(False)
+            start_time = time.time()
+            connected_devices = droid.bluetoothGetConnectedDevices()
+            print (pprint.pformat(connected_devices))
+            while time.time() < start_time + 10 and len(connected_devices) != 1:
+                time.sleep(1)
+                connected_devices = droid.bluetoothGetConnectedDevices()
+                print (pprint.pformat(connected_devices))
+            if len(connected_devices) != 1:
+                print ("died at iteration {}".format(n))
+                return False
+            test_result_list.append(test_result)
+            n += 1
+        if False in test_result_list:
+            return False
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_create_10_sms(self):
+        phone_number = input("Enter a phone number: ")
+        message_size = input("Enter message size: ")
+        for _ in range(10):
+            self.droid.smsSendTextMessage(
+                phone_number, generate_id_by_size(int(message_size)), False)
+            time.sleep(3)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_continuously_log_battery_stats(self):
+        interval = input("Enter time interval to collect stats: ")
+        while True:
+            self.log.info(
+                log_energy_info([self.droids[0]], "Log_time: {}".format(time.time())))
+            time.sleep(int(interval))
+        return True
diff --git a/acts/tests/google/sanity/ActsBaseClassTest.py b/acts/tests/google/sanity/ActsBaseClassTest.py
new file mode 100644
index 0000000..42e3a62
--- /dev/null
+++ b/acts/tests/google/sanity/ActsBaseClassTest.py
@@ -0,0 +1,261 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2015 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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.base_test import BaseTestClass
+from acts.signals import generated_test
+from acts.signals import TestSignal
+from acts.signals import TestSignalError
+from acts.utils import sync_device_time
+
+class Something:
+    """Empty class used to test json serialization check."""
+
+class ActsBaseClassTest(BaseTestClass):
+    """This test class tests the implementation of BaseTestClass.
+
+    Including:
+    - Different ways to mark the result of a test case.
+    - Test case name convention enforcement
+    - None existent test case handling.
+    """
+    EXTRA_ARG = "An extra arg"
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_none_existent",
+            "invalid_test_name",
+            "test_uncaught_exception",
+            "test_return_True",
+            "test_implicit_pass",
+            "test_return_False",
+            "test_fail",
+            "test_fail_with_int_extra",
+            "test_explicit_pass",
+            "test_explicit_pass_with_str_extra",
+            "test_assert_true",
+            "test_assert_true_with_extras",
+            "test_skip",
+            "test_skip_with_extras",
+            "test_skip_if",
+            "test_generated_tests",
+            "test_never"
+        )
+
+    def setup_class(self):
+        self.log.info("In setup_class.")
+        return True
+
+    def on_success(self, test_name, begin_time):
+        self.log.info("In on_success.")
+        msg = "%s should not have passed." % test_name
+        expected_success = (
+            "test_return_True",
+            "test_generated_return_True",
+            "test_generated_tests",
+            "test_implicit_pass",
+            "test_explicit_pass_with_str_extra",
+            "test_generated_implicit_pass",
+            "test_generated_explicit_pass_with_str_extra",
+            "test_test_args",
+            "test_explicit_pass",
+            "test_generated_explicit_pass",
+            "test_invalid_signal_details",
+            "test_invalid_signal_extras"
+        )
+        assert test_name in expected_success, msg
+
+    def on_fail(self, test_name, begin_time):
+        self.log.info("In on_fail.")
+        if test_name == "test_assert_true":
+            msg = ("Raising an exception to make sure exceptions in procedure "
+                "functions don't crash the test framework.")
+            self.log.info(msg)
+            raise Exception("Excepted exception.")
+
+    def on_skip(self, test_name, begin_time):
+        self.log.info("In on_skip")
+        msg = "%s should not have been skipped." % test_name
+        expected_skip = (
+            "test_skip",
+            "test_skip_with_extras",
+            "test_skip_if",
+            "test_generated_skip",
+            "test_generated_skip_if",
+            "test_generated_skip_with_extras",
+            "test_unsolicited_test_args",
+            "test_explicit_test_args_skip"
+        )
+        assert test_name in expected_skip, msg
+
+    def on_exception(self, test_name, begin_time):
+        self.log.info("In on_exception")
+        msg = "%s should not have thrown exception." % test_name
+        expected_exception = (
+            "test_uncaught_exception",
+            "test_generated_uncaught_exception"
+        )
+        assert test_name in expected_exception , msg
+
+    def generated_test_logic(self, param, extra_arg):
+        """Execute all the test_ functions in the generated test case.
+
+        Args:
+            param: The partial name of the test function to executed.
+            extra_arg: An extra arg added to make sure passing extra args work.
+        """
+        self.log.info("This is a generated test case with param %s" % param)
+        assert extra_arg == self.EXTRA_ARG, "Wrong extra arg %s" % extra_arg
+        # In case we want to add more fields to param, using a local var here.
+        t = param
+        test_func = getattr(self, "test_%s" % t)
+        return test_func()
+
+    def name_gen(self, param, extra_arg):
+        return "test_generated_%s" % param
+
+    """ Begin of Tests """
+
+    def invalid_test_name(self):
+        assert False, "This should never be executed!"
+
+    def test_uncaught_exception(self):
+        raise Exception("This should fail because of uncaught exception.")
+
+    def test_return_True(self):
+        self.log.info("This should pass because return True.")
+        return True
+
+    def test_implicit_pass(self):
+        self.log.info("This should pass because no error happened.")
+
+    def test_return_False(self):
+        self.log.info("This should fail because returned False.")
+        return False
+
+    def test_fail(self):
+        self.fail("Expected failure with explicit fail.")
+
+    def test_explicit_pass(self):
+        self.explicit_pass("Expected pass with explicit pass.")
+
+    def test_explicit_pass_with_str_extra(self):
+        self.explicit_pass("Should fail because asserting True on False.",
+                  extras="This is a string extra.")
+
+    def test_assert_true(self):
+        self.assert_true(False, "Should fail because asserting True on False.")
+
+    def test_assert_true_with_extras(self):
+        self.assert_true(False, "Should fail because asserting True on False.",
+                         extras={
+                             "what is this": "An extra!",
+                             "what happened": "I failed!",
+                             "cause_code": "haha"
+                         })
+
+    def test_fail_with_int_extra(self):
+        self.fail("Should fail because asserting True on False.", extras=0)
+
+    def test_skip(self):
+        self.skip("Expected skip.")
+
+    def test_skip_with_extras(self):
+        self.skip("Expected skip.",
+                  extras={
+                      "what is this": "An extra!",
+                      "what happened": "I skipped!",
+                      "cause_code": "haha"
+                  })
+
+    def test_skip_if(self):
+        self.skip_if(True, "Expected skip.")
+
+    def test_abort_class(self):
+        self.abort_class("Expected abortion of this test class.")
+
+    def test_abort_class_if(self):
+        self.abort_class_if(True, "This is expected to abort this test class.")
+
+    def test_abort_all(self):
+        self.abort_all("This is expected to abort all remaining tests.")
+
+    def test_abort_all_if(self):
+        msg = "This is expected to abort all remaining tests."
+        self.abort_all_if(True, msg)
+
+    def test_never(self):
+        self.log.error("This test should never happen.")
+        self.assert_true(False, "BAD!!")
+
+    def test_test_args(self, *args):
+        self.log.info("Got cli args: {}".format(args))
+        self.assert_true(args, ("You should specify at least one arg with "
+            "--test_args for this test."))
+
+    def test_explicit_test_args_skip(self, one_arg):
+        self.log.error("Got cli arg: {}. This test should have been skipped. "
+            "You should either specify more than one for --test_args, or no "
+            "--test_args at all.".format(one_arg))
+        self.assert_true(False, "BAD!!")
+
+    def test_unsolicited_test_args(self):
+        self.log.error("This test should have been skipped. Did you run with "
+            "--test_args specified?")
+        self.assert_true(False, "BAD!!")
+
+    def test_invalid_signal_details(self):
+        sth = Something()
+        try:
+            TestSignal(sth)
+        except TestSignalError:
+            self.explicit_pass("Got expected exception TestSignalError.")
+        self.fail("This line should not have executed.")
+
+    def test_invalid_signal_extras(self):
+        sth = Something()
+        try:
+            TestSignal("test", extras=sth)
+        except TestSignalError:
+            self.explicit_pass("Got expected exception TestSignalError.")
+        self.fail("This line should not have executed.")
+
+    @generated_test
+    def test_generated_tests(self):
+        params = [
+            "return_False",
+            "assert_true",
+            "assert_true_with_extras",
+            "implicit_pass",
+            "explicit_pass",
+            "explicit_pass_with_str_extra",
+            "fail",
+            "fail_with_int_extra",
+            "skip",
+            "skip_with_extras",
+            "skip_if",
+            "uncaught_exception",
+            "abort_class",
+            "never"
+        ]
+        failed = self.run_generated_testcases(
+            self.generated_test_logic,
+            params, self.EXTRA_ARG,
+            name_func=self.name_gen)
+        expected_fails = params[1:]
+        self.assert_true(expected_fails == failed, "This should pass.")
+        return True
+    """ End of Tests """
diff --git a/acts/tests/google/sanity/ActsRecordsTest.py b/acts/tests/google/sanity/ActsRecordsTest.py
new file mode 100644
index 0000000..ed7b92a
--- /dev/null
+++ b/acts/tests/google/sanity/ActsRecordsTest.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2015 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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.base_test import BaseTestClass
+from acts.records import TestResult
+from acts.records import TestResultEnums
+from acts.records import TestResultRecord
+from acts.signals import TestFailure
+from acts.signals import TestPass
+from acts.signals import TestSkip
+
+
+class ActsRecordsTest(BaseTestClass):
+    """This test class tests the implementation of classes in acts.records.
+    """
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_result_record_pass_none",
+            "test_result_record_pass_with_info",
+            "test_result_record_fail_none",
+            "test_result_record_fail_with_info",
+            "test_result_record_skip_none",
+            "test_result_record_skip_with_info"
+        )
+        self.tn = "test_name"
+        self.details = "Some details about the test execution."
+        self.code = 12345.56789
+
+    def name_gen(self, param, extra_arg):
+        return "test_generated_%s" % param
+
+    def verify_record(self, record, result, details, code):
+        # Verify each field.
+        self.assert_true(record.test_name == self.tn, ("Expected test name %s,"
+            " got %s") % (self.tn, record.test_name))
+        self.assert_true(record.result == result, ("Expected test result %s, "
+            "got %s") % (result, record.result))
+        self.assert_true(record.details == details, ("Expected test details %s"
+            ", got %s") % (details, record.details))
+        self.assert_true(record.cause_code == code, ("Expected test cause code"
+            " %s, got %s") % (code, record.cause_code))
+        self.assert_true(record.begin_time, "begin time should not be empty.")
+        self.assert_true(record.end_time, "end time should not be empty.")
+        self.assert_true(record.uid == None, ("UID is not used at the moment, "
+            "should always be None."))
+        # Verify to_dict.
+        d = {}
+        d[TestResultEnums.RECORD_NAME] = self.tn
+        d[TestResultEnums.RECORD_RESULT] = result
+        d[TestResultEnums.RECORD_DETAILS] = details
+        d[TestResultEnums.RECORD_CAUSE_CODE] = code
+        d[TestResultEnums.RECORD_BEGIN_TIME] = record.begin_time
+        d[TestResultEnums.RECORD_END_TIME] = record.end_time
+        d[TestResultEnums.RECORD_UID] = None
+        actual_d = record.to_dict()
+        self.assert_true(set(actual_d.items()) == set(d.items()), ("Expected "
+            "%s, got %s.") % (d, actual_d))
+        # Verify that these code paths do not cause crashes and yield non-empty
+        # results.
+        self.assert_true(str(record), "str of the record should not be empty.")
+        self.assert_true(repr(record), "the record's repr shouldn't be empty.")
+        self.assert_true(record.json_str(), ("json str of the record should "
+                         "not be empty."))
+
+    """ Begin of Tests """
+    def test_result_record_pass_none(self):
+        record = TestResultRecord(self.tn)
+        record.test_begin()
+        record.test_pass()
+        self.verify_record(record, TestResultEnums.TEST_RESULT_PASS, str(None),
+                           None)
+        return True
+
+    def test_result_record_pass_with_info(self):
+        record = TestResultRecord(self.tn)
+        record.test_begin()
+        s = TestPass(self.details, self.code)
+        record.test_pass(s)
+        self.verify_record(record, TestResultEnums.TEST_RESULT_PASS,
+                           self.details, self.code)
+        return True
+
+    def test_result_record_fail_none(self):
+        record = TestResultRecord(self.tn)
+        record.test_begin()
+        record.test_fail()
+        self.verify_record(record, TestResultEnums.TEST_RESULT_FAIL, str(None),
+                           None)
+        return True
+
+    def test_result_record_fail_with_info(self):
+        record = TestResultRecord(self.tn)
+        record.test_begin()
+        s = TestFailure(self.details, self.code)
+        record.test_fail(s)
+        self.verify_record(record, TestResultEnums.TEST_RESULT_FAIL,
+                           self.details, self.code)
+        return True
+
+    def test_result_record_skip_none(self):
+        record = TestResultRecord(self.tn)
+        record.test_begin()
+        record.test_skip()
+        self.verify_record(record, TestResultEnums.TEST_RESULT_SKIP, str(None),
+                           None)
+        return True
+
+    def test_result_record_skip_with_info(self):
+        record = TestResultRecord(self.tn)
+        record.test_begin()
+        s = TestSkip(self.details, self.code)
+        record.test_skip(s)
+        self.verify_record(record, TestResultEnums.TEST_RESULT_SKIP,
+                           self.details, self.code)
+        return True
+
+    """ End of Tests """
diff --git a/acts/tests/google/sanity/AttenuatorSanityTest.py b/acts/tests/google/sanity/AttenuatorSanityTest.py
new file mode 100644
index 0000000..9492d89
--- /dev/null
+++ b/acts/tests/google/sanity/AttenuatorSanityTest.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python3.4
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import random
+from acts.base_test import BaseTestClass
+
+CONSERVATIVE_MAX_ATTEN_VALUE = 10
+MIN_ATTEN_VALUE = 0
+
+class AttenuatorSanityTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_attenuator_validation",
+            "test_attenuator_get_max_value",
+        )
+        self.number_of_iteration = 2
+
+    def test_attenuator_validation(self):
+        """Validate attenuator set and get APIs works fine.
+        """
+        for atten in self.attenuators:
+            self.log.info("Attenuator: {}".format(atten))
+            try:
+                atten_max_value = atten.get_max_atten()
+            except ValueError as e:
+                self.log.error(e)
+                self.log.info("Using conservative max value.")
+                atten_max_value = CONSERVATIVE_MAX_ATTEN_VALUE
+
+            atten_value_list = [MIN_ATTEN_VALUE, atten_max_value]
+            for i in range(0, self.number_of_iteration):
+                atten_value_list.append(int(random.uniform(0,atten_max_value)))
+
+            for atten_val in atten_value_list:
+                self.log.info("Set atten to {}".format(atten_val))
+                atten.set_atten(atten_val)
+                current_atten = int(atten.get_atten())
+                self.log.info("Current atten = {}".format(current_atten))
+                assert atten_val == current_atten, "Setting attenuator failed."
+
+        return True
+
+    def test_attenuator_get_max_value(self):
+        """Validate attenuator get_max_atten APIs works fine.
+        """
+        for atten in self.attenuators:
+            try:
+                atten_max_value = atten.get_max_atten()
+            except ValueError as e:
+                self.log.error(e)
+                return False
+        return True
diff --git a/acts/tests/google/sanity/Sl4aSanityTest.py b/acts/tests/google/sanity/Sl4aSanityTest.py
new file mode 100644
index 0000000..eea2a04
--- /dev/null
+++ b/acts/tests/google/sanity/Sl4aSanityTest.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python3.4
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 queue import Empty
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi_test_utils import wifi_toggle_state
+from acts.test_utils.wifi_test_utils import WifiEnums
+
+class Sl4aSanityTest(BaseTestClass):
+    """Tests for sl4a basic sanity.
+
+    Run these tests individually with option -r 100.
+    """
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_bring_up_and_shutdown",
+            "test_message_then_shutdown_stress"
+        )
+
+    def test_bring_up_and_shutdown(self):
+        """Constantly start and terminate sl4a sessions.
+
+        Verify in log that the "manager map key" is always empty before a
+        session starts.
+        Verify in log by looking at timestamps that after the test finishes, no
+        more message regarding sl4a happens.
+        """
+        ad = self.android_devices[0]
+        for i in range(100):
+            self.log.info("Iteration %d, terminating." % i)
+            ad.terminate_all_sessions()
+            self.log.info("Iteration %d, starting." % i)
+            droid, ed = ad.get_droid()
+        return True
+
+    def test_message_then_shutdown_stress(self):
+        for i in range(10):
+            assert wifi_toggle_state(self.droid, self.ed, False)
+            assert wifi_toggle_state(self.droid, self.ed, True)
+        return True
diff --git a/acts/tests/google/tel/lab/TelLabCmasTest.py b/acts/tests/google/tel/lab/TelLabCmasTest.py
new file mode 100644
index 0000000..13aff62
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabCmasTest.py
@@ -0,0 +1,397 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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
+"""
+from acts.base_test import BaseTestClass
+from acts.controllers.tel.md8475a import MD8475A
+from acts.controllers.tel._anritsu_utils import AnritsuError
+from acts.controllers.tel.md8475a import CTCHSetup
+from acts.test_utils.tel.tel_test_anritsu_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.controllers.tel.md8475a import CBCHSetup
+
+class TelLabCmasTest(TelephonyBaseTest):
+    SERIAL_NO = cb_serial_number()
+    CELL_PARAM_FILE = 'C:\\MX847570\\CellParam\\2cell_param.wnscp'
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      "test_cmas_presidential_alert_lte",
+                      "test_cmas_extreme_immediate_likely_lte",
+                      "test_cmas_child_abduction_emergency_lte",
+                      "test_cmas_presidential_alert_wcdma",
+                      "test_cmas_extreme_immediate_likely_wcdma",
+                      "test_cmas_child_abduction_emergency_wcdma",
+                      "test_cmas_presidential_alert_1x",
+                      "test_cmas_extreme_immediate_likely_1x",
+                      "test_cmas_child_abduction_emergency_1x",
+                      "test_cmas_presidential_alert_gsm",
+                      "test_cmas_extreme_immediate_likely_gsm",
+                      "test_cmas_child_abduction_emergency_gsm",
+                    )
+        self.ad = self.android_devices[0]
+        self.md8475a_ip_address = self.user_params["anritsu_md8475a_ip_address"]
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log)
+        except AnritsuError:
+            self.log.error("Error in connecting to Anritsu Simulator")
+            return False
+        return True
+
+    def setup_test(self):
+        ensure_phones_idle(self.log, self.android_devices)
+        toggle_airplane_mode(self.log, self.ad, True)
+        return True
+
+    def teardown_test(self):
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        toggle_airplane_mode(self.log, self.ad, True)
+        return True
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+        return True
+
+    def _send_receive_cmas_message(self, set_simulation_func, rat,
+            message_id, warning_message,
+            c2k_response_type=CMAS_C2K_RESPONSETYPE_SHELTER,
+            c2k_severity=CMAS_C2K_SEVERITY_EXTREME,
+            c2k_urgency=CMAS_C2K_URGENCY_IMMEDIATE,
+            c2k_certainty=CMAS_C2K_CERTIANTY_OBSERVED):
+        try:
+            self.anritsu.reset()
+            self.anritsu.load_cell_paramfile(self.CELL_PARAM_FILE)
+            [self.bts1] = set_simulation_func(self.anritsu, self.user_params)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                phone_setup_func = phone_setup_csfb
+                pref_network_type = NETWORK_MODE_LTE_GSM_WCDMA
+            elif rat == RAT_WCDMA:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_WCDMA_PREF
+                self.bts1.wcdma_ctch = CTCHSetup.CTCH_ENABLE
+                self.ad.droid.toggleDataConnection(False)
+            elif rat == RAT_GSM:
+                phone_setup_func = phone_setup_2g
+                pref_network_type = NETWORK_MODE_GSM_ONLY
+                self.bts1.gsm_cbch = CBCHSetup.CBCH_ENABLE
+                self.ad.droid.toggleDataConnection(False)
+            elif rat == RAT_1XRTT:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_CDMA
+            else:
+                phone_setup_func = phone_setup_csfb
+                network_type = NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+
+            self.ad.droid.setPreferredNetwork(pref_network_type)
+            if not phone_setup_func(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                               .format(self.ad.serial))
+                return False
+            self.anritsu.wait_for_registration_state()
+            if rat != RAT_1XRTT:
+                if not cmas_receive_verify_message_lte_wcdma(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))
+                    return False
+            else:
+                if not cmas_receive_verify_message_cdma1x(self.log, self.ad,
+                                                        self.anritsu,
+                                                        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))
+                    return False
+        except AnritsuError as e:
+            self.log.error("Error in connection with Anritsu Simulator: " + str(e))
+            return False
+        except Exception as e:
+            self.log.error("Exception during CMAS send/receive: " + str(e) )
+            return False
+        return True
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_presidential_alert_lte(self):
+        """CMAS Presidential alert message reception on LTE
+
+        Tests the capability of device to receive and inform the user
+        about the CMAS presedential alert message when camped on LTE newtork
+
+        Steps:
+        1. Make Sure Phone is camped on LTE network
+        2. Send CMAS Presidential message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Presidential alert message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_lte, RAT_LTE,
+                                               CMAS_MESSAGE_PRESIDENTIAL_ALERT,
+                                               "LTE CMAS Presidential Alert")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_extreme_immediate_likely_lte(self):
+        """CMAS Extreme immediate likely message reception on LTE
+
+        Tests the capability of device to receive and inform the user
+        about the Extreme immediate likely message when camped on LTE newtork
+
+        1. Make Sure Phone is camped on LTE network
+        2. Send CMAS Extreme immediate likely message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Extreme immediate likely message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_lte, RAT_LTE,
+                                               CMAS_MESSAGE_EXTREME_IMMEDIATE_LIKELY,
+                                               "LTE CMAS Extreme Immediate Likely")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_child_abduction_emergency_lte(self):
+        """CMAS Child abduction emergency message reception on LTE
+
+        Tests the capability of device to receive and inform the user
+        about the CMAS Child abduction emergency message when camped on LTE newtork
+
+        1. Make Sure Phone is camped on LTE network
+        2. Send CMAS Child abduction emergency message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Child abduction emergency message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_lte, RAT_LTE,
+                                               CMAS_MESSAGE_CHILD_ABDUCTION_EMERGENCY,
+                                               "LTE CMAS Child abduction Alert")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_presidential_alert_wcdma(self):
+        """CMAS Presidential alert message reception on WCDMA
+
+        Tests the capability of device to receive and inform the user
+        about the CMAS presedential alert message when camped on WCDMA newtork
+
+        Steps:
+        1. Make Sure Phone is camped on WCDMA network
+        2. Send CMAS Presidential message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Presidential alert message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_wcdma, RAT_WCDMA,
+                                               CMAS_MESSAGE_PRESIDENTIAL_ALERT,
+                                               "WCDMA CMAS Presidential Alert")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_extreme_immediate_likely_wcdma(self):
+        """CMAS Extreme immediate likely message reception on WCDMA
+
+        Tests the capability of device to receive and inform the user
+        about the Extreme immediate likely message when camped on WCDMA newtork
+
+        1. Make Sure Phone is camped on WCDMA network
+        2. Send CMAS Extreme immediate likely message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Extreme immediate likely message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_wcdma, RAT_WCDMA,
+                                               CMAS_MESSAGE_EXTREME_IMMEDIATE_LIKELY,
+                                               "WCDMA CMAS Extreme Immediate Likely")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_child_abduction_emergency_wcdma(self):
+        """CMAS Child abduction emergency message reception on WCDMA
+
+        Tests the capability of device to receive and inform the user
+        about the CMAS Child abduction emergency message when camped on WCDMA newtork
+
+        1. Make Sure Phone is camped on WCDMA network
+        2. Send CMAS Child abduction emergency message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Child abduction emergency message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_wcdma, RAT_WCDMA,
+                                               CMAS_MESSAGE_CHILD_ABDUCTION_EMERGENCY,
+                                               "WCDMA CMAS Child abduction Alert")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_presidential_alert_1x(self):
+        """CMAS Presidential alert message reception on 1x
+
+        Tests the capability of device to receive and inform the user
+        about the CMAS presedential alert message when camped on 1x newtork
+
+        Steps:
+        1. Make Sure Phone is camped on 1x network
+        2. Send CMAS Presidential message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Presidential alert message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_1x, RAT_1XRTT,
+                                               CMAS_C2K_CATEGORY_PRESIDENTIAL,
+                                               "1X CMAS Presidential Alert")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_extreme_immediate_likely_1x(self):
+        """CMAS Extreme immediate likely message reception on 1x
+
+        Tests the capability of device to receive and inform the user
+        about the Extreme immediate likely message when camped on 1x newtork
+
+        1. Make Sure Phone is camped on 1x network
+        2. Send CMAS Extreme immediate likely message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Extreme immediate likely message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_1x, RAT_1XRTT,
+                                               CMAS_C2K_CATEGORY_EXTREME,
+                                               "1X CMAS Extreme Immediate Likely",
+                                               CMAS_C2K_RESPONSETYPE_EVACUATE,
+                                               CMAS_C2K_SEVERITY_EXTREME,
+                                               CMAS_C2K_URGENCY_IMMEDIATE,
+                                               CMAS_C2K_CERTIANTY_LIKELY)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_child_abduction_emergency_1x(self):
+        """CMAS Child abduction emergency message reception on 1x
+
+        Tests the capability of device to receive and inform the user
+        about the CMAS Child abduction emergency message when camped on 1x newtork
+
+        1. Make Sure Phone is camped on 1x network
+        2. Send CMAS Child abduction emergency message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Child abduction emergency message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_1x, RAT_1XRTT,
+                                               CMAS_C2K_CATEGORY_AMBER,
+                                               "1X CMAS Child abduction Alert",
+                                               CMAS_C2K_RESPONSETYPE_MONITOR,
+                                               CMAS_C2K_SEVERITY_EXTREME,
+                                               CMAS_C2K_URGENCY_IMMEDIATE,
+                                               CMAS_C2K_CERTIANTY_OBSERVED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_presidential_alert_gsm(self):
+        """CMAS Presidential alert message reception on GSM
+
+        Tests the capability of device to receive and inform the user
+        about the CMAS presedential alert message when camped on GSM newtork
+
+        Steps:
+        1. Make Sure Phone is camped on GSM network
+        2. Send CMAS Presidential message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Presidential alert message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_gsm, RAT_GSM,
+                                               CMAS_MESSAGE_PRESIDENTIAL_ALERT,
+                                               "GSM CMAS Presidential Alert")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_extreme_immediate_likely_gsm(self):
+        """CMAS Extreme immediate likely message reception on GSM
+
+        Tests the capability of device to receive and inform the user
+        about the Extreme immediate likely message when camped on GSM newtork
+
+        1. Make Sure Phone is camped on GSM network
+        2. Send CMAS Extreme immediate likely message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Extreme immediate likely message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_gsm, RAT_GSM,
+                                               CMAS_MESSAGE_EXTREME_IMMEDIATE_LIKELY,
+                                               "GSM CMAS Extreme Immediate Likely")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_child_abduction_emergency_gsm(self):
+        """CMAS Child abduction emergency message reception on GSM
+
+        Tests the capability of device to receive and inform the user
+        about the CMAS Child abduction emergency message when camped on GSM newtork
+
+        1. Make Sure Phone is camped on GSM network
+        2. Send CMAS Child abduction emergency message from Anritsu
+
+        Expected Result:
+        Phone receives CMAS Child abduction emergency message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_cmas_message(set_system_model_gsm, RAT_GSM,
+                                               CMAS_MESSAGE_CHILD_ABDUCTION_EMERGENCY,
+                                               "GSM CMAS Child abduction Alert")
+    """ Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabEmergencyCallTest.py b/acts/tests/google/tel/lab/TelLabEmergencyCallTest.py
new file mode 100644
index 0000000..cf452c9
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabEmergencyCallTest.py
@@ -0,0 +1,195 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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
+"""
+from acts.base_test import BaseTestClass
+from acts.controllers.tel.md8475a import MD8475A
+from acts.controllers.tel._anritsu_utils import AnritsuError
+from acts.controllers.tel.md8475a import VirtualPhoneStatus
+from acts.test_utils.tel.tel_test_anritsu_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.controllers.tel.md8475a import CsfbType
+from acts.controllers.tel.md8475a import VirtualPhoneAutoAnswer
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelLabEmergencyCallTest(TelephonyBaseTest):
+
+    CELL_PARAM_FILE = 'C:\\MX847570\\CellParam\\2cell_param.wnscp'
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      "test_emergency_call_lte_wcdma_csfb_redirection",
+                      "test_emergency_call_lte_wcdma_csfb_handover",
+                      "test_emergency_call_wcdma",
+                      "test_emergency_call_gsm",
+                      "test_emergency_call_1x",
+                    )
+        self.ad = self.android_devices[0]
+        self.md8475a_ip_address = self.user_params["anritsu_md8475a_ip_address"]
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log)
+        except AnritsuError:
+            self.log.error("Error in connecting to Anritsu Simulator")
+            return False
+        return True
+
+    def setup_test(self):
+        ensure_phones_idle(self.log, self.android_devices)
+        # get a handle to virtual phone
+        self.virtualPhoneHandle = self.anritsu.get_VirtualPhone()
+        toggle_airplane_mode(self.log, self.ad, True)
+        return True
+
+    def teardown_test(self):
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        toggle_airplane_mode(self.log, self.ad, True)
+        return True
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+        return True
+
+    def _setup_emergency_call(self, set_simulation_func, rat, csfb_type=None):
+        try:
+            self.anritsu.reset()
+            self.anritsu.load_cell_paramfile(self.CELL_PARAM_FILE)
+            set_simulation_func(self.anritsu, self.user_params)
+            self.virtualPhoneHandle.auto_answer = (VirtualPhoneAutoAnswer.ON, 2)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                phone_setup_func = phone_setup_csfb
+                pref_network_type = NETWORK_MODE_LTE_GSM_WCDMA
+                if csfb_type is not None:
+                    self.anritsu.csfb_type = csfb_type
+            elif rat == RAT_WCDMA:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_WCDMA_PREF
+            elif rat == RAT_GSM:
+                phone_setup_func = phone_setup_2g
+                pref_network_type = NETWORK_MODE_GSM_ONLY
+            elif rat == RAT_1XRTT:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_CDMA
+            else:
+                phone_setup_func = phone_setup_csfb
+                network_type = NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+
+            self.ad.droid.setPreferredNetwork(pref_network_type)
+            if not phone_setup_func(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                              .format(self.ad.serial))
+                return False
+            self.anritsu.wait_for_registration_state()
+            time.sleep(WAIT_TIME_ANRITSU_REG_AND_CALL)
+            if not call_mo_setup_teardown(self.log, self.ad,
+                                          self.virtualPhoneHandle,
+                                          "911", teardown_side="phone",
+                                          emergency=True):
+                self.log.error("Phone {} Failed to make emergency call to 911"
+                              .format(self.ad.serial))
+                return False
+        except AnritsuError as e:
+            self.log.error("Error in connection with Anritsu Simulator: " + str(e))
+            return False
+        except Exception as e:
+            self.log.error("Exception during emergency call procedure: " + str(e))
+            return False
+        return True
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_lte_wcdma_csfb_redirection(self):
+        """ Test Emergency call functionality on LTE (CSFB to WCDMA).
+            CSFB type is REDIRECTION
+
+        Make Sure Phone is in 4G mode
+        Make an emergency call to 911
+        Make sure Anritsu receives the call and accept
+        Tear down the call
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(set_system_model_lte_wcdma, RAT_LTE,
+                                          CsfbType.CSFB_TYPE_REDIRECTION)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_lte_wcdma_csfb_handover(self):
+        """ Test Emergency call functionality on LTE (CSFB to WCDMA).
+            CSFB type is HANDOVER
+
+        Make Sure Phone is in 4G mode
+        Make an emergency call to 911
+        Make sure Anritsu receives the call and accept
+        Tear down the call
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(set_system_model_lte_wcdma, RAT_LTE,
+                                          CsfbType.CSFB_TYPE_HANDOVER)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_wcdma(self):
+        """ Test Emergency call functionality on WCDMA
+
+        Make Sure Phone is in 3G mode
+        Make an emergency call to 911
+        Make sure Anritsu receives the call and accept
+        Tear down the call
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(set_system_model_wcdma, RAT_WCDMA)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_gsm(self):
+        """ Test Emergency call functionality on GSM
+
+        Make Sure Phone is in 2G mode
+        Make an emergency call to 911
+        Make sure Anritsu receives the call and accept
+        Tear down the call
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(set_system_model_gsm, RAT_GSM)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_1x(self):
+        """ Test Emergency call functionality on CDMA 1X
+
+        Make Sure Phone is in 3G mode
+        Make an emergency call to 911
+        Make sure Anritsu receives the call and accept
+        Tear down the call
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(set_system_model_1x, RAT_1XRTT)
+    """ Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabEtwsTest.py b/acts/tests/google/tel/lab/TelLabEtwsTest.py
new file mode 100644
index 0000000..04ae27e
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabEtwsTest.py
@@ -0,0 +1,252 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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
+"""
+from acts.base_test import BaseTestClass
+from acts.controllers.tel.md8475a import MD8475A
+from acts.controllers.tel._anritsu_utils import AnritsuError
+from acts.controllers.tel.md8475a import CTCHSetup
+from acts.test_utils.tel.tel_test_anritsu_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.controllers.tel.md8475a import CBCHSetup
+
+class TelLabEtwsTest(TelephonyBaseTest):
+    SERIAL_NO = cb_serial_number()
+    CELL_PARAM_FILE = 'C:\\MX847570\\CellParam\\2cell_param.wnscp'
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                    "test_etws_earthquake_tsunami_lte",
+                    "test_etws_other_emergency_lte",
+                    "test_etws_earthquake_tsunami_wcdma",
+                    "test_etws_other_emergency_wcdma",
+                    "test_etws_earthquake_tsunami_gsm",
+                    "test_etws_other_emergency_gsm",
+                    )
+        self.ad = self.android_devices[0]
+        self.md8475a_ip_address = self.user_params["anritsu_md8475a_ip_address"]
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log)
+        except AnritsuError:
+            self.log.error("Error in connecting to Anritsu Simulator")
+            return False
+        return True
+
+    def setup_test(self):
+        ensure_phones_idle(self.log, self.android_devices)
+        toggle_airplane_mode(self.log, self.ad, True)
+        return True
+
+    def teardown_test(self):
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        toggle_airplane_mode(self.log, self.ad, True)
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+        return True
+
+    def _send_receive_etws_message(self, set_simulation_func, rat,
+            message_id, warning_message):
+        try:
+            self.anritsu.reset()
+            self.anritsu.load_cell_paramfile(self.CELL_PARAM_FILE)
+            [self.bts1] = set_simulation_func(self.anritsu, self.user_params)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                phone_setup_func = phone_setup_csfb
+                pref_network_type = NETWORK_MODE_LTE_GSM_WCDMA
+            elif rat == RAT_WCDMA:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_WCDMA_PREF
+                self.bts1.wcdma_ctch = CTCHSetup.CTCH_ENABLE
+                self.ad.droid.toggleDataConnection(False)
+            elif rat == RAT_GSM:
+                phone_setup_func = phone_setup_2g
+                pref_network_type = NETWORK_MODE_GSM_ONLY
+                self.bts1.gsm_cbch = CBCHSetup.CBCH_ENABLE
+                self.ad.droid.toggleDataConnection(False)
+            elif rat == RAT_1XRTT:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_CDMA
+            else:
+                phone_setup_func = phone_setup_csfb
+                network_type = NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+
+            self.ad.droid.setPreferredNetwork(pref_network_type)
+            if not phone_setup_func(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                               .format(self.ad.serial))
+                return False
+            self.anritsu.wait_for_registration_state()
+            if not etws_receive_verify_message_lte_wcdma(self.log, self.ad,
+                                                        self.anritsu,
+                                                        next(TelLabEtwsTest.SERIAL_NO),
+                                                        message_id,
+                                                        warning_message ):
+                self.log.error("Phone {} Failed to receive ETWS message"
+                               .format(self.ad.serial))
+                return False
+        except AnritsuError as e:
+            self.log.error("Error in connection with Anritsu Simulator: " + str(e))
+            return False
+        except Exception as e:
+            self.log.error("Exception during ETWS send/receive: " + str(e))
+            return False
+        return True
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_etws_earthquake_tsunami_lte(self):
+        """ETWS Earthquake and Tsunami warning message reception on LTE
+
+        Tests the capability of device to receive and inform the user
+        about the ETWS Earthquake and Tsunami warning message when camped on
+        LTE newtork
+
+        Steps:
+        1. Make Sure Phone is camped on LTE network
+        2. Send ETWS Earthquake and Tsunami warning message from Anritsu
+
+        Expected Result:
+        Phone receives ETWS Earthquake and Tsunami warning message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_etws_message(set_system_model_lte, RAT_LTE,
+                                               ETWS_WARNING_EARTHQUAKETSUNAMI,
+                                               "LTE Earthquake and Tsunami")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_etws_other_emergency_lte(self):
+        """ETWS Other emergency warning message reception on LTE
+
+        Tests the capability of device to receive and inform the user
+        about the ETWS Other emergency warning message when camped on
+        LTE newtork
+
+        Steps:
+        1. Make Sure Phone is camped on LTE network
+        2. Send ETWS Earthquake and Tsunami warning message from Anritsu
+
+        Expected Result:
+        Phone receives ETWS Earthquake and Tsunami warning message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_etws_message(set_system_model_lte, RAT_LTE,
+                                               ETWS_WARNING_OTHER_EMERGENCY,
+                                               "LTE ETWS Other Emergency")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_etws_earthquake_tsunami_wcdma(self):
+        """ETWS Earthquake and Tsunami warning message reception on WCDMA
+
+        Tests the capability of device to receive and inform the user
+        about the ETWS Earthquake and Tsunami warning message when camped on
+        WCDMA newtork
+
+        Steps:
+        1. Make Sure Phone is camped on WCDMA network
+        2. Send ETWS Earthquake and Tsunami warning message from Anritsu
+
+        Expected Result:
+        Phone receives ETWS Earthquake and Tsunami warning message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_etws_message(set_system_model_wcdma, RAT_WCDMA,
+                                               ETWS_WARNING_EARTHQUAKETSUNAMI,
+                                               "WCDMA Earthquake and Tsunami")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_etws_other_emergency_wcdma(self):
+        """ETWS Other emergency warning message reception on WCDMA
+
+        Tests the capability of device to receive and inform the user
+        about the ETWS Other emergency warning message when camped on
+        WCDMA newtork
+
+        Steps:
+        1. Make Sure Phone is camped on WCDMA network
+        2. Send ETWS Earthquake and Tsunami warning message from Anritsu
+
+        Expected Result:
+        Phone receives ETWS Earthquake and Tsunami warning message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_etws_message(set_system_model_wcdma, RAT_WCDMA,
+                                               ETWS_WARNING_OTHER_EMERGENCY,
+                                               "WCDMA ETWS Other Emergency")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_etws_earthquake_tsunami_gsm(self):
+        """ETWS Earthquake and Tsunami warning message reception on GSM
+
+        Tests the capability of device to receive and inform the user
+        about the ETWS Earthquake and Tsunami warning message when camped on
+        GSM newtork
+
+        Steps:
+        1. Make Sure Phone is camped on GSM network
+        2. Send ETWS Earthquake and Tsunami warning message from Anritsu
+
+        Expected Result:
+        Phone receives ETWS Earthquake and Tsunami warning message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_etws_message(set_system_model_gsm, RAT_GSM,
+                                               ETWS_WARNING_EARTHQUAKETSUNAMI,
+                                               "GSM Earthquake and Tsunami")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_etws_other_emergency_gsm(self):
+        """ETWS Other emergency warning message reception on GSM
+
+        Tests the capability of device to receive and inform the user
+        about the ETWS Other emergency warning message when camped on
+        GSM newtork
+
+        Steps:
+        1. Make Sure Phone is camped on GSM network
+        2. Send ETWS Earthquake and Tsunami warning message from Anritsu
+
+        Expected Result:
+        Phone receives ETWS Earthquake and Tsunami warning message
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._send_receive_etws_message(set_system_model_gsm, RAT_GSM,
+                                               ETWS_WARNING_OTHER_EMERGENCY,
+                                               "GSM ETWS Other Emergency")
+    """ Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabNeighborCellTest.py b/acts/tests/google/tel/lab/TelLabNeighborCellTest.py
new file mode 100644
index 0000000..0ca9f13
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabNeighborCellTest.py
@@ -0,0 +1,1887 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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.base_test import BaseTestClass
+from acts.controllers.tel.md8475a import MD8475A
+from acts.controllers.tel._anritsu_utils import AnritsuError
+from acts.controllers.tel.md8475a import CTCHSetup
+from acts.controllers.tel.md8475a import BtsBandwidth
+from acts.controllers.tel.md8475a import BtsPacketRate
+from acts.test_utils.tel.tel_test_anritsu_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.controllers.tel.mg3710a import MG3710A
+from acts.controllers.tel.md8475a import BtsServiceState
+from cell_configurations import *
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelLabNeighborCellTest(TelephonyBaseTest):
+    # These are not actual DB Loss. the signal strength seen at phone varies this
+    # much to the power level set at Anritsu
+    LTE_DB_LOSS = -40
+    WCDMA_DB_LOSS = -45
+    GSM_DB_LOSS = -30
+    ALLOWED_VARIATION = 15
+    ANRITSU_SETTLING_TIME = 15
+    SETTLING_TIME = 75
+    LTE_MCS_DL = 5
+    LTE_MCS_UL = 5
+    NRB_DL = 50
+    NRB_UL = 50
+    CELL_PARAM_FILE = 'C:\\MX847570\\CellParam\\NEIGHBOR_CELL_TEST_TMO.wnscp'
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      "test_ncells_intra_lte_0_cells",
+                      "test_ncells_intra_lte_1_cells",
+                      "test_ncells_intra_lte_2_cells",
+                      "test_ncells_intra_lte_3_cells",
+                      "test_ncells_intra_lte_4_cells",
+                      "test_neighbor_cell_reporting_lte_intrafreq_0_tmo",
+                      "test_neighbor_cell_reporting_lte_intrafreq_1_tmo",
+                      "test_neighbor_cell_reporting_lte_intrafreq_2_tmo",
+                      "test_neighbor_cell_reporting_lte_intrafreq_3_tmo",
+                      "test_neighbor_cell_reporting_lte_interfreq_1_tmo",
+                      "test_neighbor_cell_reporting_lte_interfreq_2_tmo",
+                      "test_neighbor_cell_reporting_lte_interband_2_tmo",
+                      "test_neighbor_cell_reporting_lte_interrat_1_tmo",
+                      "test_neighbor_cell_reporting_lte_interrat_2_tmo",
+                      "test_neighbor_cell_reporting_wcdma_intrafreq_0_tmo",
+                      "test_neighbor_cell_reporting_wcdma_intrafreq_1_tmo",
+                      "test_neighbor_cell_reporting_wcdma_intrafreq_2_tmo",
+                      "test_neighbor_cell_reporting_wcdma_intrafreq_3_tmo",
+                      "test_neighbor_cell_reporting_wcdma_interfreq_1_tmo",
+                      "test_neighbor_cell_reporting_wcdma_interfreq_2_tmo",
+                      "test_neighbor_cell_reporting_wcdma_interband_2_tmo",
+                      "test_neighbor_cell_reporting_wcdma_interrat_2_tmo",
+                      "test_neighbor_cell_reporting_gsm_intrafreq_0_tmo",
+                      "test_neighbor_cell_reporting_gsm_intrafreq_1_tmo",
+                      "test_neighbor_cell_reporting_gsm_intrafreq_2_tmo",
+                      "test_neighbor_cell_reporting_gsm_intrafreq_3_tmo",
+                      "test_neighbor_cell_reporting_gsm_interfreq_2_tmo",
+                      "test_neighbor_cell_reporting_gsm_interband_2_tmo",
+                      "test_neighbor_cell_reporting_gsm_interrat_2_tmo",
+                     )
+        self.ad = self.android_devices[0]
+        self.md8475a_ip_address = self.user_params["anritsu_md8475a_ip_address"]
+        self.mg3710a_ip_address = self.user_params["anritsu_mg3710a_ip_address"]
+
+    def setup_class(self):
+        self.md8475a = None
+        self.mg3710a = None
+        try:
+            self.md8475a = MD8475A(self.md8475a_ip_address, self.log)
+        except AnritsuError as e:
+            self.log.error("Error in connecting to Anritsu MD8475A:{}".format(e) )
+            return False
+
+        try:
+            self.mg3710a = MG3710A(self.mg3710a_ip_address,self.log)
+        except AnritsuError as e:
+            self.log.error("Error in connecting to Anritsu MG3710A :{}".format(e) )
+            return False
+        return True
+
+    def setup_test(self):
+        self.turn_off_3710a_sg(1)
+        self.turn_off_3710a_sg(2)
+        self.mg3710a.set_arb_pattern_aorb_state("A", "OFF", 1)
+        self.mg3710a.set_arb_pattern_aorb_state("B", "OFF", 1)
+        self.mg3710a.set_arb_pattern_aorb_state("A", "OFF", 2)
+        self.mg3710a.set_arb_pattern_aorb_state("B", "OFF", 2)
+        self.mg3710a.set_freq_relative_display_status("OFF", 1)
+        self.mg3710a.set_freq_relative_display_status("OFF", 2)
+        self.ad.droid.setPreferredNetwork(NETWORK_MODE_LTE_GSM_WCDMA)
+        ensure_phones_idle(self.log, self.android_devices)
+        self.ad.droid.connectivityToggleAirplaneMode(True)
+        self.ad.droid.toggleDataConnection(True)
+        return True
+
+    def teardown_test(self):
+        self.ad.droid.connectivityToggleAirplaneMode(True)
+        self.turn_off_3710a_sg(1)
+        self.turn_off_3710a_sg(2)
+        self.log.info("Stopping Simulation")
+        self.md8475a.stop_simulation()
+        return True
+
+    def teardown_class(self):
+        if self.md8475a is not None:
+            self.md8475a.disconnect()
+        if self.mg3710a is not None:
+            self.mg3710a.disconnect()
+        return True
+
+    def _setup_lte_serving_cell(self, bts, dl_power, cell_id,
+             physical_cellid):
+         bts.output_level = dl_power
+         bts.bandwidth = BtsBandwidth.LTE_BANDWIDTH_10MHz
+         bts.packet_rate = BtsPacketRate.LTE_MANUAL
+         bts.lte_mcs_dl = self.LTE_MCS_DL
+         bts.lte_mcs_ul = self.LTE_MCS_UL
+         bts.nrb_dl = self.NRB_DL
+         bts.nrb_ul = self.NRB_UL
+         bts.cell_id = cell_id
+         bts.physical_cellid = physical_cellid
+         bts.neighbor_cell_mode = "DEFAULT"
+
+    def _setup_lte_neighbhor_cell_md8475a(self, bts, band, dl_power, cell_id,
+             physical_cellid):
+         bts.output_level = dl_power
+         bts.band = band
+         bts.bandwidth = BtsBandwidth.LTE_BANDWIDTH_10MHz
+         bts.cell_id = cell_id
+         bts.physical_cellid = physical_cellid
+         bts.neighbor_cell_mode = "DEFAULT"
+         bts.packet_rate = BtsPacketRate.LTE_MANUAL
+         bts.lte_mcs_dl = self.LTE_MCS_DL
+         bts.lte_mcs_ul = self.LTE_MCS_UL
+         bts.nrb_dl = self.NRB_DL
+         bts.nrb_ul = self.NRB_UL
+
+    def _setup_wcdma_serving_cell(self, bts, dl_power, cell_id):
+         bts.output_level = dl_power
+         bts.cell_id = cell_id
+         bts.neighbor_cell_mode = "DEFAULT"
+
+    def _setup_wcdma_neighbhor_cell_md8475a(self, bts, band, dl_power, cell_id):
+         bts.output_level = dl_power
+         bts.band = band
+         bts.cell_id = cell_id
+         bts.neighbor_cell_mode = "DEFAULT"
+
+    def _setup_lte_cell_md8475a(self, bts, params, dl_power):
+         bts.output_level = dl_power
+         bts.band = params['band']
+         bts.bandwidth = params['bandwidth']
+         bts.cell_id = params['cid']
+         bts.physical_cellid = params['pcid']
+         bts.mcc = params['mcc']
+         bts.mnc = params['mnc']
+         bts.tac = params['tac']
+         bts.neighbor_cell_mode = "DEFAULT"
+         bts.dl_channel = params['channel']
+         bts.packet_rate = BtsPacketRate.LTE_MANUAL
+         bts.lte_mcs_dl = self.LTE_MCS_DL
+         bts.lte_mcs_ul = self.LTE_MCS_UL
+         bts.nrb_dl = self.NRB_DL
+         bts.nrb_ul = self.NRB_UL
+
+    def _setup_wcdma_cell_md8475a(self, bts, params, dl_power):
+         bts.output_level = dl_power
+         bts.band = params['band']
+         bts.cell_id = params['cid']
+         bts.mcc = params['mcc']
+         bts.mnc = params['mnc']
+         bts.lac = params['lac']
+         bts.rac = params['rac']
+         bts.neighbor_cell_mode = "DEFAULT"
+         bts.primary_scrambling_code = params['psc']
+         bts.dl_channel = params['channel']
+
+    def _setup_gsm_cell_md8475a(self, bts, params, dl_power):
+         bts.output_level = params['power']
+         bts.band = params['band']
+         bts.cell_id = params['cid']
+         bts.mcc = params['mcc']
+         bts.mnc = params['mnc']
+         bts.lac = params['lac']
+         bts.rac = params['rac']
+         bts.neighbor_cell_mode = "DEFAULT"
+
+    def setup_3710a_waveform(self, sg_number, memory, frequency, power_level,
+            wave_package_name, wave_pattern_name):
+        self.mg3710a.set_frequency(frequency, sg_number)
+        self.mg3710a.set_arb_state("ON", sg_number)
+        self.mg3710a.set_arb_combination_mode("EDIT", sg_number)
+        self.mg3710a.select_waveform(wave_package_name, wave_pattern_name,
+                                     memory, sg_number)
+        self.mg3710a.set_arb_pattern_aorb_state(memory,"ON", sg_number)
+        self.mg3710a.set_arb_level_aorb(memory, power_level, sg_number)
+
+    def turn_on_3710a_sg(self, sg_number):
+        self.mg3710a.set_modulation_state("ON", sg_number)
+        self.mg3710a.set_rf_output_state("ON", sg_number)
+
+    def turn_off_3710a_sg(self, sg_number):
+        self.mg3710a.set_modulation_state("OFF", sg_number)
+        self.mg3710a.set_rf_output_state("OFF", sg_number)
+
+    def _verify_lte_cells_information( self, expected_no_cells, pcid_list,
+            pcid_power_map):
+        acell = self.ad.droid.getAllCellInfo()
+        if acell is not None:
+            self.log.info("All Cell Info")
+            self.log.info(acell)
+            received_no_of_cells = len(acell)
+            self.log.info("Expected Number of Cells (Including Serving Cell): {}"
+                      .format(expected_no_cells))
+            self.log.info("Received Number of Cells (Including Serving Cell): {}"
+                      .format(received_no_of_cells))
+            if received_no_of_cells is not expected_no_cells:
+                self.log.error("Wrong number of cells reported")
+                return False
+
+            for i in range(received_no_of_cells):
+                pcid = acell[i]['pcid']
+                power_level = acell[i]['rsrp']
+                if not  pcid in pcid_list:
+                    self.log.error("Wrong pcid reported :{} ".format(pcid))
+                    return False
+
+                expected_rsrp =  pcid_power_map[pcid] + self.LTE_DB_LOSS
+                received_rsrp = power_level
+                self.log.info("PCID = {}".format(pcid))
+                self.log.info("RAT = {}".format(acell[i]['rat']))
+                self.log.info("Expected RSRP = {}".format(expected_rsrp))
+                self.log.info("Received RSRP = {}".format(received_rsrp))
+                if (received_rsrp < (expected_rsrp - self.ALLOWED_VARIATION/2) or
+                    received_rsrp > (expected_rsrp + self.ALLOWED_VARIATION/2)):
+                    self.log.error("Wrong rsrp reported")
+                    return False
+            return True
+        else:
+            self.log.error("API to get cell info returned None ")
+            return False
+
+    def _verify_lte_cell( self, cell_info, params):
+        found =  False
+        for i in range(len(params)):
+            if params[i]['pcid'] == cell_info['pcid'] :
+                expected_rsrp =  params[i]['power'] + self.LTE_DB_LOSS
+                received_rsrp = cell_info['rsrp']
+                self.log.info("MCC = {}".format(cell_info['mcc']))
+                self.log.info("MNC = {}".format(cell_info['mnc']))
+                self.log.info("TAC = {}".format(cell_info['tac']))
+                self.log.info("PCID = {}".format(cell_info['pcid']))
+                self.log.info("RAT = {}".format(cell_info['rat']))
+                self.log.info("Expected RSRP = {}".format(expected_rsrp))
+                self.log.info("Received RSRP = {}".format(received_rsrp))
+                if int(params[i]['mnc']) != cell_info['mnc']:
+                    self.log.error("Wrong mnc reported")
+                    break
+                if int(params[i]['mcc']) != cell_info['mcc']:
+                    self.log.error("Wrong mcc reported")
+                    break
+                if params[i]['tac'] != cell_info['tac']:
+                    self.log.error("Wrong tac reported")
+                    break
+                if (received_rsrp < (expected_rsrp - self.ALLOWED_VARIATION) or
+                    received_rsrp > (expected_rsrp + self.ALLOWED_VARIATION)):
+                    self.log.error("Wrong rsrp reported")
+                    break
+                found =  True
+                break
+        return found
+
+    def _verify_wcdma_cell( self, cell_info, params):
+        found =  False
+        for i in range(len(params)):
+            if params[i]['cid'] == cell_info['cid'] :
+                expected_signal_strength =  params[i]['power'] + self.WCDMA_DB_LOSS
+                received_signal_strength = cell_info['signal_strength']
+                self.log.info("MCC = {}".format(cell_info['mcc']))
+                self.log.info("MNC = {}".format(cell_info['mnc']))
+                self.log.info("LAC = {}".format(cell_info['lac']))
+                self.log.info("CID = {}".format(cell_info['cid']))
+                self.log.info("RAT = {}".format(cell_info['rat']))
+                self.log.info("PSC = {}".format(cell_info['psc']))
+                self.log.info("Expected Signal Strength= {}".
+                              format(expected_signal_strength))
+                self.log.info("Received Signal Strength = {}".
+                              format(received_signal_strength))
+                if int(params[i]['mnc']) != cell_info['mnc']:
+                    self.log.error("Wrong mnc reported")
+                    break
+                if int(params[i]['mcc']) != cell_info['mcc']:
+                    self.log.error("Wrong mcc reported")
+                    break
+                if params[i]['lac'] != cell_info['lac']:
+                    self.log.error("Wrong mnc reported")
+                    break
+                if params[i]['psc'] != cell_info['psc']:
+                    self.log.error("Wrong psc reported")
+                    break
+                if (received_signal_strength < (expected_signal_strength - self.ALLOWED_VARIATION/2) or
+                    received_signal_strength > (expected_signal_strength + self.ALLOWED_VARIATION/2)):
+                    self.log.error("Wrong Signal Strength reported")
+                    break
+                found =  True
+                break
+        return found
+
+    def _verify_gsm_cell( self, cell_info, params):
+        found =  False
+        for i in range(len(params)):
+            if params[i]['cid'] == cell_info['cid'] :
+                expected_signal_strength =  params[i]['power'] + self.GSM_DB_LOSS
+                received_signal_strength = cell_info['signal_strength']
+                self.log.info("MCC = {}".format(cell_info['mcc']))
+                self.log.info("MNC = {}".format(cell_info['mnc']))
+                self.log.info("LAC = {}".format(cell_info['lac']))
+                self.log.info("CID = {}".format(cell_info['cid']))
+                self.log.info("RAT = {}".format(cell_info['rat']))
+                self.log.info("Expected Signal Strength= {}".
+                               format(expected_signal_strength))
+                self.log.info("Received Signal Strength = {}".
+                               format(received_signal_strength))
+                if int(params[i]['mnc']) != cell_info['mnc']:
+                    self.log.error("Wrong mnc reported")
+                    break
+                if int(params[i]['mcc']) != cell_info['mcc']:
+                    self.log.error("Wrong mcc reported")
+                    break
+                if params[i]['lac'] != cell_info['lac']:
+                    self.log.error("Wrong lac reported")
+                    break
+                if (received_signal_strength < (expected_signal_strength - self.ALLOWED_VARIATION/2) or
+                    received_signal_strength > (expected_signal_strength + self.ALLOWED_VARIATION/2)):
+                    self.log.error("Wrong Signal Strength reported")
+                    break
+                found =  True
+                break
+        return found
+
+    def _verify_cells_information( self, expected_no_cells, params):
+        acell = self.ad.droid.getAllCellInfo()
+        if acell is not None:
+            self.log.info("All Cell Info")
+            self.log.info(acell)
+            received_no_of_cells = len(acell)
+            self.log.info("Expected Number of Cells (Including Serving Cell): {}"
+                      .format(expected_no_cells))
+            self.log.info("Received Number of Cells (Including Serving Cell): {}"
+                      .format(received_no_of_cells))
+            if received_no_of_cells is not expected_no_cells:
+                self.log.error("Wrong number of cells reported")
+                return False
+
+            for i in range(received_no_of_cells):
+                print(acell[i])
+                if acell[i]['rat'] == 'lte':
+                    if not self._verify_lte_cell(acell[i], params):
+                        self.log.error("Wrong LTE Cell Received")
+                        return False
+                elif acell[i]['rat'] == 'wcdma':
+                    if not self._verify_wcdma_cell(acell[i], params):
+                        self.log.error("Wrong WCDMA Cell Received")
+                        return False
+                elif acell[i]['rat'] == 'gsm':
+                    if not self._verify_gsm_cell(acell[i], params):
+                        self.log.error("Wrong GSM Cell Received")
+                        return False
+            return True
+        else:
+            self.log.error("API to get cell info returned None ")
+            return False
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_ncells_intra_lte_0_cells(self):
+        """ Test Number of neighbor cells reported by Phone when no neighbor
+        cells are present (Phone camped on LTE)
+
+        Setup a single LTE cell configuration on MD8475A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell_cid = 11
+        serving_cell_pcid = 11
+        serving_cell_dlpower = -20
+        expected_no_cells = 1
+        pcid_list = [serving_cell_pcid]
+        pcid_power_map  = {
+                            serving_cell_pcid: serving_cell_dlpower,
+                          }
+
+        self.md8475a.reset()
+        [bts1] = set_system_model_lte(self.md8475a, self.user_params)
+        self._setup_lte_serving_cell(bts1, serving_cell_dlpower, serving_cell_cid,
+                                     serving_cell_pcid)
+        self.md8475a.start_simulation()
+
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_lte_cells_information(expected_no_cells,
+                                             pcid_list,
+                                             pcid_power_map)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_ncells_intra_lte_1_cells(self):
+        """ Test Number of neighbor cells reported by Phone when one neighbor
+        cell is present (Phone camped on LTE)
+
+        Setup a two LTE cell configuration on MD8475A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell_cid = 11
+        serving_cell_pcid = 11
+        neigh_cell_cid = 22
+        neigh_cell_pcid = 22
+        serving_cell_dlpower = -20
+        neigh_cell_dlpower = -24
+        expected_no_cells = 2
+        pcid_list = [serving_cell_pcid, neigh_cell_pcid ]
+        pcid_power_map  = {
+                            serving_cell_pcid: serving_cell_dlpower,
+                            neigh_cell_pcid: neigh_cell_dlpower
+                          }
+
+        self.md8475a.reset()
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+        self._setup_lte_serving_cell(bts1, serving_cell_dlpower, serving_cell_cid,
+                                     serving_cell_pcid)
+        self._setup_lte_neighbhor_cell_md8475a(bts2, LTE_BAND_2, neigh_cell_dlpower,
+                                               neigh_cell_cid, neigh_cell_pcid)
+        self.md8475a.start_simulation()
+
+        if not phone_setup_csfb(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                               .format(self.ad.serial))
+                return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_lte_cells_information(expected_no_cells,
+                                             pcid_list,
+                                             pcid_power_map)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_ncells_intra_lte_2_cells(self):
+        """ Test Number of neighbor cells reported by Phone when two neighbor
+        cells are present (Phone camped on LTE)
+
+        Setup a two LTE cell configuration on MD8475A
+        Setup one waveform on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell_cid = 11
+        serving_cell_pcid = 11
+        neigh_cell_1_cid = 22
+        neigh_cell_1_pcid = 22
+        neigh_cell_2_cid = 1
+        neigh_cell_2_pcid = 1
+        serving_cell_dlpower = -20
+        neigh_cell_1_dlpower = -24
+        neigh_cell_2_dlpower = -23
+        expected_no_cells = 3
+        pcid_list = [serving_cell_pcid, neigh_cell_1_pcid, neigh_cell_2_pcid]
+        pcid_power_map  = {
+                            serving_cell_pcid: serving_cell_dlpower,
+                            neigh_cell_1_pcid: neigh_cell_1_dlpower,
+                            neigh_cell_2_pcid: neigh_cell_2_dlpower
+                          }
+
+        self.md8475a.reset()
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+
+        self._setup_lte_serving_cell(bts1, serving_cell_dlpower, serving_cell_cid,
+                                     serving_cell_pcid)
+        self._setup_lte_neighbhor_cell_md8475a(bts2, LTE_BAND_2, neigh_cell_1_dlpower,
+                                               neigh_cell_1_cid, neigh_cell_1_pcid)
+        self.md8475a.start_simulation()
+
+        if not phone_setup_csfb(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                               .format(self.ad.serial))
+                return False
+        self.md8475a.wait_for_registration_state()
+
+        self.setup_3710a_waveform("1", "A", "1960MHZ", neigh_cell_2_dlpower,
+                                  "LTE", "10M_B1_CID1")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_lte_cells_information(expected_no_cells,
+                                             pcid_list,
+                                             pcid_power_map)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_ncells_intra_lte_3_cells(self):
+        """ Test Number of neighbor cells reported by Phone when three neighbor
+        cells are present (Phone camped on LTE)
+
+        Setup two LTE cell configuration on MD8475A
+        Setup two waveform on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell_cid = 11
+        serving_cell_pcid = 11
+        neigh_cell_1_cid = 1
+        neigh_cell_1_pcid = 1
+        neigh_cell_2_cid = 2
+        neigh_cell_2_pcid = 2
+        neigh_cell_3_cid = 3
+        neigh_cell_3_pcid = 3
+        serving_cell_dlpower = -20
+        neigh_cell_1_dlpower = -24
+        neigh_cell_2_dlpower = -22
+        neigh_cell_3_dlpower = -23
+        expected_no_cells = 4
+        pcid_list = [serving_cell_pcid, neigh_cell_1_pcid, neigh_cell_2_pcid,
+                     neigh_cell_3_pcid]
+        pcid_power_map  = {
+                            serving_cell_pcid: serving_cell_dlpower,
+                            neigh_cell_1_pcid: neigh_cell_1_dlpower,
+                            neigh_cell_2_pcid: neigh_cell_2_dlpower,
+                            neigh_cell_3_pcid: neigh_cell_3_dlpower
+                          }
+
+        self.md8475a.reset()
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+        self._setup_lte_serving_cell(bts1, serving_cell_dlpower, serving_cell_cid,
+                                     serving_cell_pcid)
+
+        self._setup_lte_neighbhor_cell_md8475a(bts2, LTE_BAND_2, neigh_cell_1_dlpower,
+                                               neigh_cell_1_cid, neigh_cell_1_pcid)
+        self.md8475a.start_simulation()
+
+        if not phone_setup_csfb(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                               .format(self.ad.serial))
+                return False
+        self.md8475a.wait_for_registration_state()
+
+        self.setup_3710a_waveform("1", "A", "1960MHZ", neigh_cell_2_dlpower,
+                                  "LTE", "10M_B1_CID2")
+
+        self.setup_3710a_waveform("1", "B", "1960MHZ", neigh_cell_3_dlpower,
+                                  "LTE", "10M_B1_CID3")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_lte_cells_information(expected_no_cells,
+                                             pcid_list,
+                                             pcid_power_map)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_ncells_intra_lte_4_cells(self):
+        """ Test Number of neighbor cells reported by Phone when four neighbor
+        cells are present (Phone camped on LTE)
+
+        Setup two LTE cell configuration on MD8475A
+        Setup three waveform on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell_cid = 11
+        serving_cell_pcid = 11
+        neigh_cell_1_cid = 1
+        neigh_cell_1_pcid = 1
+        neigh_cell_2_cid = 2
+        neigh_cell_2_pcid = 2
+        neigh_cell_3_cid = 3
+        neigh_cell_3_pcid = 3
+        neigh_cell_4_cid = 5
+        neigh_cell_4_pcid = 5
+        serving_cell_dlpower = -20
+        neigh_cell_1_dlpower = -24
+        neigh_cell_2_dlpower = -22
+        neigh_cell_3_dlpower = -24
+        neigh_cell_4_dlpower = -22
+        expected_no_cells = 5
+        pcid_list = [serving_cell_pcid, neigh_cell_1_pcid, neigh_cell_2_pcid,
+                     neigh_cell_3_pcid, neigh_cell_4_pcid]
+        pcid_power_map  = {
+                            serving_cell_pcid: serving_cell_dlpower,
+                            neigh_cell_1_pcid: neigh_cell_1_dlpower,
+                            neigh_cell_2_pcid: neigh_cell_2_dlpower,
+                            neigh_cell_3_pcid: neigh_cell_3_dlpower,
+                            neigh_cell_4_pcid: neigh_cell_4_dlpower
+                          }
+
+        self.md8475a.reset()
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+        self._setup_lte_serving_cell(bts1, serving_cell_dlpower, serving_cell_cid,
+                                     serving_cell_pcid)
+
+        self._setup_lte_neighbhor_cell_md8475a(bts2, LTE_BAND_2, neigh_cell_1_dlpower,
+                                               neigh_cell_1_cid, neigh_cell_1_pcid)
+        self.md8475a.start_simulation()
+
+        if not phone_setup_csfb(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                               .format(self.ad.serial))
+                return False
+        self.md8475a.wait_for_registration_state()
+
+        self.setup_3710a_waveform("1", "A", "1960MHZ", neigh_cell_2_dlpower,
+                                  "LTE", "10M_B1_CID2")
+
+        self.setup_3710a_waveform("1", "B", "1960MHZ", neigh_cell_3_dlpower,
+                                  "LTE", "10M_B1_CID3")
+
+        self.setup_3710a_waveform("2", "A", "1960MHZ", neigh_cell_4_dlpower,
+                                  "LTE", "10M_B1_CID5")
+        self.turn_on_3710a_sg(1)
+        self.turn_on_3710a_sg(2)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_lte_cells_information(expected_no_cells,
+                                             pcid_list,
+                                             pcid_power_map)
+
+    @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
+        cells are present (Phone camped on LTE)
+
+        Setup a single LTE cell configuration on MD8475A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = lte_band4_ch2000_fr2115_pcid1_cell
+        serving_cell['power'] = -20
+        expected_no_cells = 1
+        cell_params = [serving_cell]
+
+        self.md8475a.reset()
+        [bts1] = set_system_model_lte(self.md8475a, self.user_params)
+        self._setup_lte_cell_md8475a(bts1, serving_cell, serving_cell['power'] )
+        self.md8475a.start_simulation()
+
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                              cell_params)
+
+    @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
+        cell is present (Phone camped on LTE)
+
+        Setup a two LTE cell configuration on MD8475A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = lte_band4_ch2000_fr2115_pcid1_cell
+        neighbor_cell = lte_band4_ch2000_fr2115_pcid2_cell
+        serving_cell['power'] = -20
+        neighbor_cell['power'] = -24
+        expected_no_cells = 2
+        cell_params = [serving_cell, neighbor_cell]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+        self._setup_lte_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_lte_cell_md8475a(bts2, neighbor_cell, neighbor_cell['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("LTE", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 1, "LTE_4_C2000_F2115_PCID2")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on LTE)
+
+        Setup one LTE cell configuration on MD8475A
+        Setup two waveform on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = lte_band4_ch2000_fr2115_pcid1_cell
+        neighbor_cell_1 = lte_band4_ch2000_fr2115_pcid2_cell
+        neighbor_cell_2 = lte_band4_ch2000_fr2115_pcid3_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -23
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+        self._setup_lte_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_lte_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("LTE", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 1, "LTE_4_C2000_F2115_PCID2")
+        bts1.set_neighbor_cell_type("LTE", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 2, "LTE_4_C2000_F2115_PCID3")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        self.setup_3710a_waveform("1", "A", "2115MHz", neighbor_cell_2['power'],
+                                  "LTE", "lte_4_ch2000_pcid3")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on LTE)
+
+        Setup a one LTE cell configuration on MD8475A
+        Setup three waveforms on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = lte_band4_ch2000_fr2115_pcid1_cell
+        neighbor_cell_1 = lte_band4_ch2000_fr2115_pcid2_cell
+        neighbor_cell_2 = lte_band4_ch2000_fr2115_pcid3_cell
+        neighbor_cell_3 = lte_band4_ch2000_fr2115_pcid4_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -23
+        neighbor_cell_3['power'] = -22
+        expected_no_cells = 4
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2,
+                       neighbor_cell_3]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+        self._setup_lte_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_lte_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("LTE", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 1, "LTE_4_C2000_F2115_PCID2")
+        bts1.set_neighbor_cell_type("LTE", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 2, "LTE_4_C2000_F2115_PCID3")
+        bts1.set_neighbor_cell_type("LTE", 3, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 3, "LTE_4_C2000_F2115_PCID4")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        self.setup_3710a_waveform("1", "A", "2115MHz", neighbor_cell_2['power'],
+                                  "LTE", "lte_4_ch2000_pcid3")
+
+        self.setup_3710a_waveform("1", "B", "2115MHz", neighbor_cell_3['power'],
+                                  "LTE", "lte_4_ch2000_pcid4")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter frequency) are present (Phone camped on LTE)
+
+        Setup a a LTE cell configuration on MD8475A
+        Setup two LTE waveforms(inter frequency) on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = lte_band4_ch2000_fr2115_pcid1_cell
+        neighbor_cell_1 = lte_band4_ch2050_fr2120_pcid7_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -23
+        expected_no_cells = 2
+        cell_params = [serving_cell, neighbor_cell_1]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+        self._setup_lte_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_lte_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("LTE", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 1, "LTE_4_C2050_F2120_PCID7")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        self.md8475a.set_packet_preservation()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter frequency) are present (Phone camped on LTE)
+
+        Setup a a LTE cell configuration on MD8475A
+        Setup two LTE waveforms(inter frequency) on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = lte_band4_ch2000_fr2115_pcid1_cell
+        neighbor_cell_1 = lte_band4_ch2050_fr2120_pcid7_cell
+        neighbor_cell_2 = lte_band4_ch2250_fr2140_pcid8_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -23
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+        self._setup_lte_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_lte_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("LTE", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 1, "LTE_4_C2050_F2120_PCID7")
+        bts1.set_neighbor_cell_type("LTE", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 2, "LTE_4_C2250_F2140_PCID8")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        self.setup_3710a_waveform("1", "A", "2140MHz", neighbor_cell_2['power'],
+                                  "LTE", "lte_4_ch2250_pcid8")
+
+        self.turn_on_3710a_sg(1)
+        self.md8475a.set_packet_preservation()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter band) are present (Phone camped on LTE)
+
+        Setup one LTE cell configuration on MD8475A
+        Setup two LTE waveforms((inter band)) on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = lte_band4_ch2000_fr2115_pcid1_cell
+        neighbor_cell_1 = lte_band2_ch900_fr1960_pcid9_cell
+        neighbor_cell_2 = lte_band12_ch5095_fr737_pcid10_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_lte_lte(self.md8475a, self.user_params)
+        self._setup_lte_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_lte_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("LTE", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 1, "LTE_2_C900_F1960_PCID9")
+        bts1.set_neighbor_cell_type("LTE", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 2, "LTE_12_C5095_F737_PCID10")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        self.setup_3710a_waveform("1", "A", "737.5MHz", neighbor_cell_2['power'],
+                                  "LTE", "lte_12_ch5095_pcid10")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        self.md8475a.set_packet_preservation()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter RAT) are present (Phone camped on LTE)
+
+        Setup one LTE and one WCDMA cell configuration on MD8475A
+        Setup one GSM waveform on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = lte_band4_ch2000_fr2115_pcid1_cell
+        neighbor_cell_1 = wcdma_band1_ch10700_fr2140_cid31_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_lte_wcdma(self.md8475a, self.user_params)
+        self._setup_lte_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_wcdma_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("WCDMA", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 1, "WCDM_1_C10700_F2140_CID31")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        self.md8475a.set_packet_preservation()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter RAT) are present (Phone camped on LTE)
+
+        Setup one LTE and one WCDMA cell configuration on MD8475A
+        Setup one GSM waveform on MG3710A
+        Make Sure Phone is in LTE mode
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = lte_band4_ch2000_fr2115_pcid1_cell
+        neighbor_cell_1 = wcdma_band1_ch10700_fr2140_cid31_cell
+        neighbor_cell_2 = gsm_band1900_ch512_fr1930_cid51_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_lte_wcdma(self.md8475a, self.user_params)
+        self._setup_lte_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_wcdma_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("WCDMA", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 1, "WCDM_1_C10700_F2140_CID31")
+        bts1.set_neighbor_cell_type("GSM", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 1, "GSM_1900_C512_F1930_CID51")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_csfb(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        self.setup_3710a_waveform("1", "A", "1930.2MHz", neighbor_cell_2['power'],
+                                  "GSM", "gsm_lac51_cid51")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        self.md8475a.set_packet_preservation()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on WCDMA)
+
+        Setup a single WCDMA cell configuration on MD8475A
+        Make Sure Phone camped on WCDMA
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = wcdma_band1_ch10700_fr2140_cid31_cell
+        serving_cell['power'] = -20
+        expected_no_cells = 1
+        cell_params = [serving_cell]
+
+        self.md8475a.reset()
+        [bts1] = set_system_model_wcdma(self.md8475a, self.user_params)
+        self._setup_wcdma_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self.md8475a.start_simulation()
+
+        if not phone_setup_3g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells is present (Phone camped on WCDMA)
+
+        Setup two WCDMA cell configuration on MD8475A
+        Make Sure Phone camped on WCDMA
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = wcdma_band1_ch10700_fr2140_cid31_cell
+        neighbor_cell = wcdma_band1_ch10700_fr2140_cid34_cell
+        serving_cell['power'] = -20
+        neighbor_cell['power'] = -24
+        expected_no_cells = 2
+        cell_params = [serving_cell, neighbor_cell]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_wcdma_wcdma(self.md8475a, self.user_params)
+        self._setup_wcdma_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_wcdma_cell_md8475a(bts2, neighbor_cell, neighbor_cell['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("WCDMA", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 1, "WCDM_1_C10700_F2140_CID34")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        if not phone_setup_3g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on WCDMA)
+
+        Setup two WCDMA cell configuration on MD8475A
+        Setup one WCDMA waveform on MG3710A
+        Make Sure Phone camped on WCDMA
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = wcdma_band1_ch10700_fr2140_cid31_cell
+        neighbor_cell_1 = wcdma_band1_ch10700_fr2140_cid32_cell
+        neighbor_cell_2 = wcdma_band1_ch10700_fr2140_cid33_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_wcdma_wcdma(self.md8475a, self.user_params)
+        self._setup_wcdma_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_wcdma_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("WCDMA", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 1, "WCDM_1_C10700_F2140_CID32")
+        bts1.set_neighbor_cell_type("WCDMA", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 2, "WCDM_1_C10700_F2140_CID33")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        if not phone_setup_3g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        self.setup_3710a_waveform("1", "A", "2140MHz", neighbor_cell_2['power'],
+                                  "WCDMA", "wcdma_1_psc33_cid33")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on WCDMA)
+
+        Setup two WCDMA cell configuration on MD8475A
+        Setup two WCDMA waveform on MG3710A
+        Make Sure Phone camped on WCDMA
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = wcdma_band1_ch10700_fr2140_cid31_cell
+        neighbor_cell_1 = wcdma_band1_ch10700_fr2140_cid32_cell
+        neighbor_cell_2 = wcdma_band1_ch10700_fr2140_cid33_cell
+        neighbor_cell_3 = wcdma_band1_ch10700_fr2140_cid34_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -23
+        neighbor_cell_3['power'] = -22
+        expected_no_cells = 4
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2,
+                       neighbor_cell_3]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_wcdma_wcdma(self.md8475a, self.user_params)
+        self._setup_wcdma_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_wcdma_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("WCDMA", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 1, "WCDM_1_C10700_F2140_CID32")
+        bts1.set_neighbor_cell_type("WCDMA", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 2, "WCDM_1_C10700_F2140_CID33")
+        bts1.set_neighbor_cell_type("WCDMA", 3, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 3, "WCDM_1_C10700_F2140_CID34")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        if not phone_setup_3g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        self.setup_3710a_waveform("1", "A", "2140MHz", neighbor_cell_2['power'],
+                                  "WCDMA", "wcdma_1_psc33_cid33")
+
+        self.setup_3710a_waveform("2", "A", "2140MHz", neighbor_cell_3['power'],
+                                  "WCDMA", "wcdma_1_psc34_cid34")
+        self.turn_on_3710a_sg(1)
+        self.turn_on_3710a_sg(2)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter frequency) are present (Phone camped on WCDMA)
+
+        Setup a two WCDMA cell configuration on MD8475A
+        Setup one WCDMA waveform on MG3710A
+        Make Sure Phone camped on WCDMA
+        Verify the number of neighbor cells reported by Phonene
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = wcdma_band1_ch10700_fr2140_cid31_cell
+        neighbor_cell_1 = wcdma_band1_ch10800_fr2160_cid37_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        expected_no_cells = 2
+        cell_params = [serving_cell, neighbor_cell_1]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_wcdma_wcdma(self.md8475a, self.user_params)
+        self._setup_wcdma_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_wcdma_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("WCDMA", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 1, "WCDM_1_C10800_F2160_CID37")
+        self.md8475a.start_simulation()
+        #To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_3g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter frequency) are present (Phone camped on WCDMA)
+
+        Setup a two WCDMA cell configuration on MD8475A
+        Setup one WCDMA waveform on MG3710A
+        Make Sure Phone camped on WCDMA
+        Verify the number of neighbor cells reported by Phonene
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = wcdma_band1_ch10700_fr2140_cid31_cell
+        neighbor_cell_1 = wcdma_band1_ch10575_fr2115_cid36_cell
+        neighbor_cell_2 = wcdma_band1_ch10800_fr2160_cid37_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -23
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_wcdma_wcdma(self.md8475a, self.user_params)
+        self._setup_wcdma_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_wcdma_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("WCDMA", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 1, "WCDM_1_C10575_F2115_CID36")
+        bts1.set_neighbor_cell_type("WCDMA", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 2, "WCDM_1_C10800_F2160_CID37")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_3g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        self.setup_3710a_waveform("1", "A", "2160MHz", neighbor_cell_2['power'],
+                                  "WCDMA", "wcdma_1_psc37_cid37")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter band) are present (Phone camped on WCDMA)
+
+        Setup a two WCDMA cell configuration on MD8475A
+        Setup one WCDMA waveform on MG3710A
+        Make Sure Phone camped on WCDMA
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = wcdma_band1_ch10700_fr2140_cid31_cell
+        neighbor_cell_1 = wcdma_band2_ch9800_fr1960_cid38_cell
+        neighbor_cell_2 = wcdma_band2_ch9900_fr1980_cid39_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -23
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2 ]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_wcdma_wcdma(self.md8475a, self.user_params)
+        self._setup_wcdma_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_wcdma_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("WCDMA", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 1, "WCDM_2_C9800_F1960_CID38")
+        bts1.set_neighbor_cell_type("WCDMA", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 2, "WCDM_2_C9900_F1980_CID39")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_3g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        self.setup_3710a_waveform("1", "A", "1980MHz", neighbor_cell_2['power'],
+                                  "WCDMA", "wcdma_2_psc39_cid39")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on WCDMA)
+
+        Setup a two WCDMA cell configuration on MD8475A
+        Setup one WCDMA waveform on MG3710A
+        Make Sure Phone camped on WCDMA
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = wcdma_band1_ch10700_fr2140_cid31_cell
+        neighbor_cell_1 = gsm_band1900_ch512_fr1930_cid51_cell
+        neighbor_cell_2 = lte_band4_ch2000_fr2115_pcid1_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -23
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2 ]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_lte_wcdma(self.md8475a, self.user_params)
+        self._setup_wcdma_cell_md8475a(bts2, serving_cell, serving_cell['power'])
+        self._setup_lte_cell_md8475a(bts1, neighbor_cell_2, neighbor_cell_2['power'])
+        bts2.neighbor_cell_mode = "USERDATA"
+        bts2.set_neighbor_cell_type("LTE", 1, "CELLNAME")
+        bts2.set_neighbor_cell_name("LTE", 1, "LTE_4_C2000_F2115_PCID1")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts1.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_3g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts1.service_state =  BtsServiceState.SERVICE_STATE_IN
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on WCDMA)
+
+        Setup a two WCDMA cell configuration on MD8475A
+        Setup one WCDMA waveform on MG3710A
+        Make Sure Phone camped on WCDMA
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = wcdma_band1_ch10700_fr2140_cid31_cell
+        neighbor_cell_1 = gsm_band1900_ch512_fr1930_cid51_cell
+        neighbor_cell_2 = lte_band4_ch2000_fr2115_pcid1_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -23
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2 ]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1, bts2] = set_system_model_wcdma_gsm(self.md8475a, self.user_params)
+        self._setup_wcdma_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self._setup_gsm_cell_md8475a(bts2, neighbor_cell_1, neighbor_cell_1['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("GSM", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 1, "GSM_1900_C512_F1930_CID51")
+        bts1.set_neighbor_cell_type("LTE", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 1, "LTE_4_C2000_F2115_PCID1")
+        self.md8475a.start_simulation()
+        # To make sure phone camps on BTS1
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_OUT
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_voice_general(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        bts2.service_state =  BtsServiceState.SERVICE_STATE_IN
+        self.setup_3710a_waveform("1", "A", "2115MHz", neighbor_cell_2['power'],
+                                  "LTE", "lte_4_ch2000_pcid1")
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on GSM)
+
+        Setup one GSM cell configuration on MD8475A
+        Make Sure Phone camped on GSM
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = gsm_band1900_ch512_fr1930_cid51_cell
+        serving_cell['power'] = -30
+        expected_no_cells = 1
+        cell_params = [serving_cell]
+
+        self.md8475a.reset()
+        [bts1] = set_system_model_gsm(self.md8475a, self.user_params)
+        self._setup_gsm_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        self.md8475a.start_simulation()
+
+        if not phone_setup_voice_general(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cell is present (Phone camped on GSM)
+
+        Setup one GSM cell configuration on MD8475A
+        Make Sure Phone camped on GSM
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = gsm_band1900_ch512_fr1930_cid51_cell
+        neighbor_cell = gsm_band1900_ch512_fr1930_cid52_cell
+        serving_cell['power'] = -20
+        neighbor_cell['power'] = -22
+        expected_no_cells = 2
+        cell_params = [serving_cell, neighbor_cell]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1] = set_system_model_gsm(self.md8475a, self.user_params)
+        self._setup_gsm_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("GSM", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 1, "GSM_1900_C512_F1930_CID52")
+        self.md8475a.start_simulation()
+
+        if not phone_setup_2g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self.ANRITSU_SETTLING_TIME)
+        self.setup_3710a_waveform("1", "A", "1930.2MHz", neighbor_cell['power'],
+                                  "GSM", "gsm_lac52_cid52")
+
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on GSM)
+
+        Setup one GSM cell configuration on MD8475A
+        Setup two GSM waveforms on MG3710A
+        Make Sure Phone camped on GSM
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = gsm_band1900_ch512_fr1930_cid51_cell
+        neighbor_cell_1 = gsm_band1900_ch512_fr1930_cid52_cell
+        neighbor_cell_2 = gsm_band1900_ch512_fr1930_cid53_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2 ]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1] = set_system_model_gsm(self.md8475a, self.user_params)
+        self._setup_gsm_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("GSM", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 1, "GSM_1900_C512_F1930_CID52")
+        bts1.set_neighbor_cell_type("GSM", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 2, "GSM_1900_C512_F1930_CID53")
+        self.md8475a.start_simulation()
+
+        if not phone_setup_2g(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        self.setup_3710a_waveform("1", "A", "1930.2MHz", neighbor_cell_1['power'],
+                                  "GSM", "gsm_lac52_cid52")
+
+        self.setup_3710a_waveform("2", "A", "1930.2MHz", neighbor_cell_2['power'],
+                                  "GSM", "gsm_lac53_cid53")
+        self.turn_on_3710a_sg(1)
+        self.turn_on_3710a_sg(2)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells are present (Phone camped on GSM)
+
+        Setup one GSM cell configuration on MD8475A
+        Setup three GSM waveforms on MG3710A
+        Make Sure Phone camped on GSM
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = gsm_band1900_ch512_fr1930_cid51_cell
+        neighbor_cell_1 = gsm_band1900_ch512_fr1930_cid52_cell
+        neighbor_cell_2 = gsm_band1900_ch512_fr1930_cid53_cell
+        neighbor_cell_3 = gsm_band1900_ch512_fr1930_cid54_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -22
+        neighbor_cell_3['power'] = -24
+        expected_no_cells = 4
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2, neighbor_cell_3]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1] = set_system_model_gsm(self.md8475a, self.user_params)
+        self._setup_gsm_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("GSM", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 1, "GSM_1900_C512_F1930_CID52")
+        bts1.set_neighbor_cell_type("GSM", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 2, "GSM_1900_C512_F1930_CID53")
+        bts1.set_neighbor_cell_type("GSM", 3, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 3, "GSM_1900_C512_F1930_CID53")
+        self.md8475a.start_simulation()
+
+        if not phone_setup_voice_general(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        self.setup_3710a_waveform("1", "A", "1930.2MHz", neighbor_cell_1['power'],
+                                  "GSM", "gsm_lac52_cid52")
+
+        self.setup_3710a_waveform("2", "A", "1930.2MHz", neighbor_cell_2['power'],
+                                  "GSM", "gsm_lac53_cid53")
+
+        self.setup_3710a_waveform("2", "B", "1930.2MHz", neighbor_cell_3['power'],
+                                  "GSM", "gsm_lac54_cid54")
+        self.turn_on_3710a_sg(1)
+        self.turn_on_3710a_sg(2)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter frequency) is present (Phone camped on GSM)
+
+        Setup one GSM cell configuration on MD8475A
+        Setup two GSM waveforms on MG3710A
+        Make Sure Phone camped on GSM
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = gsm_band1900_ch512_fr1930_cid51_cell
+        neighbor_cell_1 = gsm_band1900_ch640_fr1955_cid56_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        expected_no_cells = 2
+        cell_params = [serving_cell, neighbor_cell_1]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1] = set_system_model_gsm(self.md8475a, self.user_params)
+        self._setup_gsm_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("GSM", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 1, "GSM_1900_C640_F1955_CID56")
+        self.md8475a.start_simulation()
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_voice_general(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        self.setup_3710a_waveform("1", "A", "1955.8MHz", neighbor_cell_1['power'],
+                                  "GSM", "gsm_lac56_cid56")
+
+        self.turn_on_3710a_sg(1)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter frequency) are present (Phone camped on GSM)
+
+        Setup one GSM cell configuration on MD8475A
+        Setup two GSM waveforms on MG3710A
+        Make Sure Phone camped on GSM
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = gsm_band1900_ch512_fr1930_cid51_cell
+        neighbor_cell_1 = gsm_band1900_ch640_fr1955_cid56_cell
+        neighbor_cell_2 = gsm_band1900_ch750_fr1977_cid57_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1] = set_system_model_gsm(self.md8475a, self.user_params)
+        self._setup_gsm_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("GSM", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 1, "GSM_1900_C640_F1955_CID56")
+        bts1.set_neighbor_cell_type("GSM", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 2, "GSM_1900_C750_F1977_CID57")
+        self.md8475a.start_simulation()
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_voice_general(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        self.setup_3710a_waveform("1", "A", "1955.8MHz", neighbor_cell_1['power'],
+                                  "GSM", "gsm_lac56_cid56")
+
+        self.setup_3710a_waveform("2", "A", "1977.8MHz", neighbor_cell_2['power'],
+                                  "GSM", "gsm_lac57_cid57")
+        self.turn_on_3710a_sg(1)
+        self.turn_on_3710a_sg(2)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter band) are present (Phone camped on GSM)
+
+        Setup one GSM cell configuration on MD8475A
+        Setup two GSM waveforms on MG3710A
+        Make Sure Phone camped on GSM
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = gsm_band1900_ch512_fr1930_cid51_cell
+        neighbor_cell_1 = gsm_band850_ch128_fr869_cid58_cell
+        neighbor_cell_2 = gsm_band850_ch251_fr893_cid59_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1] = set_system_model_gsm(self.md8475a, self.user_params)
+        self._setup_gsm_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("GSM", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 1, "GSM_850_C128_F869_CID58")
+        bts1.set_neighbor_cell_type("GSM", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("GSM", 2, "GSM_850_C251_F893_CID59")
+        self.md8475a.start_simulation()
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_voice_general(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        self.setup_3710a_waveform("1", "A", "869MHz", neighbor_cell_1['power'],
+                                  "GSM", "gsm_lac58_cid58")
+
+        self.setup_3710a_waveform("2", "A", "893MHz", neighbor_cell_2['power'],
+                                  "GSM", "gsm_lac59_cid59")
+        self.turn_on_3710a_sg(1)
+        self.turn_on_3710a_sg(2)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+
+    @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
+        cells(inter RAT) are present (Phone camped on GSM)
+
+        Setup one GSM cell configuration on MD8475A
+        Setup one LTE and one GSM waveforms on MG3710A
+        Make Sure Phone camped on GSM
+        Verify the number of neighbor cells reported by Phone
+
+        Returns:
+            True if pass; False if fail
+        """
+        serving_cell = gsm_band1900_ch512_fr1930_cid51_cell
+        neighbor_cell_1 = lte_band4_ch2000_fr2115_pcid1_cell
+        neighbor_cell_2 = wcdma_band1_ch10700_fr2140_cid31_cell
+        serving_cell['power'] = -20
+        neighbor_cell_1['power'] = -24
+        neighbor_cell_2['power'] = -22
+        expected_no_cells = 3
+        cell_params = [serving_cell, neighbor_cell_1, neighbor_cell_2]
+
+        self.md8475a.reset()
+        self.md8475a.load_cell_paramfile(self.CELL_PARAM_FILE)
+        [bts1] = set_system_model_gsm(self.md8475a, self.user_params)
+        self._setup_gsm_cell_md8475a(bts1, serving_cell, serving_cell['power'])
+        bts1.neighbor_cell_mode = "USERDATA"
+        bts1.set_neighbor_cell_type("LTE", 1, "CELLNAME")
+        bts1.set_neighbor_cell_name("LTE", 1, "LTE_4_C2000_F2115_PCID1")
+        bts1.set_neighbor_cell_type("WCDMA", 2, "CELLNAME")
+        bts1.set_neighbor_cell_name("WCDMA", 2, "WCDM_1_C10700_F2140_CID31")
+        self.md8475a.start_simulation()
+
+        self.ad.droid.toggleDataConnection(False)
+        if not phone_setup_voice_general(self.log, self.ad):
+            self.log.error("Phone {} Failed to Set Up Properly"
+                           .format(self.ad.serial))
+            return False
+        self.md8475a.wait_for_registration_state()
+        self.setup_3710a_waveform("1", "A", "2115MHz", neighbor_cell_1['power'],
+                                  "LTE", "lte_1_ch2000_pcid1")
+
+        self.setup_3710a_waveform("2", "A", "2140MHz", neighbor_cell_2['power'],
+                                  "WCDMA", "wcdma_1_psc31_cid31")
+        self.turn_on_3710a_sg(1)
+        self.turn_on_3710a_sg(2)
+        time.sleep(self.SETTLING_TIME)
+        return self._verify_cells_information(expected_no_cells,
+                                             cell_params)
+    """ Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabSmsTest.py b/acts/tests/google/tel/lab/TelLabSmsTest.py
new file mode 100644
index 0000000..f45032b
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabSmsTest.py
@@ -0,0 +1,952 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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
+"""
+
+from acts.base_test import BaseTestClass
+from acts.controllers.tel.md8475a import MD8475A
+from acts.controllers.tel.md8475a import VirtualPhoneStatus
+from acts.test_utils.tel.tel_test_anritsu_utils import *
+from acts.controllers.tel._anritsu_utils import AnritsuError
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.utils import rand_ascii_str
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+SINGLE_PART_LEN = 40
+MULTI_PART_LEN = 180
+SINGLE_PART_LEN_75 = 75
+
+class TelLabSmsTest(TelephonyBaseTest):
+    phoneNumber = "11234123412"
+    SETTLING_TIME = 15
+    CELL_PARAM_FILE = 'C:\\MX847570\\CellParam\\2cell_param.wnscp'
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                   "test_mo_sms_singlepart_lte",
+                   "test_mt_sms_singlepart_lte",
+                   "test_mo_sms_multipart_lte",
+                   "test_mt_sms_multipart_lte",
+                   "test_mo_sms_singlepart_eacute_lte",
+                   "test_mt_sms_singlepart_eacute_lte",
+                   "test_mo_sms_multipart1_eacute_lte",
+                   "test_mt_sms_multipart1_eacute_lte",
+                   "test_mo_sms_multipart2_eacute_lte",
+                   "test_mt_sms_multipart2_eacute_lte",
+                   "test_mo_sms_multipart12_eacute_lte",
+                   "test_mt_sms_multipart12_eacute_lte",
+                   "test_mo_sms_singlepart_71chars_eacute_lte",
+                   "test_mt_sms_singlepart_71chars_eacute_lte",
+                   "test_mo_sms_singlepart_wcdma",
+                   "test_mt_sms_singlepart_wcdma",
+                   "test_mo_sms_multipart_wcdma",
+                   "test_mt_sms_multipart_wcdma",
+                   "test_mo_sms_singlepart_eacute_wcdma",
+                   "test_mt_sms_singlepart_eacute_wcdma",
+                   "test_mo_sms_multipart1_eacute_wcdma",
+                   "test_mt_sms_multipart1_eacute_wcdma",
+                   "test_mo_sms_multipart2_eacute_wcdma",
+                   "test_mt_sms_multipart2_eacute_wcdma",
+                   "test_mo_sms_multipart12_eacute_wcdma",
+                   "test_mt_sms_multipart12_eacute_wcdma",
+                   "test_mo_sms_singlepart_71chars_eacute_wcdma",
+                   "test_mt_sms_singlepart_71chars_eacute_wcdma",
+                   "test_mo_sms_singlepart_gsm",
+                   "test_mt_sms_singlepart_gsm",
+                   "test_mo_sms_multipart_gsm",
+                   "test_mt_sms_multipart_gsm",
+                   "test_mo_sms_singlepart_eacute_gsm",
+                   "test_mt_sms_singlepart_eacute_gsm",
+                   "test_mo_sms_multipart1_eacute_gsm",
+                   "test_mt_sms_multipart1_eacute_gsm",
+                   "test_mo_sms_multipart2_eacute_gsm",
+                   "test_mt_sms_multipart2_eacute_gsm",
+                   "test_mo_sms_multipart12_eacute_gsm",
+                   "test_mt_sms_multipart12_eacute_gsm",
+                   "test_mo_sms_singlepart_71chars_eacute_gsm",
+                   "test_mt_sms_singlepart_71chars_eacute_gsm",
+                   "test_mo_sms_singlepart_1x",
+                   "test_mt_sms_singlepart_1x",
+                   "test_mo_sms_multipart_1x",
+                   "test_mt_sms_multipart_1x",
+                   )
+        self.ad = self.android_devices[0]
+        self.md8475a_ip_address = self.user_params["anritsu_md8475a_ip_address"]
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log)
+        except AnritsuError:
+            self.log.error("Error in connecting to Anritsu Simulator")
+            return False
+        return True
+
+    def setup_test(self):
+        ensure_phones_idle(self.log, self.android_devices)
+        self.virtualPhoneHandle = self.anritsu.get_VirtualPhone()
+        self.ad.droid.connectivityToggleAirplaneMode(True)
+        return True
+
+    def teardown_test(self):
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        self.ad.droid.connectivityToggleAirplaneMode(True)
+        return True
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+        return True
+
+    def _setup_sms(self, set_simulation_func, rat, phone_number, message,
+            mo_mt=MOBILE_ORIGINATED):
+        try:
+            self.anritsu.reset()
+            self.anritsu.load_cell_paramfile(self.CELL_PARAM_FILE)
+            set_simulation_func(self.anritsu, self.user_params)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                phone_setup_func = phone_setup_csfb
+                pref_network_type = NETWORK_MODE_LTE_GSM_WCDMA
+            elif rat == RAT_WCDMA:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_WCDMA_PREF
+            elif rat == RAT_GSM:
+                phone_setup_func = phone_setup_2g
+                pref_network_type = NETWORK_MODE_GSM_ONLY
+            elif rat == RAT_1XRTT:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_CDMA
+            else:
+                phone_setup_func = phone_setup_csfb
+                network_type = NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+
+            self.ad.droid.setPreferredNetwork(pref_network_type)
+            if not phone_setup_func(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                               .format(self.ad.serial))
+                return False
+            self.anritsu.wait_for_registration_state()
+            time.sleep(self.SETTLING_TIME)
+            if mo_mt == MOBILE_ORIGINATED:
+                if not sms_mo_send(self.log, self.ad, self.virtualPhoneHandle,
+                                   phone_number, message, rat):
+                    self.log.error("Phone {} Failed to send SMS"
+                                   .format(self.ad.serial))
+                    return False
+            else:
+                if not sms_mt_receive_verify(self.log, self.ad,
+                                             self.virtualPhoneHandle,
+                                             phone_number, message, rat):
+                    self.log.error("Phone {} Failed to receive MT SMS"
+                                   .format(self.ad.serial))
+                    return False
+
+        except AnritsuError as e:
+            self.log.error("Error in connection with Anritsu Simulator: " + str(e))
+            return False
+        except Exception as e:
+            self.log.error("Exception during emergency call procedure: " + str(e))
+            return False
+        return True
+
+    def insert_string_into_message(self, message, string, index):
+        return message[:index] + string + message[index:]
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_lte(self):
+        """ Test MO SMS(less than 160 charcters) functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               rand_ascii_str(SINGLE_PART_LEN), MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_lte(self):
+        """ Test MT SMS(less than 160 charcters) functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               rand_ascii_str(SINGLE_PART_LEN), MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart_lte(self):
+        """ Test MO SMS(more than 160 charcters) functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               rand_ascii_str(MULTI_PART_LEN), MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart_lte(self):
+        """ Test MT SMS(more than 160 charcters) functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               rand_ascii_str(MULTI_PART_LEN), MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_eacute_lte(self):
+        """ Test MO SMS(single part contains é) functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN),
+                                               "single part contains é",
+                                               10)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_eacute_lte(self):
+        """ Test MT SMS(single part contains é) functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN),
+                                               "single part contains é",
+                                               10)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart1_eacute_lte(self):
+        """ Test MO SMS(multi part contains é in first part) functionality on
+        LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in first part",
+                                               10)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart1_eacute_lte(self):
+        """ Test MT SMS(multi part contains é in first part) functionality on
+        LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in first part",
+                                               10)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart2_eacute_lte(self):
+        """ Test MO SMS(multi part contains é in second part) functionality on
+        LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart2_eacute_lte(self):
+        """ Test MT SMS(multi part contains é in second part) functionality on
+        LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in second part",
+                                               10)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart12_eacute_lte(self):
+        """ Test MO SMS(multi part contains é in both parts) functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = rand_ascii_str(MULTI_PART_LEN)
+        text = self.insert_string_into_message(text,
+                                               "é in first part",
+                                               50)
+        text = self.insert_string_into_message(text,
+                                               "é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart12_eacute_lte(self):
+        """ Test MT SMS(multi part contains é in both parts) functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = rand_ascii_str(MULTI_PART_LEN)
+        text = self.insert_string_into_message(text,
+                                               "é in first part",
+                                               50)
+        text = self.insert_string_into_message(text,
+                                               "é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_71chars_eacute_lte(self):
+        """ Test MO SMS(single part more than 71 characters with é)
+        functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN_75),
+                                               "single part more than 71 characters with é",
+                                               72)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_71chars_eacute_lte(self):
+        """ Test MT SMS(single part more than 71 characters with é)
+        functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Send a SMS from Anritsu
+        Verify  Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN_75),
+                                               "single part more than 71 characters with é",
+                                               72)
+        return self._setup_sms(set_system_model_lte, RAT_LTE, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_wcdma(self):
+        """ Test MO SMS(less than 160 charcters) functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               rand_ascii_str(SINGLE_PART_LEN), MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_wcdma(self):
+        """ Test MT SMS(less than 160 charcters) functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               rand_ascii_str(SINGLE_PART_LEN), MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart_wcdma(self):
+        """ Test MO SMS(more than 160 charcters) functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               rand_ascii_str(MULTI_PART_LEN), MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart_wcdma(self):
+        """ Test MT SMS(more than 160 charcters) functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               rand_ascii_str(MULTI_PART_LEN), MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_eacute_wcdma(self):
+        """ Test MO SMS(single part contains é) functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN),
+                                               "single part contains é",
+                                               10)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_eacute_wcdma(self):
+        """ Test MT SMS(single part contains é) functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN),
+                                               "single part contains é",
+                                               10)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart1_eacute_wcdma(self):
+        """ Test MO SMS(multi part contains é in first part) functionality on
+        WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in first part",
+                                               10)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart1_eacute_wcdma(self):
+        """ Test MT SMS(multi part contains é in first part) functionality on
+        WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in first part",
+                                               10)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart2_eacute_wcdma(self):
+        """ Test MO SMS(multi part contains é in second part) functionality on
+        WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart2_eacute_wcdma(self):
+        """ Test MT SMS(multi part contains é in second part) functionality on
+        WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in second part",
+                                               10)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart12_eacute_wcdma(self):
+        """ Test MO SMS(multi part contains é in both parts) functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = rand_ascii_str(MULTI_PART_LEN)
+        text = self.insert_string_into_message(text,
+                                               "é in first part",
+                                               50)
+        text = self.insert_string_into_message(text,
+                                               "é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart12_eacute_wcdma(self):
+        """ Test MT SMS(multi part contains é in both parts) functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = rand_ascii_str(MULTI_PART_LEN)
+        text = self.insert_string_into_message(text,
+                                               "é in first part",
+                                               50)
+        text = self.insert_string_into_message(text,
+                                               "é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_71chars_eacute_wcdma(self):
+        """ Test MO SMS(single part more than 71 characters with é)
+        functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN_75),
+                                               "single part more than 71 characters with é",
+                                               72)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_71chars_eacute_wcdma(self):
+        """ Test MT SMS(single part more than 71 characters with é)
+        functionality on WCDMA
+
+        Make Sure Phone is in WCDMA mode
+        Send a SMS from Anritsu
+        Verify  Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN_75),
+                                               "single part more than 71 characters with é",
+                                               72)
+        return self._setup_sms(set_system_model_wcdma, RAT_WCDMA, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_gsm(self):
+        """ Test MO SMS(less than 160 charcters) functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               rand_ascii_str(SINGLE_PART_LEN), MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_gsm(self):
+        """ Test MT SMS(less than 160 charcters) functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               rand_ascii_str(SINGLE_PART_LEN), MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart_gsm(self):
+        """ Test MO SMS(more than 160 charcters) functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               rand_ascii_str(MULTI_PART_LEN), MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart_gsm(self):
+        """ Test MT SMS(more than 160 charcters) functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               rand_ascii_str(MULTI_PART_LEN), MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_eacute_gsm(self):
+        """ Test MO SMS(single part contains é) functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN),
+                                               "single part contains é",
+                                               10)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_eacute_gsm(self):
+        """ Test MT SMS(single part contains é) functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN),
+                                               "single part contains é",
+                                               10)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart1_eacute_gsm(self):
+        """ Test MO SMS(multi part contains é in first part) functionality on
+        GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in first part",
+                                               10)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart1_eacute_gsm(self):
+        """ Test MT SMS(multi part contains é in first part) functionality on
+        GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in first part",
+                                               10)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart2_eacute_gsm(self):
+        """ Test MO SMS(multi part contains é in second part) functionality on
+        GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart2_eacute_gsm(self):
+        """ Test MT SMS(multi part contains é in second part) functionality on
+        GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(MULTI_PART_LEN),
+                                               "multi part contains é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart12_eacute_gsm(self):
+        """ Test MO SMS(multi part contains é in both parts) functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = rand_ascii_str(MULTI_PART_LEN)
+        text = self.insert_string_into_message(text,
+                                               "é in first part",
+                                               50)
+        text = self.insert_string_into_message(text,
+                                               "é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart12_eacute_gsm(self):
+        """ Test MT SMS(multi part contains é in both parts) functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = rand_ascii_str(MULTI_PART_LEN)
+        text = self.insert_string_into_message(text,
+                                               "é in first part",
+                                               50)
+        text = self.insert_string_into_message(text,
+                                               "é in second part",
+                                               170)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_71chars_eacute_gsm(self):
+        """ Test MO SMS(single part more than 71 characters with é)
+        functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN_75),
+                                               "single part more than 71 characters with é",
+                                               72)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_71chars_eacute_gsm(self):
+        """ Test MT SMS(single part more than 71 characters with é)
+        functionality on GSM
+
+        Make Sure Phone is in GSM mode
+        Send a SMS from Anritsu
+        Verify  Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        text = self.insert_string_into_message(rand_ascii_str(SINGLE_PART_LEN_75),
+                                               "single part more than 71 characters with é",
+                                               72)
+        return self._setup_sms(set_system_model_gsm, RAT_GSM, self.phoneNumber,
+                               text, MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_singlepart_1x(self):
+        """ Test MO SMS(less than 160 charcters) functionality on CDMA1X
+
+        Make Sure Phone is in CDMA1X mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_1x, RAT_1XRTT, self.phoneNumber,
+                               rand_ascii_str(SINGLE_PART_LEN), MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_singlepart_1x(self):
+        """ Test MT SMS(less than 160 charcters) functionality on CDMA1X
+
+        Make Sure Phone is in CDMA1X mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_1x, RAT_1XRTT, self.phoneNumber,
+                               rand_ascii_str(SINGLE_PART_LEN), MOBILE_TERMINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_sms_multipart_1x(self):
+        """ Test MO SMS(more than 160 charcters) functionality on CDMA1X
+
+        Make Sure Phone is in CDMA1X mode
+        Send a SMS from Phone
+        Verify Anritsu receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_sms(set_system_model_1x, RAT_1XRTT, self.phoneNumber,
+                               rand_ascii_str(MULTI_PART_LEN), MOBILE_ORIGINATED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_sms_multipart_1x(self):
+        """ Test MT SMS(more than 160 charcters) functionality on CDMA1X
+
+        Make Sure Phone is in CDMA1X mode
+        Send a SMS from Anritsu
+        Verify Phone receives the SMS
+
+        Returns:
+            True if pass; False if fail
+        """
+        # TODO  This test is failing now. It seems anritsu is not sending message.
+        # Need to debug
+        return self._setup_sms(set_system_model_1x, RAT_1XRTT, self.phoneNumber,
+                               rand_ascii_str(MULTI_PART_LEN), MOBILE_TERMINATED)
+    """ Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabUeIdentityTest.py b/acts/tests/google/tel/lab/TelLabUeIdentityTest.py
new file mode 100644
index 0000000..61d4c6e
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabUeIdentityTest.py
@@ -0,0 +1,301 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Tests for reading UE Identity
+"""
+from acts.base_test import BaseTestClass
+from acts.controllers.tel.md8475a import MD8475A
+from acts.controllers.tel._anritsu_utils import AnritsuError
+from acts.test_utils.tel.tel_test_anritsu_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.controllers.tel.md8475a import UEIdentityType
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelLabUeIdentityTest(TelephonyBaseTest):
+
+    CELL_PARAM_FILE = 'C:\\MX847570\\CellParam\\2cell_param.wnscp'
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                    "test_read_imsi_lte",
+                    "test_read_imei_lte",
+                    "test_read_imeisv_lte",
+                    "test_read_imsi_wcdma",
+                    "test_read_imei_wcdma",
+                    "test_read_imeisv_wcdma",
+                    "test_read_imsi_gsm",
+                    "test_read_imei_gsm",
+                    "test_read_imeisv_gsm"
+                    )
+        self.ad = self.android_devices[0]
+        self.md8475a_ip_address = self.user_params["anritsu_md8475a_ip_address"]
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log)
+        except AnritsuError:
+            self.log.error("Error in connecting to Anritsu Simulator")
+            return False
+        return True
+
+    def setup_test(self):
+        ensure_phones_idle(self.log, self.android_devices)
+        toggle_airplane_mode(self.log, self.ad, True)
+        return True
+
+    def teardown_test(self):
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        toggle_airplane_mode(self.log, self.ad, True)
+        return True
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+        return True
+
+    def _read_identity(self, set_simulation_func, rat, identity_type):
+        try:
+            self.anritsu.reset()
+            self.anritsu.load_cell_paramfile(self.CELL_PARAM_FILE)
+            set_simulation_func(self.anritsu, self.user_params)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                phone_setup_func = phone_setup_csfb
+                pref_network_type = NETWORK_MODE_LTE_GSM_WCDMA
+            elif rat == RAT_WCDMA:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_WCDMA_PREF
+            elif rat == RAT_GSM:
+                phone_setup_func = phone_setup_2g
+                pref_network_type = NETWORK_MODE_GSM_ONLY
+            elif rat == RAT_1XRTT:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_CDMA
+            else:
+                self.log.error("Invalid RAT - Please specify a valid RAT")
+                return False
+
+            self.ad.droid.setPreferredNetwork(pref_network_type)
+            self.ad.droid.toggleDataConnection(True)
+            if not phone_setup_func(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                               .format(self.ad.serial))
+                return False
+            self.anritsu.wait_for_registration_state()
+            time.sleep(WAIT_TIME_ANRITSU_REG_AND_OPER)
+            identity = read_ue_identity(self.log, self.ad,
+                                        self.anritsu,
+                                        identity_type)
+            if identity is None:
+                self.log.error("Phone {} Failed to get {}"
+                               .format(self.ad.serial, identity_type.value))
+                return False
+            else:
+                self.log.info("{}".format(identity))
+        except AnritsuError as e:
+            self.log.error("Error in connection with Anritsu Simulator: " + str(e))
+            return False
+        except Exception as e:
+            self.log.error("Exception during reading identity: " + str(e))
+            return False
+        return True
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_read_imsi_lte(self):
+        """Reading the IMSI of UE on LTE
+
+        Reads the IMSI of the UE when device is camped on LTE newtork
+
+        Steps:
+        1. Make Sure Phone is camped on LTE network
+        2. Send IMSI request from Anritsu
+        3. Display the IMSI returned by UE
+
+        Expected Result:
+        UE sends the correct IMSI to the Anritsu
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._read_identity(set_system_model_lte, RAT_LTE,
+                                   UEIdentityType.IMSI)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_read_imei_lte(self):
+        """Reading the IMEI of UE on LTE
+
+        Reads the IMEI of the UE when device is camped on LTE newtork
+
+        Steps:
+        1. Make Sure Phone is camped on LTE network
+        2. Send IMEI request from Anritsu
+        3. Display the IMEI returned by UE
+
+        Expected Result:
+        UE sends the correct IMEI to the Anritsu
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._read_identity(set_system_model_lte, RAT_LTE,
+                                   UEIdentityType.IMEI)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_read_imeisv_lte(self):
+        """Reading the IMEISV of UE on LTE
+
+        Reads the IMEISV of the UE when device is camped on LTE newtork
+
+        Steps:
+        1. Make Sure Phone is camped on LTE network
+        2. Send IMEISV request from Anritsu
+        3. Display the IMEISV returned by UE
+
+        Expected Result:
+        UE sends the correct IMEISV to the Anritsu
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._read_identity(set_system_model_lte, RAT_LTE,
+                                   UEIdentityType.IMEISV)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_read_imsi_wcdma(self):
+        """Reading the IMSI of UE on WCDMA
+
+        Reads the IMSI of the UE when device is camped on WCDMA newtork
+
+        Steps:
+        1. Make Sure Phone is camped on WCDMA network
+        2. Send IMSI request from Anritsu
+        3. Display the IMSI returned by UE
+
+        Expected Result:
+        UE sends the correct IMSI to the Anritsu
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._read_identity(set_system_model_wcdma, RAT_WCDMA,
+                                   UEIdentityType.IMSI)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_read_imei_wcdma(self):
+        """Reading the IMEI of UE on WCDMA
+
+        Reads the IMEI of the UE when device is camped on WCDMA newtork
+
+        Steps:
+        1. Make Sure Phone is camped on WCDMA network
+        2. Send IMEI request from Anritsu
+        3. Display the IMEI returned by UE
+
+        Expected Result:
+        UE sends the correct IMEI to the Anritsu
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._read_identity(set_system_model_wcdma, RAT_WCDMA,
+                                   UEIdentityType.IMEI)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_read_imeisv_wcdma(self):
+        """Reading the IMEISV of UE on WCDMA
+
+        Reads the IMEISV of the UE when device is camped on WCDMA newtork
+
+        Steps:
+        1. Make Sure Phone is camped on WCDMA network
+        2. Send IMEISV request from Anritsu
+        3. Display the IMEISV returned by UE
+
+        Expected Result:
+        UE sends the correct IMEISV to the Anritsu
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._read_identity(set_system_model_wcdma, RAT_WCDMA,
+                                   UEIdentityType.IMEISV)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_read_imsi_gsm(self):
+        """Reading the IMSI of UE on GSM
+
+        Reads the IMSI of the UE when device is camped on GSM newtork
+
+        Steps:
+        1. Make Sure Phone is camped on GSM network
+        2. Send IMSI request from Anritsu
+        3. Display the IMSI returned by UE
+
+        Expected Result:
+        UE sends the correct IMSI to the Anritsu
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._read_identity(set_system_model_gsm, RAT_GSM,
+                                   UEIdentityType.IMSI)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_read_imei_gsm(self):
+        """Reading the IMEI of UE on GSM
+
+        Reads the IMEI of the UE when device is camped on GSM newtork
+
+        Steps:
+        1. Make Sure Phone is camped on GSM network
+        2. Send IMEI request from Anritsu
+        3. Display the IMEI returned by UE
+
+        Expected Result:
+        UE sends the correct IMEI to the Anritsu
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._read_identity(set_system_model_gsm, RAT_GSM,
+                                   UEIdentityType.IMEI)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_read_imeisv_gsm(self):
+        """Reading the IMEISV of UE on GSM
+
+        Reads the IMEISV of the UE when device is camped on GSM newtork
+
+        Steps:
+        1. Make Sure Phone is camped on GSM network
+        2. Send IMEISV request from Anritsu
+        3. Display the IMEISV returned by UE
+
+        Expected Result:
+        UE sends the correct IMEISV to the Anritsu
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._read_identity(set_system_model_gsm, RAT_GSM,
+                                   UEIdentityType.IMEISV)
+    """ Tests End """
diff --git a/acts/tests/google/tel/lab/TelLabVoiceTest.py b/acts/tests/google/tel/lab/TelLabVoiceTest.py
new file mode 100644
index 0000000..8b39535
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabVoiceTest.py
@@ -0,0 +1,513 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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
+"""
+
+from acts.base_test import BaseTestClass
+from acts.controllers.tel.md8475a import MD8475A
+from acts.controllers.tel.md8475a import VirtualPhoneStatus
+from acts.test_utils.tel.tel_test_anritsu_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.controllers.tel.md8475a import CsfbType
+from acts.controllers.tel.md8475a import VirtualPhoneAutoAnswer
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelLabVoiceTest(TelephonyBaseTest):
+
+    CELL_PARAM_FILE = 'C:\\MX847570\\CellParam\\2cell_param.wnscp'
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                    "test_mo_voice_call_lte_wcdma_csfb_redirection_phone_hangup",
+                    "test_mo_voice_call_lte_wcdma_csfb_redirection_remote_hangup",
+                    "test_mo_voice_call_lte_wcdma_csfb_handover_phone_hangup",
+                    "test_mo_voice_call_lte_wcdma_csfb_handover_remote_hangup",
+                    "test_mt_voice_call_lte_wcdma_csfb_redirection_phone_hangup",
+                    "test_mt_voice_call_lte_wcdma_csfb_redirection_remote_hangup",
+                    "test_mt_voice_call_lte_wcdma_csfb_handover_phone_hangup",
+                    "test_mt_voice_call_lte_wcdma_csfb_handover_remote_hangup",
+                    "test_mo_voice_call_wcdma_phone_hangup",
+                    "test_mo_voice_call_wcdma_remote_hangup",
+                    "test_mt_voice_call_wcdma_phone_hangup",
+                    "test_mt_voice_call_wcdma_remote_hangup",
+                    "test_mo_voice_call_gsm_phone_hangup",
+                    "test_mo_voice_call_gsm_remote_hangup",
+                    "test_mt_voice_call_gsm_phone_hangup",
+                    "test_mt_voice_call_gsm_remote_hangup",
+                    "test_mo_voice_call_1x_phone_hangup",
+                    "test_mo_voice_call_1x_remote_hangup",
+                    "test_mt_voice_call_1x_phone_hangup",
+                    "test_mt_voice_call_1x_remote_hangup",
+                    )
+        self.ad = self.android_devices[0]
+        self.md8475a_ip_address = self.user_params["anritsu_md8475a_ip_address"]
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log)
+        except AnritsuError:
+            self.log.error("Error in connecting to Anritsu Simulator")
+            return False
+        return True
+
+    def setup_test(self):
+        ensure_phones_idle(self.log, self.android_devices)
+        self.virtualPhoneHandle = self.anritsu.get_VirtualPhone()
+        toggle_airplane_mode(self.log, self.ad, True)
+        return True
+
+    def teardown_test(self):
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        toggle_airplane_mode(self.log, self.ad, True)
+        return True
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+        return True
+
+    def _setup_voice_call(self, set_simulation_func, rat, mo_mt=MOBILE_ORIGINATED,
+        teardown_side=CALL_TEARDOWN_PHONE, csfb_type=None):
+        try:
+            self.anritsu.reset()
+            self.anritsu.load_cell_paramfile(self.CELL_PARAM_FILE)
+            set_simulation_func(self.anritsu, self.user_params)
+            self.virtualPhoneHandle.auto_answer = (VirtualPhoneAutoAnswer.ON, 2)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                phone_setup_func = phone_setup_csfb
+                pref_network_type = NETWORK_MODE_LTE_GSM_WCDMA
+                if csfb_type is not None:
+                    self.anritsu.csfb_type = csfb_type
+            elif rat == RAT_WCDMA:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_WCDMA_PREF
+            elif rat == RAT_GSM:
+                phone_setup_func = phone_setup_2g
+                pref_network_type = NETWORK_MODE_GSM_ONLY
+            elif rat == RAT_1XRTT:
+                phone_setup_func = phone_setup_3g
+                pref_network_type = NETWORK_MODE_CDMA
+            else:
+                phone_setup_func = phone_setup_csfb
+                network_type = NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+
+            self.ad.droid.setPreferredNetwork(pref_network_type)
+            if not phone_setup_func(self.log, self.ad):
+                self.log.error("Phone {} Failed to Set Up Properly"
+                              .format(self.ad.serial))
+                return False
+            self.anritsu.wait_for_registration_state()
+            time.sleep(10)
+            if mo_mt == MOBILE_ORIGINATED:
+                if not call_mo_setup_teardown(self.log, self.ad,
+                                             self.virtualPhoneHandle,
+                                             "777777", teardown_side,
+                                             emergency=False):
+                    self.log.error("Phone {} Failed to make MO call to 777777"
+                                 .format(self.ad.serial))
+                    return False
+            else:
+                if not call_mt_setup_teardown(self.log, self.ad,
+                                             self.virtualPhoneHandle,
+                                             "777777", teardown_side, rat):
+                    self.log.error("Phone {} Failed to make MT call from 777777"
+                                 .format(self.ad.serial))
+                    return False
+
+        except AnritsuError as e:
+            self.log.error("Error in connection with Anritsu Simulator: " + str(e))
+            return False
+        except Exception as e:
+            self.log.error("Exception during emergency call procedure: " + str(e))
+            return False
+        return True
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_lte_wcdma_csfb_redirection_phone_hangup(self):
+        """ Test MO Call on LTE - CSFB Redirection, hangup at phone
+
+        Make Sure Phone is in LTE mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call from Phone
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_lte_wcdma,
+                                      RAT_LTE,MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_PHONE,
+                                      CsfbType.CSFB_TYPE_REDIRECTION)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_lte_wcdma_csfb_redirection_remote_hangup(self):
+        """ Test MO Call on LTE - CSFB Redirection, hangup at remote
+
+        Make Sure Phone is in LTE mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call at Anritsu
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_lte_wcdma,
+                                      RAT_LTE, MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_REMOTE,
+                                      CsfbType.CSFB_TYPE_REDIRECTION)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_lte_wcdma_csfb_handover_phone_hangup(self):
+        """ Test MO Call on LTE - CSFB Handover, hangup at phone
+
+        Make Sure Phone is in LTE mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call at Anritsu
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_lte_wcdma,
+                                      RAT_LTE,MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_PHONE,
+                                      CsfbType.CSFB_TYPE_HANDOVER)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_lte_wcdma_csfb_handover_remote_hangup(self):
+        """ Test MO Call on LTE - CSFB Handover, hangup at remote
+
+        Make Sure Phone is in LTE mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call at Anritsu
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_lte_wcdma,
+                                      RAT_LTE, MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_REMOTE,
+                                      CsfbType.CSFB_TYPE_HANDOVER)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_lte_wcdma_csfb_redirection_phone_hangup(self):
+        """ Test MT Call on LTE - CSFB Redirection, hangup at phone
+
+        Make Sure Phone is in LTE mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from Phone
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_lte_wcdma,
+                                      RAT_LTE,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_PHONE,
+                                      CsfbType.CSFB_TYPE_REDIRECTION)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_lte_wcdma_csfb_redirection_remote_hangup(self):
+        """ Test MT Call on LTE - CSFB Redirection, hangup at remote
+
+        Make Sure Phone is in LTE mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from remote
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_lte_wcdma,
+                                      RAT_LTE,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_REMOTE,
+                                      CsfbType.CSFB_TYPE_REDIRECTION)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_lte_wcdma_csfb_handover_phone_hangup(self):
+        """ Test MT Call on LTE - CSFB Handover, hangup at phone
+
+        Make Sure Phone is in LTE mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from Phone
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_lte_wcdma,
+                                      RAT_LTE,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_PHONE,
+                                      CsfbType.CSFB_TYPE_HANDOVER)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_lte_wcdma_csfb_handover_remote_hangup(self):
+        """ Test MO Call on LTE - CSFB Handover, hangup at phone
+
+        Make Sure Phone is in LTE mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from remote
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_lte_wcdma,
+                                      RAT_LTE,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_REMOTE,
+                                      CsfbType.CSFB_TYPE_HANDOVER)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_wcdma_phone_hangup(self):
+        """ Test MO Call on WCDMA - hangup at phone
+
+        Make Sure Phone is in WCDMA mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call at Anritsu
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_wcdma,
+                                      RAT_WCDMA,MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_PHONE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_wcdma_remote_hangup(self):
+        """ Test MO Call on WCDMA - hangup at remote
+
+        Make Sure Phone is in WCDMA mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call at Anritsu
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_wcdma,
+                                      RAT_WCDMA,MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_REMOTE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_wcdma_phone_hangup(self):
+        """ Test MO Call on WCDMA - hangup at phone
+
+        Make Sure Phone is in WCDMA mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from remote
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_wcdma,
+                                      RAT_WCDMA,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_PHONE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_wcdma_remote_hangup(self):
+        """ Test MO Call on WCDMA - hangup at phone
+
+        Make Sure Phone is in WCDMA mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from remote
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_wcdma,
+                                      RAT_WCDMA,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_REMOTE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_gsm_phone_hangup(self):
+        """ Test MO Call on GSM - hangup at phone
+
+        Make Sure Phone is in GSM mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call at Anritsu
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_gsm,
+                                      RAT_GSM,MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_PHONE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_gsm_remote_hangup(self):
+        """ Test MO Call on GSM - hangup at remote
+
+        Make Sure Phone is in GSM mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call at Anritsu
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_gsm,
+                                      RAT_GSM,MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_REMOTE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_gsm_phone_hangup(self):
+        """ Test MO Call on GSM - hangup at phone
+
+        Make Sure Phone is in GSM mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from remote
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_gsm,
+                                      RAT_GSM,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_PHONE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_gsm_remote_hangup(self):
+        """ Test MO Call on GSM - hangup at phone
+
+        Make Sure Phone is in GSM mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from remote
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_gsm,
+                                      RAT_GSM,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_REMOTE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_1x_phone_hangup(self):
+        """ Test MO Call on CDMA 1X  - hangup at phone
+
+        Make Sure Phone is in CDMA 1X  mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call at Anritsu
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_1x,
+                                      RAT_1XRTT,MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_PHONE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mo_voice_call_1x_remote_hangup(self):
+        """ Test MO Call on CDMA 1X  - hangup at remote
+
+        Make Sure Phone is in CDMA 1X  mode
+        Initiate a voice call from Phone
+        Answer the call at Anritsu
+        Verify call state
+        Disconnects the call at Anritsu
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_1x,
+                                      RAT_1XRTT,MOBILE_ORIGINATED,
+                                      CALL_TEARDOWN_REMOTE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_1x_phone_hangup(self):
+        """ Test MO Call on CDMA 1X  - hangup at phone
+
+        Make Sure Phone is in CDMA 1X  mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from remote
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_1x,
+                                      RAT_1XRTT,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_PHONE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mt_voice_call_1x_remote_hangup(self):
+        """ Test MO Call on CDMA 1X - hangup at phone
+
+        Make Sure Phone is in CDMA 1X  mode
+        Initiate a voice call from Anritsu
+        Answer the call at phone
+        Verify call state
+        Disconnects the call from remote
+        Verify call state
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(set_system_model_1x,
+                                      RAT_1XRTT,MOBILE_TERMINATED,
+                                      CALL_TEARDOWN_REMOTE)
+    """ Tests End """
diff --git a/acts/tests/google/tel/lab/TelephonyConnectivitySanityTest.py b/acts/tests/google/tel/lab/TelephonyConnectivitySanityTest.py
new file mode 100644
index 0000000..fc2f728
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelephonyConnectivitySanityTest.py
@@ -0,0 +1,592 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 base_test import BaseTestClass
+
+from tel.md8475a import MD8475A
+from tel.md8475a import BtsTechnology
+from tel.md8475a import ProcessingStatus
+from tel.md8475a import BtsServiceState
+from tel.md8475a import TriggerMessageIDs
+from tel.md8475a import TriggerMessageReply
+import tel_utils
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelephonyConnectivitySanityTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                   "test_network_registration",
+                   "test_network_params_verification",
+                   "test_network_deregistration",
+                   "test_network_out_of_service",
+                   "test_network_return_inservice",
+                   "test_set_preferred_network",
+                   "test_network_emergency",
+                   )
+        self.anritsu = MD8475A(tel_utils.MD8475A_IP_ADDRESS)
+
+    def setup_test(self):
+        self.lte_bts, self.wcdma_bts = tel_utils.set_system_model(self.anritsu,
+                                                                  "LTE_WCDMA")
+        tel_utils.init_phone(self.droid, self.ed)
+        self.droid.phoneStartTrackingServiceStateChange()
+        self.droid.phoneStartTrackingDataConnectionStateChange()
+        self.log.info("Starting Simulation")
+        self.anritsu.start_simulation()
+        return True
+
+    def teardown_test(self):
+        self.droid.phoneStopTrackingServiceStateChange()
+        self.droid.phoneStopTrackingDataConnectionStateChange()
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        # turn off modem
+        tel_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_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_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_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_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_utils.WCDMA_NW_NAME
+                mcc = tel_utils.NW_MCC
+                mnc = tel_utils.NW_MNC
+            elif rat_info == BtsTechnology.LTE.value:
+                expected_voice_nwtype = "LTE"
+                operator_name = tel_utils.LTE_NW_NAME
+                mcc = tel_utils.NW_MCC
+                mnc = tel_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_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_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_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_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_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_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_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_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_utils.wait_for_network_registration(self.ed,
+                                                                self.anritsu,
+                                                                self.log)
+        self.log.info("Waiting for data state: DATA_CONNECTED")
+        test_status, event = tel_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_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_utils.wait_for_network_registration(
+                                                                   self.ed,
+                                                                   self.anritsu,
+                                                                   self.log)
+            self.log.info("Waiting for data state: DATA_CONNECTED")
+            test_status, event = tel_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_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_utils.wait_for_network_registration(
+                                                                   self.ed,
+                                                                   self.anritsu,
+                                                                   self.log)
+            self.log.info("Waiting for data state: DATA_CONNECTED")
+            test_status, event = tel_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_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_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_utils.NETWORK_MODE_LTE_ONLY
+                expected_nwtype = "LTE"
+            elif rat_info == BtsTechnology.LTE.value:
+                pref_nwtype = tel_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.setPreferredNetwork(pref_nwtype)
+            self.log.info("Waiting for service state: IN_SERVICE in "
+                          + expected_nwtype)
+            test_status, event = tel_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_utils.NETWORK_MODE_LTE_ONLY
+                expected_nwtype = "LTE"
+            elif rat_info == BtsTechnology.LTE.value:
+                pref_nwtype = tel_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.setPreferredNetwork(pref_nwtype)
+            self.log.info("Waiting for service state: IN_SERVICE in "
+                          + expected_nwtype)
+            test_status, event = tel_utils.wait_for_network_registration(
+                                                                self.ed,
+                                                                self.anritsu,
+                                                                self.log,
+                                                                expected_nwtype)
+        # setting the preferred network type to default
+        self.droid.setPreferredNetwork(tel_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_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for service state: emergency")
+        test_status, event = tel_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_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/lab/TelephonyDataSanityTest.py b/acts/tests/google/tel/lab/TelephonyDataSanityTest.py
new file mode 100644
index 0000000..50d96db
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelephonyDataSanityTest.py
@@ -0,0 +1,163 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 base_test import BaseTestClass
+
+from tel.md8475a import MD8475A
+from tel.md8475a import BtsNumber
+from tel.md8475a import BtsTechnology
+import tel_utils
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelephonyDataSanityTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                    "test_data_conn_state_when_access_enabled",
+                    "test_data_conn_state_when_access_disabled",
+                    )
+        self.anritsu = MD8475A(tel_utils.MD8475A_IP_ADDRESS)
+
+    def setup_test(self):
+        self.lte_bts, self.wcdma_bts = tel_utils.set_system_model(self.anritsu,
+                                                                  "LTE_WCDMA")
+        tel_utils.init_phone(self.droid, self.ed)
+        self.droid.phoneStartTrackingServiceStateChange()
+        self.droid.phoneStartTrackingDataConnectionStateChange()
+        self.log.info("Starting Simulation")
+        self.anritsu.start_simulation()
+        return True
+
+    def teardown_test(self):
+        self.droid.phoneStopTrackingServiceStateChange()
+        self.droid.phoneStopTrackingDataConnectionStateChange()
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        # turn off modem
+        tel_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_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_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_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_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_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_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.toggleDataConnection(False)
+            self.log.info("Waiting for data state: DATA_DISCONNECTED")
+            test_status, event = tel_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/tel/lab/cell_configurations.py b/acts/tests/google/tel/lab/cell_configurations.py
new file mode 100644
index 0000000..e14327b
--- /dev/null
+++ b/acts/tests/google/tel/lab/cell_configurations.py
@@ -0,0 +1,322 @@
+#!/usr/bin/python3.4
+# Copyright (C) 2014- The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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
+"""
+from acts.controllers.tel.md8475a import BtsBandwidth
+from acts.test_utils.tel.tel_test_anritsu_utils import *
+
+# Different Cell configurations
+# TMO bands
+lte_band4_ch2000_fr2115_pcid1_cell = {
+                                    'band': LTE_BAND_4,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 11,
+                                    'cid': 1,
+                                    'pcid': 1,
+                                    'channel':2000
+                                   }
+
+lte_band4_ch2000_fr2115_pcid2_cell = {
+                                    'band': LTE_BAND_4,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 12,
+                                    'cid': 2,
+                                    'pcid': 2,
+                                    'channel':2000
+                                   }
+
+lte_band4_ch2000_fr2115_pcid3_cell = {
+                                    'band': LTE_BAND_4,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 13,
+                                    'cid': 3,
+                                    'pcid': 3,
+                                    'channel':2000
+                                   }
+
+lte_band4_ch2000_fr2115_pcid4_cell = {
+                                    'band': LTE_BAND_4,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 14,
+                                    'cid': 4,
+                                    'pcid': 4,
+                                    'channel':2000
+                                   }
+
+lte_band4_ch2000_fr2115_pcid5_cell = {
+                                    'band': LTE_BAND_4,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 15,
+                                    'cid': 5,
+                                    'pcid': 5,
+                                    'channel':2000
+                                   }
+
+lte_band4_ch2000_fr2115_pcid6_cell = {
+                                    'band': LTE_BAND_4,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 16,
+                                    'cid': 6,
+                                    'pcid': 6,
+                                    'channel':2000
+                                   }
+
+lte_band4_ch2050_fr2120_pcid7_cell = {
+                                    'band': LTE_BAND_4,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 17,
+                                    'cid': 7,
+                                    'pcid': 7,
+                                    'channel':2050
+                                   }
+
+lte_band4_ch2250_fr2140_pcid8_cell = {
+                                    'band': LTE_BAND_4,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 18,
+                                    'cid': 8,
+                                    'pcid': 8,
+                                    'channel':2250
+                                   }
+
+lte_band2_ch900_fr1960_pcid9_cell = {
+                                    'band': LTE_BAND_2,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 19,
+                                    'cid': 9,
+                                    'pcid': 9,
+                                    'channel':900
+                                   }
+
+lte_band12_ch5095_fr737_pcid10_cell = {
+                                    'band': LTE_BAND_12,
+                                    'bandwidth': BtsBandwidth.LTE_BANDWIDTH_10MHz,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'tac': 20,
+                                    'cid': 10,
+                                    'pcid': 10,
+                                    'channel':5095
+                                   }
+
+wcdma_band1_ch10700_fr2140_cid31_cell = {
+                                    'band': WCDMA_BAND_1,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 31,
+                                    'rac': 31,
+                                    'cid': 31,
+                                    'channel':10700,
+                                    'psc': 31
+                                   }
+
+wcdma_band1_ch10700_fr2140_cid32_cell = {
+                                    'band': WCDMA_BAND_1,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 32,
+                                    'rac': 32,
+                                    'cid': 32,
+                                    'channel':10700,
+                                    'psc': 32
+                                   }
+
+wcdma_band1_ch10700_fr2140_cid33_cell = {
+                                    'band': WCDMA_BAND_1,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 33,
+                                    'rac': 33,
+                                    'cid': 33,
+                                    'channel':10700,
+                                    'psc': 33
+                                   }
+
+wcdma_band1_ch10700_fr2140_cid34_cell = {
+                                    'band': WCDMA_BAND_1,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 34,
+                                    'rac': 34,
+                                    'cid': 34,
+                                    'channel':10700,
+                                    'psc': 34
+                                   }
+
+wcdma_band1_ch10700_fr2140_cid35_cell = {
+                                    'band': WCDMA_BAND_1,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 35,
+                                    'rac': 35,
+                                    'cid': 35,
+                                    'channel':10700,
+                                    'psc': 35
+                                   }
+
+wcdma_band1_ch10575_fr2115_cid36_cell = {
+                                    'band': WCDMA_BAND_1,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 36,
+                                    'rac': 36,
+                                    'cid': 36,
+                                    'channel':10575,
+                                    'psc': 36
+                                   }
+
+wcdma_band1_ch10800_fr2160_cid37_cell = {
+                                    'band': WCDMA_BAND_1,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 37,
+                                    'rac': 37,
+                                    'cid': 37,
+                                    'channel':10800,
+                                    'psc': 37
+                                   }
+
+wcdma_band2_ch9800_fr1960_cid38_cell = {
+                                    'band': WCDMA_BAND_2,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 38,
+                                    'rac': 38,
+                                    'cid': 38,
+                                    'channel':9800,
+                                    'psc': 38
+                                   }
+
+wcdma_band2_ch9900_fr1980_cid39_cell = {
+                                    'band': WCDMA_BAND_2,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 39,
+                                    'rac': 39,
+                                    'cid': 39,
+                                    'channel':9900,
+                                    'psc': 39
+                                   }
+
+gsm_band1900_ch512_fr1930_cid51_cell = {
+                                    'band': GSM_BAND_PCS1900,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 51,
+                                    'rac': 51,
+                                    'cid': 51,
+                                    'channel':512,
+                                   }
+
+gsm_band1900_ch512_fr1930_cid52_cell = {
+                                    'band': GSM_BAND_PCS1900,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 52,
+                                    'rac': 52,
+                                    'cid': 52,
+                                    'channel':512,
+                                   }
+
+gsm_band1900_ch512_fr1930_cid53_cell = {
+                                    'band': GSM_BAND_PCS1900,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 53,
+                                    'rac': 53,
+                                    'cid': 53,
+                                    'channel':512,
+                                   }
+
+gsm_band1900_ch512_fr1930_cid54_cell = {
+                                    'band': GSM_BAND_PCS1900,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 54,
+                                    'rac': 54,
+                                    'cid': 54,
+                                    'channel':512,
+                                   }
+
+gsm_band1900_ch512_fr1930_cid55_cell = {
+                                    'band': GSM_BAND_PCS1900,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 55,
+                                    'rac': 55,
+                                    'cid': 55,
+                                    'channel':512,
+                                   }
+
+gsm_band1900_ch640_fr1955_cid56_cell = {
+                                    'band': GSM_BAND_PCS1900,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 56,
+                                    'rac': 56,
+                                    'cid': 56,
+                                    'channel':640,
+                                   }
+
+gsm_band1900_ch750_fr1977_cid57_cell = {
+                                    'band': GSM_BAND_PCS1900,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 57,
+                                    'rac': 57,
+                                    'cid': 57,
+                                    'channel':750,
+                                   }
+
+gsm_band850_ch128_fr869_cid58_cell = {
+                                    'band': GSM_BAND_GSM850,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 58,
+                                    'rac': 58,
+                                    'cid': 58,
+                                    'channel':128,
+                                   }
+
+gsm_band850_ch251_fr893_cid59_cell = {
+                                    'band': GSM_BAND_GSM850,
+                                    'mcc': '001',
+                                    'mnc': '01',
+                                    'lac': 59,
+                                    'rac': 59,
+                                    'cid': 59,
+                                    'channel':251,
+                                   }
+
diff --git a/acts/tests/google/tel/live/TelLiveDataTest.py b/acts/tests/google/tel/live/TelLiveDataTest.py
new file mode 100644
index 0000000..d2af18b
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveDataTest.py
@@ -0,0 +1,1900 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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.base_test import BaseTestClass
+from queue import Empty
+from acts.test_utils.wifi_test_utils import WifiEnums
+from acts.test_utils.tel.tel_lookup_tables import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_data_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.utils import load_config
+from acts.utils import enable_doze
+from acts.utils import disable_doze
+
+
+
+class TelLiveDataTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      "test_airplane_mode",
+                      "test_4g",
+                      "test_3g",
+                      "test_2g",
+                      "test_lte_wifi_switching",
+                      "test_wcdma_wifi_switching",
+                      "test_gsm_wifi_switching",
+                      "test_wifi_connect_disconnect",
+                      "test_lte_multi_bearer",
+                      "test_wcdma_multi_bearer",
+
+                      "test_2g_wifi_not_associated",
+                      "test_3g_wifi_not_associated",
+                      "test_4g_wifi_not_associated",
+
+                      # WiFi Tethering tests
+                      "test_tethering_entitlement_check",
+                      "test_tethering_2g_to_2gwifi",
+                      "test_tethering_2g_to_5gwifi",
+                      "test_tethering_3g_to_5gwifi",
+                      "test_tethering_3g_to_2gwifi",
+                      "test_tethering_4g_to_5gwifi",
+                      "test_tethering_4g_to_2gwifi",
+                      "test_tethering_4g_to_2gwifi_2clients",
+                      "test_toggle_apm_during_active_wifi_tethering",
+                      "test_toggle_data_during_active_wifi_tethering",
+                      "test_disable_wifi_tethering_resume_connected_wifi",
+                      "test_change_rat_during_active_wifi_tethering_lte_to_3g",
+                      "test_change_rat_during_active_wifi_tethering_3g_to_lte",
+                      "test_change_rat_during_active_wifi_tethering_3g_to_2g",
+                      "test_change_rat_during_active_wifi_tethering_2g_to_3g",
+                      "test_tethering_wifi_ssid_quotes",
+                      "test_tethering_wifi_no_password",
+                      "test_tethering_wifi_password_escaping_characters",
+                      "test_tethering_wifi_ssid",
+                      "test_tethering_wifi_password",
+
+                      "test_tethering_wifi_volte_call",
+                      "test_tethering_wifi_csfb_call",
+                      "test_tethering_wifi_3g_call",
+
+                      "test_tethering_wifi_reboot",
+                      "test_connect_wifi_start_tethering_wifi_reboot",
+                      "test_connect_wifi_reboot_start_tethering_wifi",
+                      "test_tethering_wifi_screen_off_enable_doze_mode",
+
+
+                      # stress tests
+                      "test_4g_stress",
+                      "test_3g_stress",
+                      "test_lte_multi_bearer_stress",
+                      "test_wcdma_multi_bearer_stress",
+                      "test_tethering_4g_to_2gwifi_stress",
+
+                      # SIM2 test cases
+                      "test_3g_sim2",
+                      "test_2g_sim2",
+                      "test_wcdma_wifi_switching_sim2",
+                      "test_gsm_wifi_switching_sim2",
+
+                      )
+        self.stress_test_number = int(self.user_params["stress_test_number"])
+        self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+
+        try:
+            self.wifi_network_pass = self.user_params["wifi_network_pass"]
+        except KeyError:
+            self.wifi_network_pass = None
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_airplane_mode(self):
+        """ Test airplane mode basic on Phone and Live SIM.
+
+        Ensure phone attach, data on, WiFi off and verify Internet.
+        Turn on airplane mode to make sure detach.
+        Turn off airplane mode to make sure attach.
+        Verify Internet connection.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return airplane_mode_test(self.log, self.android_devices[0])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_lte_wifi_switching(self):
+        """Test data connection network switching when phone camped on LTE.
+
+        Ensure phone is camped on LTE
+        Ensure WiFi can connect to live network,
+        Airplane mode is off, data connection is on, WiFi is on.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+        Turn on WiFi, verify data is on WiFi and browse to google.com is OK.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+
+        Returns:
+            True if pass.
+        """
+        return wifi_cell_switching(self.log, self.android_devices[0],
+                                   self.wifi_network_ssid, self.wifi_network_pass,
+                                   RAT_LTE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_wifi_switching(self):
+        """Test data connection network switching when phone camped on WCDMA.
+
+        Ensure phone is camped on WCDMA
+        Ensure WiFi can connect to live network,
+        Airplane mode is off, data connection is on, WiFi is on.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+        Turn on WiFi, verify data is on WiFi and browse to google.com is OK.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+
+        Returns:
+            True if pass.
+        """
+        return wifi_cell_switching(self.log, self.android_devices[0],
+                                   self.wifi_network_ssid, self.wifi_network_pass,
+                                   RAT_WCDMA)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_gsm_wifi_switching(self):
+        """Test data connection network switching when phone camped on GSM.
+
+        Ensure phone is camped on GSM
+        Ensure WiFi can connect to live network,,
+        Airplane mode is off, data connection is on, WiFi is on.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+        Turn on WiFi, verify data is on WiFi and browse to google.com is OK.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+
+        Returns:
+            True if pass.
+        """
+        return wifi_cell_switching(self.log, self.android_devices[0],
+                                   self.wifi_network_ssid, self.wifi_network_pass,
+                                   RAT_GSM)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_lte_multi_bearer(self):
+        """Test LTE data connection before call and in call. (VoLTE call)
+
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in LTE, verify Internet.
+        Initiate a voice call. verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+        Hangup Voice Call, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        toggle_volte(self.log, self.android_devices[0], True)
+
+        if not ensure_network_rat(
+                self.log, self.android_devices[0], RAT_LTE,
+                WAIT_TIME_NW_SELECTION):
+
+            self.log.error("Device {} failed to reselect in {}s.".format(
+                self.android_devices[0].serial, WAIT_TIME_NW_SELECTION))
+            return False
+
+        wait_for_droid_in_network_rat(
+            self.log, self.android_devices[0], RAT_LTE,
+            WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_VOICE)
+
+        return self._test_data_connectivity_multi_bearer(RAT_LTE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_multi_bearer(self):
+        """Test WCDMA data connection before call and in call.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in WCDMA, verify Internet.
+        Initiate a voice call. verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+        Hangup Voice Call, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        return self._test_data_connectivity_multi_bearer(RAT_WCDMA)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_multi_bearer_stress(self):
+        """Stress Test WCDMA data connection before call and in call.
+
+        This is a stress test for "test_wcdma_multi_bearer".
+        Default MINIMUM_SUCCESS_RATE is set to 95%.
+
+        Returns:
+            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
+
+        for i in range(1, self.stress_test_number+1):
+
+            ensure_phones_default_state(self.log,
+                [self.android_devices[0], self.android_devices[1]])
+
+            if self.test_wcdma_multi_bearer():
+                success_count += 1
+                result_str = "Succeeded"
+            else:
+                fail_count += 1
+                result_str = "Failed"
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_lte_multi_bearer_stress(self):
+        """Stress Test LTE data connection before call and in call. (VoLTE call)
+
+        This is a stress test for "test_lte_multi_bearer".
+        Default MINIMUM_SUCCESS_RATE is set to 95%.
+
+        Returns:
+            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
+
+        for i in range(1, self.stress_test_number+1):
+
+            ensure_phones_default_state(self.log,
+                [self.android_devices[0], self.android_devices[1]])
+
+            if self.test_lte_multi_bearer():
+                success_count += 1
+                result_str = "Succeeded"
+            else:
+                fail_count += 1
+                result_str = "Failed"
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    def _test_data_connectivity_multi_bearer(self, nw_gen):
+        """Test data connection before call and in call.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in WCDMA, verify Internet.
+        Initiate a voice call. verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+        Hangup Voice Call, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        class _LocalException(Exception):
+            pass
+
+        ad_list = [self.android_devices[0], self.android_devices[1]]
+        ensure_phones_idle(self.log, ad_list)
+
+        if not ensure_network_rat(
+                self.log, self.android_devices[0], nw_gen,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+
+            self.log.error("Device failed to reselect in {}s.".format(
+                WAIT_TIME_NW_SELECTION))
+            return False
+
+        # Temporary hack to give phone enough time to register.
+        # TODO: Proper check using SL4A API.
+        time.sleep(5)
+
+        self.log.info("Step1 WiFi is Off, Data is on Cell.")
+        toggle_airplane_mode(self.log, self.android_devices[0], False)
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        self.android_devices[0].droid.toggleDataConnection(True)
+        if (not wait_for_cell_data_connection(
+                self.log, self.android_devices[0], True) or not
+                verify_http_connection(
+                    self.log, self.android_devices[0])):
+            self.log.error("Data not available on cell")
+            return False
+
+        try:
+            self.log.info("Step2 Initiate call and accept.")
+            if not call_setup_teardown(self.log,
+                                       self.android_devices[0],
+                                       self.android_devices[1],
+                                       None,
+                                       None,
+                                       None):
+                self.log.error("Failed to Establish Voice Call")
+                return False
+
+            self.log.info("Step3 Verify internet.")
+            time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+            if not verify_http_connection(self.log, self.android_devices[0]):
+                raise _LocalException("Internet Inaccessible when Enabled")
+
+            self.log.info("Step4 Turn off data and verify not connected.")
+            self.android_devices[0].droid.toggleDataConnection(False)
+            if not wait_for_cell_data_connection(
+                    self.log, self.android_devices[0], False):
+                raise _LocalException("Failed to Disable Cellular Data")
+
+            if verify_http_connection(self.log, self.android_devices[0]):
+                raise _LocalException("Internet Accessible when Disabled")
+
+            self.log.info("Step5 Re-enable data.")
+            self.android_devices[0].droid.toggleDataConnection(True)
+            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_http_connection(self.log, self.android_devices[0]):
+                raise _LocalException("Internet Inaccessible when Enabled")
+
+            self.log.info("Step5 Verify phones still in call and Hang up.")
+            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_http_connection(self.log, self.android_devices[0]):
+                raise _LocalException("Internet Inaccessible when Enabled")
+
+        except _LocalException as e:
+            self.log.error(str(e))
+            try:
+                hangup_call(self.log, self.android_devices[0])
+                self.android_devices[0].droid.toggleDataConnection(True)
+            except Exception:
+                pass
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_2g(self):
+        """Test data connection in 2G.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Ensure phone data generation is 2G.
+        Verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        WifiUtils.wifi_reset(self.log, self.android_devices[0])
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        return data_connectivity_single_bearer(self.log,
+                                               self.android_devices[0],
+                                               RAT_2G)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_2g_wifi_not_associated(self):
+        """Test data connection in 2G.
+
+        Turn off airplane mode, enable WiFi (but not connected), enable Cellular Data.
+        Ensure phone data generation is 2G.
+        Verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        WifiUtils.wifi_reset(self.log, self.android_devices[0])
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], True)
+        return data_connectivity_single_bearer(self.log,
+                                               self.android_devices[0],
+                                               RAT_2G)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_3g(self):
+        """Test data connection in 3G.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Ensure phone data generation is 3G.
+        Verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        WifiUtils.wifi_reset(self.log, self.android_devices[0])
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        return data_connectivity_single_bearer(self.log,
+                                               self.android_devices[0],
+                                               RAT_3G)
+    @TelephonyBaseTest.tel_test_wrap
+    def test_3g_wifi_not_associated(self):
+        """Test data connection in 3G.
+
+        Turn off airplane mode, enable WiFi (but not connected), enable Cellular Data.
+        Ensure phone data generation is 3G.
+        Verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        WifiUtils.wifi_reset(self.log, self.android_devices[0])
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], True)
+        return data_connectivity_single_bearer(self.log,
+                                               self.android_devices[0],
+                                               RAT_3G)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_4g(self):
+        """Test data connection in 4g.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Ensure phone data generation is 4g.
+        Verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        WifiUtils.wifi_reset(self.log, self.android_devices[0])
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        return data_connectivity_single_bearer(self.log,
+                                               self.android_devices[0],
+                                               RAT_4G)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_4g_wifi_not_associated(self):
+        """Test data connection in 4g.
+
+        Turn off airplane mode, enable WiFi (but not connected), enable Cellular Data.
+        Ensure phone data generation is 4g.
+        Verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        WifiUtils.wifi_reset(self.log, self.android_devices[0])
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], True)
+        return data_connectivity_single_bearer(self.log,
+                                               self.android_devices[0],
+                                               RAT_4G)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_3g_stress(self):
+        """Stress Test data connection in 3G.
+
+        This is a stress test for "test_3g".
+        Default MINIMUM_SUCCESS_RATE is set to 95%.
+
+        Returns:
+            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
+
+        for i in range(1, self.stress_test_number+1):
+
+            ensure_phones_default_state(self.log,
+                [self.android_devices[0], self.android_devices[1]])
+            WifiUtils.wifi_reset(self.log, self.android_devices[0])
+            WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+
+            if data_connectivity_single_bearer(self.log, self.android_devices[0],
+                                               RAT_3G):
+                success_count += 1
+                result_str = "Succeeded"
+            else:
+                fail_count += 1
+                result_str = "Failed"
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_4g_stress(self):
+        """Stress Test data connection in 4g.
+
+        This is a stress test for "test_4g".
+        Default MINIMUM_SUCCESS_RATE is set to 95%.
+
+        Returns:
+            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
+
+        for i in range(1, self.stress_test_number+1):
+
+            ensure_phones_default_state(self.log,
+                [self.android_devices[0], self.android_devices[1]])
+            WifiUtils.wifi_reset(self.log, self.android_devices[0])
+            WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+
+            if data_connectivity_single_bearer(self.log, self.android_devices[0],
+                                               RAT_4G):
+                success_count += 1
+                result_str = "Succeeded"
+            else:
+                fail_count += 1
+                result_str = "Failed"
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    def _test_setup_tethering(self, ads, network_generation):
+        """Pre setup steps for WiFi tethering test.
+
+        Ensure all ads are idle.
+        Ensure tethering provider:
+            turn off APM, turn off WiFI, turn on Data.
+            have Internet connection, no active ongoing WiFi tethering.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ensure_phones_idle(self.log, ads)
+
+        if not ensure_network_generation(
+                self.log, self.android_devices[0], network_generation,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            self.log.error("Device failed to reselect in {}s.".format(
+                WAIT_TIME_NW_SELECTION))
+            return False
+
+        self.log.info("Airplane Off, Wifi Off, Data On.")
+        toggle_airplane_mode(self.log, self.android_devices[0], False)
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        self.android_devices[0].droid.toggleDataConnection(True)
+        if not wait_for_cell_data_connection(
+                self.log, self.android_devices[0], True):
+            self.log.error("Failed to enable data connection.")
+            return False
+
+        self.log.info("Verify internet")
+        if not verify_http_connection(self.log, self.android_devices[0]):
+            self.log.error("Data not available on cell.")
+            return False
+
+        # Turn off active SoftAP if any.
+        if ads[0].droid.wifiIsApEnabled():
+            WifiUtils.stop_wifi_tethering(self.log, ads[0])
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_4g_to_2gwifi(self):
+        """WiFi Tethering test: LTE to WiFI 2.4G Tethering
+
+        1. DUT in LTE mode, idle.
+        2. DUT start 2.4G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                             check_interval=10,
+                                             check_iteration=10)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_4g_to_5gwifi(self):
+        """WiFi Tethering test: LTE to WiFI 5G Tethering
+
+        1. DUT in LTE mode, idle.
+        2. DUT start 5G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_5G,
+                                             check_interval=10,
+                                             check_iteration=10)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_3g_to_2gwifi(self):
+        """WiFi Tethering test: 3G to WiFI 2.4G Tethering
+
+        1. DUT in 3G mode, idle.
+        2. DUT start 2.4G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_3G):
+            self.log.error("Verify 3G Internet access failed.")
+            return False
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                             check_interval=10,
+                                             check_iteration=10)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_3g_to_5gwifi(self):
+        """WiFi Tethering test: 3G to WiFI 5G Tethering
+
+        1. DUT in 3G mode, idle.
+        2. DUT start 5G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_3G):
+            self.log.error("Verify 3G Internet access failed.")
+            return False
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_5G,
+                                             check_interval=10,
+                                             check_iteration=10)
+
+    @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(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1], ads[2]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                             check_interval=10,
+                                             check_iteration=10)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_2g_to_2gwifi(self):
+        """WiFi Tethering test: 2G to WiFI 2.4G Tethering
+
+        1. DUT in 2G mode, idle.
+        2. DUT start 2.4G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_2G):
+            self.log.error("Verify 2G Internet access failed.")
+            return False
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                             check_interval=10,
+                                             check_iteration=10)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_2g_to_5gwifi(self):
+        """WiFi Tethering test: 2G to WiFI 5G Tethering
+
+        1. DUT in 2G mode, idle.
+        2. DUT start 5G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_2G):
+            self.log.error("Verify 2G Internet access failed.")
+            return False
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_5G,
+                                             check_interval=10,
+                                             check_iteration=10)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_disable_wifi_tethering_resume_connected_wifi(self):
+        """WiFi Tethering test: WiFI connected to 2.4G network,
+        start (LTE) 2.4G WiFi tethering, then stop tethering
+
+        1. DUT in LTE mode, idle. WiFi connected to 2.4G Network
+        2. DUT start 2.4G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+        5. Disable WiFi Tethering on DUT. Turn on DUT WiFi.
+        6. Verify DUT automatically connect to previous WiFI network
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+
+        if not ensure_wifi_connected(self.log,
+                                     ads[0],
+                                     self.wifi_network_ssid,
+                                     self.wifi_network_pass):
+            self.log.error("WiFi connect fail.")
+            return False
+
+        if not wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             check_interval=10,
+                                             check_iteration=2):
+            self.log.error("WiFi Tethering failed.")
+            return False
+
+        WifiUtils.wifi_toggle_state(self.log, ads[0], True)
+        if (not wait_for_wifi_data_connection(self.log, ads[0], True) or not
+                verify_http_connection(self.log, ads[0])):
+            self.log.error("Provider data did not return to Wifi")
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_toggle_data_during_active_wifi_tethering(self):
+        """WiFi Tethering test: Toggle Data during active WiFi Tethering
+
+        1. DUT in LTE mode, idle.
+        2. DUT start 2.4G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+        5. Disable Data on DUT, verify PhoneB still connected to WiFi, but no Internet access.
+        6. Enable Data on DUT, verify PhoneB still connected to WiFi and have Internet access.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+        try:
+            ssid = rand_ascii_str(10)
+            if not wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                                 ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                                 check_interval=10,
+                                                 check_iteration=2,
+                                                 do_cleanup=False,
+                                                 ssid=ssid):
+                self.log.error("WiFi Tethering failed.")
+                return False
+
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+
+            self.log.info("Disable Data on Provider, verify no data on Client.")
+            ads[0].droid.toggleDataConnection(False)
+            time.sleep(WAIT_TIME_FOR_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
+            if verify_http_connection(self.log, ads[0]):
+                self.log.error("Disable data on provider failed.")
+                return False
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+            wifi_info = ads[1].droid.wifiGetConnectionInfo()
+
+            if wifi_info[WifiEnums.SSID_KEY] != ssid:
+                self.log.error("WiFi error. Info: {}".format(wifi_info))
+                return False
+            if verify_http_connection(self.log, ads[1]):
+                self.log.error("Client should not have Internet connection.")
+                return False
+
+            self.log.info("Enable Data on Provider, verify data available on Client.")
+            ads[0].droid.toggleDataConnection(True)
+            time.sleep(WAIT_TIME_FOR_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
+            if not verify_http_connection(self.log, ads[0]):
+                self.log.error("Enable data on provider failed.")
+                return False
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+            wifi_info = ads[1].droid.wifiGetConnectionInfo()
+
+            if wifi_info[WifiEnums.SSID_KEY] != ssid:
+                self.log.error("WiFi error. Info: {}".format(wifi_info))
+                return False
+            if not verify_http_connection(self.log, ads[1]):
+                self.log.error("Client have no Internet connection!")
+                return False
+        finally:
+            if not wifi_tethering_cleanup(self.log, ads[0], [ads[1]]):
+                return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_change_rat_during_active_wifi_tethering_lte_to_3g(self):
+        """WiFi Tethering test: Change Cellular Data RAT generation from LTE to 3G,
+            during active WiFi Tethering.
+
+        1. DUT in LTE mode, idle.
+        2. DUT start 2.4G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verily Internet access on DUT and PhoneB
+        5. Change DUT Cellular Data RAT generation from LTE to 3G.
+        6. Verify both DUT and PhoneB have Internet access.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+        try:
+            if not wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                                 ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                                 check_interval=10,
+                                                 check_iteration=2,
+                                                 do_cleanup=False):
+                self.log.error("WiFi Tethering failed.")
+                return False
+
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+
+            self.log.info("Provider change RAT from LTE to 3G.")
+            if not ensure_network_generation(self.log, ads[0], RAT_3G,
+                voice_or_data=NETWORK_SERVICE_DATA,
+                toggle_apm_after_setting=False):
+                self.log.error("Provider failed to reselect to 3G.")
+                return False
+            time.sleep(WAIT_TIME_FOR_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
+            if not verify_http_connection(self.log, ads[0]):
+                self.log.error("Data not available on Provider.")
+                return False
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+            if not tethering_check_internet_connection(self.log, ads[0],
+                                                       [ads[1]], 10, 5):
+                return False
+        finally:
+            if not wifi_tethering_cleanup(self.log, ads[0], [ads[1]]):
+                return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_change_rat_during_active_wifi_tethering_3g_to_lte(self):
+        """WiFi Tethering test: Change Cellular Data RAT generation from 3G to LTE,
+            during active WiFi Tethering.
+
+        1. DUT in 3G mode, idle.
+        2. DUT start 2.4G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verily Internet access on DUT and PhoneB
+        5. Change DUT Cellular Data RAT generation from 3G to LTE.
+        6. Verify both DUT and PhoneB have Internet access.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_3G):
+            self.log.error("Verify 3G Internet access failed.")
+            return False
+        try:
+            if not wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                                 ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                                 check_interval=10,
+                                                 check_iteration=2,
+                                                 do_cleanup=False):
+                self.log.error("WiFi Tethering failed.")
+                return False
+
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+
+            self.log.info("Provider change RAT from 3G to 4G.")
+            if not ensure_network_generation(self.log, ads[0], RAT_4G,
+                voice_or_data=NETWORK_SERVICE_DATA,
+                toggle_apm_after_setting=False):
+                self.log.error("Provider failed to reselect to 4G.")
+                return False
+            time.sleep(WAIT_TIME_FOR_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
+            if not verify_http_connection(self.log, ads[0]):
+                self.log.error("Data not available on Provider.")
+                return False
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+            if not tethering_check_internet_connection(self.log, ads[0],
+                                                       [ads[1]], 10, 5):
+                return False
+        finally:
+            if not wifi_tethering_cleanup(self.log, ads[0], [ads[1]]):
+                return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_toggle_apm_during_active_wifi_tethering(self):
+        """WiFi Tethering test: Toggle APM during active WiFi Tethering
+
+        1. DUT in LTE mode, idle.
+        2. DUT start 2.4G WiFi Tethering
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+        5. DUT toggle APM on, verify WiFi tethering stopped, PhoneB lost WiFi connection.
+        6. DUT toggle APM off, verify PhoneA have cellular data and Internet connection.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+        try:
+            ssid = rand_ascii_str(10)
+            if not wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                                 ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                                 check_interval=10,
+                                                 check_iteration=2,
+                                                 do_cleanup=False,
+                                                 ssid=ssid):
+                self.log.error("WiFi Tethering failed.")
+                return False
+
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+
+            self.log.info("Provider turn on APM, verify no wifi/data on Client.")
+            if not toggle_airplane_mode(self.log, ads[0], True):
+                self.log.error("Provider turn on APM failed.")
+                return False
+            time.sleep(WAIT_TIME_FOR_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
+            if ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering not stopped.")
+                return False
+            if verify_http_connection(self.log, ads[1]):
+                self.log.error("Client should not have Internet connection.")
+                return False
+            wifi_info = ads[1].droid.wifiGetConnectionInfo()
+            self.log.info("WiFi Info: {}".format(wifi_info))
+
+            if wifi_info[WifiEnums.SSID_KEY] == ssid:
+                self.log.error("WiFi error. WiFi should not be connected.".
+                               format(wifi_info))
+                return False
+
+            self.log.info("Provider turn off APM.")
+            if not toggle_airplane_mode(self.log, ads[0], False):
+                self.log.error("Provider turn on APM failed.")
+                return False
+            time.sleep(WAIT_TIME_FOR_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
+            if ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering should not on.")
+                return False
+            if not verify_http_connection(self.log, ads[0]):
+                self.log.error("Provider should have Internet connection.")
+                return False
+        finally:
+            ads[1].droid.toggleDataConnection(True)
+            WifiUtils.wifi_reset(self.log, ads[1])
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_entitlement_check(self):
+        """Tethering Entitlement Check Test
+
+        Get tethering entitlement check result.
+
+        Returns:
+            True if entitlement check returns True.
+        """
+        ad = self.android_devices[0]
+
+        result = ad.droid.phoneIsTetheringModeAllowed(TETHERING_MODE_WIFI,
+            TETHERING_ENTITLEMENT_CHECK_TIMEOUT)
+        self.log.info("{} tethering entitlement check result: {}.".
+                format(ad.serial, result))
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_4g_to_2gwifi_stress(self):
+        """Stress Test LTE to WiFI 2.4G Tethering
+
+        This is a stress test for "test_tethering_4g_to_2gwifi".
+        Default MINIMUM_SUCCESS_RATE is set to 95%.
+
+        Returns:
+            True stress pass rate is higher than MINIMUM_SUCCESS_RATE.
+            False otherwise.
+        """
+        MINIMUM_SUCCESS_RATE = .95
+        success_count = 0
+        fail_count = 0
+
+        for i in range(1, self.stress_test_number+1):
+
+            ensure_phones_default_state(self.log,
+                [self.android_devices[0], self.android_devices[1]])
+
+            if self.test_tethering_4g_to_2gwifi():
+                success_count += 1
+                result_str = "Succeeded"
+            else:
+                fail_count += 1
+                result_str = "Failed"
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    #SIM2 tests
+    def _reset_subscriptions_to_sim1(self, ads):
+        set_call_state_listen_level(self.log, ads[0], False,
+                                    self.sim_sub_ids[0][1])
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        setup_sim(self.log, ads[0], self.sim_sub_ids[0][0], True, False, True)
+        ads[0].droid.phoneSetPreferredNetworkTypeForSubscription(
+                RAT_3G, self.sim_sub_ids[0][0])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_3g_sim2(self):
+        """Test data connection in 3G.
+
+        Set SIM2 as the data SIM
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Ensure phone data generation is 3G.
+        Verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1],
+                True, False, True):
+                self.log.error("Failed to Switch data SIM")
+                return False
+            WifiUtils.wifi_reset(self.log, self.android_devices[0])
+            WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+            return data_connectivity_single_bearer(self.log,
+                                                   self.android_devices[0],
+                                                   RAT_3G)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_2g_sim2(self):
+        """Test data connection in 2G.
+
+        Set SIM2 as the data SIM
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Ensure phone data generation is 2G.
+        Verify Internet.
+        Disable Cellular Data, verify Internet is inaccessible.
+        Enable Cellular Data, verify Internet.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1],
+               True, False, True):
+                self.log.error("Failed to Switch data SIM")
+                return False
+            WifiUtils.wifi_reset(self.log, self.android_devices[0])
+            WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+            return data_connectivity_single_bearer(self.log,
+                                                   self.android_devices[0],
+                                                   RAT_2G)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_wifi_switching_sim2(self):
+        """Test data connection network switching when SIM2 camped on WCDMA.
+
+        Set SIM2 as the data SIM
+        Ensure SIM2 is camped on WCDMA
+        Ensure WiFi can connect to live network,
+        Airplane mode is off, data connection is on, WiFi is on.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+        Turn on WiFi, verify data is on WiFi and browse to google.com is OK.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+
+        Returns:
+            True if pass.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1],
+                True, False, True):
+                self.log.error("Failed to Switch data SIM")
+                return False
+            return wifi_cell_switching(self.log, self.android_devices[0],
+                                       self.wifi_network_ssid, self.wifi_network_pass,
+                                       RAT_WCDMA)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_gsm_wifi_switching_sim2(self):
+        """Test data connection network switching when SIM2 camped on GSM.
+
+        Set SIM2 as the data SIM
+        Ensure SIM2 is camped on GSM
+        Ensure WiFi can connect to live network,
+        Airplane mode is off, data connection is on, WiFi is on.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+        Turn on WiFi, verify data is on WiFi and browse to google.com is OK.
+        Turn off WiFi, verify data is on cell and browse to google.com is OK.
+
+        Returns:
+            True if pass.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1],
+                True, False, True):
+                self.log.error("Failed to Switch data SIM")
+                return False
+            return wifi_cell_switching(self.log, self.android_devices[0],
+                                       self.wifi_network_ssid, self.wifi_network_pass,
+                                       RAT_GSM)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_ssid_quotes(self):
+        """WiFi Tethering test: SSID name have quotes.
+        1. Set SSID name have double quotes.
+        2. Start LTE to WiFi (2.4G) tethering.
+        3. Verify tethering.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        # TODO (yangxliu): Add more test for Hotspot SSID. b/23312590
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+        ssid = "\"" + rand_ascii_str(10) + "\""
+        self.log.info("Starting WiFi Tethering test with ssid: {}".
+            format(ssid))
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                             check_interval=10,
+                                             check_iteration=10,
+                                             ssid=ssid)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_password_escaping_characters(self):
+        """WiFi Tethering test: password have escaping characters.
+        1. Set password have escaping characters.
+            e.g.: '"DQ=/{Yqq;M=(^_3HzRvhOiL8S%`]w&l<Qp8qH)bs<4E9v_q=HLr^)}w$blA0Kg'
+        2. Start LTE to WiFi (2.4G) tethering.
+        3. Verify tethering.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        # TODO (yangxliu): Add more test for Hotspot password. b/23313568
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+
+        password = '"DQ=/{Yqq;M=(^_3HzRvhOiL8S%`]w&l<Qp8qH)bs<4E9v_q=HLr^)}w$blA0Kg'
+        self.log.info("Starting WiFi Tethering test with password: {}".
+            format(password))
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                             check_interval=10,
+                                             check_iteration=10,
+                                             password=password)
+
+    def _test_start_wifi_tethering_connect_teardown(self, ad_host, ad_client,
+                                                    ssid, password):
+        """Private test util for WiFi Tethering.
+
+        1. Host start WiFi tethering.
+        2. Client connect to tethered WiFi.
+        3. Host tear down WiFi tethering.
+
+        Args:
+            ad_host: android device object for host
+            ad_client: android device object for client
+            ssid: WiFi tethering ssid
+            password: WiFi tethering password
+
+        Returns:
+            True if no error happen, otherwise False.
+        """
+        result = True
+        # Turn off active SoftAP if any.
+        if ad_host.droid.wifiIsApEnabled():
+            WifiUtils.stop_wifi_tethering(self.log, ad_host)
+
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        if not WifiUtils.start_wifi_tethering(self.log, ad_host, ssid, password,
+                                              WifiUtils.WIFI_CONFIG_APBAND_2G):
+            self.log.error("Provider start WiFi tethering failed.")
+            result = False
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        if not ensure_wifi_connected(self.log, ad_client, ssid, password):
+            self.log.error("Client connect to WiFi failed.")
+            result = False
+        if not WifiUtils.wifi_reset(self.log, ad_client):
+            self.log.error("Reset client WiFi failed. {}".format(ad_client.serial))
+            result = False
+        if not WifiUtils.stop_wifi_tethering(self.log, ad_host):
+            self.log.error("Provider strop WiFi tethering failed.")
+            result = False
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_ssid(self):
+        """WiFi Tethering test: start WiFi tethering with all kinds of SSIDs.
+
+        For each listed SSID, start WiFi tethering on DUT, client connect WiFi,
+        then tear down WiFi tethering.
+
+        Returns:
+            True if WiFi tethering succeed on all SSIDs.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Setup Failed.")
+            return False
+        ssid_list = [" !\"#$%&'()*+,-./0123456789:;<=>?",
+                     "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_",
+                     "`abcdefghijklmnopqrstuvwxyz{|}~",
+                     " a ",
+                     "!b!",
+                     "#c#",
+                     "$d$",
+                     "%e%",
+                     "&f&",
+                     "'g'",
+                     "(h(",
+                     ")i)",
+                     "*j*",
+                     "+k+",
+                     "-l-",
+                     ".m.",
+                     "/n/",
+                     "_",
+                     " !\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}",
+                     "\u0644\u062c\u0648\u062c",
+                     "\u8c37\u6b4c",
+                     "\uad6c\uae00"
+                     "\u30b0\u30fc\u30eb",
+                     "\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0443\u0439"
+                     ]
+        fail_list = {}
+
+        for ssid in ssid_list:
+            password = rand_ascii_str(8)
+            self.log.info("SSID: <{}>, Password: <{}>".format(ssid, password))
+            if not self._test_start_wifi_tethering_connect_teardown(
+                ads[0], ads[1], ssid, password):
+                fail_list[ssid] = password
+
+        if (len(fail_list) > 0):
+            self.log.error("Failed cases: {}".format(fail_list))
+            return False
+        else:
+            return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_password(self):
+        """WiFi Tethering test: start WiFi tethering with all kinds of passwords.
+
+        For each listed password, start WiFi tethering on DUT, client connect WiFi,
+        then tear down WiFi tethering.
+
+        Returns:
+            True if WiFi tethering succeed on all passwords.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Setup Failed.")
+            return False
+        password_list = [" !\"#$%&'()*+,-./0123456789:;<=>?",
+                         "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_",
+                         "`abcdefghijklmnopqrstuvwxyz{|}~",
+                         " !\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}",
+                         "abcdefgh",
+                         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!",
+                         " a12345 ",
+                         "!b12345!",
+                         "#c12345#",
+                         "$d12345$",
+                         "%e12345%",
+                         "&f12345&",
+                         "'g12345'",
+                         "(h12345(",
+                         ")i12345)",
+                         "*j12345*",
+                         "+k12345+",
+                         "-l12345-",
+                         ".m12345.",
+                         "/n12345/"
+                        ]
+        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(
+                ads[0], ads[1], ssid, password):
+                fail_list[ssid] = password
+
+        if (len(fail_list) > 0):
+            self.log.error("Failed cases: {}".format(fail_list))
+            return False
+        else:
+            return True
+
+    def _test_tethering_wifi_and_voice_call(self, provider, client,
+        provider_data_rat, provider_setup_func, provider_in_call_check_func):
+        if not self._test_setup_tethering([provider, client],
+                                          provider_data_rat):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+
+        tasks = [(provider_setup_func, (self.log, provider)),
+                 (phone_setup_voice_general, (self.log, client))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up VoLTE.")
+            return False
+
+        try:
+            self.log.info("1. Setup WiFi Tethering.")
+            if not wifi_tethering_setup_teardown(self.log, provider, [client],
+                ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                check_interval=10, check_iteration=2, do_cleanup=False):
+                self.log.error("WiFi Tethering failed.")
+                return False
+            self.log.info("2. Make outgoing call.")
+            if not call_setup_teardown(self.log, provider, client,
+                                   ad_hangup=None,
+                                   verify_caller_func=provider_in_call_check_func):
+                self.log.error("Setup Call Failed.")
+                return False
+            self.log.info("3. Verify data.")
+            if not verify_http_connection(self.log, provider):
+                self.log.error("Provider have no Internet access.")
+            if not verify_http_connection(self.log, client):
+                self.log.error("Client have no Internet access.")
+            hangup_call(self.log, provider)
+
+            time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+            self.log.info("4. Make incoming call.")
+            if not call_setup_teardown(self.log, client, provider,
+                                   ad_hangup=None,
+                                   verify_callee_func=provider_in_call_check_func):
+                self.log.error("Setup Call Failed.")
+                return False
+            self.log.info("5. Verify data.")
+            if not verify_http_connection(self.log, provider):
+                self.log.error("Provider have no Internet access.")
+            if not verify_http_connection(self.log, client):
+                self.log.error("Client have no Internet access.")
+            hangup_call(self.log, provider)
+
+        finally:
+            if not wifi_tethering_cleanup(self.log, provider, [client]):
+                return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_volte_call(self):
+        """WiFi Tethering test: VoLTE call during WiFi tethering
+        1. Start LTE to WiFi (2.4G) tethering.
+        2. Verify tethering.
+        3. Make outgoing VoLTE call on tethering provider.
+        4. Verify tethering still works.
+        5. Make incoming VoLTE call on tethering provider.
+        6. Verify tethering still works.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        return self._test_tethering_wifi_and_voice_call(self.android_devices[0],
+            self.android_devices[1], RAT_4G, phone_setup_volte,
+            is_phone_in_call_volte)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_csfb_call(self):
+        """WiFi Tethering test: CSFB call during WiFi tethering
+        1. Start LTE to WiFi (2.4G) tethering.
+        2. Verify tethering.
+        3. Make outgoing CSFB call on tethering provider.
+        4. Verify tethering still works.
+        5. Make incoming CSFB call on tethering provider.
+        6. Verify tethering still works.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        return self._test_tethering_wifi_and_voice_call(self.android_devices[0],
+            self.android_devices[1], RAT_4G, phone_setup_csfb,
+            is_phone_in_call_csfb)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_3g_call(self):
+        """WiFi Tethering test: 3G call during WiFi tethering
+        1. Start 3G to WiFi (2.4G) tethering.
+        2. Verify tethering.
+        3. Make outgoing CS call on tethering provider.
+        4. Verify tethering still works.
+        5. Make incoming CS call on tethering provider.
+        6. Verify tethering still works.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        return self._test_tethering_wifi_and_voice_call(self.android_devices[0],
+            self.android_devices[1], RAT_3G, phone_setup_3g,
+            is_phone_in_call_3g)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_no_password(self):
+        """WiFi Tethering test: Start WiFi tethering with no password
+
+        1. DUT in 4G mode, idle.
+        2. DUT start 2.4G WiFi Tethering, with no WiFi password.
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                             check_interval=10,
+                                             check_iteration=10,
+                                             password="")
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_reboot(self):
+        """WiFi Tethering test: Start WiFi tethering then Reboot device
+
+        1. DUT in 4G mode, idle.
+        2. DUT start 2.4G WiFi Tethering.
+        3. PhoneB disable data, connect to DUT's softAP
+        4. Verify Internet access on DUT and PhoneB
+        5. Reboot DUT
+        6. After DUT reboot, verify tethering is stopped.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+        try:
+            if not wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                                 ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                                 check_interval=10,
+                                                 check_iteration=2,
+                                                 do_cleanup=False):
+                self.log.error("WiFi Tethering failed.")
+                return False
+
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+
+            self.log.info("Reboot DUT:{}".format(ads[0].serial))
+            ads[0].reboot()
+            time.sleep(WAIT_TIME_AFTER_REBOOT +
+                WAIT_TIME_FOR_TETHERING_AFTER_REBOOT)
+
+            self.log.info("After reboot check if tethering stopped.")
+            if ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering did NOT stopped.")
+                return False
+        finally:
+            ads[1].droid.toggleDataConnection(True)
+            WifiUtils.wifi_reset(self.log, ads[1])
+            if ads[0].droid.wifiIsApEnabled():
+                WifiUtils.stop_wifi_tethering(self.log, ads[0])
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_connect_wifi_start_tethering_wifi_reboot(self):
+        """WiFi Tethering test: WiFI connected, then start WiFi tethering,
+            then reboot device.
+
+        Initial Condition: DUT in 4G mode, idle, DUT connect to WiFi.
+        1. DUT start 2.4G WiFi Tethering.
+        2. PhoneB disable data, connect to DUT's softAP
+        3. Verify Internet access on DUT and PhoneB
+        4. Reboot DUT
+        5. After DUT reboot, verify tethering is stopped. DUT is able to connect
+            to previous WiFi AP.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+
+        self.log.info("Make sure DUT can connect to live network by WIFI")
+        if ((not ensure_wifi_connected(log, ads[0], self.wifi_network_ssid,
+                                     self.wifi_network_pass)) or
+            (not verify_http_connection(self.log, ads[0]))):
+            self.log.error("WiFi connect fail.")
+            return False
+
+        try:
+            if not wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                                 ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                                 check_interval=10,
+                                                 check_iteration=2,
+                                                 do_cleanup=False):
+                self.log.error("WiFi Tethering failed.")
+                return False
+
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+
+            self.log.info("Reboot DUT:{}".format(ads[0].serial))
+            ads[0].reboot()
+            time.sleep(WAIT_TIME_AFTER_REBOOT)
+            time.sleep(WAIT_TIME_FOR_TETHERING_AFTER_REBOOT)
+
+            self.log.info("After reboot check if tethering stopped.")
+            if ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering did NOT stopped.")
+                return False
+
+            self.log.info("Turn on WiFi, make sure WiFi can connect automatically.")
+            WifiUtils.wifi_toggle_state(self.log, ads[0], True)
+            if (not wait_for_wifi_data_connection(self.log, ads[0], True) or not
+                    verify_http_connection(self.log, ads[0])):
+                self.log.error("Data did not return to WiFi")
+                return False
+
+        finally:
+            ads[1].droid.toggleDataConnection(True)
+            WifiUtils.wifi_reset(self.log, ads[1])
+            if ads[0].droid.wifiIsApEnabled():
+                WifiUtils.stop_wifi_tethering(self.log, ads[0])
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_connect_wifi_reboot_start_tethering_wifi(self):
+        """WiFi Tethering test: DUT connected to WiFi, then reboot,
+        After reboot, start WiFi tethering, verify tethering actually works.
+
+        Initial Condition: Device set to 4G mode, idle, DUT connect to WiFi.
+        1. Verify Internet is working on DUT (by WiFi).
+        2. Reboot DUT.
+        3. DUT start 2.4G WiFi Tethering.
+        4. PhoneB disable data, connect to DUT's softAP
+        5. Verify Internet access on DUT and PhoneB
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify 4G Internet access failed.")
+            return False
+
+        self.log.info("Make sure DUT can connect to live network by WIFI")
+        if ((not ensure_wifi_connected(log, ads[0], self.wifi_network_ssid,
+                                     self.wifi_network_pass)) or
+            (not verify_http_connection(self.log, ads[0]))):
+            self.log.error("WiFi connect fail.")
+            return False
+
+        self.log.info("Reboot DUT:{}".format(ads[0].serial))
+        ads[0].reboot()
+        time.sleep(WAIT_TIME_AFTER_REBOOT)
+        time.sleep(WAIT_TIME_FOR_TETHERING_AFTER_REBOOT)
+
+        return wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                             ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                             check_interval=10,
+                                             check_iteration=10)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tethering_wifi_screen_off_enable_doze_mode(self):
+        """WiFi Tethering test: Start WiFi tethering, then turn off DUT's screen,
+            then enable doze mode.
+
+        1. Start WiFi tethering on DUT.
+        2. PhoneB disable data, and connect to DUT's softAP
+        3. Verify Internet access on DUT and PhoneB
+        4. Turn off DUT's screen. Wait for 1 minute and
+            verify Internet access on Client PhoneB.
+        5. Enable doze mode on DUT. Wait for 1 minute and
+            verify Internet access on Client PhoneB.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+        if not self._test_setup_tethering(ads, RAT_4G):
+            self.log.error("Verify LTE Internet access failed.")
+            return False
+        try:
+            if not wifi_tethering_setup_teardown(self.log, ads[0], [ads[1]],
+                                                 ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                                 check_interval=10,
+                                                 check_iteration=2,
+                                                 do_cleanup=False):
+                self.log.error("WiFi Tethering failed.")
+                return False
+
+            if not ads[0].droid.wifiIsApEnabled():
+                self.log.error("Provider WiFi tethering stopped.")
+                return False
+
+            self.log.info("Turn off screen on provider: <{}>.".
+                          format(ads[0].serial))
+            ads[0].droid.goToSleepNow()
+            time.sleep(60)
+            if not verify_http_connection(self.log, ads[1]):
+                self.log.error("Client have no Internet access.")
+                return False
+
+            self.log.info("Enable doze mode on provider: <{}>.".
+                          format(ads[0].serial))
+            if not enable_doze(ads[0]):
+                self.log.error("Failed to enable doze mode.")
+                return False
+            time.sleep(60)
+            if not verify_http_connection(self.log, ads[1]):
+                self.log.error("Client have no Internet access.")
+                return False
+        finally:
+            self.log.info("Disable doze mode.")
+            if not disable_doze(ads[0]):
+                self.log.error("Failed to disable doze mode.")
+                return False
+            if not wifi_tethering_cleanup(self.log, ads[0], [ads[1]]):
+                return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wifi_connect_disconnect(self):
+        """Perform multiple connects and disconnects from WiFi and verify that
+            data switches between WiFi and Cell.
+
+        Steps:
+        1. Reset Wifi on DUT
+        2. Connect DUT to a WiFi AP
+        3. Repeat steps 1-2, alternately disconnecting and disabling wifi
+
+        Expected Results:
+        1. Verify Data on Cell
+        2. Verify Data on Wifi
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ad = self.android_devices[0]
+
+        wifi_toggles = [True, False,
+                        True, False, False,
+                        True, False, False, False, False,
+                        True, False, False, False, False,
+                            False, False, False, False]
+
+        if not ensure_network_rat(
+                self.log, ad, RAT_LTE,
+                WAIT_TIME_NW_SELECTION):
+
+            self.log.error("Device {} failed to reselect in {}s.".format(
+                ad.serial, WAIT_TIME_NW_SELECTION))
+            return False
+
+        for toggle in wifi_toggles:
+
+            WifiUtils.wifi_reset(self.log, ad, toggle)
+
+            if not wait_for_cell_data_connection(
+                    self.log, ad, True, WAIT_TIME_WIFI_CONNECTION):
+                self.log.error("Failed wifi connection, aborting!")
+                return False
+
+            if not verify_http_connection(
+                self.log, ad, 'http://www.google.com', 100, .1):
+                self.log.error("Failed to get user-plane traffic, aborting!")
+                return False
+
+            if toggle:
+                WifiUtils.wifi_toggle_state(self.log, ad, True)
+
+            WifiUtils.wifi_connect(
+                self.log, ad, self.wifi_network_ssid, self.wifi_network_pass)
+
+            if not wait_for_wifi_data_connection(
+                    self.log, ad, True, WAIT_TIME_WIFI_CONNECTION):
+                self.log.error("Failed wifi connection, aborting!")
+                return False
+
+            if not verify_http_connection(
+                self.log, ad, 'http://www.google.com', 100, .1):
+                self.log.error("Failed to get user-plane traffic, aborting!")
+                return False
+        """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveIwlanTest.py b/acts/tests/google/tel/live/TelLiveIwlanTest.py
new file mode 100644
index 0000000..934dce4
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveIwlanTest.py
@@ -0,0 +1,370 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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.base_test import BaseTestClass
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+
+class TelLiveIwlanTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = ('test_iwlan_setup_delay_wifi_on',
+                'test_iwlan_connect_disconnect_wifi_lte_volte',
+                'test_iwlan_toggle_wifi_enable_lte_volte',
+                'test_iwlan_toggle_cell_mode_wifi_mode_lte_volte'
+                )
+
+        self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+        self.wifi_network_pass = self.user_params["wifi_network_pass"]
+
+    # TODO: Set pass/fail criteria
+    @TelephonyBaseTest.tel_test_wrap
+    def test_iwlan_setup_delay_wifi_on(self):
+
+        """ Measures the time delay in enabling WiFi calling
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ad = self.android_devices[0]
+
+        time_values = {
+            'start': 0,
+            'wifi_enabled': 0,
+            'wifi_connected': 0,
+            'wifi_data': 0,
+            'iwlan_rat': 0,
+            'ims_registered': 0,
+            'wfc_enabled': 0,
+            'mo_call_success': 0
+        }
+
+        WifiUtils.wifi_reset(self.log, ad)
+        toggle_airplane_mode(self.log, ad, True)
+
+        set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED)
+
+        time_values['start'] = time.time()
+
+        self.log.info("Start Time {}s".format(time_values['start']))
+
+        WifiUtils.wifi_toggle_state(self.log, ad, True)
+        time_values['wifi_enabled'] = time.time()
+        self.log.info("WiFi Enabled After {}s".format(
+            time_values['wifi_enabled'] - time_values['start']))
+
+        WifiUtils.wifi_connect(
+            self.log, ad, self.wifi_network_ssid, self.wifi_network_pass)
+
+        # FIXME: bug/25296245
+        ad.droid.wakeUpNow()
+
+        if not wait_for_wifi_data_connection(
+                self.log, ad, True, WAIT_TIME_WIFI_CONNECTION):
+            self.log.error("Failed wifi connection, aborting!")
+            return False
+        time_values['wifi_connected'] = time.time()
+
+        self.log.info("Wifi Connected After {}s".format(
+            time_values['wifi_connected'] - time_values['wifi_enabled']))
+
+        if not verify_http_connection(
+                self.log, ad, 'http://www.google.com', 100, .1):
+            self.log.error("Failed to get user-plane traffic, aborting!")
+            return False
+
+        time_values['wifi_data'] = time.time()
+        self.log.info("WifiData After {}s".format(
+            time_values['wifi_data'] - time_values['wifi_connected']))
+
+        if not wait_for_droid_in_network_rat(
+                self.log, ad, RAT_IWLAN, WAIT_TIME_NW_SELECTION,
+                NETWORK_SERVICE_DATA):
+            self.log.error("Failed to set-up iwlan, aborting!")
+            if is_droid_in_network_rat(self.log, ad, RAT_IWLAN,
+                                       NETWORK_SERVICE_DATA):
+                self.log.error("Never received the event, but droid in iwlan")
+            else:
+                return False
+        time_values['iwlan_rat'] = time.time()
+        self.log.info("iWLAN Reported After {}s".format(
+            time_values['iwlan_rat'] - time_values['wifi_data']))
+
+        if not wait_for_ims_registered(
+                self.log, ad, WAIT_TIME_IMS_REGISTRATION):
+            self.log.error("Never received IMS registered, aborting")
+            return False
+        time_values['ims_registered'] = time.time()
+        self.log.info("Ims Registered After {}s".format(
+            time_values['ims_registered'] - time_values['iwlan_rat']))
+
+        if not wait_for_wfc_enabled(self.log, ad, WAIT_TIME_WFC_ENABLED):
+            self.log.error("Never received WFC feature, aborting")
+            return False
+
+        time_values['wfc_enabled'] = time.time()
+        self.log.info("Wifi Calling Feature Enabled After {}s".format(
+            time_values['wfc_enabled'] - time_values['ims_registered']))
+
+        set_wfc_mode(self.log, ad, WFC_MODE_DISABLED)
+
+        wait_for_droid_not_in_network_rat(
+            self.log, ad, RAT_IWLAN, WAIT_TIME_NW_SELECTION,
+            NETWORK_SERVICE_DATA)
+
+        # TODO: Format the output nicely in the log
+        self.log.info(time_values)
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_iwlan_connect_disconnect_wifi_lte_volte(self):
+        """ Test IWLAN<->Cell switching when repeatedly connecting to
+            and disconnecting from a wifi network.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._test_iwlan_in_out_lte_volte(
+            self._iwlan_in_connect_wifi, self._iwlan_out_disconnect_wifi)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_iwlan_toggle_wifi_enable_lte_volte(self):
+        """ Test IWLAN<->Cell switching when repeatedly enabling and disabling
+        wifi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._test_iwlan_in_out_lte_volte(
+            self._iwlan_in_enable_wifi, self._iwlan_out_disable_wifi)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_iwlan_toggle_cell_mode_wifi_mode_lte_volte(self):
+        """ Test IWLAN<->Cell switching when toggling between "wifi preferred"
+        and "cellular only"/"wifi calling disabled" modes of wifi calling.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ad = self.android_devices[0]
+
+        self.log.info("Setup Device in LTE with VoLTE Enabled, Wifi Disabled")
+
+        if not ensure_network_rat(
+                self.log, ad, RAT_LTE, WAIT_TIME_NW_SELECTION,
+                NETWORK_SERVICE_DATA):
+            self.log.error("Device failed to select {}".format("lte"))
+            return False
+
+        set_wfc_mode(self.log, ad, WFC_MODE_DISABLED)
+
+        toggle_volte(self.log, ad, True)
+        if not wait_for_volte_enabled(self.log, ad, WAIT_TIME_VOLTE_ENABLED):
+            self.log.error("Device failed to acquire VoLTE service")
+            return False
+
+        WifiUtils.wifi_toggle_state(self.log, ad, True)
+        WifiUtils.wifi_connect(
+            self.log, ad, self.wifi_network_ssid, self.wifi_network_pass)
+
+        # FIXME: bug/25296245
+        ad.droid.wakeUpNow()
+
+        if not wait_for_wifi_data_connection(
+                self.log, ad, True,
+                WAIT_TIME_WIFI_CONNECTION + WAIT_TIME_USER_PLANE_DATA):
+            self.log.error("Data did not come up on wifi")
+            return False
+
+        if not verify_http_connection(
+                self.log, ad):
+            self.log.error("No data over WiFi!")
+            return False
+
+        # FIXME this number should be a parameter
+        for i in range(1, 6):
+
+            self.log.info("Step {}-1: Set WiFi Preferred".format(i))
+
+            set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED)
+
+            self.log.info("Step {}-2: Wait for IWLAN Tunnel".format(i))
+            self.log.info("WiFi RSSI: {}".format(
+                ad.droid.wifiGetConnectionInfo()['rssi']))
+
+            # This isn't a real network selection
+            # it's iwlan tunnel establishment, which should take <<30s
+            if not wait_for_droid_in_network_rat(
+                    self.log, ad, RAT_IWLAN, WAIT_TIME_NW_SELECTION,
+                    NETWORK_SERVICE_DATA):
+                self.log.error("Failed to set-up iwlan!")
+                return False
+
+            self.log.info("Step {}-3: Wait for IMS Registration".format(i))
+
+            if not wait_for_ims_registered(
+                    self.log, ad, WAIT_TIME_IMS_REGISTRATION):
+                self.log.error("Never received IMS registered!")
+                return False
+
+            self.log.info("Step {}-4: Wait for WFC Feature Enabled".format(i))
+
+            # Once IMS is registered on IWLAN,
+            # this should be almost instantaneous.
+            # leaving 2 seconds to be generous
+            if not wait_for_wfc_enabled(self.log, ad, WAIT_TIME_WFC_ENABLED):
+                self.log.error("Never received WFC Feature!")
+                return False
+
+            self.log.info(
+                "Step {}-5: Set WFC to Cellular Only mode".format(i))
+
+            set_wfc_mode(self.log, ad, WFC_MODE_DISABLED)
+
+            if not wait_for_droid_in_network_rat(
+                    self.log, ad, RAT_LTE, WAIT_TIME_NW_SELECTION,
+                    NETWORK_SERVICE_DATA):
+                self.log.error("Device never returned to LTE!")
+                return False
+
+            self.log.info("Step {}-7: Wait for VoLTE Feature Enabled".format(i))
+
+            if not wait_for_volte_enabled(
+                    self.log, ad, WAIT_TIME_VOLTE_ENABLED):
+                self.log.error("Device failed to acquire VoLTE service")
+                return False
+        return True
+
+    ####################################################################
+    # Begin Internal Helper Functions
+    ####################################################################
+
+
+    def _iwlan_in_enable_wifi(self):
+        ad = self.android_devices[0]
+        WifiUtils.wifi_toggle_state(self.log, ad, True)
+        WifiUtils.wifi_connect(
+            self.log, ad, self.wifi_network_ssid, self.wifi_network_pass)
+
+    def _iwlan_out_disable_wifi(self):
+        ad = self.android_devices[0]
+        WifiUtils.wifi_toggle_state(self.log, ad, False)
+
+    def _iwlan_in_connect_wifi(self):
+        self._iwlan_in_enable_wifi()
+
+    def _iwlan_out_disconnect_wifi(self):
+        ad = self.android_devices[0]
+        WifiUtils.wifi_reset(self.log, ad, False)
+
+    def _test_iwlan_in_out_lte_volte(self, iwlan_in_func, iwlan_out_func):
+
+        ad = self.android_devices[0]
+
+        self.log.info("Setup Device in LTE with VoLTE Enabled, Wifi Disabled")
+
+        if not ensure_network_rat(
+                self.log, ad, RAT_LTE, WAIT_TIME_NW_SELECTION,
+                NETWORK_SERVICE_DATA):
+            self.log.error("Device failed to select {}".format("lte"))
+            return False
+
+        toggle_volte(self.log, ad, True)
+
+        if not wait_for_volte_enabled(self.log, ad, WAIT_TIME_VOLTE_ENABLED):
+            self.log.error("Device failed to acquire VoLTE service")
+            return False
+
+        set_wfc_mode(self.log, ad, "WIFI_PREFERRED")
+
+        # FIXME this number should be a parameter
+        for i in range(1, 6):
+
+            self.log.info("Step {}-1: Connect to WiFi".format(i))
+
+            iwlan_in_func()
+
+            # FIXME: bug/25296245
+            ad.droid.wakeUpNow()
+
+            if not wait_for_wifi_data_connection(
+                    self.log, ad, True,
+                    WAIT_TIME_WIFI_CONNECTION + WAIT_TIME_USER_PLANE_DATA):
+                self.log.error("Data did not come up on wifi")
+
+            self.log.info("Step {}-2: Wait for Wifi Data Connection".format(i))
+
+            if not verify_http_connection(
+                    self.log, ad):
+                self.log.error("No data over WiFi!")
+                return False
+
+            self.log.info("Step {}-3: Wait for IWLAN Tunnel".format(i))
+            self.log.info("WiFi RSSI: {}".format(
+                self.android_devices[0].droid.wifiGetConnectionInfo()['rssi']))
+
+            # This isn't a real network selection
+            # it's iwlan tunnel establishment, which should take <<30s
+            if not wait_for_droid_in_network_rat(
+                    self.log, ad, RAT_IWLAN, WAIT_TIME_NW_SELECTION,
+                    NETWORK_SERVICE_DATA):
+                self.log.error("Failed to set-up iwlan!")
+                return False
+
+            self.log.info("Step {}-4: Wait for IMS Registration".format(i))
+
+            if not wait_for_ims_registered(
+                    self.log, ad, WAIT_TIME_IMS_REGISTRATION):
+                self.log.error("Never received IMS registered!")
+                return False
+
+            self.log.info("Step {}-5: Wait for WFC Feature Enabled".format(i))
+
+
+            # Once IMS is registered on IWLAN,
+            # this should be almost instantaneous.
+            # leaving 2 seconds to be generous
+            if not wait_for_wfc_enabled(self.log, ad, WAIT_TIME_WFC_ENABLED):
+                self.log.error("Never received WFC Feature!")
+                return False
+
+            self.log.info(
+                "Step {}-6: Disable Wifi, wait for LTE Data".format(i))
+
+            iwlan_out_func()
+
+            if not wait_for_droid_in_network_rat(
+                    self.log, ad, RAT_LTE, WAIT_TIME_NW_SELECTION,
+                    NETWORK_SERVICE_DATA):
+                self.log.error("Failed to return to LTE!")
+                return False
+
+            self.log.info("Step {}-7: Wait for VoLTE Feature Enabled".format(i))
+
+            if not wait_for_volte_enabled(
+                    self.log, ad, WAIT_TIME_VOLTE_ENABLED):
+                self.log.error("Device failed to acquire VoLTE service")
+                return False
+        return True
+
+if __name__ == "__main__":
+    raise Exception("Cannot run this class directly")
+
diff --git a/acts/tests/google/tel/live/TelLiveMsimTest.py b/acts/tests/google/tel/live/TelLiveMsimTest.py
new file mode 100644
index 0000000..99305d0
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveMsimTest.py
@@ -0,0 +1,578 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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 MSIM
+"""
+
+import time
+from acts.base_test import BaseTestClass
+from queue import Empty
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_test_utils import *
+from acts.utils import load_config
+from acts.test_utils.wifi_test_utils import reset_droid_wifi
+from acts.test_utils.wifi_test_utils import start_wifi_connection_scan
+from acts.test_utils.wifi_test_utils import wifi_toggle_state
+
+class TelLiveMsimTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.droid0 = self.droid
+        self.ed0 = self.ed
+        self.droid1, self.ed1 = self.droids[1], self.eds[1]
+        self.tests = (
+                      "test_data_connection_on_each_sim",
+                      "test_call_est_basic_on_each_sim",
+                      "test_sms_basic_on_each_sim",
+                      "test_airplane_mode_basic_attach_detach_connectivity",
+                      "test_data_pretest_ensure_wifi_connect_to_live_network",
+                      "test_data_conn_network_switching",
+                      "test_pretest_ensure_3g",
+                      "test_data_connectivity_3g",
+                      )
+        # The path for "sim config file" should be set
+        # in "testbed.config" entry "sim_conf_file".
+        self.simconf = load_config(self.user_params["sim_conf_file"])
+        self.time_wait_in_call = 15
+        self.wifi_network_ssid = "GoogleGuest"
+
+    def setup_class(self):
+        self.phone_number_0 = None
+        self.phone_number_0_sim0 = None
+        self.phone_number_0_sim1 = None
+        self.operator_name_droid0 = None
+        self.operator_name_droid0_sim0 = None
+        self.operator_name_droid0_sim1 = None
+        self.phone_number_tbl = {}
+        self.operator_tbl = {}
+        wait_for_droid_in_network_rat(self.log, self.droid1, RAT_LTE,
+                                      WAIT_TIME_NW_SELECTION,
+                                      NETWORK_SERVICE_VOICE)
+
+        # self.droid0 (PhonaA) is MSIM device.
+        # phone_number_0_sim0 and phone_number_0_sim1 are numbers for MSIM device.
+        # self.droid1 (PhoneB) is test harness to test MSIM device.
+        subInfo = self.droid0.subscriptionGetAllSubInfoList()
+        for i in range(len(subInfo)):
+            subId = subInfo[i]["subId"]
+            simserial = subInfo[i]["iccId"]
+            num = self.simconf[simserial]["phone_num"]
+            assert num, "Fail to get phone number on phoneA sim{}".format(i)
+            self.phone_number_tbl[subId] = num
+            setattr(self,"phone_number_0_sim" + str(i), num)
+            # TODO: we shouldn't access TelEnums directly: create accessor func
+            operator_name = get_operator_name(
+                self.droid0.getSimOperatorBySubId(subId))
+            self.operator_tbl[subId] = operator_name
+            setattr(self,"operator_name_droid0_sim" + str(i), operator_name)
+            self.log.info("Information for PhoneA subId: {}".format(subId))
+            self.log.info("phone_number_0_sim{} : {} <{}>".format(i, num,
+                                                                  operator_name))
+            self.log.info("IMEI: {}".
+                          format(self.droid0.getDeviceIdBySlotId(subInfo[i]["slotId"])))
+            self.log.info("Roaming: {}".
+                          format(self.droid0.checkNetworkRoamingBySubId(subId)))
+            self.log.info("Current Network Type: Voice {}, Data {}.".
+                          format(self.droid0.getVoiceNetworkTypeForSubscriber(subId),
+                                 self.droid0.getDataNetworkTypeForSubscriber(subId)))
+
+        self.phone_number_1 = get_phone_number(self.log, self.droid1, self.simconf)
+        assert self.phone_number_1, "Fail to get phone number on PhoneB"
+        self.log.info("phone_number_1 : {} <{}>".format(self.phone_number_1,
+                                                        get_operator_name(self.log, self.droid1)))
+        for droid in [self.droid0, self.droid1]:
+            if droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform():
+                toggle_volte(droid, False)
+        return True
+
+    def _setting_subId_for_voice_data_sms(self, droid, voice=None,
+                                          data=None, sms=None):
+        if voice:
+            # droid.subscriptionSetDefaultVoiceSubId not working
+            # if subscriptionSetDefaultVoiceSubId is called to set default subId
+            # to make a call, then the outgoing subId will not change.
+            # need to use the following one
+            droid.telecomSetUserSelectedOutgoingPhoneAccount(str(voice))
+        if data:
+            droid.subscriptionSetDefaultDataSubId(str(data))
+        if sms:
+            droid.subscriptionSetDefaultSmsSubId(str(sms))
+        time.sleep(5)
+        # Wait to make sure settings take effect
+
+    def _call_process_helper_NonVoLTE(self, params):
+        """Wrapper function for _call_process.
+
+        This is to wrap call_process, so it can be executed by generated
+        testcases with a set of params.
+        """
+        (droid_caller, droid_callee, ed_caller,ed_callee, delay_in_call,
+         caller_number, callee_number, droid_hangup) = params
+        result = call_process(self.log, droid_caller, droid_callee,
+                              ed_caller, ed_callee, delay_in_call,
+                              caller_number, callee_number,
+                              hangup = True, droid_hangup = droid_hangup,
+                              verify_call_mode_caller = True,
+                              verify_call_mode_callee = True,
+                              caller_mode_VoLTE = False, callee_mode_VoLTE = False)
+        return result
+
+    def gen_call_process_test_name(self, params):
+        (droid_caller, droid_callee, ed_caller,ed_callee, delay_in_call,
+         caller_number, callee_number, droid_hangup) = params
+        return "test_call_{}_to_{}".format(caller_number, callee_number)
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_data_connection_on_each_sim(self):
+        toggle_airplane_mode(self.log, self.droid0, self.ed0, False)
+        toggle_airplane_mode(self.log, self.droid1, self.ed1, False)
+        self.log.info("Turn off Wifi, turn on Data.")
+        wifi_toggle_state(self.droid0, self.ed0, False)
+        self.droid0.toggleDataConnection(True)
+
+        subInfo = self.droid0.subscriptionGetAllSubInfoList()
+        for i in range(len(subInfo)):
+            subId = subInfo[i]["subId"]
+            self.log.info("Setting Data/Voice/Sms to subId: {}".format(subId))
+            self._setting_subId_for_voice_data_sms(self.droid0, subId, subId, subId)
+            time.sleep(5)
+            print(self.droid0.getDataConnectionState())
+            if self.droid0.getDataConnectionState() != DATA_STATE_CONNECTED:
+                result = wait_for_data_connection_status(self.log, self.droid0,
+                                                          self.ed0, True)
+                print("wait for connected")
+                print(result)
+                assert result, "Data not connected on PhoneA subId: {}".format(subId)
+            self.droid0.toggleDataConnection(False)
+            time.sleep(5)
+            print(self.droid0.getDataConnectionState())
+            if self.droid0.getDataConnectionState() != DATA_STATE_DISCONNECTED:
+                result = wait_for_data_connection_status(self.log, self.droid0,
+                                                          self.ed0, False)
+                print("wait for disconnected")
+                print(result)
+                assert result, "Disable data fail on PhoneA subId: {}".format(subId)
+            self.droid0.toggleDataConnection(True)
+            time.sleep(5)
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_est_basic_on_each_sim(self):
+        """ Test call establishment basic ok on two phones.
+
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        result = True
+        toggle_airplane_mode(self.log, self.droid0, self.ed0, False)
+        toggle_airplane_mode(self.log, self.droid1, self.ed1, False)
+        subInfo = self.droid0.subscriptionGetAllSubInfoList()
+
+        for i in range(len(subInfo)):
+            subId = subInfo[i]["subId"]
+            self.log.info("Setting Data/Voice/Sms to subId: {}".format(subId))
+            self._setting_subId_for_voice_data_sms(self.droid0, subId, subId, subId)
+            self.phone_number_0 = self.phone_number_tbl[subId]
+            self.operator_name_droid0 = self.operator_tbl[subId]
+            call_params = [(self.droid0, self.droid1,
+                            self.ed0, self.ed1, self.time_wait_in_call,
+                            self.phone_number_0, self.phone_number_1, self.droid0),
+                           (self.droid1, self.droid0,
+                            self.ed1, self.ed0, self.time_wait_in_call,
+                            self.phone_number_1, self.phone_number_0, self.droid0),
+                           (self.droid0, self.droid1,
+                            self.ed0, self.ed1, self.time_wait_in_call,
+                            self.phone_number_0, self.phone_number_1, self.droid1),
+                           (self.droid1, self.droid0,
+                            self.ed1, self.ed0, self.time_wait_in_call,
+                            self.phone_number_1, self.phone_number_0, self.droid1)]
+            params = list(call_params)
+            failed = self.run_generated_testcases(self._call_process_helper_NonVoLTE,
+                                                  params,
+                                                  name_func=self.gen_call_process_test_name)
+            self.log.debug("Failed ones: " + str(failed))
+            if failed:
+                result = False
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_airplane_mode_basic_attach_detach_connectivity(self):
+        """ Test airplane mode basic on Phone and Live SIM.
+
+        Turn on airplane mode to make sure detach.
+        Turn off airplane mode to make sure attach.
+        Verify voice call and internet connection.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        self.log.debug("Step1 ensure attach: " + self.phone_number_0)
+        toggle_airplane_mode(self.log, self.droid0, self.ed0, False)
+        self.log.debug("Step2 enable airplane mode and ensure detach: " +
+                      self.phone_number_0)
+        toggle_airplane_mode(self.log, self.droid0, self.ed0, True)
+        self.log.debug("Step3 disable airplane mode and ensure attach: " +
+                      self.phone_number_0)
+        toggle_airplane_mode(self.log, self.droid0, self.ed0, False)
+        wifi_toggle_state(self.droid0, self.ed0, False)
+        self.log.debug("Step4 verify voice call: " + self.phone_number_0)
+        result_call = call_process(self.log, self.droid0, self.droid1,
+                                   self.ed0, self.ed1,
+                                   self.time_wait_in_call,
+                                   self.phone_number_0, self.phone_number_1,
+                                   hangup = True, droid_hangup = self.droid0)
+        if not result_call:
+            self.log.error("Step4 verify call error")
+            # FIXME: Why doesn't this return a failure here!??!?
+        wait_for_data_connection_status(self.log, self.droid0, self.ed0, True)
+
+        self.log.debug("Step5 verify internet: " + self.phone_number_0)
+        result_internet = self.droid0.connectivityNetworkIsConnected()
+        network_type = connection_type_from_type_string(
+            self.droid0.connectivityNetworkGetActiveConnectionTypeName())
+        if not result_internet or network_type != NETWORK_CONNECTION_TYPE_CELL:
+            self.log.error("Step5 internet error. Network type: " + network_type)
+            return False
+
+        verify_http_connection(self.log, self.droid0)
+        if not result_call:
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_data_pretest_ensure_wifi_connect_to_live_network(self):
+        """Pre test for network switching.
+
+        This is pre test for network switching.
+        The purpose is to make sure the phone can connect to live network by WIFI.
+
+        Returns:
+            True if pass.
+        """
+        reset_droid_wifi(self.droid0, self.ed0)
+        wifi_toggle_state(self.droid0, self.ed0, True)
+        start_wifi_connection_scan(self.droid0, self.ed0)
+        wifi_results = self.droid0.wifiGetScanResults()
+        self.log.debug(str(wifi_results))
+        self.droid0.wifiStartTrackingStateChange()
+        nId = self.droid0.wifiAddNetwork(self.wifi_network_ssid)
+        self.droid0.wifiEnableNetwork(nId, True)
+        self.ed0.pop_event("WifiNetworkConnected")
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_data_conn_network_switching(self):
+        """Test data connection network switching.
+
+        Before test started, ensure wifi can connect to live network,
+        airplane mode is off, data connection is on, wifi is on.
+        Turn off wifi, verify data is on cell and browse to google.com is ok.
+        Turn on wifi, verify data is on wifi and browse to google.com is ok.
+        Turn off wifi, verify data is on cell and browse to google.com is ok.
+
+        Returns:
+            True if pass.
+        """
+        self.droid0.phoneStartTrackingDataConnectionStateChange()
+        self.log.info("Step1 Airplane Off, Wifi On, Data On.")
+        toggle_airplane_mode(self.log, self.droid0, self.ed0, False)
+        wifi_toggle_state(self.droid0, self.ed0, True)
+        self.droid0.toggleDataConnection(True)
+        self.log.info("Step2 Wifi is Off, Data is on Cell.")
+        toggle_wifi_verify_data_connection(self.log, self.droid0,
+                                           self.ed0, False)
+        self.log.info("Step3 Wifi is On, Data is on Wifi.")
+        toggle_wifi_verify_data_connection(self.log, self.droid0,
+                                           self.ed0, True)
+        self.log.info("Step4 Wifi is Off, Data is on Cell.")
+        toggle_wifi_verify_data_connection(self.log, self.droid0,
+                                           self.ed0, False)
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_pretest_ensure_3g(self):
+        """Pretest operation: ensure preferred network is 3G.
+
+        Set preferred network to 3G.
+        Toggle ON/OFF airplane mode.
+        """
+        self._setting_subId_for_voice_data_sms(self.droid0, "2", "2", "2")
+        self.phone_number_0 = self.phone_number_0_sim1
+        self.operator_name_droid0 = self.operator_name_droid0_sim1
+
+
+        for droid in [self.droid0, self.droid1]:
+            set_preferred_network_type(droid, RAT_3G)
+        for (droid, ed) in [(self.droid0, self.ed0), (self.droid1, self.ed1)]:
+            toggle_airplane_mode(self.log, droid, ed, True)
+            toggle_airplane_mode(self.log, droid, ed, False)
+        self.log.info("Waiting for droids to be in 3g mode.")
+        wait_for_droids_in_network_generation(
+            self.log, [self.droid0, self.droid1], RAT_3G, WAIT_TIME_NW_SELECTION)
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_data_connectivity_3g(self):
+        """Test 3G data connection before call and in call.
+
+        Turn off airplane, turn off wifi, turn on data, verify internet.
+        Initial call and accept.
+        Verify internet.
+        Turn off data and verify not connected.
+        Hangup and turn data back on.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        result = False
+        # Wait for droids in 3g mode, before proceed.
+        self.log.info("Waiting for droids to be in 3g mode.")
+        wait_for_droids_in_network_generation(
+            self.log, [self.droid0, self.droid1], RAT_3G, WAIT_TIME_NW_SELECTION)
+        self.droid0.phoneStartTrackingDataConnectionStateChange()
+        self.log.info("Step1 Airplane Off, Wifi Off, Data On, verify internet.")
+        toggle_airplane_mode(self.log, self.droid0, self.ed0, False)
+        wifi_toggle_state(self.droid0, self.ed0, False)
+        self.droid0.toggleDataConnection(True)
+        toggle_wifi_verify_data_connection(self.log, self.droid0,
+                                           self.ed0, False)
+        self.log.info("Step2 Initiate call and accept.")
+        result = call_process(self.log, self.droid0, self.droid1,
+                              self.ed0, self.ed1, 5,
+                              self.phone_number_0, self.phone_number_1)
+        if not result:
+            self.log.error("Step2 initiate call failed.")
+            return False
+        # Check in technology_tbl to see is this type can
+        # do data and voice simultaneously or not.
+        voice_technology = get_network_rat(self.droid0, NETWORK_SERVICE_VOICE)
+        if is_rat_svd_capable(voice_technology):
+            self.log.info("Step3 Verify internet.")
+            verify_http_connection(self.log, self.droid0)
+            self.log.info("Step4 Turn off data and verify not connected.")
+            self.droid0.toggleDataConnection(False)
+            wait_for_data_connection_status(self.log, self.droid0, self.ed0, False)
+            result = True
+            try:
+                verify_http_connection(self.log, self.droid0)
+            except Exception:
+                result = False
+            if result:
+                self.log.error("Step4 turn off data failed.")
+                return False
+            self.droid0.toggleDataConnection(True)
+            wait_for_data_connection_status(self.log, self.droid0, self.ed0, True)
+        else:
+            self.log.info("Skip Verify internet.")
+        self.log.info("Step5 Hang up.")
+        hangup_call(self.log, self.droid0)
+        # Wait for droids back in 3g mode.
+        self.log.info("Step6 Waiting for droids back in 3g mode.")
+        wait_for_droids_in_network_generation(
+            self.log, [self.droid0, self.droid1], RAT_3G,
+                                              WAIT_TIME_NW_SELECTION)
+        return True
+
+    def _sms_process_helper(self, params):
+        """Wrapper function for sms_send_receive_verify.
+
+        This is to wrap sms_send_receive_verify, so it can be executed by
+        generated testcases with a set of params.
+        """
+        (droid_tx, droid_rx, phone_number_tx,
+         phone_number_rx, ed_tx, ed_rx, length) = params
+        result = sms_send_receive_verify(self.log, droid_tx, droid_rx,
+                                         phone_number_tx, phone_number_rx,
+                                         ed_tx, ed_rx, length)
+        return result
+
+    def gen_sms_test_name(self, params):
+        (droid_tx, droid_rx, phone_number_tx,
+         phone_number_rx, ed_tx, ed_rx, length) = params
+        return "test_sms_{}_to_{}".format(phone_number_tx, phone_number_rx)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_basic_on_each_sim(self):
+        """Test SMS basic function between two phone.
+
+        Airplane mode is off.
+        Send SMS from PhoneA to PhoneB.
+        Verify received message on PhoneB is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        # Tmo->Att if longer than 160, test will fail
+        # Seems it is carrier limit
+        result = True
+        toggle_airplane_mode(self.log, self.droid0, self.ed0, False)
+        toggle_airplane_mode(self.log, self.droid1, self.ed1, False)
+        subInfo = self.droid0.subscriptionGetAllSubInfoList()
+
+        for i in range(len(subInfo)):
+            subId = subInfo[i]["subId"]
+            self.log.info("Setting Data/Voice/Sms to subId: {}".format(subId))
+            self._setting_subId_for_voice_data_sms(self.droid0, subId, subId, subId)
+            self.phone_number_0 = self.phone_number_tbl[subId]
+            self.operator_name_droid0 = self.operator_tbl[subId]
+            sms_params = [(self.droid0, self.droid1,
+                           self.phone_number_0, self.phone_number_1,
+                           self.ed0, self.ed1, 50),
+                          (self.droid0, self.droid1,
+                           self.phone_number_0, self.phone_number_1,
+                           self.ed0, self.ed1, 160),
+                          (self.droid0, self.droid1,
+                           self.phone_number_0, self.phone_number_1,
+                           self.ed0, self.ed1, 180),
+                          (self.droid1, self.droid0,
+                           self.phone_number_1, self.phone_number_0,
+                           self.ed1, self.ed0, 50),
+                          (self.droid1, self.droid0,
+                           self.phone_number_1, self.phone_number_0,
+                           self.ed1, self.ed0, 160),
+                          (self.droid1, self.droid0,
+                           self.phone_number_1, self.phone_number_0,
+                           self.ed1, self.ed0, 180)]
+            params = list(sms_params)
+            failed = self.run_generated_testcases(self._sms_process_helper,
+                                                  params,
+                                                  name_func=self.gen_sms_test_name)
+            self.log.debug("Failed ones: " + str(failed))
+            if failed:
+                result = False
+        return result
+    """ Tests End """
+
+def call_process(log, ad_caller, ad_callee, delay_in_call,
+                 caller_number=None, callee_number=None, delay_answer=1,
+                 hangup=False, ad_hangup=None,
+                 verify_call_mode_caller=False, verify_call_mode_callee=False,
+                 caller_mode_VoLTE=None, callee_mode_VoLTE=None):
+    """ Call process, including make a phone call from caller,
+    accept from callee, and hang up.
+
+    In call process, call from <droid_caller> to <droid_callee>,
+    after ringing, wait <delay_answer> to accept the call,
+    wait <delay_in_call> during the call process,
+    (optional)then hang up from <droid_hangup>.
+
+    Args:
+        ad_caller: Caller Android Device Object.
+        ad_callee: Callee Android Device Object.
+        delay_in_call: Wait time in call process.
+        caller_number: Optional, caller phone number.
+            If None, will get number by SL4A.
+        callee_number: Optional, callee phone number.
+            if None, will get number by SL4A.
+        delay_answer: After callee ringing state, wait time before accept.
+            If None, default value is 1.
+        hangup: Whether hangup in this function.
+            Optional. Default value is False.
+        ad_hangup: Android Device Object end the phone call.
+            Optional. Default value is None.
+        verify_call_mode_caller: Whether verify caller call mode (VoLTE or CS).
+            Optional. Default value is False, not verify.
+        verify_call_mode_callee: Whether verify callee call mode (VoLTE or CS).
+            Optional. Default value is False, not verify.
+        caller_mode_VoLTE: If verify_call_mode_caller is True,
+            this is expected caller mode. True for VoLTE mode. False for CS mode.
+            Optional. Default value is None.
+        callee_mode_VoLTE: If verify_call_mode_callee is True,
+            this is expected callee mode. True for VoLTE mode. False for CS mode.
+            Optional. Default value is None.
+
+    Returns:
+        True if call process without any error.
+        False if error happened.
+
+    Raises:
+        TelTestUtilsError if VoLTE check fail.
+    """
+    warnings.warn("tel_utils.call_process() is Deprecated"
+                  " use tel_utils.call_setup_teardown()",
+                  DeprecationWarning)
+
+    if not caller_number:
+        caller_number = ad_caller.droid.getLine1Number()
+    if not callee_number:
+        callee_number = ad_callee.droid.getLine1Number()
+    if not caller_number or not callee_number:
+        log.error("Caller or Callee number invalid.")
+        return False
+
+    log.info("Call from {} to {}".format(caller_number, callee_number))
+    result = initiate_call(log, ad_caller, callee_number)
+    if not result:
+        log.error("Initiate call failed.")
+        return False
+
+    result = wait_and_answer_call(
+        log, ad_callee, incoming_number=caller_number)
+    if not result:
+        log.error("Answer call fail.")
+        return False
+
+    time.sleep(1)  # ensure that all internal states are updated in telecom
+    if (not ad_caller.droid.telecomIsInCall() or not
+            ad_callee.droid.telecomIsInCall()):
+        log.error("Call connection failed.")
+        return False
+
+    if verify_call_mode_caller:
+        network_type = get_network_rat(log, ad_caller, NETWORK_SERVICE_VOICE)
+        if caller_mode_VoLTE and network_type != RAT_LTE:
+            raise TelTestUtilsError("Error: Caller not in VoLTE. Expected VoLTE. Type:{}".
+                                    format(network_type))
+            return False
+        if not caller_mode_VoLTE and network_type == RAT_LTE:
+            raise TelTestUtilsError("Error: Caller in VoLTE. Expected not VoLTE. Type:{}".
+                                    format(network_type))
+            return False
+    if verify_call_mode_callee:
+        network_type = get_network_rat(log, ad_callee, NETWORK_SERVICE_VOICE)
+        if callee_mode_VoLTE and network_type != RAT_LTE:
+            raise TelTestUtilsError("Error: Callee not in VoLTE. Expected VoLTE. Type:{}".
+                                    format(network_type))
+            return False
+        if not callee_mode_VoLTE and network_type == RAT_LTE:
+            raise TelTestUtilsError("Error: Callee in VoLTE. Expected not VoLTE.. Type:{}".
+                                    format(network_type))
+            return False
+
+    time.sleep(delay_in_call)
+
+    if (not ad_caller.droid.telecomIsInCall() or not
+            ad_callee.droid.telecomIsInCall()):
+        log.error("Call ended before delay_in_call.")
+        return False
+
+    if not hangup:
+        return result
+    if result:
+        result = hangup_call(log, ad_hangup)
+    if not result:
+        for ad in [ad_caller, ad_callee]:
+            if ad.droid.telecomIsInCall():
+                ad.droid.telecomEndCall()
+    return result
\ No newline at end of file
diff --git a/acts/tests/google/tel/live/TelLivePreflightTest.py b/acts/tests/google/tel/live/TelLivePreflightTest.py
new file mode 100644
index 0000000..f7c973b
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLivePreflightTest.py
@@ -0,0 +1,165 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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 Flight check.
+"""
+
+import time
+from acts.base_test import BaseTestClass
+from queue import Empty
+from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts.test_utils.tel.tel_test_utils import get_operator_name
+from acts.test_utils.tel.tel_test_utils import get_sub_ids_for_sim_slots
+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 wait_for_voice_attach_for_subscription
+from acts.test_utils.tel.tel_defines import AOSP_PREFIX
+from acts.test_utils.tel.tel_defines import CAPABILITY_PHONE
+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_MSIM
+from acts.test_utils.tel.tel_defines import CAPABILITY_OMADM
+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 WAIT_TIME_AFTER_REBOOT
+from acts.test_utils.tel.tel_defines import WAIT_TIME_NW_SELECTION
+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.TelephonyBaseTest import TelephonyBaseTest
+
+
+from acts.utils import load_config
+
+
+
+class TelLivePreflightTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      "test_pre_flight_check",
+                      )
+
+        self.simconf = load_config(self.user_params["sim_conf_file"])
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_pre_flight_check(self):
+        def droid_has_phone(log, ad):
+            #check for sim and service
+            subInfo = ad.droid.subscriptionGetAllSubInfoList()
+            if not subInfo or len(subInfo) < 1:
+                return False
+            toggle_airplane_mode(log, ad, False)
+            sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+            if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
+                                                  WAIT_TIME_NW_SELECTION):
+                log.error("{} didn't find a cell network".format(ad.serial))
+                return False
+            return True
+        def droid_has_provisioning(log, ad):
+            if not ad.droid.imsIsVolteProvisionedOnDevice():
+                log.error("{}: VoLTE Not Provisioned on the Platform".
+                    format(ad.serial))
+                return False
+            else:
+                log.info("{} VoLTE Provisioned".format(ad.serial))
+            return True
+
+        def droid_has_volte(log, ad):
+            if not ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform():
+                log.error("{}: VoLTE Not Enabled on the Platform".
+                    format(ad.serial))
+                return False
+            else:
+                log.info("{} VoLTE Enabled by platform".format(ad.serial))
+            return True
+
+        def droid_has_wifi_calling(log, ad):
+            if not ad.droid.imsIsWfcEnabledByPlatform():
+                log.error("{}: WFC Not Enabled on the Platform".
+                    format(ad.serial))
+                return False
+            else:
+                log.info("{} WFC Enabled by platform".format(ad.serial))
+            return True
+
+        def droid_has_vt(log, ad):
+            if not ad.droid.imsIsVtEnabledByPlatform():
+                log.error("{}: VT Not Enabled on the Platform".
+                    format(ad.serial))
+                return False
+            else:
+                log.info("{} VT Enabled by platform".format(ad.serial))
+            return True
+
+        try:
+            for ad in self.android_devices:
+                model = ad.model
+                # Remove aosp prefix
+                if model.startswith(AOSP_PREFIX):
+                    model = model[len(AOSP_PREFIX):]
+
+                # Special capability phone, needed to get SIM Operator
+                if not CAPABILITY_PHONE in device_capabilities[model]:
+                    self.log.info("Skipping {}:{}: not a phone".
+                                  format(ad.serial, model))
+                    return True
+
+                operator = get_operator_name(self.log, ad)
+                self.log.info("Pre-flight check for <{}>, <{}:{}>, build<{}>".
+                              format(operator, model, ad.serial,
+                                     ad.droid.getBuildID()))
+
+                if("force_provisioning" in self.user_params and
+                   CAPABILITY_OMADM in device_capabilities[model] and
+                   CAPABILITY_OMADM in operator_capabilities[operator] and
+                   not droid_has_provisioning(self.log, ad)):
+                    self.log.info("{} not IMS Provisioned!!".format(ad.serial))
+                    self.log.info("{} Forcing IMS Provisioning!".format(ad.serial))
+                    ad.droid.imsSetVolteProvisioning(True)
+                    self.log.info("{} reboot!".format(ad.serial))
+                    ad.reboot()
+                    self.log.info("{} wait {}s for radio up.".
+                        format(ad.serial, WAIT_TIME_AFTER_REBOOT))
+                    # This sleep WAIT_TIME_AFTER_REBOOT seconds is waiting for
+                    # radio to initiate after phone reboot.
+                    time.sleep(WAIT_TIME_AFTER_REBOOT)
+
+                active_capabilities = [CAPABILITY_PHONE, CAPABILITY_OMADM,
+                                       CAPABILITY_VOLTE, CAPABILITY_WFC]
+                for capability in active_capabilities:
+                    if(capability in device_capabilities[model] and
+                       capability in operator_capabilities[operator]):
+                        if not {
+                            # FIXME: make the check table global
+                            CAPABILITY_PHONE: droid_has_phone,
+                            CAPABILITY_OMADM: droid_has_provisioning,
+                            CAPABILITY_VOLTE: droid_has_volte,
+                            CAPABILITY_WFC: droid_has_wifi_calling,
+                            CAPABILITY_VT: droid_has_vt
+                        }[capability](self.log, ad):
+                            self.abort_all("Pre-flight check FAILED for <{}>, <{}:{}>".
+                                           format(operator, model, ad.serial))
+        except Exception as e:
+            self.abort_all("Pre-flight check exception: {}".format(e))
+        return True
+""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveSmokeTest.py b/acts/tests/google/tel/live/TelLiveSmokeTest.py
new file mode 100644
index 0000000..81e6e42
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveSmokeTest.py
@@ -0,0 +1,461 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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 Smoke Test
+"""
+
+import time
+from acts.base_test import BaseTestClass
+from queue import Empty
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_data_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.utils import load_config
+from acts.utils import rand_ascii_str
+from acts.keys import Config
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+SKIP = 'Skip'
+
+class TelLiveSmokeTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      "test_wfc_capability_phone_smoke",
+                      )
+
+        self.simconf = load_config(self.user_params["sim_conf_file"])
+
+        self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+        try:
+            self.wifi_network_pass = self.user_params["wifi_network_pass"]
+        except KeyError:
+            self.wifi_network_pass = None
+
+    """ Tests Begin """
+    def _test_smoke_volte(self):
+        ads = self.android_devices
+        sms_idle_result = False
+        sms_incall_result = False
+        data_idle_result = False
+        data_incall_result = False
+        call_result = False
+
+        self.log.info("--------start test_smoke_volte--------")
+        ensure_phones_default_state(self.log, ads)
+        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 VoLTE.")
+            return False
+
+        # TODO: this is hack to reduce call fail in VoLTE mode.
+        time.sleep(10)
+
+        self.log.info("1. SMS in LTE idle.")
+        sms_idle_result = sms_send_receive_verify(self.log, ads[0], ads[1],
+                                                  [rand_ascii_str(50)])
+
+        self.log.info("2. Data in LTE idle.")
+        if (wait_for_cell_data_connection(self.log, ads[0], True) and
+            verify_http_connection(self.log, ads[0])):
+            data_idle_result = True
+
+        self.log.info("3. Setup VoLTE Call.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_volte,
+                               verify_callee_func=is_phone_in_call_volte,
+                               wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS):
+            self.log.error("Setup VoLTE Call Failed.")
+            return False
+
+        self.log.info("4. Verify SMS in call.")
+        sms_incall_result = sms_send_receive_verify(self.log, ads[0], ads[1],
+                                                    [rand_ascii_str(51)])
+
+        self.log.info("5. Verify Data in call.")
+        if (wait_for_cell_data_connection(self.log, ads[0], True) and
+            verify_http_connection(self.log, ads[0])):
+            data_incall_result = True
+
+        self.log.info("6. Verify Call not drop and hangup.")
+        if (is_phone_in_call_volte(self.log, ads[0]) and
+            is_phone_in_call_volte(self.log, ads[1]) and
+            hangup_call(self.log, ads[0])):
+            call_result = True
+
+        self.log.info("Summary-VoLTE Smoke Test: SMS idle: {}, Data idle: {}, "
+                      "VoLTE Call: {}, SMS in call: {}, Data in call: {}".
+            format(sms_idle_result, data_idle_result,
+                   call_result, sms_incall_result, data_incall_result))
+
+        return (sms_idle_result and data_idle_result and call_result and
+                sms_incall_result and data_incall_result)
+
+    def _test_smoke_csfb_3g(self):
+        ads = self.android_devices
+        sms_idle_result = False
+        sms_incall_result = False
+        data_idle_result = False
+        data_incall_result = False
+        call_result = False
+
+        self.log.info("--------start test_smoke_csfb_3g--------")
+        ensure_phones_default_state(self.log, ads)
+        tasks = [(phone_setup_csfb, (self.log, ads[0])),
+                 (phone_setup_csfb, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up CSFB_3G.")
+            return False
+
+        # TODO: this is hack to reduce SMS send failure in CSFB mode.
+        time.sleep(10)
+
+        self.log.info("1. SMS in LTE idle (no IMS).")
+        sms_idle_result = sms_send_receive_verify(self.log, ads[0], ads[1],
+                                                  [rand_ascii_str(50)])
+
+        self.log.info("2. Data in LTE idle (no IMS).")
+        if (wait_for_cell_data_connection(self.log, ads[0], True) and
+            verify_http_connection(self.log, ads[0])):
+            data_idle_result = True
+
+        self.log.info("3. Setup CSFB_3G Call.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_csfb,
+                               verify_callee_func=is_phone_in_call_csfb):
+            self.log.error("Setup CSFB_3G Call Failed.")
+            return False
+
+        self.log.info("4. Verify SMS in call.")
+        sms_incall_result = sms_send_receive_verify(self.log, ads[0], ads[1],
+                                                    [rand_ascii_str(51)])
+
+        self.log.info("5. Verify Data in call.")
+        if is_rat_svd_capable(get_network_rat(self.log, ads[0],
+            NETWORK_SERVICE_VOICE)):
+            if (wait_for_cell_data_connection(self.log, ads[0], True) and
+                verify_http_connection(self.log, ads[0])):
+                data_incall_result = True
+        else:
+            self.log.info("Data in call not supported on current RAT."
+                          "Skip Data verification.")
+            data_incall_result = SKIP
+
+        self.log.info("6. Verify Call not drop and hangup.")
+        if (is_phone_in_call_csfb(self.log, ads[0]) and
+            is_phone_in_call_csfb(self.log, ads[1]) and
+            hangup_call(self.log, ads[0])):
+            call_result = True
+
+        self.log.info("Summary-CSFB 3G Smoke Test: SMS idle: {}, Data idle: {},"
+                      " CSFB_3G Call: {}, SMS in call: {}, Data in call: {}".
+            format(sms_idle_result, data_idle_result,
+                   call_result, sms_incall_result, data_incall_result))
+
+        return (sms_idle_result and data_idle_result and call_result and
+                sms_incall_result and
+                ((data_incall_result is True) or (data_incall_result == SKIP)))
+
+    def _test_smoke_3g(self):
+        ads = self.android_devices
+        sms_idle_result = False
+        sms_incall_result = False
+        data_idle_result = False
+        data_incall_result = False
+        call_result = False
+
+        self.log.info("--------start test_smoke_3g--------")
+        ensure_phones_default_state(self.log, ads)
+        tasks = [(phone_setup_3g, (self.log, ads[0])),
+                 (phone_setup_3g, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up 3G.")
+            return False
+        self.log.info("1. SMS in LTE idle (no IMS).")
+        sms_idle_result = sms_send_receive_verify(self.log, ads[0], ads[1],
+                                                  [rand_ascii_str(50)])
+
+        self.log.info("2. Data in LTE idle (no IMS).")
+        if (wait_for_cell_data_connection(self.log, ads[0], True) and
+            verify_http_connection(self.log, ads[0])):
+            data_idle_result = True
+
+        self.log.info("3. Setup 3G Call.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_3g,
+                               verify_callee_func=is_phone_in_call_3g):
+            self.log.error("Setup 3G Call Failed.")
+            return False
+
+        self.log.info("4. Verify SMS in call.")
+        sms_incall_result = sms_send_receive_verify(self.log, ads[0], ads[1],
+                                                    [rand_ascii_str(51)])
+
+        self.log.info("5. Verify Data in call.")
+        if is_rat_svd_capable(get_network_rat(self.log, ads[0],
+            NETWORK_SERVICE_VOICE)):
+            if (wait_for_cell_data_connection(self.log, ads[0], True) and
+                verify_http_connection(self.log, ads[0])):
+                data_incall_result = True
+        else:
+            self.log.info("Data in call not supported on current RAT."
+                          "Skip Data verification.")
+            data_incall_result = SKIP
+
+        self.log.info("6. Verify Call not drop and hangup.")
+        if (is_phone_in_call_3g(self.log, ads[0]) and
+            is_phone_in_call_3g(self.log, ads[1]) and
+            hangup_call(self.log, ads[0])):
+            call_result = True
+
+        self.log.info("Summary-3G Smoke Test: SMS idle: {}, Data idle: {},"
+                      " 3G Call: {}, SMS in call: {}, Data in call: {}".
+            format(sms_idle_result, data_idle_result,
+                   call_result, sms_incall_result, data_incall_result))
+
+        return (sms_idle_result and data_idle_result and call_result and
+                sms_incall_result and
+                ((data_incall_result is True) or (data_incall_result == SKIP)))
+
+    def _test_smoke_wfc(self):
+        ads = self.android_devices
+        sms_idle_result = False
+        sms_incall_result = False
+        call_result = False
+
+        self.log.info("--------start test_smoke_wfc--------")
+        for ad in [ads[0], ads[1]]:
+            if not ad.droid.imsIsWfcEnabledByPlatform():
+                self.log.info("WFC not supported by platform.")
+                return SKIP
+
+        ensure_phones_default_state(self.log, ads)
+        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 WiFI Calling.")
+            return False
+
+        self.log.info("1. Verify SMS in idle.")
+        if sms_send_receive_verify(self.log, ads[0], ads[1],
+                                   [rand_ascii_str(50)]):
+            sms_idle_result = True
+
+        self.log.info("2. Setup WiFi Call.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_iwlan,
+                               verify_callee_func=is_phone_in_call_iwlan):
+            self.log.error("Setup WiFi Call Failed.")
+            self.log.info("sms_idle_result:{}".format(sms_idle_result))
+            return False
+
+        self.log.info("3. Verify SMS in call.")
+        if sms_send_receive_verify(self.log, ads[0], ads[1],
+                                   [rand_ascii_str(51)]):
+            sms_incall_result = True
+
+        self.log.info("4. Verify Call not drop and hangup.")
+        if (is_phone_in_call_iwlan(self.log, ads[0]) and
+            is_phone_in_call_iwlan(self.log, ads[1]) and
+            hangup_call(self.log, ads[0])):
+            call_result = True
+
+        self.log.info("Summary-WFC Smoke Test: SMS in idle:{}, WiFi Call:{},"
+                      " SMS in call:{}".
+                      format(sms_idle_result, call_result, sms_incall_result))
+
+        return (call_result and sms_idle_result and sms_incall_result)
+
+    def _test_smoke_data(self):
+        ads = self.android_devices
+        apm_result = False
+        nw_switch_result = False
+        tethering_result = False
+
+        self.log.info("--------start test_smoke_data--------")
+        ensure_phones_default_state(self.log, ads)
+        self.log.info("1. Verify toggle airplane mode.")
+        apm_result = airplane_mode_test(self.log, ads[0])
+        self.log.info("2. Verify LTE-WiFi network switch.")
+        nw_switch_result = wifi_cell_switching(self.log, ads[0],
+                                               self.wifi_network_ssid,
+                                               self.wifi_network_pass,
+                                               RAT_LTE)
+        if ads[0].droid.phoneIsTetheringModeAllowed(TETHERING_MODE_WIFI,
+            TETHERING_ENTITLEMENT_CHECK_TIMEOUT):
+            self.log.info("3. Verify WiFi Tethering.")
+            if ads[0].droid.wifiIsApEnabled():
+                WifiUtils.stop_wifi_tethering(self.log, ads[0])
+            tethering_result = wifi_tethering_setup_teardown(self.log,
+                ads[0], [ads[1]], ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                check_interval=10, check_iteration=4)
+            # check_interval=10, check_iteration=4: in this Smoke test,
+            # check tethering connection for 4 times, each time delay 10s,
+            # to provide a reasonable check_time (10*4=40s) and also reduce test
+            # execution time. In regular test, check_iteration is set to 10.
+        else:
+            self.log.info("3. Skip WiFi Tethering."
+                          "Tethering not allowed on SIM.")
+            tethering_result = SKIP
+
+        self.log.info("Summary-Data Smoke Test: Airplane Mode:{}, "
+                      "Network Switch:{}, Tethering:{}".
+                      format(apm_result, nw_switch_result, tethering_result))
+
+        return (apm_result and nw_switch_result and
+                ((tethering_result is True) or (tethering_result == SKIP)))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wfc_capability_phone_smoke(self):
+        """Verify basic WFC, VoLTE, CS, Data, and Messaging features.
+        1. Verify WFC features.
+            Setup WFC in APM mode, WiFi-preferred mode.
+            Send/Receive one SMS in WFC idle mode.
+            Make WFC call.
+            Send/Receive one SMS in WFC active call.
+        2. Verify VoLTE+LTE Data/Message features.
+            Setup VoLTE.
+            Verify SMS and Data in idle.
+            Make VoLTE call.
+            Send/Receive one SMS in VoLTE active call.
+            Verify Data in VoLTE active call.
+        3. Verify Data features.
+            Verify toggle airplane mode.
+            Verify LTE-WiFi network switch.
+            Verify WiFI Tethering.
+        4. Verify CSFB+LTE Data/Message features.
+            Setup LTE without VoLTE.
+            Verify SMS and Data in idle.
+            Make CSFB call.
+            Send/Receive one SMS in CSFB 3G active call.
+            Verify Data in CSFB 3G active call (if current network rat support).
+        5. Verify 3G Voice/Data/Message features.
+            Setup 3G.
+            Verify SMS and Data in idle.
+            Make 3G voice call.
+            Send/Receive one SMS in 3G active call.
+            Verify Data in 3G active call (if current network rat support).
+        """
+        try:
+            result_wfc = self._test_smoke_wfc()
+            result_volte = self._test_smoke_volte()
+            result_data = self._test_smoke_data()
+            result_csfb_3g = self._test_smoke_csfb_3g()
+            result_3g = self._test_smoke_3g()
+
+            self.log.info("Summary for test run. Testbed:<{}>. WFC:{}, "
+                "VoLTE+LTE Data/Message:{}, Data Basic:{}, "
+                "CSFB+LTE Data/Message:{}, 3G Voice/Data/Message:{}".
+                format(getattr(self, Config.ikey_testbed_name.value),
+                       result_wfc, result_volte, result_data, result_csfb_3g,
+                       result_3g))
+
+            return (result_volte and result_data and result_csfb_3g
+                    and result_3g and ((result_wfc is True)
+                                       or (result_wfc == SKIP)))
+        except Exception as e:
+            self.log.error("Summary Failed. Exception:{}".format(e))
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_capability_phone_smoke(self):
+        """Verify basic VoLTE, CS, Data, and Messaging features.
+        1. Verify VoLTE+LTE Data/Message features.
+            Setup VoLTE.
+            Verify SMS and Data in idle.
+            Make VoLTE call.
+            Send/Receive one SMS in VoLTE active call.
+            Verify Data in VoLTE active call.
+        2. Verify Data features.
+            Verify toggle airplane mode.
+            Verify LTE-WiFi network switch.
+            Verify WiFI Tethering.
+        3. Verify CSFB+LTE Data/Message features.
+            Setup LTE without VoLTE.
+            Verify SMS and Data in idle.
+            Make CSFB call.
+            Send/Receive one SMS in CSFB 3G active call.
+            Verify Data in CSFB 3G active call (if current network rat support).
+        4. Verify 3G Voice/Data/Message features.
+            Setup 3G.
+            Verify SMS and Data in idle.
+            Make 3G voice call.
+            Send/Receive one SMS in 3G active call.
+            Verify Data in 3G active call (if current network rat support).
+        """
+        try:
+            result_volte = self._test_smoke_volte()
+            result_data = self._test_smoke_data()
+            result_csfb_3g = self._test_smoke_csfb_3g()
+            result_3g = self._test_smoke_3g()
+
+            self.log.info("Summary for test run. Testbed:<{}>. "
+                "VoLTE+LTE Data/Message:{}, Data Basic:{}, "
+                "CSFB+LTE Data/Message:{}, 3G Voice/Data/Message:{}".
+                format(getattr(self, Config.ikey_testbed_name.value),
+                    result_volte, result_data, result_csfb_3g, result_3g))
+
+            return (result_volte and result_data and result_csfb_3g
+                    and result_3g)
+        except Exception as e:
+            self.log.error("Summary Failed. Exception:{}".format(e))
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_lte_capability_phone_smoke(self):
+        """Verify basic CS, Data, and Messaging features.
+        1. Verify Data features.
+            Verify toggle airplane mode.
+            Verify LTE-WiFi network switch.
+            Verify WiFI Tethering.
+        2. Verify CSFB+LTE Data/Message features.
+            Setup LTE without VoLTE.
+            Verify SMS and Data in idle.
+            Make CSFB call.
+            Send/Receive one SMS in CSFB 3G active call.
+            Verify Data in CSFB 3G active call (if current network rat support).
+        3. Verify 3G Voice/Data/Message features.
+            Setup 3G.
+            Verify SMS and Data in idle.
+            Make 3G voice call.
+            Send/Receive one SMS in 3G active call.
+            Verify Data in 3G active call (if current network rat support).
+        """
+        try:
+            result_data = self._test_smoke_data()
+            result_csfb_3g = self._test_smoke_csfb_3g()
+            result_3g = self._test_smoke_3g()
+
+            self.log.info("Summary for test run. Testbed:<{}>. Data Basic:{}, "
+                "CSFB+LTE Data/Message:{}, 3G Voice/Data/Message:{}".
+                format(getattr(self, Config.ikey_testbed_name.value),
+                    result_data, result_csfb_3g, result_3g))
+
+            return (result_data and result_csfb_3g and result_3g)
+        except Exception as e:
+            self.log.error("Summary Failed. Exception:{}".format(e))
+            return False
+    """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveSmsTest.py b/acts/tests/google/tel/live/TelLiveSmsTest.py
new file mode 100644
index 0000000..ec5719a
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveSmsTest.py
@@ -0,0 +1,1206 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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.base_test import BaseTestClass
+from queue import Empty
+from acts.test_utils.tel.tel_test_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+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.test_utils.tel.tel_video_utils import video_call_setup_teardown
+from acts.utils import load_config
+from acts.utils import rand_ascii_str
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+
+class TelLiveSmsTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      "test_sms_mo_4g",
+                      "test_sms_mt_4g",
+                      "test_sms_mo_in_call_volte",
+                      "test_sms_mt_in_call_volte",
+                      "test_sms_mo_in_call_csfb",
+                      "test_sms_mt_in_call_csfb",
+                      "test_sms_mo_in_call_csfb_1x",
+                      "test_sms_mt_in_call_csfb_1x",
+
+                      "test_sms_mo_3g",
+                      "test_sms_mt_3g",
+                      "test_sms_mo_in_call_wcdma",
+                      "test_sms_mt_in_call_wcdma",
+                      "test_sms_mo_in_call_1x",
+                      "test_sms_mt_in_call_1x",
+
+                      "test_sms_mo_2g",
+                      "test_sms_mt_2g",
+                      "test_sms_mo_in_call_gsm",
+                      "test_sms_mt_in_call_gsm",
+
+                      "test_sms_mo_iwlan",
+                      "test_sms_mt_iwlan",
+                      "test_sms_mo_in_call_iwlan",
+                      "test_sms_mt_in_call_iwlan",
+
+                      "test_sms_mo_in_call_vt",
+                      "test_sms_mt_in_call_vt",
+
+                      #SIM2 cases
+                      "test_sms_mo_3g_sim2",
+                      "test_sms_mt_3g_sim2",
+                      "test_sms_mo_in_call_wcdma_sim2",
+                      "test_sms_mt_in_call_wcdma_sim2",
+
+                      "test_sms_mo_2g_sim2",
+                      "test_sms_mt_2g_sim2",
+                      "test_sms_mo_in_call_gsm_sim2",
+                      "test_sms_mt_in_call_gsm_sim2",
+                      )
+        # The path for "sim config file" should be set
+        # in "testbed.config" entry "sim_conf_file".
+        self.simconf = load_config(self.user_params["sim_conf_file"])
+        self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+
+        try:
+            self.wifi_network_pass = self.user_params["wifi_network_pass"]
+        except KeyError:
+            self.wifi_network_pass = None
+
+    def _sms_test(self, ads):
+        """Test SMS between two phones.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        sms_params = [(ads[0], ads[1])]
+        message_arrays = [[rand_ascii_str(50)],
+                          [rand_ascii_str(160)],
+                          [rand_ascii_str(180)]]
+
+        for outer_param in sms_params:
+            outer_param = (self.log,) + outer_param
+            for message_array in message_arrays:
+                inner_param = outer_param + (message_array,)
+                if not sms_send_receive_verify(*inner_param):
+                    return False
+
+        return True
+
+    def _sms_test_mo(self, ads):
+        return self._sms_test([ads[0], ads[1]])
+
+    def _sms_test_mt(self, ads):
+        return self._sms_test([ads[1], ads[0]])
+
+    def _mo_sms_in_3g_call(self, ads):
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_3g,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mo(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    def _mt_sms_in_3g_call(self, ads):
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_3g,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mt(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    def _mo_sms_in_2g_call(self, ads):
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_2g,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mo(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    def _mt_sms_in_2g_call(self, ads):
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_2g,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mt(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_2g(self):
+        """Test SMS basic function between two phone. Phones in 3g network.
+
+        Airplane mode is off.
+        Send SMS from PhoneA to PhoneB.
+        Verify received message on PhoneB is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_2g, (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
+
+        return self._sms_test_mo(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_2g(self):
+        """Test SMS basic function between two phone. Phones in 3g network.
+
+        Airplane mode is off.
+        Send SMS from PhoneB to PhoneA.
+        Verify received message on PhoneA is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_2g, (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
+
+        return self._sms_test_mt(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_3g(self):
+        """Test SMS basic function between two phone. Phones in 3g network.
+
+        Airplane mode is off.
+        Send SMS from PhoneA to PhoneB.
+        Verify received message on PhoneB is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        ads = self.android_devices
+
+        tasks = [(phone_setup_3g, (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
+
+        return self._sms_test_mo(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_3g(self):
+        """Test SMS basic function between two phone. Phones in 3g network.
+
+        Airplane mode is off.
+        Send SMS from PhoneB to PhoneA.
+        Verify received message on PhoneA is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        ads = self.android_devices
+
+        tasks = [(phone_setup_3g, (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
+
+        return self._sms_test_mt(ads)
+
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_4g(self):
+        """Test SMS basic function between two phone. Phones in LTE network.
+
+        Airplane mode is off.
+        Send SMS from PhoneA to PhoneB.
+        Verify received message on PhoneB is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        ads = self.android_devices
+
+        tasks = [(phone_setup_csfb, (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
+
+        return self._sms_test_mo(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_4g(self):
+        """Test SMS basic function between two phone. Phones in LTE network.
+
+        Airplane mode is off.
+        Send SMS from PhoneB to PhoneA.
+        Verify received message on PhoneA is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        ads = self.android_devices
+
+        tasks = [(phone_setup_csfb, (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
+
+        return self._sms_test_mt(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_volte(self):
+        """ Test MO SMS during a MO VoLTE call.
+
+        Make Sure PhoneA is in LTE mode (with VoLTE).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_volte,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mo(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_volte(self):
+        """ Test MT SMS during a MO VoLTE call.
+
+        Make Sure PhoneA is in LTE mode (with VoLTE).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_volte,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mt(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_wcdma(self):
+        """ Test MO SMS during a MO wcdma call.
+
+        Make Sure PhoneA is in wcdma mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma SMS test.")
+            return False
+
+        tasks = [(phone_setup_3g, (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
+
+        return self._mo_sms_in_3g_call(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_wcdma(self):
+        """ Test MT SMS during a MO wcdma call.
+
+        Make Sure PhoneA is in wcdma mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma SMS test.")
+            return False
+
+        tasks = [(phone_setup_3g, (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
+
+        return self._mt_sms_in_3g_call(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_csfb(self):
+        """ Test MO SMS during a MO csfb wcdma/gsm call.
+
+        Make Sure PhoneA is in LTE mode (no VoLTE).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this csfb wcdma SMS test.")
+            return False
+
+        tasks = [(phone_setup_csfb, (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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_csfb,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mo(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_csfb(self):
+        """ Test MT SMS during a MO csfb wcdma/gsm call.
+
+        Make Sure PhoneA is in LTE mode (no VoLTE).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive receive on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this csfb wcdma SMS test.")
+            return False
+
+        tasks = [(phone_setup_csfb, (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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_csfb,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mt(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_1x(self):
+        """ Test MO SMS during a MO 1x call.
+
+        Make Sure PhoneA is in 1x mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is CDMA phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("Not CDMA phone, abort this 1x SMS test.")
+            return False
+
+        tasks = [(phone_setup_3g, (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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_1x,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mo(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_1x(self):
+        """ Test MT SMS during a MO 1x call.
+
+        Make Sure PhoneA is in 1x mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is CDMA phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("Not CDMA phone, abort this 1x SMS test.")
+            return False
+
+        tasks = [(phone_setup_3g, (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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_1x,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mt(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_csfb_1x(self):
+        """ Test MO SMS during a MO csfb 1x call.
+
+        Make Sure PhoneA is in LTE mode (no VoLTE).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is CDMA phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("Not CDMA phone, abort this csfb 1x SMS test.")
+            return False
+
+        tasks = [(phone_setup_csfb, (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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_1x,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mo(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_csfb_1x(self):
+        """ Test MT SMS during a MO csfb 1x call.
+
+        Make Sure PhoneA is in LTE mode (no VoLTE).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is CDMA phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("Not CDMA phone, abort this csfb 1x SMS test.")
+            return False
+
+        tasks = [(phone_setup_csfb, (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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_1x,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mt(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_iwlan(self):
+        """ Test MO SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
+
+        Make Sure PhoneA APM, WiFi connected, WFC WiFi preferred mode.
+        Make sure PhoneA report iwlan as data rat.
+        Make Sure PhoneB is able to make/receive call/sms.
+        Send SMS 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_voice_general, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        return self._sms_test_mo(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_iwlan(self):
+        """ Test MT SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
+
+        Make Sure PhoneA APM, WiFi connected, WFC WiFi preferred mode.
+        Make sure PhoneA report iwlan as data rat.
+        Make Sure PhoneB is able to make/receive call/sms.
+        Receive SMS 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_voice_general, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        return self._sms_test_mt(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_iwlan(self):
+        """ Test MO SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
+
+        Make Sure PhoneA APM, WiFi connected, WFC WiFi preferred mode.
+        Make sure PhoneA report iwlan as data rat.
+        Make Sure PhoneB is able to make/receive call/sms.
+        Call from PhoneA to PhoneB, accept on PhoneB.
+        Send SMS 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_voice_general, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_iwlan,
+                               verify_callee_func=None):
+            return False
+
+        return self._sms_test_mo(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_iwlan(self):
+        """ Test MT SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
+
+        Make Sure PhoneA APM, WiFi connected, WFC WiFi preferred mode.
+        Make sure PhoneA report iwlan as data rat.
+        Make Sure PhoneB is able to make/receive call/sms.
+        Call from PhoneA to PhoneB, accept on PhoneB.
+        Receive SMS 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_voice_general, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_iwlan,
+                               verify_callee_func=None):
+            return False
+
+        return self._sms_test_mt(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_vt(self):
+        """ Test MO SMS, Phone in ongoing VT call.
+
+        Make Sure PhoneA and PhoneB in LTE and can make VT call.
+        Make Video Call from PhoneA to PhoneB, accept on PhoneB as Video Call.
+        Send SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        return self._sms_test_mo(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_vt(self):
+        """ Test MT SMS, Phone in ongoing VT call.
+
+        Make Sure PhoneA and PhoneB in LTE and can make VT call.
+        Make Video Call from PhoneA to PhoneB, accept on PhoneB as Video Call.
+        Receive SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        return self._sms_test_mt(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_4g(self):
+        """Test MMS text function between two phone. Phones in LTE network.
+
+        Airplane mode is off.
+        Send SMS from PhoneA to PhoneB.
+        Verify received message on PhoneB is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        self.log.error("Test Case is non-functional: b/21569494")
+        return False
+
+        ads = self.android_devices
+
+        tasks = [(phone_setup_csfb, (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
+
+        return mms_send_receive_verify(
+            self.log, ads[0], ads[1],
+            [("Test Message", "Basic Message Body", None)])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_4g(self):
+        """Test MMS text function between two phone. Phones in LTE network.
+
+        Airplane mode is off.
+        Send SMS from PhoneB to PhoneA.
+        Verify received message on PhoneA is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        self.log.error("Test Case is non-functional: b/21569494")
+        return False
+
+        ads = self.android_devices
+
+        tasks = [(phone_setup_csfb, (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
+
+        return mms_send_receive_verify(
+            self.log, ads[1], ads[0],
+            [("Test Message", "Basic Message Body", None)])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_gsm(self):
+        """ Test MO SMS during a MO gsm call.
+
+        Make Sure PhoneA is in gsm mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this gsm SMS test.")
+            return False
+
+        tasks = [(phone_setup_voice_2g, (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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_2g,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mo(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_gsm(self):
+        """ Test MT SMS during a MO gsm call.
+
+        Make Sure PhoneA is in gsm mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this gsm SMS test.")
+            return False
+
+        tasks = [(phone_setup_voice_2g, (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
+
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_2g,
+                               verify_callee_func=None):
+            return False
+
+        if not self._sms_test_mt(ads):
+            self.log.error("SMS test fail.")
+            return False
+
+        return True
+
+    #SIM2 tests
+    def _reset_subscriptions_to_sim1(self, ads):
+        set_call_state_listen_level(self.log, ads[0], False,
+                                    self.sim_sub_ids[0][1])
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        setup_sim(self.log, ads[0], self.sim_sub_ids[0][0], True, True)
+        ads[0].droid.phoneSetPreferredNetworkTypeForSubscription(
+                RAT_3G, self.sim_sub_ids[0][0])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_3g_sim2(self):
+        """Test SMS basic function between two phone. Phones in 3g network.
+
+        Set SIM2 as the default messaging SIM
+        Airplane mode is off.
+        Send SMS from PhoneA to PhoneB.
+        Verify received message on PhoneB is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                 return False
+
+            tasks = [(phone_setup_3g, (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
+
+            return self._sms_test_mo(ads)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_3g_sim2(self):
+        """Test SMS basic function between two phone. Phones in 3g network.
+
+        Set SIM2 as the default messaging SIM
+        Airplane mode is off.
+        Send SMS from PhoneB to PhoneA.
+        Verify received message on PhoneA is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            tasks = [(phone_setup_3g, (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
+
+            return self._sms_test_mt(ads)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_wcdma_sim2(self):
+        """ Test MO SMS during a MO wcdma call.
+
+        Set SIM2 as the default voice and messaging SIM
+        Make Sure PhoneA is in wcdma mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            # make sure PhoneA is GSM phone before proceed.
+            if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("Not GSM phone, abort this wcdma SMS test.")
+                return False
+
+            tasks = [(phone_setup_voice_3g, (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
+
+            return self._mo_sms_in_3g_call(ads)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_wcdma_sim2(self):
+        """ Test MT SMS during a MO wcdma call.
+
+        Set SIM2 as the default voice and messaging SIM
+        Make Sure PhoneA is in wcdma mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            # make sure PhoneA is GSM phone before proceed.
+            if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("Not GSM phone, abort this wcdma SMS test.")
+                return False
+
+            tasks = [(phone_setup_3g, (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
+
+            return self._mt_sms_in_3g_call(ads)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_2g_sim2(self):
+        """Test SMS basic function between two phone. Phones in 2g network.
+
+        Set SIM2 as the default messaging SIM
+        Airplane mode is off.
+        Send SMS from PhoneA to PhoneB.
+        Verify received message on PhoneB is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            tasks = [(phone_setup_voice_2g, (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
+
+            return self._sms_test_mo(ads)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_2g_sim2(self):
+        """Test SMS basic function between two phone. Phones in 2g network.
+
+        Set SIM2 as the default messaging SIM
+        Airplane mode is off.
+        Send SMS from PhoneB to PhoneA.
+        Verify received message on PhoneA is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            tasks = [(phone_setup_voice_2g, (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
+
+            return self._sms_test_mt(ads)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_in_call_gsm_sim2(self):
+        """ Test MO SMS during a MO gsm call.
+
+        Set SIM2 as the default voice and messaging SIM
+        Make Sure PhoneA is in gsm mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            # make sure PhoneA is GSM phone before proceed.
+            if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("Not GSM phone, abort this gsm SMS test.")
+                return False
+
+            tasks = [(phone_setup_voice_2g, (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
+
+            return self._mo_sms_in_2g_call(ads)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mt_in_call_gsm_sim2(self):
+        """ Test MT SMS during a MO gsm call.
+
+        Set SIM2 as the default voice and messaging SIM
+        Make Sure PhoneA is in gsm mode.
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive SMS on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            # make sure PhoneA is GSM phone before proceed.
+            if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("Not GSM phone, abort this gsm SMS test.")
+                return False
+
+            tasks = [(phone_setup_voice_2g, (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
+
+            return self._mt_sms_in_2g_call(ads)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+    """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveStressCallTest.py b/acts/tests/google/tel/live/TelLiveStressCallTest.py
new file mode 100644
index 0000000..a503570
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveStressCallTest.py
@@ -0,0 +1,170 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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 integration with TF
+"""
+
+import time
+from acts.base_test import BaseTestClass
+from queue import Empty
+from acts.test_utils.tel import tel_defines
+from acts.test_utils.tel.tel_voice_utils import phone_setup_3g
+from acts.test_utils.tel.tel_voice_utils import phone_idle_3g
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
+from acts.test_utils.tel.tel_test_utils import ensure_phone_idle
+from acts.test_utils.tel.tel_test_utils import verify_active_call_number
+
+MAX_NUMBER_REDIALS = 20
+INCORRECT_STATE_MSG = "Caller not in correct state!"
+
+class TelLiveStressCallTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+                "test_call_3g_stress",
+                )
+
+    def setup_class(self):
+        self.ad_caller = self.android_devices[0]
+        self.stress_test_callee_number = self.user_params["call_server_number"]
+        self.phone_call_iteration = self.user_params["phone_call_iteration"]
+        return True
+
+    def setup_test(self):
+        # try removing lock
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        self.assert_true(ensure_phone_default_state(self.log, self.ad_caller),
+                "Make sure phone is in default state")
+        return True
+
+    def teardown_test(self):
+        self.droid.wakeLockRelease()
+        self.droid.goToSleepNow()
+        self.assert_true(ensure_phone_default_state(self.log, self.ad_caller),
+                "Make sure phone returns to default state")
+
+    """ Tests Begin """
+    def test_call_3g_stress(self):
+        """ 3G to 800 call test
+
+        Steps:
+        1. Make Sure PhoneA is in 3G mode.
+        2. Call from PhoneA to a 800 number, hang up on PhoneA.
+        3, Repeat 2 around 100 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.
+        """
+        ad_caller = self.ad_caller
+        callee_number = self.stress_test_callee_number
+        self.assert_true(phone_setup_3g(self.log, ad_caller), "Phone Failed to Set Up Properly.")
+
+        # Make sure phone is idle.
+        ensure_phone_idle(self.log, ad_caller)
+        self.assert_true(phone_idle_3g(self.log, ad_caller), "DUT Failed to Reselect")
+
+        self.log.info("Call test:{} to {}".format(ad_caller.serial, callee_number))
+        subid_caller = ad_caller.droid.subscriptionGetDefaultVoiceSubId()
+
+        total_iteration = self.phone_call_iteration
+        current_iteration = 0
+        redial_time = 0
+        while current_iteration < total_iteration:
+            self.log.info("---> Call test: iteration {} redial {}<---"
+                    .format(current_iteration,redial_time))
+            self.log.info("Checking Telephony Manager Call State")
+            self.assert_true(self._check_phone_call_status(ad_caller,
+                    tel_defines.TELEPHONY_STATE_IDLE), INCORRECT_STATE_MSG)
+
+            self.log.info("Making a phone call")
+            self.assert_true(initiate_call(self.log, ad_caller, callee_number),
+                        "Initiate call failed.")
+
+            self.log.info("Ensure that all internal states are updated")
+            time.sleep(tel_defines.WAIT_TIME_ANDROID_STATE_SETTLING)
+            self.assert_true(is_phone_in_call_3g(self.log, ad_caller), INCORRECT_STATE_MSG)
+            self.assert_true(self._check_phone_call_status(ad_caller,
+                    tel_defines.TELEPHONY_STATE_OFFHOOK,tel_defines.CALL_STATE_DIALING),
+                    INCORRECT_STATE_MSG)
+
+            time.sleep(tel_defines.WAIT_TIME_IN_CALL)
+            self.log.info("Checking Telephony Manager Call State after waiting for a while")
+            if  (self._check_phone_call_status(ad_caller,
+                        tel_defines.TELEPHONY_STATE_OFFHOOK,tel_defines.CALL_STATE_ACTIVE)):
+                current_iteration += 1
+                redial_time = 0
+            elif (self._check_phone_call_status(ad_caller,
+                        tel_defines.TELEPHONY_STATE_OFFHOOK,tel_defines.CALL_STATE_DIALING)):
+                self.log.info("The line is busy, try again")
+                redial_time += 1
+                if redial_time > MAX_NUMBER_REDIALS:
+                    self.assert_true(False, "Re-dial {} times and still having busy signal"
+                            .format(redial_time))
+            else:
+                self.assert_true(False, INCORRECT_STATE_MSG)
+                current_iteration += 1
+
+            self.log.info("Hang up phone for this iteration")
+            self.assert_true(hangup_call(self.log, ad_caller), "Error in Hanging-Up Call")
+            time.sleep(tel_defines.WAIT_TIME_ANDROID_STATE_SETTLING)
+            self.log.info("Checking Telephony Manager Call State after hang up")
+            self.assert_true(self._check_phone_call_status(ad_caller,
+                    tel_defines.TELEPHONY_STATE_IDLE), INCORRECT_STATE_MSG)
+
+            ensure_phone_idle(self.log, ad_caller)
+
+    """ Tests End """
+
+    def _check_phone_call_status(self, ad, telecom_status, call_status = None):
+        """Check existing event until we get either "ACTIVE" or "DIALING" event
+        Args:
+            ad: Android object
+            telecome_status: expected telecom call state
+            call_status: expcted telecomcall call state
+
+        Return:
+            True if all the status are matching, False otherwise
+         """
+        # Checking phone call status
+        if ad.droid.telecomGetCallState() != telecom_status:
+            return False
+        if call_status:
+            call_list = ad.droid.telecomCallGetCallIds()
+            if not call_list:
+                return False
+            if not verify_active_call_number(self.log,ad, 1):
+                return False
+            call_id = call_list[0]
+            self.log.info("TelecomCall Call State {}"
+                    .format(ad.droid.telecomCallGetCallState(call_id)))
+            if ad.droid.telecomCallGetCallState(call_id) != call_status:
+                return False
+        return True
+
+
+if __name__ == "__main__":
+    pass
+
diff --git a/acts/tests/google/tel/live/TelLiveVideoDataTest.py b/acts/tests/google/tel/live/TelLiveVideoDataTest.py
new file mode 100644
index 0000000..5fc0f78
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveVideoDataTest.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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 VT Data test
+"""
+
+import time
+from queue import Empty
+
+from acts.base_test import BaseTestClass
+from acts.test_utils.tel.tel_video_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.utils import load_config
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelLiveVideoDataTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      # Data during VT call
+                      "test_internet_access_during_video_call",
+                      )
+
+        self.simconf = load_config(self.user_params["sim_conf_file"])
+        self.stress_test_number = int(self.user_params["stress_test_number"])
+        self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+
+        try:
+            self.wifi_network_pass = self.user_params["wifi_network_pass"]
+        except KeyError:
+            self.wifi_network_pass = None
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_internet_access_during_video_call(self):
+        """ Test Internet access during VT<->VT call.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as Bi-Directional Video,
+        Accept on PhoneB as video call.
+        Verify PhoneA have Internet access.
+        Hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Make MO VT call.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup+teardown a call")
+            return False
+
+        self.log.info("Step2: Verify Internet on PhoneA.")
+        if not verify_http_connection(self.log, ads[0]):
+            self.log.error("Verify Internet on PhoneA failed.")
+            return False
+
+        return hangup_call(self.log, ads[0])
+""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveVideoTest.py b/acts/tests/google/tel/live/TelLiveVideoTest.py
new file mode 100644
index 0000000..14e6d28
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveVideoTest.py
@@ -0,0 +1,2177 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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 VT live call test
+"""
+
+import time
+from queue import Empty
+
+from acts.base_test import BaseTestClass
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts.test_utils.tel.tel_video_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.utils import load_config
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelLiveVideoTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+            "test_call_video_to_video",
+            "test_call_video_accept_as_voice",
+            "test_call_video_to_video_mo_disable_camera",
+            "test_call_video_to_video_mt_disable_camera",
+            "test_call_video_to_video_mo_mt_disable_camera",
+            "test_call_video_to_video_mt_mo_disable_camera",
+            "test_call_volte_to_volte_mo_upgrade_bidirectional",
+            "test_call_video_accept_as_voice_mo_upgrade_bidirectional",
+            "test_call_volte_to_volte_mo_upgrade_reject",
+            "test_call_video_accept_as_voice_mo_upgrade_reject",
+            "test_call_video_to_video_mo_to_backgroundpause_foregroundresume",
+            "test_call_video_to_video_mt_to_backgroundpause_foregroundresume",
+
+            # Video Call + Voice Call
+            "test_call_video_add_mo_voice",
+            "test_call_video_add_mt_voice",
+            "test_call_volte_add_mo_video",
+            "test_call_volte_add_mt_video",
+            "test_call_video_add_mt_voice_swap_once_local_drop",
+            "test_call_video_add_mt_voice_swap_twice_remote_drop_voice_unhold_video",
+
+            # Video + Video
+            "test_call_video_add_mo_video",
+            "test_call_video_add_mt_video",
+            "test_call_mt_video_add_mt_video",
+            "test_call_mt_video_add_mo_video",
+
+            # VT conference
+            "test_call_volte_add_mo_video_accept_as_voice_merge_drop",
+            "test_call_volte_add_mt_video_accept_as_voice_merge_drop",
+            "test_call_video_add_mo_voice_swap_downgrade_merge_drop",
+            "test_call_video_add_mt_voice_swap_downgrade_merge_drop",
+            "test_call_volte_add_mo_video_downgrade_merge_drop",
+            "test_call_volte_add_mt_video_downgrade_merge_drop",
+
+            # Disable Data, VT not available
+            "test_disable_data_vt_unavailable",
+            )
+
+        self.simconf = load_config(self.user_params["sim_conf_file"])
+        self.stress_test_number = int(self.user_params["stress_test_number"])
+        self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+
+        try:
+            self.wifi_network_pass = self.user_params["wifi_network_pass"]
+        except KeyError:
+            self.wifi_network_pass = None
+
+    """ Tests Begin """
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_to_video(self):
+        """ Test VT<->VT call functionality.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        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_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup+teardown a call")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_accept_as_voice(self):
+        """ Test VT<->VT call functionality.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as Bi-Directional Video,
+        Accept on PhoneB as audio only, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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_AUDIO_ONLY,
+                verify_caller_func=is_phone_in_call_voice_hd,
+                verify_callee_func=is_phone_in_call_voice_hd):
+            self.log.error("Failed to setup+teardown a call")
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_to_video_mo_disable_camera(self):
+        """ Test VT<->VT call functionality.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as Bi-Directional Video,
+        Accept on PhoneB as video call.
+        On PhoneA disabled video transmission.
+        Verify PhoneA as RX_ENABLED and PhoneB as TX_ENABLED.
+        Hangup on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Disable video on PhoneA:{}".format(ads[0].serial))
+        if not video_call_downgrade(
+            self.log,
+            ads[0], get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL),
+            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
+        return hangup_call(self.log, ads[0])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_to_video_mt_disable_camera(self):
+        """ Test VT<->VT call functionality.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as Bi-Directional Video,
+        Accept on PhoneB as video call.
+        On PhoneB disabled video transmission.
+        Verify PhoneB as RX_ENABLED and PhoneA as TX_ENABLED.
+        Hangup on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Disable video on PhoneB:{}".format(ads[1].serial))
+        if not video_call_downgrade(
+            self.log,
+            ads[1], get_call_id_in_video_state(self.log, ads[1], VT_STATE_BIDIRECTIONAL),
+            ads[0], get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+            ):
+            self.log.error("Failed to disable video on PhoneB.")
+            return False
+        return hangup_call(self.log, ads[0])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_to_video_mo_mt_disable_camera(self):
+        """ Test VT<->VT call functionality.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as Bi-Directional Video,
+        Accept on PhoneB as video call.
+        On PhoneA disabled video transmission.
+        Verify PhoneA as RX_ENABLED and PhoneB as TX_ENABLED.
+        On PhoneB disabled video transmission.
+        Verify PhoneA as AUDIO_ONLY and PhoneB as AUDIO_ONLY.
+        Hangup on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Disable video on PhoneA:{}".format(ads[0].serial))
+        if not video_call_downgrade(
+            self.log,
+            ads[0], get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL),
+            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
+
+        self.log.info("Disable video on PhoneB:{}".format(ads[1].serial))
+        if not video_call_downgrade(
+            self.log,
+            ads[1], get_call_id_in_video_state(self.log, ads[1], VT_STATE_TX_ENABLED),
+            ads[0], get_call_id_in_video_state(self.log, ads[0], VT_STATE_RX_ENABLED)
+            ):
+            self.log.error("Failed to disable video on PhoneB.")
+            return False
+        return hangup_call(self.log, ads[0])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_to_video_mt_mo_disable_camera(self):
+        """ Test VT<->VT call functionality.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as Bi-Directional Video,
+        Accept on PhoneB as video call.
+        On PhoneB disabled video transmission.
+        Verify PhoneB as RX_ENABLED and PhoneA as TX_ENABLED.
+        On PhoneA disabled video transmission.
+        Verify PhoneA as AUDIO_ONLY and PhoneB as AUDIO_ONLY.
+        Hangup on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Disable video on PhoneB:{}".format(ads[1].serial))
+        if not video_call_downgrade(
+            self.log,
+            ads[1], get_call_id_in_video_state(self.log, ads[1], VT_STATE_BIDIRECTIONAL),
+            ads[0], get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+            ):
+            self.log.error("Failed to disable video on PhoneB.")
+            return False
+
+        self.log.info("Disable video on PhoneA:{}".format(ads[0].serial))
+        if not video_call_downgrade(
+            self.log,
+            ads[0], get_call_id_in_video_state(self.log, ads[0], VT_STATE_TX_ENABLED),
+            ads[1], get_call_id_in_video_state(self.log, ads[1], VT_STATE_RX_ENABLED)
+            ):
+            self.log.error("Failed to disable video on PhoneB.")
+            return False
+        return hangup_call(self.log, ads[0])
+
+    def _mo_upgrade_bidirectional(self, ads):
+        """Send + accept an upgrade request from Phone A to B.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_id_requester = get_call_id_in_video_state(
+            self.log, ads[0], VT_STATE_AUDIO_ONLY)
+
+        call_id_responder = get_call_id_in_video_state(
+            self.log, ads[1], VT_STATE_AUDIO_ONLY)
+
+        if not call_id_requester or not call_id_responder:
+            self.log.error("Couldn't find a candidate call id {}:{}, {}:{}"
+                           .format(ads[0].serial, call_id_requester,
+                                   ads[1].serial, call_id_responder))
+            return False
+
+        if not video_call_modify_video(self.log, ads[0], call_id_requester,
+                                       ads[1], call_id_responder,
+                                       VT_STATE_BIDIRECTIONAL):
+            self.log.error("Failed to upgrade video call!")
+            return False
+
+        #Wait for a completed upgrade and ensure the call is stable
+        time.sleep(WAIT_TIME_IN_CALL)
+
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            self.log.error("_mo_upgrade_bidirectional: Call Drop!")
+            return False
+
+        if (get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL) !=
+                call_id_requester):
+            self.log.error(
+                "Caller not in correct state: {}".format(VT_STATE_BIDIRECTIONAL))
+            return False
+
+        if (get_call_id_in_video_state(self.log, ads[1], VT_STATE_BIDIRECTIONAL) !=
+                call_id_responder):
+            self.log.error(
+                "Callee not in correct state: {}".format(VT_STATE_BIDIRECTIONAL))
+            return False
+
+        return hangup_call(self.log, ads[0])
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_accept_as_voice_mo_upgrade_bidirectional(self):
+        """ Test Upgrading from VoLTE to Bi-Directional VT.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as Video, accept on PhoneB as audio only.
+        Send + accept an upgrade request from Phone A to B.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_AUDIO_ONLY,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+
+        return self._mo_upgrade_bidirectional(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_volte_mo_upgrade_bidirectional(self):
+        """ Test Upgrading from VoLTE to Bi-Directional VT.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as VoLTE, accept on PhoneB.
+        Send + accept an upgrade request from Phone A to B.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+        if not call_setup_teardown(self.log, ads[0], ads[1], None,
+                                   is_phone_in_call_volte,
+                                   is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+
+        return self._mo_upgrade_bidirectional(ads)
+
+    def _mo_upgrade_reject(self, ads):
+        """Send + reject an upgrade request from Phone A to B.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_id_requester = get_call_id_in_video_state(
+            self.log, ads[0], VT_STATE_AUDIO_ONLY)
+
+        call_id_responder = get_call_id_in_video_state(
+            self.log, ads[1], VT_STATE_AUDIO_ONLY)
+
+        if not call_id_requester or not call_id_responder:
+            self.log.error("Couldn't find a candidate call id {}:{}, {}:{}"
+                           .format(ads[0].serial, call_id_requester,
+                                   ads[1].serial, call_id_responder))
+            return False
+
+        if not video_call_modify_video(self.log, ads[0], call_id_requester,
+                                       ads[1], call_id_responder,
+                                       VT_STATE_BIDIRECTIONAL, VT_VIDEO_QUALITY_DEFAULT,
+                                       VT_STATE_AUDIO_ONLY, VT_VIDEO_QUALITY_DEFAULT):
+            self.log.error("Failed to upgrade video call!")
+            return False
+
+        time.sleep(WAIT_TIME_IN_CALL)
+
+        if not is_phone_in_call_voice_hd(self.log, ads[0]):
+            self.log.error("PhoneA not in correct state.")
+            return False
+        if not is_phone_in_call_voice_hd(self.log, ads[1]):
+            self.log.error("PhoneB not in correct state.")
+            return False
+
+        return hangup_call(self.log, ads[0])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_volte_mo_upgrade_reject(self):
+        """ Test Upgrading from VoLTE to Bi-Directional VT and reject.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as VoLTE, accept on PhoneB.
+        Send an upgrade request from Phone A to PhoneB.
+        Reject on PhoneB. Verify PhoneA and PhoneB ad AUDIO_ONLY.
+        Verify call continues.
+        Hangup on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+        if not call_setup_teardown(self.log, ads[0], ads[1], None,
+                                   is_phone_in_call_volte,
+                                   is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+
+        return self._mo_upgrade_reject(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_accept_as_voice_mo_upgrade_reject(self):
+        """ Test Upgrading from VoLTE to Bi-Directional VT and reject.
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Call from PhoneA to PhoneB as Video, accept on PhoneB as audio only.
+        Send an upgrade request from Phone A to PhoneB.
+        Reject on PhoneB. Verify PhoneA and PhoneB ad AUDIO_ONLY.
+        Verify call continues.
+        Hangup on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_AUDIO_ONLY,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+
+        return self._mo_upgrade_reject(ads)
+
+    def _test_put_call_to_backgroundpause_and_foregroundresume(self,
+        ad_requester, ad_responder):
+        call_id_requester = get_call_id_in_video_state(
+            self.log, ad_requester, VT_STATE_BIDIRECTIONAL)
+        call_id_responder = get_call_id_in_video_state(
+            self.log, ad_responder, VT_STATE_BIDIRECTIONAL)
+        ad_requester.droid.telecomCallVideoStartListeningForEvent(
+            call_id_requester, EventSessionEvent)
+        ad_responder.droid.telecomCallVideoStartListeningForEvent(
+            call_id_responder, EventSessionEvent)
+        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(
+                EventTelecomVideoCallSessionEvent,
+                WAIT_TIME_VIDEO_SESSION_EVENT)
+            event_on_requester = ad_requester.ed.pop_event(
+                EventTelecomVideoCallSessionEvent,
+                WAIT_TIME_VIDEO_SESSION_EVENT)
+            if event_on_responder['data']['Event'] != SessionEventRxPause:
+                self.log.error("Event not correct. event_on_responder: {}. Expected :{}".
+                    format(event_on_responder, SessionEventRxPause))
+                return False
+            if event_on_requester['data']['Event'] != SessionEventRxPause:
+                self.log.error("Event not correct. event_on_requester: {}. Expected :{}".
+                    format(event_on_requester, SessionEventRxPause))
+                return False
+        except Empty:
+            self.log.error("Expected event not received.")
+            return False
+        finally:
+            ad_requester.droid.telecomCallVideoStopListeningForEvent(
+                call_id_requester, EventSessionEvent)
+            ad_responder.droid.telecomCallVideoStopListeningForEvent(
+                call_id_responder, EventSessionEvent)
+        time.sleep(WAIT_TIME_IN_CALL)
+
+        if not verify_video_call_in_expected_state(self.log, ad_requester,
+                                                   call_id_requester,
+                                                   VT_STATE_BIDIRECTIONAL_PAUSED,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ad_responder,
+                                                   call_id_responder,
+                                                   VT_STATE_BIDIRECTIONAL_PAUSED,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        self.log.info("Put In-Call UI on {} to foreground.".format(ad_requester.serial))
+        ad_requester.droid.telecomCallVideoStartListeningForEvent(
+            call_id_requester, EventSessionEvent)
+        ad_responder.droid.telecomCallVideoStartListeningForEvent(
+            call_id_responder, EventSessionEvent)
+        ad_requester.droid.telecomShowInCallScreen()
+        try:
+            event_on_responder = ad_responder.ed.pop_event(
+                EventTelecomVideoCallSessionEvent,
+                WAIT_TIME_VIDEO_SESSION_EVENT)
+            event_on_requester = ad_requester.ed.pop_event(
+                EventTelecomVideoCallSessionEvent,
+                WAIT_TIME_VIDEO_SESSION_EVENT)
+            if event_on_responder['data']['Event'] != SessionEventRxResume:
+                self.log.error("Event not correct. event_on_responder: {}. Expected :{}".
+                    format(event_on_responder, SessionEventRxResume))
+                return False
+            if event_on_requester['data']['Event'] != SessionEventRxResume:
+                self.log.error("Event not correct. event_on_requester: {}. Expected :{}".
+                    format(event_on_requester, SessionEventRxResume))
+                return False
+        except Empty:
+            self.log.error("Expected event not received.")
+            return False
+        finally:
+            ad_requester.droid.telecomCallVideoStopListeningForEvent(
+                call_id_requester, EventSessionEvent)
+            ad_responder.droid.telecomCallVideoStopListeningForEvent(
+                call_id_responder, EventSessionEvent)
+        time.sleep(WAIT_TIME_IN_CALL)
+        self.log.info("Verify both calls are in bi-directional/active state.")
+        if not verify_video_call_in_expected_state(self.log, ad_requester,
+                                                   call_id_requester,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ad_responder,
+                                                   call_id_responder,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_to_video_mo_to_backgroundpause_foregroundresume(self):
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+
+        return self._test_put_call_to_backgroundpause_and_foregroundresume(
+            ads[0], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_to_video_mt_to_backgroundpause_foregroundresume(self):
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        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], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+
+        return self._test_put_call_to_backgroundpause_and_foregroundresume(
+            ads[1], ads[0])
+
+    def _vt_test_multi_call_hangup(self, ads):
+        """private function to hangup calls for VT tests.
+
+        Hangup on PhoneB.
+        Verify PhoneA and PhoneC still in call.
+        Hangup on PhoneC.
+        Verify all phones not in call.
+        """
+        if not hangup_call(self.log, ads[1]):
+            return False
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[2]], True):
+            return False
+        if not hangup_call(self.log, ads[2]):
+            return False
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_add_mo_voice(self):
+        """
+        From Phone_A, Initiate a Bi-Directional Video Call to Phone_B
+        Accept the call on Phone_B as Bi-Directional Video
+        From Phone_A, add a voice call to Phone_C
+        Accept the call on Phone_C
+        Verify both calls remain active.
+        """
+        # This test case is not supported by VZW.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneA->PhoneB.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Voice Call PhoneA->PhoneC.")
+        if not call_setup_teardown(
+                self.log, ads[0], ads[2], None,
+                verify_caller_func=None,
+                verify_callee_func=is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video:
+                call_id_voice = call
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        return self._vt_test_multi_call_hangup(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_add_mt_voice(self):
+        """
+        From Phone_A, Initiate a Bi-Directional Video Call to Phone_B
+        Accept the call on Phone_B as Bi-Directional Video
+        From Phone_C, add a voice call to Phone_A
+        Accept the call on Phone_A
+        Verify both calls remain active.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneA->PhoneB.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Voice Call PhoneC->PhoneA.")
+        if not call_setup_teardown(
+                self.log, ads[2], ads[0], None,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=None):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video:
+                call_id_voice = call
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL_PAUSED,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        return self._vt_test_multi_call_hangup(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_add_mo_video(self):
+        """
+        From Phone_A, Initiate a VoLTE Call to Phone_B
+        Accept the call on Phone_B
+        From Phone_A, add a Video call to Phone_C
+        Accept the call on Phone_C as Video
+        Verify both calls remain active.
+        """
+        # This test case is not supported by VZW.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_volte, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate VoLTE Call PhoneA->PhoneB.")
+        if not call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            self.log.error("Active call numbers in PhoneA is not 1.")
+            return False
+        call_id_voice = calls[0]
+
+        self.log.info("Step2: Initiate Video Call PhoneA->PhoneC.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[2], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        return self._vt_test_multi_call_hangup(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_add_mt_video(self):
+        """
+        From Phone_A, Initiate a VoLTE Call to Phone_B
+        Accept the call on Phone_B
+        From Phone_C, add a Video call to Phone_A
+        Accept the call on Phone_A as Video
+        Verify both calls remain active.
+        """
+        # TODO (b/21437650):
+        # Test will fail. After established 2nd call ~15s, Phone C will drop call.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_volte, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate VoLTE Call PhoneA->PhoneB.")
+        if not call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            self.log.error("Active call numbers in PhoneA is not 1.")
+            return False
+        call_id_voice = calls[0]
+
+        self.log.info("Step2: Initiate Video Call PhoneC->PhoneA.")
+        if not video_call_setup_teardown(
+                self.log, ads[2], ads[0], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        call_id_video = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        return self._vt_test_multi_call_hangup(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_add_mt_voice_swap_once_local_drop(self):
+        """
+        From Phone_A, Initiate a Bi-Directional Video Call to Phone_B
+        Accept the call on Phone_B as Bi-Directional Video
+        From Phone_C, add a voice call to Phone_A
+        Accept the call on Phone_A
+        Verify both calls remain active.
+        Swap calls on PhoneA.
+        End Video call on PhoneA.
+        End Voice call on PhoneA.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneA->PhoneB.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Voice Call PhoneC->PhoneA.")
+        if not call_setup_teardown(
+                self.log, ads[2], ads[0], None,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=None):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video:
+                call_id_voice = call
+
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL_PAUSED,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        self.log.info("Step4: Verify all phones remain in-call.")
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("Step5: Swap calls on PhoneA and verify call state correct.")
+        ads[0].droid.telecomCallHold(call_id_voice)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        for ad in [ads[0], ads[1]]:
+            if get_audio_route(self.log, ad) != AUDIO_ROUTE_SPEAKER:
+                self.log.error("{} Audio is not on speaker.".format(ad.serial))
+                # TODO: Define expected behavior. Return false or not.
+
+            set_audio_route(self.log, ad, AUDIO_ROUTE_EARPIECE)
+
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        self.log.info("Step6: Drop Video Call on PhoneA.")
+        disconnect_call_by_id(self.log, ads[0], call_id_video)
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[2]], True):
+            return False
+        disconnect_call_by_id(self.log, ads[0], call_id_voice)
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_add_mt_voice_swap_twice_remote_drop_voice_unhold_video(self):
+        """
+        From Phone_A, Initiate a Bi-Directional Video Call to Phone_B
+        Accept the call on Phone_B as Bi-Directional Video
+        From Phone_C, add a voice call to Phone_A
+        Accept the call on Phone_A
+        Verify both calls remain active.
+        Swap calls on PhoneA.
+        Swap calls on PhoneA.
+        End Voice call on PhoneC.
+        Unhold Video call on PhoneA.
+        End Video call on PhoneA.
+        """
+
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneA->PhoneB.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Voice Call PhoneC->PhoneA.")
+        if not call_setup_teardown(
+                self.log, ads[2], ads[0], None,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=None):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video:
+                call_id_voice = call
+
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL_PAUSED,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        self.log.info("Step4: Verify all phones remain in-call.")
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("Step5: Swap calls on PhoneA and verify call state correct.")
+        ads[0].droid.telecomCallHold(call_id_voice)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        for ad in [ads[0], ads[1]]:
+            if get_audio_route(self.log, ad) != AUDIO_ROUTE_SPEAKER:
+                self.log.error("{} Audio is not on speaker.".format(ad.serial))
+                # TODO: Define expected behavior. Return false or not.
+            set_audio_route(self.log, ad, AUDIO_ROUTE_EARPIECE)
+
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        self.log.info("Step6: Swap calls on PhoneA and verify call state correct.")
+        ads[0].droid.telecomCallHold(call_id_video)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        # 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))
+                # TODO: Define expected behavior. Return false or not.
+
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        self.log.info("Step7: Drop Voice Call on PhoneC.")
+        hangup_call(self.log, ads[2])
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            return False
+
+        self.log.info("Step8: Unhold Video call on PhoneA and verify call state.")
+        ads[0].droid.telecomCallUnhold(call_id_video)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        # 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))
+                # TODO: Define expected behavior. Return false or not.
+
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        self.log.info("Step9: Drop Video Call on PhoneA.")
+        disconnect_call_by_id(self.log, ads[0], call_id_video)
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_add_mo_video(self):
+        """
+        From Phone_A, Initiate a Bi-Directional Video Call to Phone_B
+        Accept the call on Phone_B as Bi-Directional Video
+        From Phone_A, add a Bi-Directional Video Call to Phone_C
+        Accept the call on Phone_C
+        Verify both calls remain active.
+        """
+        # This test case is not supported by VZW.
+        # TODO: (b/21435901). Test will fail. 2nd Video call can not be added.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneA->PhoneB.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video_ab = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video_ab is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Video Call PhoneA->PhoneC.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[2], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video calls in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video_ab:
+                call_id_video_ac = call
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ac,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        return self._vt_test_multi_call_hangup(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_add_mt_video(self):
+        """
+        From Phone_A, Initiate a Bi-Directional Video Call to Phone_B
+        Accept the call on Phone_B as Bi-Directional Video
+        From Phone_C, add a Bi-Directional Video Call to Phone_A
+        Accept the call on Phone_A
+        Verify both calls remain active.
+        Hang up on PhoneC.
+        Hang up on PhoneA.
+        """
+        # TODO (b/21437650):
+        # Test will fail. After established 2nd call ~15s, Phone C will drop call.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneA->PhoneB.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video_ab = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video_ab is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Video Call PhoneC->PhoneA.")
+        if not video_call_setup_teardown(
+                self.log, ads[2], ads[0], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video calls in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video_ab:
+                call_id_video_ac = call
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_BIDIRECTIONAL_PAUSED,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ac,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        self.log.info("Step4: Hangup on PhoneC.")
+        if not hangup_call(self.log, ads[2]):
+            return False
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            return False
+        self.log.info("Step4: Hangup on PhoneA.")
+        if not hangup_call(self.log, ads[0]):
+            return False
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mt_video_add_mt_video(self):
+        """
+        From Phone_B, Initiate a Bi-Directional Video Call to Phone_A
+        Accept the call on Phone_A as Bi-Directional Video
+        From Phone_C, add a Bi-Directional Video Call to Phone_A
+        Accept the call on Phone_A
+        Verify both calls remain active.
+        Hang up on PhoneC.
+        Hang up on PhoneA.
+        """
+        # TODO (b/21437650):
+        # Test will fail. After established 2nd call ~15s, Phone C will drop call.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneB->PhoneA.")
+        if not video_call_setup_teardown(
+                self.log, ads[1], ads[0], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video_ab = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video_ab is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Video Call PhoneC->PhoneA.")
+        if not video_call_setup_teardown(
+                self.log, ads[2], ads[0], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video calls in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video_ab:
+                call_id_video_ac = call
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_BIDIRECTIONAL_PAUSED,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ac,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        self.log.info("Step4: Hangup on PhoneC.")
+        if not hangup_call(self.log, ads[2]):
+            return False
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            return False
+        self.log.info("Step4: Hangup on PhoneA.")
+        if not hangup_call(self.log, ads[0]):
+            return False
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mt_video_add_mo_video(self):
+        """
+        From Phone_B, Initiate a Bi-Directional Video Call to Phone_A
+        Accept the call on Phone_A as Bi-Directional Video
+        From Phone_A, add a Bi-Directional Video Call to Phone_C
+        Accept the call on Phone_C
+        Verify both calls remain active.
+        """
+        # This test case is not supported by VZW.
+        # TODO: (b/21435901). Test will fail. 2nd Video call can not be added.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneB->PhoneA.")
+        if not video_call_setup_teardown(
+                self.log, ads[1], ads[0], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video_ab = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video_ab is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Video Call PhoneA->PhoneC.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[2], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video calls in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video_ab:
+                call_id_video_ac = call
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ac,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        return self._vt_test_multi_call_hangup(ads)
+
+    def _test_vt_conference_merge_drop(self, ads, call_ab_id, call_ac_id):
+        """Test conference merge and drop for VT call test.
+
+        PhoneA in call with PhoneB.
+        PhoneA in call with PhoneC.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneB, check call continues between AC.
+        Hangup on PhoneC.
+        Hangup on PhoneA.
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            True if succeed;
+            False if failed.
+        """
+        self.log.info("Merge - Step1: Merge to Conf Call and verify Conf Call.")
+        ads[0].droid.telecomCallJoinCallsInConf(call_ab_id, call_ac_id)
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            self.log.error("Total number of call ids in {} is not 1.".
+                           format(ads[0].serial))
+            return False
+        call_conf_id = None
+        for call_id in calls:
+            if call_id != call_ab_id and call_id != call_ac_id:
+                call_conf_id = call_id
+        if not call_conf_id:
+            self.log.error("Merge call fail, no new conference call id.")
+            return False
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        # 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)))
+            return False
+
+        self.log.info("Merge - Step2: End call on PhoneB and verify call continues.")
+        ads[1].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if not verify_incall_state(self.log, [ads[0], ads[2]], True):
+            return False
+        if not verify_incall_state(self.log, [ads[1]], False):
+            return False
+
+        ads[1].droid.telecomEndCall()
+        ads[0].droid.telecomEndCall()
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_add_mo_video_accept_as_voice_merge_drop(self):
+        """Conference call
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with VoLTE).
+        Make Sure PhoneC is in LTE mode (with Video Calling).
+        PhoneA VoLTE call to PhoneB. Accept on PhoneB.
+        PhoneA add a Bi-Directional Video call to PhoneC.
+        PhoneC accept as voice.
+        Merge call on PhoneA.
+        Hang up on PhoneB.
+        Hang up on PhoneC.
+        """
+        # This test case is not supported by VZW.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_volte, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+        self.log.info("Step1: Initiate VoLTE Call PhoneA->PhoneB.")
+        if not call_setup_teardown(self.log, ads[0], ads[1], None,
+                                   is_phone_in_call_volte,
+                                   is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            self.log.error("Active call numbers in PhoneA is not 1.")
+            return False
+        call_ab_id = calls[0]
+
+        self.log.info("Step2: Initiate Video Call PhoneA->PhoneC and accept as voice.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[2], None,
+                video_state=VT_STATE_AUDIO_ONLY,
+                verify_caller_func=is_phone_in_call_voice_hd,
+                verify_callee_func=is_phone_in_call_voice_hd):
+            self.log.error("Failed to setup a call")
+            return False
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_ab_id:
+                call_ac_id = call
+
+        self.log.info("Step3: Verify calls in correct state.")
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_ab_id,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_ac_id,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        return self._test_vt_conference_merge_drop(ads, call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_add_mt_video_accept_as_voice_merge_drop(self):
+        """Conference call
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with VoLTE).
+        Make Sure PhoneC is in LTE mode (with Video Calling).
+        PhoneA VoLTE call to PhoneB. Accept on PhoneB.
+        PhoneC add a Bi-Directional Video call to PhoneA.
+        PhoneA accept as voice.
+        Merge call on PhoneA.
+        Hang up on PhoneB.
+        Hang up on PhoneC.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_volte, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+        self.log.info("Step1: Initiate VoLTE Call PhoneA->PhoneB.")
+        if not call_setup_teardown(self.log, ads[0], ads[1], None,
+                                   is_phone_in_call_volte,
+                                   is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            self.log.error("Active call numbers in PhoneA is not 1.")
+            return False
+        call_ab_id = calls[0]
+
+        self.log.info("Step2: Initiate Video Call PhoneC->PhoneA and accept as voice.")
+        if not video_call_setup_teardown(
+                self.log, ads[2], ads[0], None,
+                video_state=VT_STATE_AUDIO_ONLY,
+                verify_caller_func=is_phone_in_call_voice_hd,
+                verify_callee_func=is_phone_in_call_voice_hd):
+            self.log.error("Failed to setup a call")
+            return False
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_ab_id:
+                call_ac_id = call
+
+        self.log.info("Step3: Verify calls in correct state.")
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_ab_id,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_ac_id,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        return self._test_vt_conference_merge_drop(ads, call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_add_mo_voice_swap_downgrade_merge_drop(self):
+        """Conference call
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Make Sure PhoneC is in LTE mode (with VoLTE).
+        PhoneA add a Bi-Directional Video call to PhoneB.
+        PhoneB accept as Video.
+        PhoneA VoLTE call to PhoneC. Accept on PhoneC.
+        Swap Active call on PhoneA.
+        Downgrade Video call on PhoneA and PhoneB to audio only.
+        Merge call on PhoneA.
+        Hang up on PhoneB.
+        Hang up on PhoneC.
+        """
+        # This test case is not supported by VZW.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneA->PhoneB.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video_ab = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video_ab is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Voice Call PhoneA->PhoneC.")
+        if not call_setup_teardown(
+                self.log, ads[0], ads[2], None,
+                verify_caller_func=None,
+                verify_callee_func=is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video_ab:
+                call_id_voice_ac = call
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ac,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        self.log.info("Step4: Swap calls on PhoneA and verify call state correct.")
+        ads[0].droid.telecomCallHold(call_id_voice_ac)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        for ad in [ads[0], ads[1]]:
+            self.log.info("{} audio: {}".
+                format(ad.serial, get_audio_route(self.log, ad)))
+            set_audio_route(self.log, ad, AUDIO_ROUTE_EARPIECE)
+
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ac,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            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)
+            ):
+            self.log.error("Failed to disable video on PhoneA.")
+            return False
+        if not video_call_downgrade(
+            self.log,
+            ads[1], get_call_id_in_video_state(self.log, ads[1], VT_STATE_TX_ENABLED),
+            ads[0], call_id_video_ab
+            ):
+            self.log.error("Failed to disable video on PhoneB.")
+            return False
+
+        self.log.info("Step6: Verify calls in correct state.")
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ac,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        return self._test_vt_conference_merge_drop(
+            ads, call_id_video_ab, call_id_voice_ac)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_add_mt_voice_swap_downgrade_merge_drop(self):
+        """Conference call
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with Video Calling).
+        Make Sure PhoneC is in LTE mode (with VoLTE).
+        PhoneA add a Bi-Directional Video call to PhoneB.
+        PhoneB accept as Video.
+        PhoneC VoLTE call to PhoneA. Accept on PhoneA.
+        Swap Active call on PhoneA.
+        Downgrade Video call on PhoneA and PhoneB to audio only.
+        Merge call on PhoneA.
+        Hang up on PhoneB.
+        Hang up on PhoneC.
+        """
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate Video Call PhoneA->PhoneB.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video_ab = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video_ab is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step2: Initiate Voice Call PhoneC->PhoneA.")
+        if not call_setup_teardown(
+                self.log, ads[2], ads[0], None,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=None):
+            self.log.error("Failed to setup a call")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+        for call in calls:
+            if call != call_id_video_ab:
+                call_id_voice_ac = call
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_BIDIRECTIONAL_PAUSED,
+                                                   CALL_STATE_HOLDING):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ac,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+
+        self.log.info("Step4: Swap calls on PhoneA and verify call state correct.")
+        ads[0].droid.telecomCallHold(call_id_voice_ac)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        for ad in [ads[0], ads[1]]:
+            if get_audio_route(self.log, ad) != AUDIO_ROUTE_SPEAKER:
+                self.log.error("{} Audio is not on speaker.".format(ad.serial))
+                # TODO: Define expected behavior. Return false or not.
+            set_audio_route(self.log, ad, AUDIO_ROUTE_EARPIECE)
+
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ac,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            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)
+            ):
+            self.log.error("Failed to disable video on PhoneA.")
+            return False
+        if not video_call_downgrade(
+            self.log,
+            ads[1], get_call_id_in_video_state(self.log, ads[1], VT_STATE_TX_ENABLED),
+            ads[0], call_id_video_ab
+            ):
+            self.log.error("Failed to disable video on PhoneB.")
+            return False
+
+        self.log.info("Step6: Verify calls in correct state.")
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ab,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ac,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        return self._test_vt_conference_merge_drop(
+            ads, call_id_video_ab, call_id_voice_ac)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_add_mo_video_downgrade_merge_drop(self):
+        """Conference call
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with VoLTE).
+        Make Sure PhoneC is in LTE mode (with Video Calling).
+        PhoneA VoLTE call to PhoneB. Accept on PhoneB.
+        PhoneA add a Bi-Directional Video call to PhoneC.
+        PhoneC accept as Video.
+        Downgrade Video call on PhoneA and PhoneC to audio only.
+        Merge call on PhoneA.
+        Hang up on PhoneB.
+        Hang up on PhoneC.
+        """
+        # This test case is not supported by VZW.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_volte, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate VoLTE Call PhoneA->PhoneB.")
+        if not call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            self.log.error("Active call numbers in PhoneA is not 1.")
+            return False
+        call_id_voice_ab = calls[0]
+
+        self.log.info("Step2: Initiate Video Call PhoneA->PhoneC.")
+        if not video_call_setup_teardown(
+                self.log, ads[0], ads[2], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video_ac = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video_ac is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ac,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ab,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            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)
+            ):
+            self.log.error("Failed to disable video on PhoneA.")
+            return False
+        if not video_call_downgrade(
+            self.log,
+            ads[2], get_call_id_in_video_state(self.log, ads[2], VT_STATE_TX_ENABLED),
+            ads[0], call_id_video_ac
+            ):
+            self.log.error("Failed to disable video on PhoneB.")
+            return False
+
+        self.log.info("Step6: Verify calls in correct state.")
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ac,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ab,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        return self._test_vt_conference_merge_drop(
+            ads, call_id_video_ac, call_id_voice_ab)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_add_mt_video_downgrade_merge_drop(self):
+        """Conference call
+
+        Make Sure PhoneA is in LTE mode (with Video Calling).
+        Make Sure PhoneB is in LTE mode (with VoLTE).
+        Make Sure PhoneC is in LTE mode (with Video Calling).
+        PhoneA VoLTE call to PhoneB. Accept on PhoneB.
+        PhoneC add a Bi-Directional Video call to PhoneA.
+        PhoneA accept as Video.
+        Downgrade Video call on PhoneA and PhoneC to audio only.
+        Merge call on PhoneA.
+        Hang up on PhoneB.
+        Hang up on PhoneC.
+        """
+        # TODO (b/21437650):
+        # Test will fail. After established 2nd call ~15s, Phone C will drop call.
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_volte, (self.log, ads[1])),
+                 (phone_setup_video, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        self.log.info("Step1: Initiate VoLTE Call PhoneA->PhoneB.")
+        if not call_setup_teardown(
+                self.log, ads[0], ads[1], None,
+                verify_caller_func=is_phone_in_call_volte,
+                verify_callee_func=is_phone_in_call_volte):
+            self.log.error("Failed to setup a call")
+            return False
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            self.log.error("Active call numbers in PhoneA is not 1.")
+            return False
+        call_id_voice_ab = calls[0]
+
+        self.log.info("Step2: Initiate Video Call PhoneC->PhoneA.")
+        if not video_call_setup_teardown(
+                self.log, ads[2], ads[0], None,
+                video_state=VT_STATE_BIDIRECTIONAL,
+                verify_caller_func=is_phone_in_call_video_bidirectional,
+                verify_callee_func=is_phone_in_call_video_bidirectional):
+            self.log.error("Failed to setup a call")
+            return False
+        call_id_video_ac = get_call_id_in_video_state(self.log, ads[0], VT_STATE_BIDIRECTIONAL)
+        if call_id_video_ac is None:
+            self.log.error("No active video call in PhoneA.")
+            return False
+
+        self.log.info("Step3: Verify PhoneA's video/voice call in correct state.")
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Active call numbers in PhoneA is not 2.")
+            return False
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ac,
+                                                   VT_STATE_BIDIRECTIONAL,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ab,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            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)
+            ):
+            self.log.error("Failed to disable video on PhoneA.")
+            return False
+        if not video_call_downgrade(
+            self.log,
+            ads[2], get_call_id_in_video_state(self.log, ads[2], VT_STATE_TX_ENABLED),
+            ads[0], call_id_video_ac
+            ):
+            self.log.error("Failed to disable video on PhoneB.")
+            return False
+
+        self.log.info("Step6: Verify calls in correct state.")
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_video_ac,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_ACTIVE):
+            return False
+        if not verify_video_call_in_expected_state(self.log, ads[0],
+                                                   call_id_voice_ab,
+                                                   VT_STATE_AUDIO_ONLY,
+                                                   CALL_STATE_HOLDING):
+            return False
+
+        return self._test_vt_conference_merge_drop(
+            ads, call_id_video_ac, call_id_voice_ab)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_disable_data_vt_unavailable(self):
+        """Disable Data, phone should no be able to make VT call.
+
+        Make sure PhoneA and PhoneB can make VT call.
+        Disable Data on PhoneA.
+        Make sure phoneA report vt_enabled as false.
+        Attempt to make a VT call from PhoneA to PhoneB,
+        Verify the call succeed as Voice call.
+        """
+        # TODO: Currently this test will fail at step3.
+        # Reason is b/21473800.
+
+        self.log.info("Step1 Make sure Phones are able make VT call")
+        ads = self.android_devices
+        ads[0], ads[1] = ads[1], ads[0]
+        tasks = [(phone_setup_video, (self.log, ads[0])),
+                 (phone_setup_video, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        try:
+            self.log.info("Step2 Turn off data and verify not connected.")
+            ads[0].droid.toggleDataConnection(False)
+            if verify_http_connection(self.log, ads[0]):
+                self.log.error("Internet Accessible when Disabled")
+                return False
+
+            self.log.info("Step3 Verify vt_enabled return false.")
+            if wait_for_video_enabled(
+                    self.log, ads[0], WAIT_TIME_VOLTE_ENABLED):
+                self.log.error("{} failed to <report vt enabled false> for {}s."
+                               .format(ads[0].serial, WAIT_TIME_VOLTE_ENABLED))
+                return False
+            self.log.info(
+                "Step4 Attempt to make VT call, verify call is AUDIO_ONLY.")
+            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_voice_hd,
+                    verify_callee_func=is_phone_in_call_voice_hd):
+                self.log.error("Call failed or is not AUDIO_ONLY")
+                return False
+
+        finally:
+            ads[0].droid.toggleDataConnection(True)
+
+        return True
+""" Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveVoiceConfTest.py b/acts/tests/google/tel/live/TelLiveVoiceConfTest.py
new file mode 100644
index 0000000..666b38d
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveVoiceConfTest.py
@@ -0,0 +1,10098 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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 Live Network Telephony Conference Call
+"""
+
+import time
+from acts.base_test import BaseTestClass
+from queue import Empty
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.utils import load_config
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelLiveVoiceConfTest(TelephonyBaseTest):
+
+    # Note: Currently Conference Call do not verify voice.
+    # So even if test cases passed, does not necessarily means
+    # conference call functionality is working.
+    # Need to add code to check for voice.
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      # GSM
+                      "test_gsm_mo_mo_add_merge_drop",
+                      "test_gsm_mt_mt_add_merge_drop"
+
+                      # 1x conference
+                      "test_1x_mo_mo_add_merge_drop_from_participant",
+                      "test_1x_mo_mo_add_merge_drop_from_host",
+                      # 1x multi call
+                      "test_1x_mo_mt_add_drop_active",
+                      "test_1x_mo_mt_add_drop_held",
+                      "test_1x_mo_mt_add_drop_on_dut",
+                      "test_1x_mt_mt_add_drop_active",
+                      "test_1x_mt_mt_add_drop_held",
+                      "test_1x_mt_mt_add_drop_on_dut",
+                      "test_1x_mo_mt_add_swap_twice_drop_active",
+                      "test_1x_mo_mt_add_swap_twice_drop_held",
+                      "test_1x_mo_mt_add_swap_twice_drop_on_dut",
+                      "test_1x_mt_mt_add_swap_twice_drop_active",
+                      "test_1x_mt_mt_add_swap_twice_drop_held",
+                      "test_1x_mt_mt_add_swap_twice_drop_on_dut",
+                      "test_1x_mo_mt_add_swap_once_drop_active",
+                      "test_1x_mo_mt_add_swap_once_drop_held",
+                      "test_1x_mo_mt_add_swap_once_drop_on_dut",
+                      "test_1x_mt_mt_add_swap_once_drop_active",
+                      "test_1x_mt_mt_add_swap_once_drop_held",
+                      "test_1x_mt_mt_add_swap_once_drop_on_dut",
+
+                      # WCDMA
+                      "test_wcdma_mo_mo_add_merge_drop",
+                      "test_wcdma_mt_mt_add_merge_drop",
+
+                      "test_wcdma_mo_mo_add_swap_twice_drop_held",
+                      "test_wcdma_mo_mo_add_swap_twice_drop_active",
+                      "test_wcdma_mo_mt_add_swap_twice_drop_held",
+                      "test_wcdma_mo_mt_add_swap_twice_drop_active",
+                      "test_wcdma_mo_mo_add_swap_once_drop_held",
+                      "test_wcdma_mo_mo_add_swap_once_drop_active",
+                      "test_wcdma_mo_mt_add_swap_once_drop_held",
+                      "test_wcdma_mo_mt_add_swap_once_drop_active",
+
+                      "test_wcdma_mo_mo_add_swap_once_merge_drop",
+                      "test_wcdma_mo_mo_add_swap_twice_merge_drop",
+                      "test_wcdma_mo_mt_add_swap_once_merge_drop",
+                      "test_wcdma_mo_mt_add_swap_twice_merge_drop",
+                      "test_wcdma_mt_mt_add_swap_once_merge_drop",
+                      "test_wcdma_mt_mt_add_swap_twice_merge_drop",
+                      "test_wcdma_mt_mt_add_merge_unmerge_swap_drop",
+
+                      # CSFB WCDMA
+                      "test_csfb_wcdma_mo_mo_add_swap_twice_drop_held",
+                      "test_csfb_wcdma_mo_mo_add_swap_twice_drop_active",
+                      "test_csfb_wcdma_mo_mt_add_swap_twice_drop_held",
+                      "test_csfb_wcdma_mo_mt_add_swap_twice_drop_active",
+                      "test_csfb_wcdma_mo_mo_add_swap_once_drop_held",
+                      "test_csfb_wcdma_mo_mo_add_swap_once_drop_active",
+                      "test_csfb_wcdma_mo_mt_add_swap_once_drop_held",
+                      "test_csfb_wcdma_mo_mt_add_swap_once_drop_active",
+
+                      "test_csfb_wcdma_mo_mo_add_swap_once_merge_drop",
+                      "test_csfb_wcdma_mo_mo_add_swap_twice_merge_drop",
+                      "test_csfb_wcdma_mo_mt_add_swap_once_merge_drop",
+                      "test_csfb_wcdma_mo_mt_add_swap_twice_merge_drop",
+
+                      # VoLTE
+                      "test_volte_mo_mo_add_volte_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mt_add_volte_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mt_mt_add_volte_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mo_add_wcdma_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mt_add_wcdma_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mt_mt_add_wcdma_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mo_add_1x_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mt_add_1x_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mt_mt_add_1x_merge_drop_second_call_from_participant_no_cep",
+
+                      "test_volte_mo_mo_add_volte_swap_twice_drop_held",
+                      "test_volte_mo_mo_add_volte_swap_twice_drop_active",
+                      "test_volte_mo_mt_add_volte_swap_twice_drop_held",
+                      "test_volte_mo_mt_add_volte_swap_twice_drop_active",
+                      "test_volte_mo_mo_add_volte_swap_once_drop_held",
+                      "test_volte_mo_mo_add_volte_swap_once_drop_active",
+                      "test_volte_mo_mt_add_volte_swap_once_drop_held",
+                      "test_volte_mo_mt_add_volte_swap_once_drop_active",
+
+                      "test_volte_mo_mo_add_volte_swap_twice_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mt_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mo_add_volte_swap_once_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mt_add_volte_swap_once_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mt_mt_add_volte_swap_once_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mo_add_wcdma_swap_once_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mt_add_wcdma_swap_once_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mt_mt_add_wcdma_swap_once_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mo_add_1x_swap_once_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mo_add_1x_swap_twice_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mt_add_1x_swap_once_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mo_mt_add_1x_swap_twice_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mt_mt_add_1x_swap_once_merge_drop_second_call_from_participant_no_cep",
+                      "test_volte_mt_mt_add_1x_swap_twice_merge_drop_second_call_from_participant_no_cep",
+
+                      # VoLTE CEP
+                      "test_volte_mo_mo_add_volte_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mo_add_volte_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mo_add_volte_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mo_add_volte_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mt_add_volte_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mt_add_volte_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mt_add_volte_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mt_add_volte_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mt_mt_add_volte_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mt_mt_add_volte_merge_drop_second_call_from_host_cep",
+                      "test_volte_mt_mt_add_volte_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mt_mt_add_volte_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mo_add_wcdma_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mo_add_wcdma_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mo_add_wcdma_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mo_add_wcdma_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mt_add_wcdma_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mt_add_wcdma_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mt_add_wcdma_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mt_add_wcdma_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mt_mt_add_wcdma_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mt_mt_add_wcdma_merge_drop_second_call_from_host_cep",
+                      "test_volte_mt_mt_add_wcdma_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mt_mt_add_wcdma_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mo_add_1x_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mo_add_1x_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mo_add_1x_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mo_add_1x_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mt_add_1x_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mt_add_1x_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mt_add_1x_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mt_add_1x_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mt_mt_add_1x_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mt_mt_add_1x_merge_drop_second_call_from_host_cep",
+                      "test_volte_mt_mt_add_1x_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mt_mt_add_1x_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mo_add_volte_swap_once_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mo_add_volte_swap_once_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mo_add_volte_swap_once_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mo_add_volte_swap_once_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mo_add_volte_swap_twice_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mo_add_volte_swap_twice_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mo_add_volte_swap_twice_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mo_add_volte_swap_twice_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mt_add_volte_swap_once_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mt_add_volte_swap_once_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mt_add_volte_swap_once_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mt_add_volte_swap_once_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mt_add_volte_swap_twice_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mt_add_volte_swap_twice_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mt_add_volte_swap_twice_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mt_mt_add_volte_swap_once_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mt_mt_add_volte_swap_once_merge_drop_second_call_from_host_cep",
+                      "test_volte_mt_mt_add_volte_swap_once_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mt_mt_add_volte_swap_once_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mt_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mt_mt_add_volte_swap_twice_merge_drop_second_call_from_host_cep",
+                      "test_volte_mt_mt_add_volte_swap_twice_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mt_mt_add_volte_swap_twice_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mo_add_wcdma_swap_once_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mo_add_wcdma_swap_once_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mo_add_wcdma_swap_once_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mo_add_wcdma_swap_once_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mt_add_wcdma_swap_once_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mt_add_wcdma_swap_once_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mt_add_wcdma_swap_once_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mt_add_wcdma_swap_once_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mt_mt_add_wcdma_swap_once_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mt_mt_add_wcdma_swap_once_merge_drop_second_call_from_host_cep",
+                      "test_volte_mt_mt_add_wcdma_swap_once_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mt_mt_add_wcdma_swap_once_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_second_call_from_host_cep",
+                      "test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mo_add_1x_swap_once_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mo_add_1x_swap_once_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mo_add_1x_swap_once_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mo_add_1x_swap_once_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mo_add_1x_swap_twice_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mo_add_1x_swap_twice_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mo_add_1x_swap_twice_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mo_add_1x_swap_twice_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mt_add_1x_swap_once_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mt_add_1x_swap_once_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mt_add_1x_swap_once_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mt_add_1x_swap_once_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mo_mt_add_1x_swap_twice_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mo_mt_add_1x_swap_twice_merge_drop_second_call_from_host_cep",
+                      "test_volte_mo_mt_add_1x_swap_twice_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mo_mt_add_1x_swap_twice_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mt_mt_add_1x_swap_once_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mt_mt_add_1x_swap_once_merge_drop_second_call_from_host_cep",
+                      "test_volte_mt_mt_add_1x_swap_once_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mt_mt_add_1x_swap_once_merge_drop_first_call_from_host_cep",
+
+                      "test_volte_mt_mt_add_1x_swap_twice_merge_drop_second_call_from_participant_cep",
+                      "test_volte_mt_mt_add_1x_swap_twice_merge_drop_second_call_from_host_cep",
+                      "test_volte_mt_mt_add_1x_swap_twice_merge_drop_first_call_from_participant_cep",
+                      "test_volte_mt_mt_add_1x_swap_twice_merge_drop_first_call_from_host_cep",
+
+                      # WiFi Calling Conference
+                      # WiFi_Only mode is not supported for now.
+                      # epdg, WFC, noAPM, WiFi Only, cell strong, wifi strong
+                      "test_epdg_mo_mo_add_epdg_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_epdg_merge_drop_wfc_wifi_only",
+                      "test_epdg_mt_mt_add_epdg_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_volte_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_volte_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_wcdma_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_wcdma_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_1x_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_1x_merge_drop_wfc_wifi_only",
+
+                      "test_epdg_mo_mo_add_epdg_swap_twice_drop_held_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_epdg_swap_twice_drop_active_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_epdg_swap_twice_drop_held_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_epdg_swap_twice_drop_active_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_epdg_swap_once_drop_held_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_epdg_swap_once_drop_active_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_epdg_swap_once_drop_held_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_epdg_swap_once_drop_active_wfc_wifi_only",
+
+                      "test_epdg_mo_mo_add_epdg_swap_twice_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_epdg_swap_twice_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_epdg_swap_once_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_epdg_swap_once_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_volte_swap_twice_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_volte_swap_twice_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_volte_swap_once_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_volte_swap_once_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_wcdma_swap_twice_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_wcdma_swap_twice_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_wcdma_swap_once_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_wcdma_swap_once_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_1x_swap_twice_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_1x_swap_twice_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mo_add_1x_swap_once_merge_drop_wfc_wifi_only",
+                      "test_epdg_mo_mt_add_1x_swap_once_merge_drop_wfc_wifi_only",
+
+                      # epdg, WFC, noAPM, WiFi preferred, cell strong, wifi strong
+                      "test_epdg_mo_mo_add_epdg_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_epdg_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mt_mt_add_epdg_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_volte_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_wcdma_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_wcdma_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_1x_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_1x_merge_drop_wfc_wifi_preferred",
+
+                      "test_epdg_mo_mo_add_epdg_swap_twice_drop_held_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_epdg_swap_twice_drop_active_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_epdg_swap_twice_drop_held_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_epdg_swap_twice_drop_active_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_epdg_swap_once_drop_held_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_epdg_swap_once_drop_active_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_epdg_swap_once_drop_held_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_epdg_swap_once_drop_active_wfc_wifi_preferred",
+
+                      "test_epdg_mo_mo_add_epdg_swap_twice_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_epdg_swap_twice_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_epdg_swap_once_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_epdg_swap_once_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_volte_swap_twice_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_volte_swap_once_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_wcdma_swap_twice_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_wcdma_swap_twice_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_wcdma_swap_once_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_wcdma_swap_once_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_1x_swap_twice_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_1x_swap_twice_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mo_add_1x_swap_once_merge_drop_wfc_wifi_preferred",
+                      "test_epdg_mo_mt_add_1x_swap_once_merge_drop_wfc_wifi_preferred",
+
+                      # WiFi Calling Multi Call Swap Only
+                      "test_epdg_mo_mo_add_epdg_swap_twice_drop_active_apm_wifi_preferred",
+                      "test_epdg_mo_mt_add_epdg_swap_once_drop_held_apm_wifi_preferred",
+                      "test_epdg_mo_mo_add_epdg_swap_once_drop_active_apm_wfc_wifi_preferred",
+
+                      # WiFi Calling Conference No_CEP
+                      # Airplane Mode, WiFi Preferred
+                      "test_epdg_mo_mo_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_no_cep",
+                      "test_epdg_mo_mt_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_no_cep",
+                      "test_epdg_mt_mt_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_no_cep",
+
+                      # WiFi Calling Multi Call Swap + Conference No_CEP
+                      # Airplane Mode, WiFi Preferred
+                      "test_epdg_mo_mt_add_epdg_swap_once_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_no_cep",
+
+                      # WiFi Calling Conference CEP
+                      # Airplane Mode, WiFi Preferred
+                      "test_epdg_mo_mo_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mo_mo_add_epdg_merge_drop_second_call_from_host_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mo_mo_add_epdg_merge_drop_first_call_from_participant_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mo_mo_add_epdg_merge_drop_first_call_from_host_wfc_apm_wifi_preferred_cep",
+
+                      "test_epdg_mo_mt_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mo_mt_add_epdg_merge_drop_second_call_from_host_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mo_mt_add_epdg_merge_drop_first_call_from_participant_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mo_mt_add_epdg_merge_drop_first_call_from_host_wfc_apm_wifi_preferred_cep",
+
+                      "test_epdg_mt_mt_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mt_mt_add_epdg_merge_drop_second_call_from_host_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mt_mt_add_epdg_merge_drop_first_call_from_participant_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mt_mt_add_epdg_merge_drop_first_call_from_host_wfc_apm_wifi_preferred_cep",
+
+                      # WiFi Calling Multi Call Swap + Conference CEP
+                      # Airplane Mode, WiFi Preferred
+                      "test_epdg_mo_mt_add_epdg_swap_once_merge_drop_second_call_from_host_wfc_apm_wifi_preferred_cep",
+                      "test_epdg_mo_mt_add_epdg_swap_once_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_cep",
+
+                      #SIM2 test cases
+                      "test_wcdma_mo_mo_add_merge_drop_sim2",
+                      "test_wcdma_mt_mt_add_merge_drop_sim2",
+                      "test_gsm_mo_mo_add_merge_drop_sim2",
+                      "test_gsm_mt_mt_add_merge_drop_sim2"
+                      )
+
+        self.simconf = load_config(self.user_params["sim_conf_file"])
+        self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+
+        try:
+            self.wifi_network_pass = self.user_params["wifi_network_pass"]
+        except KeyError:
+            self.wifi_network_pass = None
+
+    """ Private Test Utils """
+    def _three_phone_call_mo_add_mo(self, ads, phone_setups, verify_funcs):
+        """Use 3 phones to make MO calls.
+
+        Call from PhoneA to PhoneB, accept on PhoneB.
+        Call from PhoneA to PhoneC, accept on PhoneC.
+
+        Args:
+            ads: list of ad object.
+                The list should have three objects.
+            phone_setups: list of phone setup functions.
+                The list should have three objects.
+            verify_funcs: list of phone call verify functions.
+                The list should have three objects.
+
+        Returns:
+            If success, return 'call_AB' id in PhoneA.
+            if fail, return None.
+        """
+        class _CallException(Exception):
+            pass
+
+        try:
+            verify_func_a, verify_func_b, verify_func_c = verify_funcs
+            tasks = []
+            for ad, setup_func in zip(ads, phone_setups):
+                if setup_func is not None:
+                    tasks.append((setup_func, (self.log, ad)))
+            if tasks != [] and not multithread_func(self.log, tasks):
+                self.log.error("Phone Failed to Set Up Properly.")
+                raise _CallException("Setup failed.")
+            for ad in ads:
+                ad.droid.telecomCallClearCallList()
+                if num_active_calls(self.log, ad) != 0:
+                    self.log.error("Phone {} Call List is not empty."
+                                   .format(ad.serial))
+                    raise _CallException("Clear call list failed.")
+
+            self.log.info("Step1: Call From PhoneA to PhoneB.")
+            if not call_setup_teardown(self.log, ads[0], ads[1],
+                                       ad_hangup=None,
+                                       verify_caller_func=verify_func_a,
+                                       verify_callee_func=verify_func_b):
+                raise _CallException("PhoneA call PhoneB failed.")
+
+            calls = ads[0].droid.telecomCallGetCallIds()
+            self.log.info("Calls in PhoneA{}".format(calls))
+            if num_active_calls(self.log, ads[0]) != 1:
+                raise _CallException("Call list verify failed.")
+            call_ab_id = calls[0]
+
+            self.log.info("Step2: Call From PhoneA to PhoneC.")
+            if not call_setup_teardown(self.log, ads[0], ads[2],
+                                       ad_hangup=None,
+                                       verify_caller_func=verify_func_a,
+                                       verify_callee_func=verify_func_c):
+                raise _CallException("PhoneA call PhoneC failed.")
+            if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+                raise _CallException("Not All phones are in-call.")
+
+        except _CallException:
+            return None
+
+        return call_ab_id
+
+    def _three_phone_call_mo_add_mt(self, ads, phone_setups, verify_funcs):
+        """Use 3 phones to make MO call and MT call.
+
+        Call from PhoneA to PhoneB, accept on PhoneB.
+        Call from PhoneC to PhoneA, accept on PhoneA.
+
+        Args:
+            ads: list of ad object.
+                The list should have three objects.
+            phone_setups: list of phone setup functions.
+                The list should have three objects.
+            verify_funcs: list of phone call verify functions.
+                The list should have three objects.
+
+        Returns:
+            If success, return 'call_AB' id in PhoneA.
+            if fail, return None.
+        """
+        class _CallException(Exception):
+            pass
+
+        try:
+            verify_func_a, verify_func_b, verify_func_c = verify_funcs
+            tasks = []
+            for ad, setup_func in zip(ads, phone_setups):
+                if setup_func is not None:
+                    tasks.append((setup_func, (self.log, ad)))
+            if tasks != [] and not multithread_func(self.log, tasks):
+                self.log.error("Phone Failed to Set Up Properly.")
+                raise _CallException("Setup failed.")
+            for ad in ads:
+                ad.droid.telecomCallClearCallList()
+                if num_active_calls(self.log, ad) != 0:
+                    self.log.error("Phone {} Call List is not empty."
+                                   .format(ad.serial))
+                    raise _CallException("Clear call list failed.")
+
+            self.log.info("Step1: Call From PhoneA to PhoneB.")
+            if not call_setup_teardown(self.log, ads[0], ads[1],
+                                       ad_hangup=None,
+                                       verify_caller_func=verify_func_a,
+                                       verify_callee_func=verify_func_b):
+                raise _CallException("PhoneA call PhoneB failed.")
+
+            calls = ads[0].droid.telecomCallGetCallIds()
+            self.log.info("Calls in PhoneA{}".format(calls))
+            if num_active_calls(self.log, ads[0]) != 1:
+                raise _CallException("Call list verify failed.")
+            call_ab_id = calls[0]
+
+            self.log.info("Step2: Call From PhoneC to PhoneA.")
+            if not call_setup_teardown(self.log, ads[2], ads[0],
+                                       ad_hangup=None,
+                                       verify_caller_func=verify_func_c,
+                                       verify_callee_func=verify_func_a):
+                raise _CallException("PhoneA call PhoneC failed.")
+            if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+                raise _CallException("Not All phones are in-call.")
+
+        except _CallException:
+            return None
+
+        return call_ab_id
+
+    def _three_phone_call_mt_add_mt(self, ads, phone_setups, verify_funcs):
+        """Use 3 phones to make MT call and MT call.
+
+        Call from PhoneB to PhoneA, accept on PhoneA.
+        Call from PhoneC to PhoneA, accept on PhoneA.
+
+        Args:
+            ads: list of ad object.
+                The list should have three objects.
+            phone_setups: list of phone setup functions.
+                The list should have three objects.
+            verify_funcs: list of phone call verify functions.
+                The list should have three objects.
+
+        Returns:
+            If success, return 'call_AB' id in PhoneA.
+            if fail, return None.
+        """
+        class _CallException(Exception):
+            pass
+
+        try:
+            verify_func_a, verify_func_b, verify_func_c = verify_funcs
+            tasks = []
+            for ad, setup_func in zip(ads, phone_setups):
+                if setup_func is not None:
+                    tasks.append((setup_func, (self.log, ad)))
+            if tasks != [] and not multithread_func(self.log, tasks):
+                self.log.error("Phone Failed to Set Up Properly.")
+                raise _CallException("Setup failed.")
+            for ad in ads:
+                ad.droid.telecomCallClearCallList()
+                if num_active_calls(self.log, ad) != 0:
+                    self.log.error("Phone {} Call List is not empty."
+                                   .format(ad.serial))
+                    raise _CallException("Clear call list failed.")
+
+            self.log.info("Step1: Call From PhoneB to PhoneA.")
+            if not call_setup_teardown(self.log, ads[1], ads[0],
+                                       ad_hangup=None,
+                                       verify_caller_func=verify_func_b,
+                                       verify_callee_func=verify_func_a):
+                raise _CallException("PhoneB call PhoneA failed.")
+
+            calls = ads[0].droid.telecomCallGetCallIds()
+            self.log.info("Calls in PhoneA{}".format(calls))
+            if num_active_calls(self.log, ads[0]) != 1:
+                raise _CallException("Call list verify failed.")
+            call_ab_id = calls[0]
+
+            self.log.info("Step2: Call From PhoneC to PhoneA.")
+            if not call_setup_teardown(self.log, ads[2], ads[0],
+                                       ad_hangup=None,
+                                       verify_caller_func=verify_func_c,
+                                       verify_callee_func=verify_func_a):
+                raise _CallException("PhoneA call PhoneC failed.")
+            if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+                raise _CallException("Not All phones are in-call.")
+
+        except _CallException:
+            return None
+
+        return call_ab_id
+
+    def _test_1x_mo_mo_add(self):
+        """Test multi call feature in 1x call.
+
+        PhoneA (1x) call PhoneB, accept on PhoneB.
+        PhoneA (1x) call PhoneC, accept on PhoneC.
+
+        Returns:
+            call_ab_id, call_ac_id, call_conf_id if succeed;
+            None, None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is CDMA phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("{} not CDMA phone, abort this 1x test.".
+                            format(ads[0].serial))
+            return None, None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [phone_setup_3g, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_1x, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 3:
+            return None, None, None
+        for call_id in calls:
+            if (CALL_CAPABILITY_MERGE_CONFERENCE in
+                ads[0].droid.telecomCallGetCapabilities(call_id)):
+                call_conf_id = call_id;
+            elif call_id != call_ab_id:
+                call_ac_id = call_id;
+
+        return call_ab_id, call_ac_id, call_conf_id
+
+    def _test_1x_mo_mt_add_swap_x(self, num_swaps):
+        """Test multi call feature in 1x call.
+
+        PhoneA (1x) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (1x), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Returns:
+            call_ab_id, call_ac_id, call_conf_id if succeed;
+            None, None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is CDMA phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("{} not CDMA phone, abort this 1x test.".
+                            format(ads[0].serial))
+            return None, None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_3g, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_1x, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None, None
+
+        call_conf_id  = None
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 3:
+            return None, None, None
+        for call_id in calls:
+            if (CALL_CAPABILITY_SWAP_CONFERENCE in
+                ads[0].droid.telecomCallGetCapabilities(call_id)):
+                call_conf_id = call_id;
+            elif call_id != call_ab_id:
+                call_ac_id = call_id;
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps,
+                check_call_status=False):
+                self.log.error("Swap test failed.")
+                return None, None, None
+
+        return call_ab_id, call_ac_id, call_conf_id
+
+    def _test_1x_mt_mt_add_swap_x(self, num_swaps):
+        """Test multi call feature in 1x call.
+
+        PhoneB call PhoneA (1x), accept on PhoneA.
+        PhoneC call PhoneA (1x), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Returns:
+            call_ab_id, call_ac_id, call_conf_id if succeed;
+            None, None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is CDMA phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("{} not CDMA phone, abort this 1x test.".
+                            format(ads[0].serial))
+            return None, None, None
+
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_3g, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_1x, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None, None
+
+        call_conf_id  = None
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 3:
+            return None, None, None
+        for call_id in calls:
+            if (CALL_CAPABILITY_SWAP_CONFERENCE in
+                ads[0].droid.telecomCallGetCapabilities(call_id)):
+                call_conf_id = call_id;
+            elif call_id != call_ab_id:
+                call_ac_id = call_id;
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps,
+                check_call_status=False):
+                self.log.error("Swap test failed.")
+                return None, None, None
+
+        return call_ab_id, call_ac_id, call_conf_id
+
+    def _test_1x_multi_call_drop_from_participant(self, host,
+        first_drop_ad, second_drop_ad):
+        """Test private function to drop call from participant in 1x multi call.
+
+        Host(1x) is in multi call scenario with first_drop_ad and second_drop_ad.
+        Drop call on first_drop_ad.
+        Verify call continues between host and second_drop_ad.
+        Drop call on second_drop_ad and verify host also ends.
+
+        Args:
+            host: android device object for multi-call/conference-call host.
+            first_drop_ad: android device object for call participant, end call
+                on this participant first.
+            second_drop_ad: android device object for call participant, end call
+                on this participant second.
+
+        Returns:
+            True if no error happened. Otherwise False.
+        """
+        self.log.info("Drop 1st call.")
+        first_drop_ad.droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = host.droid.telecomCallGetCallIds()
+        self.log.info("Calls in Host{}".format(calls))
+        if num_active_calls(self.log, host) != 3:
+            return False
+        if not verify_incall_state(self.log, [host, second_drop_ad], True):
+            return False
+        if not verify_incall_state(self.log, [first_drop_ad], False):
+            return False
+
+        self.log.info("Drop 2nd call.")
+        second_drop_ad.droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log,
+                                   [host, second_drop_ad, first_drop_ad],
+                                   False):
+            return False
+        return True
+
+    def _test_1x_multi_call_drop_from_host(self, host,
+        active_participant_ad, held_participant_ad):
+        """Test private function to drop call from host in 1x multi call.
+
+        Host(1x) is in multi call scenario with first_drop_ad and second_drop_ad.
+        Drop call on host. Then active_participant_ad should ends as well.
+        Host should receive a call back from held_participant_ad. Answer on host.
+        Drop call on host. Then verify held_participant_ad ends as well.
+
+        Args:
+            host: android device object for multi-call/conference-call host.
+            active_participant_ad: android device object for the current active
+                call participant.
+            held_participant_ad: android device object for the current held
+                call participant.
+
+        Returns:
+            True if no error happened. Otherwise False.
+        """
+        self.log.info("Drop current call on DUT.")
+        host.droid.telecomEndCall()
+        if not wait_and_answer_call(self.log, host,
+            get_phone_number(self.log, held_participant_ad)):
+            self.log.error("Did not receive call back.")
+            return False
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [host, held_participant_ad], True):
+            return False
+        if not verify_incall_state(self.log, [active_participant_ad], False):
+            return False
+
+        self.log.info("Drop current call on DUT.")
+        host.droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log,
+            [host, held_participant_ad, active_participant_ad], False):
+            return False
+        return True
+
+    def _test_1x_conf_call_drop_from_host(self, host, participant_list):
+        """Test private function to drop call from host in 1x conference call.
+
+        Host(1x) is in conference call scenario with phones in participant_list.
+        End call on host. Then all phones in participant_list should end call.
+
+        Args:
+            host: android device object for multi-call/conference-call host.
+            participant_list: android device objects list for all other
+                participants in multi-call/conference-call.
+
+        Returns:
+            True if no error happened. Otherwise False.
+        """
+        self.log.info("Drop conference call on host.")
+        host.droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [host], False):
+            return False
+        if not verify_incall_state(self.log, participant_list, False):
+            return False
+        return True
+
+    def _test_1x_merge_conference(self, host, participant_list, call_conf_id):
+        """Test private function to merge to conference in 1x multi call scenario.
+
+        Host(1x) is in multi call scenario with phones in participant_list.
+        Merge to conference on host.
+        Verify merge succeed.
+
+        Args:
+            host: android device object for multi-call/conference-call host.
+            participant_list: android device objects list for all other
+                participants in multi-call/conference-call.
+            call_conf_id: conference call id in host android device object.
+
+        Returns:
+            True if no error happened. Otherwise False.
+        """
+        host.droid.telecomCallMergeToConf(call_conf_id)
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = host.droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, host) != 3:
+            return False
+        if not verify_incall_state(self.log, [host], True):
+            return False
+        if not verify_incall_state(self.log, participant_list, True):
+            return False
+        # TODO(yangxliu): use difference method to verify call merged for 1x.
+        if (CALL_CAPABILITY_MERGE_CONFERENCE in
+            host.droid.telecomCallGetCapabilities(call_conf_id)):
+            self.log.error("Merge conference failed.")
+            return False
+        return True
+
+    def _test_volte_mo_mo_add_volte_swap_x(self, num_swaps):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [phone_setup_volte, phone_setup_volte, phone_setup_volte],
+            [is_phone_in_call_volte, is_phone_in_call_volte, is_phone_in_call_volte])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_volte_mo_mt_add_volte_swap_x(self, num_swaps):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA. (N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_volte, phone_setup_volte, phone_setup_volte],
+            [is_phone_in_call_volte, is_phone_in_call_volte, is_phone_in_call_volte])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_volte_mt_mt_add_volte_swap_x(self, num_swaps):
+        """Test swap feature in VoLTE call.
+
+        PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA. (N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_volte, phone_setup_volte, phone_setup_volte],
+            [is_phone_in_call_volte, is_phone_in_call_volte, is_phone_in_call_volte])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_volte_mo_mo_add_wcdma_swap_x(self, num_swaps):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are GSM phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("{} not GSM phone, abort wcdma swap test.".
+                               format(ad.serial))
+                return None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [phone_setup_volte, phone_setup_3g, phone_setup_3g],
+            [is_phone_in_call_volte, is_phone_in_call_wcdma, is_phone_in_call_wcdma])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_volte_mo_mt_add_wcdma_swap_x(self, num_swaps):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are GSM phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("{} not GSM phone, abort wcdma swap test.".
+                               format(ad.serial))
+                return None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_volte, phone_setup_3g, phone_setup_3g],
+            [is_phone_in_call_volte, is_phone_in_call_wcdma, is_phone_in_call_wcdma])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_volte_mt_mt_add_wcdma_swap_x(self, num_swaps):
+        """Test swap feature in VoLTE call.
+
+        PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are GSM phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("{} not GSM phone, abort wcdma swap test.".
+                               format(ad.serial))
+                return None, None
+
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_volte, phone_setup_3g, phone_setup_3g],
+            [is_phone_in_call_volte, is_phone_in_call_wcdma, is_phone_in_call_wcdma])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_volte_mo_mo_add_1x_swap_x(self, num_swaps):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are CDMA phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_CDMA):
+                self.log.error("{} not CDMA phone, abort 1x swap test.".
+                               format(ad.serial))
+                return None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [phone_setup_volte, phone_setup_3g, phone_setup_3g],
+            [is_phone_in_call_volte, is_phone_in_call_1x, is_phone_in_call_1x])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_volte_mo_mt_add_1x_swap_x(self, num_swaps):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are CDMA phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_CDMA):
+                self.log.error("{} not CDMA phone, abort 1x swap test.".
+                               format(ad.serial))
+                return None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_volte, phone_setup_3g, phone_setup_3g],
+            [is_phone_in_call_volte, is_phone_in_call_1x, is_phone_in_call_1x])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_volte_mt_mt_add_1x_swap_x(self, num_swaps):
+        """Test swap feature in VoLTE call.
+
+        PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are CDMA phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_CDMA):
+                self.log.error("{} not CDMA phone, abort 1x swap test.".
+                               format(ad.serial))
+                return None, None
+
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_volte, phone_setup_3g, phone_setup_3g],
+            [is_phone_in_call_volte, is_phone_in_call_1x, is_phone_in_call_1x])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_wcdma_mo_mo_add_swap_x(self, num_swaps):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA. (N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("{} not GSM phone, abort wcdma swap test.".
+                           format(ads[0].serial))
+            return None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [phone_setup_3g, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_3g, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_wcdma_mt_mt_add_swap_x(self, num_swaps):
+        """Test swap feature in WCDMA call.
+
+        PhoneB call PhoneA (WCDMA), accept on PhoneA.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA. (N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("{} not GSM phone, abort wcdma swap test.".
+                           format(ads[0].serial))
+            return None, None
+
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_3g, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_3g, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_wcdma_mo_mt_add_swap_x(self, num_swaps):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA. (N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("{} not GSM phone, abort wcdma swap test.".
+                            format(ads[0].serial))
+            return None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_3g, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_wcdma, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_csfb_wcdma_mo_mo_add_swap_x(self, num_swaps):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (CSFB WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA. (N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("{} not GSM phone, abort wcdma swap test.".
+                            format(ads[0].serial))
+            return None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [phone_setup_csfb, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_wcdma, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0:
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_csfb_wcdma_mo_mt_add_swap_x(self, num_swaps):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (CSFB WCDMA), accept on PhoneA.
+        Swap active call on PhoneA. (N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("{} not GSM phone, abort wcdma swap test.".
+                            format(ads[0].serial))
+            return None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_csfb, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_wcdma, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0:
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_ims_conference_merge_drop_second_call_no_cep(self, call_ab_id, call_ac_id):
+        """Test conference merge and drop in VoLTE call.
+
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneB.
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneC.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            True if succeed;
+            False if failed.
+        """
+        ads = self.android_devices
+
+        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)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            self.log.error("Total number of call ids in {} is not 1.".
+                           format(ads[0].serial))
+            if get_cep_conference_call_id(ads[0]) is not None:
+                self.log.error("CEP enabled.")
+            else:
+                self.log.error("Merge failed.")
+            return False
+        call_conf_id = None
+        for call_id in calls:
+            if call_id != call_ab_id and call_id != call_ac_id:
+                call_conf_id = call_id
+        if not call_conf_id:
+            self.log.error("Merge call fail, no new conference call id.")
+            return False
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        # 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)))
+            return False
+
+        self.log.info("Step5: End call on PhoneC and verify call continues.")
+        ads[2].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            return False
+        if not verify_incall_state(self.log, [ads[2]], False):
+            return False
+
+        # Given the fact that VZW VoLTE(b/18413009),
+        # Even if all participants drop, Conference call is active is WAI.
+        # So return True without checking.
+        # TODO: delete operator-specific code.
+        sub_id = ads[0].droid.subscriptionGetDefaultVoiceSubId()
+        if(ads[0].cfg['subscription'][sub_id]['operator'] == CARRIER_VZW):
+            ads[0].droid.telecomEndCall()
+            ads[1].droid.telecomEndCall()
+            return True
+        else:
+            self.log.info("Step6: End call on PhoneB and verify PhoneA end.")
+            ads[1].droid.telecomEndCall()
+            time.sleep(WAIT_TIME_IN_CALL)
+            if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+                return False
+            return True
+
+    def _merge_cep_conference_call(self, call_ab_id, call_ac_id):
+        """Merge CEP conference call.
+
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneB.
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneC.
+        Merge calls to conference on PhoneA (CEP enabled IMS conference).
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            call_id for conference
+        """
+        ads = self.android_devices
+
+        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)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+
+        call_conf_id = get_cep_conference_call_id(ads[0])
+        if call_conf_id is None:
+            self.log.error("No call with children. Probably CEP not enabled or merge failed.")
+            return None
+        calls.remove(call_conf_id)
+        if (set(ads[0].droid.telecomCallGetCallChildren(call_conf_id)) !=
+            set(calls)):
+            self.log.error("Children list<{}> for conference call is not correct.".
+                format(ads[0].droid.telecomCallGetCallChildren(call_conf_id)))
+            return None
+
+        if (CALL_PROPERTY_CONFERENCE not in
+            ads[0].droid.telecomCallGetProperties(call_conf_id)):
+            self.log.error("Conf call id properties wrong: {}".
+                format(ads[0].droid.telecomCallGetProperties(call_conf_id)))
+            return None
+
+        if (CALL_CAPABILITY_MANAGE_CONFERENCE not in
+            ads[0].droid.telecomCallGetCapabilities(call_conf_id)):
+            self.log.error("Conf call id capabilities wrong: {}".
+                format(ads[0].droid.telecomCallGetCapabilities(call_conf_id)))
+            return None
+
+        if (call_ab_id in calls) or (call_ac_id in calls):
+            self.log.error("Previous call ids should not in new call list after merge.")
+            return None
+
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return None
+
+        # 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)))
+            return None
+
+        return call_conf_id
+
+    def _test_ims_conference_merge_drop_second_call_from_participant_cep(
+      self, call_ab_id, call_ac_id):
+        """Test conference merge and drop in IMS (VoLTE or WiFi Calling) call.
+        (CEP enabled).
+
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneB.
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneC.
+        Merge calls to conference on PhoneA (CEP enabled IMS conference).
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            True if succeed;
+            False if failed.
+        """
+        ads = self.android_devices
+
+        call_conf_id = self._merge_cep_conference_call(
+            call_ab_id, call_ac_id)
+        if call_conf_id is None:
+            return False
+
+        self.log.info("Step5: End call on PhoneC and verify call continues.")
+        ads[2].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            return False
+        if not verify_incall_state(self.log, [ads[2]], False):
+            return False
+
+        self.log.info("Step6: End call on PhoneB and verify PhoneA end.")
+        ads[1].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    def _test_ims_conference_merge_drop_first_call_from_participant_cep(
+      self, call_ab_id, call_ac_id):
+        """Test conference merge and drop in IMS (VoLTE or WiFi Calling) call.
+        (CEP enabled).
+
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneB.
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneC.
+        Merge calls to conference on PhoneA (CEP enabled IMS conference).
+        Hangup on PhoneB, check call continues between AC.
+        Hangup on PhoneC, check A ends.
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            True if succeed;
+            False if failed.
+        """
+        ads = self.android_devices
+
+        call_conf_id = self._merge_cep_conference_call(
+            call_ab_id, call_ac_id)
+        if call_conf_id is None:
+            return False
+
+        self.log.info("Step5: End call on PhoneB and verify call continues.")
+        ads[1].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[2]], True):
+            return False
+        if not verify_incall_state(self.log, [ads[1]], False):
+            return False
+
+        self.log.info("Step6: End call on PhoneC and verify PhoneA end.")
+        ads[2].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    def _test_ims_conference_merge_drop_second_call_from_host_cep(
+      self, call_ab_id, call_ac_id):
+        """Test conference merge and drop in IMS (VoLTE or WiFi Calling) call.
+        (CEP enabled).
+
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneB.
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneC.
+        Merge calls to conference on PhoneA (CEP enabled IMS conference).
+        On PhoneA, disconnect call between A-C, verify PhoneA PhoneB still in call.
+        On PhoneA, disconnect call between A-B, verify PhoneA PhoneB disconnected.
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            True if succeed;
+            False if failed.
+        """
+        ads = self.android_devices
+
+        call_ab_uri = get_call_uri(ads[0], call_ab_id)
+        call_ac_uri = get_call_uri(ads[0], call_ac_id)
+
+        call_conf_id = self._merge_cep_conference_call(
+            call_ab_id, call_ac_id)
+        if call_conf_id is None:
+            return False
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        calls.remove(call_conf_id)
+
+        self.log.info("Step5: Disconnect call A-C and verify call continues.")
+        call_to_disconnect = None
+        for call in calls:
+            if is_uri_equivalent(call_ac_uri, get_call_uri(ads[0], call)):
+                call_to_disconnect = call
+                calls.remove(call_to_disconnect)
+                break
+        if call_to_disconnect is None:
+            self.log.error("Can NOT find call on host represents A-C.")
+            return False
+        else:
+            ads[0].droid.telecomCallDisconnect(call_to_disconnect)
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            return False
+        if not verify_incall_state(self.log, [ads[2]], False):
+            return False
+
+        self.log.info("Step6: Disconnect call A-B and verify PhoneA PhoneB end.")
+        call_to_disconnect = None
+        for call in calls:
+            if is_uri_equivalent(call_ab_uri, get_call_uri(ads[0], call)):
+                call_to_disconnect = call
+                calls.remove(call_to_disconnect)
+                break
+        if call_to_disconnect is None:
+            self.log.error("Can NOT find call on host represents A-B.")
+            return False
+        else:
+            ads[0].droid.telecomCallDisconnect(call_to_disconnect)
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    def _test_ims_conference_merge_drop_first_call_from_host_cep(
+      self, call_ab_id, call_ac_id):
+        """Test conference merge and drop in IMS (VoLTE or WiFi Calling) call.
+        (CEP enabled).
+
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneB.
+        PhoneA in IMS (VoLTE or WiFi Calling) call with PhoneC.
+        Merge calls to conference on PhoneA (CEP enabled IMS conference).
+        On PhoneA, disconnect call between A-B, verify PhoneA PhoneC still in call.
+        On PhoneA, disconnect call between A-C, verify PhoneA PhoneC disconnected.
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            True if succeed;
+            False if failed.
+        """
+        ads = self.android_devices
+
+        call_ab_uri = get_call_uri(ads[0], call_ab_id)
+        call_ac_uri = get_call_uri(ads[0], call_ac_id)
+
+        call_conf_id = self._merge_cep_conference_call(
+            call_ab_id, call_ac_id)
+        if call_conf_id is None:
+            return False
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        calls.remove(call_conf_id)
+
+        self.log.info("Step5: Disconnect call A-B and verify call continues.")
+        call_to_disconnect = None
+        for call in calls:
+            if is_uri_equivalent(call_ab_uri, get_call_uri(ads[0], call)):
+                call_to_disconnect = call
+                calls.remove(call_to_disconnect)
+                break
+        if call_to_disconnect is None:
+            self.log.error("Can NOT find call on host represents A-B.")
+            return False
+        else:
+            ads[0].droid.telecomCallDisconnect(call_to_disconnect)
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[2]], True):
+            return False
+        if not verify_incall_state(self.log, [ads[1]], False):
+            return False
+
+        self.log.info("Step6: Disconnect call A-C and verify PhoneA PhoneC end.")
+        call_to_disconnect = None
+        for call in calls:
+            if is_uri_equivalent(call_ac_uri, get_call_uri(ads[0], call)):
+                call_to_disconnect = call
+                calls.remove(call_to_disconnect)
+                break
+        if call_to_disconnect is None:
+            self.log.error("Can NOT find call on host represents A-C.")
+            return False
+        else:
+            ads[0].droid.telecomCallDisconnect(call_to_disconnect)
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    def _test_wcdma_conference_merge_drop(self, call_ab_id, call_ac_id):
+        """Test conference merge and drop in WCDMA/CSFB_WCDMA call.
+
+        PhoneA in WCDMA (or CSFB_WCDMA) call with PhoneB.
+        PhoneA in WCDMA (or CSFB_WCDMA) call with PhoneC.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            True if succeed;
+            False if failed.
+        """
+        ads = self.android_devices
+
+        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)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 3:
+            self.log.error("Total number of call ids in {} is not 3.".
+                           format(ads[0].serial))
+            return False
+        call_conf_id = None
+        for call_id in calls:
+            if call_id != call_ab_id and call_id != call_ac_id:
+                call_conf_id = call_id
+        if not call_conf_id:
+            self.log.error("Merge call fail, no new conference call id.")
+            return False
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        # 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)))
+            return False
+
+        self.log.info("Step5: End call on PhoneC and verify call continues.")
+        ads[2].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            return False
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            return False
+        if not verify_incall_state(self.log, [ads[2]], False):
+            return False
+
+        self.log.info("Step6: End call on PhoneB and verify PhoneA end.")
+        ads[1].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    def _three_phone_hangup_call_verify_call_state(self, ad_hangup,
+                                                   ad_verify, call_id, call_state,
+                                                   ads_active):
+        """Private Test utility for swap test.
+
+        Hangup on 'ad_hangup'.
+        Verify 'call_id' on 'ad_verify' is in expected 'call_state'
+        Verify each ad in ads_active are 'in-call'.
+
+        Args:
+            ad_hangup: android object to hangup call.
+            ad_verify: android object to verify call id state.
+            call_id: call id in 'ad_verify'.
+            call_state: expected state for 'call_id'.
+                'call_state' is either CALL_STATE_HOLDING or CALL_STATE_ACTIVE.
+            ads_active: list of android object.
+                Each one of them should be 'in-call' after 'hangup' operation.
+
+        Returns:
+            True if no error happened. Otherwise False.
+
+        """
+
+        self.log.info("Hangup at {}, verify call continues.".format(ad_hangup.serial))
+        ad_hangup.droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+
+        if ad_verify.droid.telecomCallGetCallState(call_id) != call_state:
+            self.log.error("Call_id:{}, state:{}, expected: {}".
+                           format(call_id,
+                                  ad_verify.droid.telecomCallGetCallState(call_id),
+                                  call_state))
+            return False
+        # TODO: Future add voice check.
+
+        if not verify_incall_state(self.log, ads_active, True):
+            return False
+        if not verify_incall_state(self.log, [ad_hangup], False):
+            return False
+
+        return True
+
+    def _test_epdg_mo_mo_add_epdg_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mo_add_epdg_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_iwlan, is_phone_in_call_iwlan])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mo_mt_add_epdg_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mt_add_epdg_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_iwlan, is_phone_in_call_iwlan])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mt_mt_add_epdg_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneB (epdg) call PhoneA (epdg), accept on PhoneA.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mt_mt_add_epdg_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_iwlan, is_phone_in_call_iwlan])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mo_mo_add_volte_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mo_add_volte_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_volte, is_phone_in_call_volte])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mo_mt_add_volte_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mt_add_volte_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_volte, is_phone_in_call_volte])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mt_mt_add_volte_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneB (VoLTE) call PhoneA (epdg), accept on PhoneA.
+        PhoneC (VoLTE) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mt_mt_add_volte_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_volte, is_phone_in_call_volte])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mo_mo_add_wcdma_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (WCDMA), accept on PhoneC.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are GSM phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("{} not GSM phone, abort wcdma swap test.".
+                               format(ad.serial))
+                return None, None
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mo_add_wcdma_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_wcdma, is_phone_in_call_wcdma])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mo_mt_add_wcdma_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneC (WCDMA) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are GSM phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("{} not GSM phone, abort wcdma swap test.".
+                               format(ad.serial))
+                return None, None
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mt_add_wcdma_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_wcdma, is_phone_in_call_wcdma])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mt_mt_add_wcdma_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneB (WCDMA) call PhoneA (epdg), accept on PhoneA.
+        PhoneC (WCDMA) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are GSM phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("{} not GSM phone, abort wcdma swap test.".
+                               format(ad.serial))
+                return None, None
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mt_add_wcdma_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_wcdma, is_phone_in_call_wcdma])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mo_mo_add_1x_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (1x), accept on PhoneC.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are CDMA phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_CDMA):
+                self.log.error("{} not CDMA phone, abort 1x swap test.".
+                               format(ad.serial))
+                return None, None
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mo_add_1x_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_1x, is_phone_in_call_1x])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mo_mt_add_1x_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneC (1x) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are CDMA phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_CDMA):
+                self.log.error("{} not CDMA phone, abort 1x swap test.".
+                               format(ad.serial))
+                return None, None
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mt_add_1x_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mo_add_mt([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_1x, is_phone_in_call_1x])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_mt_mt_add_1x_swap_x(self, num_swaps):
+        """Test swap feature in epdg call.
+
+        PhoneB (1x) call PhoneA (epdg), accept on PhoneA.
+        PhoneC (1x) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.(N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneB and PhoneC are CDMA phone before proceed.
+        for ad in [ads[1], ads[2]]:
+            if (ad.droid.getPhoneType() != PHONE_TYPE_CDMA):
+                self.log.error("{} not CDMA phone, abort 1x swap test.".
+                               format(ad.serial))
+                return None, None
+
+        # To make thing simple, for epdg, setup should be called before calling
+        # _test_epdg_mo_mt_add_1x_swap_x in test cases.
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [None, None, None],
+            [is_phone_in_call_iwlan, is_phone_in_call_1x, is_phone_in_call_1x])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_epdg_conference_merge_drop(self, call_ab_id, call_ac_id):
+        """Test conference merge and drop in epdg call.
+
+        PhoneA in epdg call with PhoneB.
+        PhoneA in epdg call with PhoneC.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            True if succeed;
+            False if failed.
+        """
+
+        ads = self.android_devices
+
+        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)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            self.log.error("Total number of call ids in {} is not 1.".
+                           format(ads[0].serial))
+            return False
+        call_conf_id = None
+        for call_id in calls:
+            if call_id != call_ab_id and call_id != call_ac_id:
+                call_conf_id = call_id
+        if not call_conf_id:
+            self.log.error("Merge call fail, no new conference call id.")
+            return False
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        # 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)))
+            return False
+
+        self.log.info("Step5: End call on PhoneC and verify call continues.")
+        ads[2].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            return False
+        if not verify_incall_state(self.log, [ads[2]], False):
+            return False
+
+        self.log.info("Step6: End call on PhoneB and verify PhoneA end.")
+        ads[1].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mo_add_merge_drop(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA to PhoneB, accept on PhoneB.
+        Call from PhoneA to PhoneC, accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mo_add_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mt_mt_add_merge_drop(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneB to PhoneA, accept on PhoneA.
+        Call from PhoneC to PhoneA, accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_wcdma_mt_mt_add_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mo_add_merge_drop_from_participant(self):
+        """ Test 1x Conf Call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. Call from DUT to PhoneB, accept on PhoneB.
+        3. Call from DUT to PhoneC, accept on PhoneC.
+        4. On DUT, merge to conference call.
+        5. End call PhoneC, verify call continues on DUT and PhoneB.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        4. Merge Call succeed on DUT.
+        5. PhoneC drop call, DUT and PhoneB call continues.
+        6. PhoneB drop call, call also end on DUT.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mo_add()
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Merge to Conf Call and verify Conf Call.")
+        if not self._test_1x_merge_conference(ads[0], [ads[1], ads[2]],
+            call_conf_id):
+            self.log.error("1x Conference merge failed.")
+
+        self.log.info("End call on PhoneC, and end call on PhoneB.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mo_add_merge_drop_from_host(self):
+        """ Test 1x Conf Call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. Call from DUT to PhoneB, accept on PhoneB.
+        3. Call from DUT to PhoneC, accept on PhoneC.
+        4. On DUT, merge to conference call.
+        5. End call on DUT, make sure all participants drop.
+
+        Expected Results:
+        4. Merge Call succeed on DUT.
+        5. Make sure DUT and all participants drop call.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mo_add()
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Merge to Conf Call and verify Conf Call.")
+        if not self._test_1x_merge_conference(ads[0], [ads[1], ads[2]],
+            call_conf_id):
+            self.log.error("1x Conference merge failed.")
+
+        self.log.info("End call on PhoneC, and end call on PhoneB.")
+        return self._test_1x_conf_call_drop_from_host(ads[0], [ads[2], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mt_add_drop_active(self):
+        """ Test 1x MO+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. Call from DUT to PhoneB, accept on PhoneB.
+        3. Call from PhoneC to DUT, accept on DUT.
+        4. End call PhoneC, verify call continues on DUT and PhoneB.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        4. PhoneC drop call, DUT and PhoneB call continues.
+        5. PhoneB drop call, call also end on DUT.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(0)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneC, and end call on PhoneB.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mt_add_swap_twice_drop_active(self):
+        """ Test 1x MO+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. DUT MO call to PhoneB, answer on PhoneB.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Swap active call on DUT.
+        6. Drop on PhoneC.
+        7. Drop on PhoneB.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Swap call succeed.
+        6. Call between DUT and PhoneB continues.
+        7. All participant call end.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(2)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneC, and end call on PhoneB.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mt_add_swap_once_drop_active(self):
+        """ Test 1x MO+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. DUT MO call to PhoneB, answer on PhoneB.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Drop on PhoneB.
+        6. Drop on PhoneC.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Call between DUT and PhoneC continues.
+        6. All participant call end.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(1)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneB, and end call on PhoneC.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[1], ads[2])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mt_add_drop_held(self):
+        """ Test 1x MO+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. Call from DUT to PhoneB, accept on PhoneB.
+        3. Call from PhoneC to DUT, accept on DUT.
+        4. End call PhoneB, verify call continues on DUT and PhoneC.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Expected Results:
+        4. DUT drop call, PhoneC also end. Then DUT receive callback from PhoneB.
+        5. DUT drop call, call also end on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(0)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneB, and end call on PhoneC.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[1], ads[2])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mt_add_swap_twice_drop_held(self):
+        """ Test 1x MO+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. DUT MO call to PhoneB, answer on PhoneB.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Swap active call on DUT.
+        6. Drop on PhoneB.
+        7. Drop on PhoneC.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Swap call succeed.
+        6. Call between DUT and PhoneC continues.
+        7. All participant call end.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(2)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneB, and end call on PhoneC.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[1], ads[2])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mt_add_swap_once_drop_held(self):
+        """ Test 1x MO+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. DUT MO call to PhoneB, answer on PhoneB.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Drop on PhoneC.
+        6. Drop on PhoneB.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Call between DUT and PhoneB continues.
+        6. All participant call end.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(1)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneC, and end call on PhoneB.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mt_add_drop_on_dut(self):
+        """ Test 1x MO+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. Call from DUT to PhoneB, accept on PhoneB.
+        3. Call from PhoneC to DUT, accept on DUT.
+        4. End call on DUT.
+        5. End call on DUT.
+
+        Expected Results:
+        4. DUT drop call, PhoneC also end. Then DUT receive callback from PhoneB.
+        5. DUT drop call, call also end on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(0)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on DUT, DUT should receive callback.")
+        return self._test_1x_multi_call_drop_from_host(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mt_add_swap_twice_drop_on_dut(self):
+        """ Test 1x MO+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. DUT MO call to PhoneB, answer on PhoneB.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Swap active call on DUT.
+        6. Drop current call on DUT.
+        7. Drop current call on DUT.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Swap call succeed.
+        6. DUT drop call, PhoneC also end. Then DUT receive callback from PhoneB.
+        7. DUT drop call, call also end on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(2)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on DUT, DUT should receive callback.")
+        return self._test_1x_multi_call_drop_from_host(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mo_mt_add_swap_once_drop_on_dut(self):
+        """ Test 1x MO+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. DUT MO call to PhoneB, answer on PhoneB.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Drop current call on DUT.
+        6. Drop current call on DUT.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. DUT drop call, PhoneB also end. Then DUT receive callback from PhoneC.
+        6. DUT drop call, call also end on PhoneC.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mo_mt_add_swap_x(1)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on DUT, DUT should receive callback.")
+        return self._test_1x_multi_call_drop_from_host(
+            ads[0], ads[1], ads[2])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mt_mt_add_drop_active(self):
+        """ Test 1x MT+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. Call from PhoneB to DUT, accept on DUT.
+        3. Call from PhoneC to DUT, accept on DUT.
+        4. End call PhoneC, verify call continues on DUT and PhoneB.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        4. PhoneC drop call, DUT and PhoneB call continues.
+        5. PhoneB drop call, call also end on DUT.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(0)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneC, and end call on PhoneB.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mt_mt_add_swap_twice_drop_active(self):
+        """ Test 1x MT+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. PhoneB call to DUT, answer on DUT.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Swap active call on DUT.
+        6. Drop on PhoneC.
+        7. Drop on PhoneB.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Swap call succeed.
+        6. Call between DUT and PhoneB continues.
+        7. All participant call end.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(2)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneC, and end call on PhoneB.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mt_mt_add_swap_once_drop_active(self):
+        """ Test 1x MT+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. PhoneB call to DUT, answer on DUT.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Drop on PhoneB.
+        6. Drop on PhoneC.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Call between DUT and PhoneC continues.
+        6. All participant call end.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(1)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneB, and end call on PhoneC.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[1], ads[2])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mt_mt_add_drop_held(self):
+        """ Test 1x MT+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. Call from PhoneB to DUT, accept on DUT.
+        3. Call from PhoneC to DUT, accept on DUT.
+        4. End call PhoneB, verify call continues on DUT and PhoneC.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Expected Results:
+        4. PhoneB drop call, DUT and PhoneC call continues.
+        5. PhoneC drop call, call also end on DUT.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(0)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneB, and end call on PhoneC.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[1], ads[2])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mt_mt_add_swap_twice_drop_held(self):
+        """ Test 1x MT+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. PhoneB call to DUT, answer on DUT.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Swap active call on DUT.
+        6. Drop on PhoneB.
+        7. Drop on PhoneC.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Swap call succeed.
+        6. Call between DUT and PhoneC continues.
+        7. All participant call end.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(2)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneB, and end call on PhoneC.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[1], ads[2])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mt_mt_add_swap_once_drop_held(self):
+        """ Test 1x MT+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. PhoneB call to DUT, answer on DUT.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Drop on PhoneC.
+        6. Drop on PhoneB.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Call between DUT and PhoneB continues.
+        6. All participant call end.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(1)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on PhoneC, and end call on PhoneB.")
+        return self._test_1x_multi_call_drop_from_participant(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mt_mt_add_drop_on_dut(self):
+        """ Test 1x MT+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. Call from PhoneB to DUT, accept on DUT.
+        3. Call from PhoneC to DUT, accept on DUT.
+        4. End call on DUT.
+        5. End call on DUT.
+
+        Expected Results:
+        4. DUT drop call, PhoneC also end. Then DUT receive callback from PhoneB.
+        5. DUT drop call, call also end on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(0)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on DUT, DUT should receive callback.")
+        return self._test_1x_multi_call_drop_from_host(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mt_mt_add_swap_twice_drop_on_dut(self):
+        """ Test 1x MT+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. PhoneB call to DUT, answer on DUT.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Swap active call on DUT.
+        6. Drop current call on DUT.
+        7. Drop current call on DUT.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. Swap call succeed.
+        6. DUT drop call, PhoneC also end. Then DUT receive callback from PhoneB.
+        7. DUT drop call, call also end on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(2)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on DUT, DUT should receive callback.")
+        return self._test_1x_multi_call_drop_from_host(
+            ads[0], ads[2], ads[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_1x_mt_mt_add_swap_once_drop_on_dut(self):
+        """ Test 1x MT+MT call among three phones.
+
+        Steps:
+        1. DUT in 1x idle, PhoneB and PhoneC idle.
+        2. PhoneB call to DUT, answer on DUT.
+        3. PhoneC call to DUT, answer on DUT
+        4. Swap active call on DUT.
+        5. Drop current call on DUT.
+        6. Drop current call on DUT.
+
+        Expected Results:
+        4. Swap call succeed.
+        5. DUT drop call, PhoneB also end. Then DUT receive callback from PhoneC.
+        6. DUT drop call, call also end on PhoneC.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        call_ab_id,  call_ac_id, call_conf_id = self._test_1x_mt_mt_add_swap_x(1)
+        if ((call_ab_id is None) or
+            (call_ac_id is None) or
+            (call_conf_id is None)):
+            self.log.error("Failed to setup 3 way call.")
+            return False
+
+        self.log.info("Verify no one dropped call.")
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        self.log.info("End call on DUT, DUT should receive callback.")
+        return self._test_1x_multi_call_drop_from_host(
+            ads[0], ads[1], ads[2])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        Call from PhoneA (VoLTE) to PhoneC (VoLTE), accept on PhoneC.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_merge_drop_second_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (VoLTE), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_merge_drop_second_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (VoLTE), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_merge_drop_first_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (VoLTE), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_merge_drop_first_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (VoLTE), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_merge_drop_second_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        2. Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_merge_drop_second_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        2. Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_merge_drop_first_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        2. Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_merge_drop_first_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (VoLTE), accept on PhoneB.
+        2. Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        Call from PhoneB (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_merge_drop_second_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_merge_drop_second_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_merge_drop_first_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_merge_drop_first_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (VoLTE) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        Call from PhoneA (VOLTE) to PhoneC (WCDMA), accept on PhoneC.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_merge_drop_second_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (WCDMA), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_merge_drop_second_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (WCDMA), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_merge_drop_first_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (WCDMA), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_merge_drop_first_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (WCDMA), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_merge_drop_second_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        2. Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_merge_drop_second_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        2. Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_merge_drop_first_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        2. Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_merge_drop_first_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (WCDMA), accept on PhoneB.
+        2. Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        Call from PhoneB (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_merge_drop_second_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_merge_drop_second_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_merge_drop_first_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_merge_drop_first_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (WCDMA) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        Call from PhoneA (VOLTE) to PhoneC (1x), accept on PhoneC.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_merge_drop_second_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (1x), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_merge_drop_second_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (1x), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_merge_drop_first_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (1x), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_merge_drop_first_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        2. Call from PhoneA (VoLTE) to PhoneC (1x), accept on PhoneC.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_merge_drop_second_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        2. Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_merge_drop_second_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        2. Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_merge_drop_first_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        2. Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_merge_drop_first_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneA (VoLTE) to PhoneB (1x), accept on PhoneB.
+        2. Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        Call from PhoneB (1x) to PhoneA (VoLTE), accept on PhoneA.
+        Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_merge_drop_second_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (1x) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_merge_drop_second_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (1x) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_merge_drop_first_call_from_participant_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (1x) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_merge_drop_first_call_from_host_cep(self):
+        """ Test VoLTE Conference Call among three phones. CEP enabled.
+
+        1. Call from PhoneB (1x) to PhoneA (VoLTE), accept on PhoneA.
+        2. Call from PhoneC (1x) to PhoneA (VoLTE), accept on PhoneA.
+        3. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_twice_drop_held(self):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_twice_drop_active(self):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_twice_drop_held(self):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_twice_drop_active(self):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_once_drop_held(self):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_once_drop_active(self):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_once_drop_held(self):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_once_drop_active(self):
+        """Test swap feature in VoLTE call.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mo_add_swap_twice_drop_held(self):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mo_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mo_add_swap_twice_drop_active(self):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mo_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mt_add_swap_twice_drop_held(self):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mt_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mt_add_swap_twice_drop_active(self):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mt_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mo_add_swap_once_drop_held(self):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mo_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mo_add_swap_once_drop_active(self):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mo_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mt_add_swap_once_drop_held(self):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mt_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mt_add_swap_once_drop_active(self):
+        """Test swap feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mt_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mo_add_swap_twice_drop_held(self):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (CSFB WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mo_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mo_add_swap_twice_drop_active(self):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (CSFB WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mo_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mt_add_swap_twice_drop_held(self):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (CSFB WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mt_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mt_add_swap_twice_drop_active(self):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (CSFB WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mt_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mo_add_swap_once_drop_held(self):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (CSFB WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mo_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mo_add_swap_once_drop_active(self):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (CSFB WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mo_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mt_add_swap_once_drop_held(self):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (CSFB WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mt_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mt_add_swap_once_drop_active(self):
+        """Test swap feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (CSFB WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mt_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_once_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_once_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_once_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_once_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneB, verify call continues.
+        6. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_once_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-B, verify call continues.
+        6. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_twice_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_twice_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneC, verify call continues.
+        7. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_twice_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-C, verify call continues.
+        7. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_twice_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneB, verify call continues.
+        7. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_volte_swap_twice_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (VoLTE), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-B, verify call continues.
+        7. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_once_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_once_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_once_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_once_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneB, verify call continues.
+        6. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_once_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-B, verify call continues.
+        6. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneC, verify call continues.
+        7. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_twice_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-C, verify call continues.
+        7. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_twice_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneB, verify call continues.
+        7. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_volte_swap_twice_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (VoLTE), accept on PhoneB.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-B, verify call continues.
+        7. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_once_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_once_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_once_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_once_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneB, verify call continues.
+        6. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_once_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-B, verify call continues.
+        6. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneC, verify call continues.
+        7. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_twice_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-C, verify call continues.
+        7. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_twice_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneB, verify call continues.
+        7. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_volte_swap_twice_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (VoLTE) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-B, verify call continues.
+        7. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_once_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_once_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_once_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_once_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneB, verify call continues.
+        6. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_once_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-B, verify call continues.
+        6. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneC, verify call continues.
+        7. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-C, verify call continues.
+        7. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneB, verify call continues.
+        7. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_wcdma_swap_twice_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (WCDMA), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-B, verify call continues.
+        7. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_once_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_once_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_once_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_once_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneB, verify call continues.
+        6. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_once_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-B, verify call continues.
+        6. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneC, verify call continues.
+        7. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-C, verify call continues.
+        7. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneB, verify call continues.
+        7. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_wcdma_swap_twice_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (WCDMA), accept on PhoneB.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-B, verify call continues.
+        7. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_once_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_once_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_once_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_once_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneB, verify call continues.
+        6. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_once_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-B, verify call continues.
+        6. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test VoLTE Conference Call among three phones. No CEP.
+
+        PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneC, verify call continues.
+        7. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-C, verify call continues.
+        7. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneB, verify call continues.
+        7. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_wcdma_swap_twice_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (WCDMA) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-B, verify call continues.
+        7. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_once_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_once_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_once_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_once_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneB, verify call continues.
+        6. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_once_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-B, verify call continues.
+        6. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_twice_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_twice_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneC, verify call continues.
+        7. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_twice_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-C, verify call continues.
+        7. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_twice_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneB, verify call continues.
+        7. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mo_add_1x_swap_twice_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneA (VoLTE) call PhoneC (1x), accept on PhoneC.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-B, verify call continues.
+        7. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mo_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_once_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_once_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_once_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_once_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneB, verify call continues.
+        6. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_once_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-B, verify call continues.
+        6. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_twice_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_twice_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneC, verify call continues.
+        7. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_twice_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-C, verify call continues.
+        7. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_twice_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneB, verify call continues.
+        7. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mo_mt_add_1x_swap_twice_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneA (VoLTE) call PhoneB (1x), accept on PhoneB.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-B, verify call continues.
+        7. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mo_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_once_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_once_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_once_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_once_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. End call on PhoneB, verify call continues.
+        6. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_once_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        5. On PhoneA disconnect call between A-B, verify call continues.
+        6. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_twice_merge_drop_second_call_from_participant_no_cep(self):
+        """ Test swap and merge features in VoLTE call. No CEP.
+
+        PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        On PhoneA, merge to conference call (No CEP).
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_twice_merge_drop_second_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneC, verify call continues.
+        7. End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_twice_merge_drop_second_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-C, verify call continues.
+        7. On PhoneA disconnect call between A-B, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_twice_merge_drop_first_call_from_participant_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. End call on PhoneB, verify call continues.
+        7. End call on PhoneC, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_mt_mt_add_1x_swap_twice_merge_drop_first_call_from_host_cep(self):
+        """ Test swap and merge features in VoLTE call. CEP enabled.
+
+        1. PhoneB (1x) call PhoneA (VoLTE), accept on PhoneA.
+        2. PhoneC (1x) call PhoneA (VoLTE), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. Swap active call on PhoneA.
+        5. On PhoneA, merge to conference call (VoLTE CEP conference call).
+        6. On PhoneA disconnect call between A-B, verify call continues.
+        7. On PhoneA disconnect call between A-C, verify call continues.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_volte_mt_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mo_add_swap_once_merge_drop(self):
+        """Test swap and merge feature in CSFB WCDMA call.
+
+        PhoneA (CSFB_WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (CSFB_WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mo_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mo_add_swap_twice_merge_drop(self):
+        """Test swap and merge feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (CSFB WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mo_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mt_add_swap_once_merge_drop(self):
+        """Test swap and merge feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (CSFB WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mt_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wcdma_mo_mt_add_swap_twice_merge_drop(self):
+        """Test swap and merge feature in CSFB WCDMA call.
+
+        PhoneA (CSFB WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (CSFB WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_csfb_wcdma_mo_mt_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mo_add_swap_once_merge_drop(self):
+        """Test swap and merge feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mo_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mo_add_swap_twice_merge_drop(self):
+        """Test swap and merge feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneA (WCDMA) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mo_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mt_add_swap_once_merge_drop(self):
+        """Test swap and merge feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mt_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mt_add_swap_twice_merge_drop(self):
+        """Test swap and merge feature in WCDMA call.
+
+        PhoneA (WCDMA) call PhoneB, accept on PhoneB.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_wcdma_mo_mt_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mt_mt_add_swap_once_merge_drop(self):
+        """Test swap and merge feature in WCDMA call.
+
+        PhoneB call PhoneA (WCDMA), accept on PhoneA.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_wcdma_mt_mt_add_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mt_mt_add_swap_twice_merge_drop(self):
+        """Test swap and merge feature in WCDMA call.
+
+        PhoneB call PhoneA (WCDMA), accept on PhoneA.
+        PhoneC call PhoneA (WCDMA), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        call_ab_id, call_ac_id = self._test_wcdma_mt_mt_add_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mt_mt_add_merge_unmerge_swap_drop(self):
+        """Test Conference Call Unmerge operation.
+
+        Phones A, B, C are in WCDMA Conference call (MT-MT-Merge)
+        Unmerge call with B on PhoneA
+        Check the number of Call Ids to be 2 on PhoneA
+        Check if call AB is active since 'B' was unmerged
+        Swap call to C
+        Check if call AC is active
+        Tear down calls
+        All Phones should be in Idle
+
+        """
+        call_ab_id, call_ac_id = self._test_wcdma_mt_mt_add_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            self.log.error("Either of Call AB ID or Call AC ID is None.")
+            return False
+
+        ads = self.android_devices
+
+        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)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 3:
+            self.log.error("Total number of call ids in {} is not 3.".
+                           format(ads[0].serial))
+            return False
+        call_conf_id = None
+        for call_id in calls:
+            if call_id != call_ab_id and call_id != call_ac_id:
+                call_conf_id = call_id
+        if not call_conf_id:
+            self.log.error("Merge call fail, no new conference call id.")
+            return False
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        # Check if Conf Call currently active
+        if ads[0].droid.telecomCallGetCallState(call_conf_id) != CALL_STATE_ACTIVE:
+            self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
+                           format(call_conf_id,
+                                  ads[0].droid.telecomCallGetCallState(
+                                      call_conf_id)))
+            return False
+
+        # Unmerge
+        self.log.info("Step5: UnMerge Conf Call into individual participants.")
+        ads[0].droid.telecomCallSplitFromConf(call_ab_id)
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+
+        # Are there 2 calls?
+        if num_active_calls(self.log, ads[0]) != 2:
+            self.log.error("Total number of call ids in {} is not 2".
+                           format(ads[0].serial))
+            return False
+
+        # Unmerged calls not dropped?
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            self.log.error("Either Call_AB or Call_AC was dropped")
+            return False
+
+        # Unmerged call in call state ACTIVE?
+        if ads[0].droid.telecomCallGetCallState(call_ab_id) != CALL_STATE_ACTIVE:
+            self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
+                           format(call_ab_id,
+                                  ads[0].droid.telecomCallGetCallState(
+                                      call_ab_id)))
+            return False
+
+        # Swap call
+        self.log.info("Step6: Swap call and see if Call_AC is ACTIVE.")
+        num_swaps = 1
+        if not swap_calls(self.log, ads, call_ac_id, call_ab_id, num_swaps):
+            self.log.error("Failed to swap calls.")
+            return False
+
+        # Other call in call state ACTIVE?
+        if ads[0].droid.telecomCallGetCallState(call_ac_id) != CALL_STATE_ACTIVE:
+            self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
+                           format(call_ac_id,
+                                  ads[0].droid.telecomCallGetCallState(
+                                      call_ac_id)))
+            return False
+
+        # All calls still CONNECTED?
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id,
+                    call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_merge_drop_wfc_wifi_only(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (epdg), accept on PhoneB.
+        Call from PhoneA (epdg) to PhoneC (epdg), accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_merge_drop_wfc_wifi_preferred(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (epdg), accept on PhoneB.
+        Call from PhoneA (epdg) to PhoneC (epdg), accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_merge_drop_wfc_wifi_only(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (epdg), accept on PhoneB.
+        Call from PhoneC (epdg) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_merge_drop_wfc_wifi_preferred(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (epdg), accept on PhoneB.
+        Call from PhoneC (epdg) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mt_mt_add_epdg_merge_drop_wfc_wifi_only(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneB (epdg) to PhoneA (epdg), accept on PhoneA.
+        Call from PhoneC (epdg) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mt_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mt_mt_add_epdg_merge_drop_wfc_wifi_preferred(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneB (epdg) to PhoneA (epdg), accept on PhoneA.
+        Call from PhoneC (epdg) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mt_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_volte_merge_drop_wfc_wifi_only(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (VoLTE), accept on PhoneB.
+        Call from PhoneA (epdg) to PhoneC (VoLTE), accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_volte, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_volte_merge_drop_wfc_wifi_preferred(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (VoLTE), accept on PhoneB.
+        Call from PhoneA (epdg) to PhoneC (VoLTE), accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_volte, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_volte_merge_drop_wfc_wifi_only(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (VoLTE), accept on PhoneB.
+        Call from PhoneC (VoLTE) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_volte, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_volte_merge_drop_wfc_wifi_preferred(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (VoLTE), accept on PhoneB.
+        Call from PhoneC (VoLTE) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_volte, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_volte_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_wcdma_merge_drop_wfc_wifi_only(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (WCDMA), accept on PhoneB.
+        Call from PhoneA (epdg) to PhoneC (WCDMA), accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_wcdma_merge_drop_wfc_wifi_preferred(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (WCDMA), accept on PhoneB.
+        Call from PhoneA (epdg) to PhoneC (WCDMA), accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_wcdma_merge_drop_wfc_wifi_only(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (WCDMA), accept on PhoneB.
+        Call from PhoneC (WCDMA) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_wcdma_merge_drop_wfc_wifi_preferred(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (WCDMA), accept on PhoneB.
+        Call from PhoneC (WCDMA) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_wcdma_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_1x_merge_drop_wfc_wifi_only(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (1x), accept on PhoneB.
+        Call from PhoneA (epdg) to PhoneC (1x), accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_1x_merge_drop_wfc_wifi_preferred(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (1x), accept on PhoneB.
+        Call from PhoneA (epdg) to PhoneC (1x), accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_1x_merge_drop_wfc_wifi_only(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (1x), accept on PhoneB.
+        Call from PhoneC (1x) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_1x_merge_drop_wfc_wifi_preferred(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA (epdg) to PhoneB (1x), accept on PhoneB.
+        Call from PhoneC (1x) to PhoneA (epdg), accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end 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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_1x_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_once_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_once_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_twice_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_twice_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_twice_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_twice_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_volte_swap_once_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        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])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_volte_swap_once_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_volte, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_volte_swap_twice_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        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])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_volte_swap_twice_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (VoLTE), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_volte, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_volte_swap_once_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        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])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_volte_swap_once_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_volte, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_volte_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_volte_swap_twice_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        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])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_volte_swap_twice_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (VoLTE), accept on PhoneB.
+        PhoneC (VoLTE) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_volte, (self.log, ads[1])),
+                 (phone_setup_volte, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_volte_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_wcdma_swap_once_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (WCDMA), accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_wcdma_swap_once_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (WCDMA), accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_wcdma_swap_twice_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (WCDMA), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_wcdma_swap_twice_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (WCDMA), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_wcdma_swap_once_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneC (WCDMA) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_wcdma_swap_once_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneC (WCDMA) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_wcdma_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_wcdma_swap_twice_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneC (WCDMA) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_wcdma_swap_twice_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (WCDMA), accept on PhoneB.
+        PhoneC (WCDMA) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_wcdma_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_1x_swap_once_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (1x), accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_1x_swap_once_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (1x), accept on PhoneC.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_1x_swap_twice_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (1x), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_1x_swap_twice_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (1x), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_1x_swap_once_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneC (1x) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_1x_swap_once_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneC (1x) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_1x_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_1x_swap_twice_merge_drop_wfc_wifi_only(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneC (1x) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_1x_swap_twice_merge_drop_wfc_wifi_preferred(self):
+        """Test swap and merge feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (1x), accept on PhoneB.
+        PhoneC (1x) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        """
+        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_3g, (self.log, ads[1])),
+                 (phone_setup_3g, (self.log, ads[2]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_1x_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_epdg_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_twice_drop_held_wfc_wifi_only(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_twice_drop_held_wfc_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_twice_drop_active_wfc_wifi_only(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_twice_drop_active_wfc_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_twice_drop_active_apm_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_twice_drop_held_wfc_wifi_only(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_twice_drop_held_wfc_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_twice_drop_active_wfc_wifi_only(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_twice_drop_active_wfc_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(2)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_once_drop_held_wfc_wifi_only(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_once_drop_held_wfc_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_once_drop_active_wfc_wifi_only(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_once_drop_active_wfc_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_swap_once_drop_active_apm_wfc_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneA (epdg) call PhoneC (epdg), accept on PhoneC.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_drop_held_wfc_wifi_only(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_drop_held_wfc_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_drop_held_apm_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneC, check if call continues between AB.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[2],
+                    ad_verify=ads[0], call_id=call_ab_id, call_state=CALL_STATE_ACTIVE,
+                    ads_active=[ads[0], ads[1]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_drop_active_wfc_wifi_only(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[2], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_drop_active_wfc_wifi_preferred(self):
+        """Test swap feature in epdg call.
+
+        PhoneA (epdg) call PhoneB (epdg), accept on PhoneB.
+        PhoneC (epdg) call PhoneA (epdg), accept on PhoneA.
+        Swap active call on PhoneA.
+        Hangup call from PhoneB, check if call continues between AC.
+
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._three_phone_hangup_call_verify_call_state(ad_hangup=ads[1],
+                    ad_verify=ads[0], call_id=call_ac_id, call_state=CALL_STATE_HOLDING,
+                    ads_active=[ads[0], ads[2]])
+
+    def _test_gsm_mo_mo_add_swap_x(self, num_swaps):
+        """Test swap feature in GSM call.
+
+        PhoneA (GSM) call PhoneB, accept on PhoneB.
+        PhoneA (GSM) call PhoneC, accept on PhoneC.
+        Swap active call on PhoneA. (N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("{} not GSM phone, abort wcdma swap test.".
+                           format(ads[0].serial))
+            return None, None
+
+        call_ab_id = self._three_phone_call_mo_add_mo([ads[0], ads[1], ads[2]],
+            [phone_setup_voice_2g, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_2g, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_gsm_mt_mt_add_swap_x(self, num_swaps):
+        """Test swap feature in GSM call.
+
+        PhoneB call PhoneA (GSM), accept on PhoneA.
+        PhoneC call PhoneA (GSM), accept on PhoneA.
+        Swap active call on PhoneA. (N times)
+
+        Args:
+            num_swaps: do swap for 'num_swaps' times.
+                This value can be 0 (no swap operation).
+
+        Returns:
+            call_ab_id, call_ac_id if succeed;
+            None, None if failed.
+
+        """
+        ads = self.android_devices
+
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("{} not GSM phone, abort wcdma swap test.".
+                           format(ads[0].serial))
+            return None, None
+
+        call_ab_id = self._three_phone_call_mt_add_mt([ads[0], ads[1], ads[2]],
+            [phone_setup_voice_2g, phone_setup_voice_general, phone_setup_voice_general],
+            [is_phone_in_call_2g, None, None])
+        if call_ab_id is None:
+            self.log.error("Failed to get call_ab_id")
+            return None, None
+
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 2:
+            return None, None
+        if calls[0] == call_ab_id:
+            call_ac_id = calls[1]
+        else:
+            call_ac_id = calls[0]
+
+        if num_swaps > 0 :
+            self.log.info("Step3: Begin Swap x{} test.".format(num_swaps))
+            if not swap_calls(self.log, ads, call_ab_id, call_ac_id, num_swaps):
+                self.log.error("Swap test failed.")
+                return None, None
+
+        return call_ab_id, call_ac_id
+
+    def _test_gsm_conference_merge_drop(self, call_ab_id, call_ac_id):
+        """Test conference merge and drop in GSM call.
+
+        PhoneA in GSM call with PhoneB.
+        PhoneA in GSM call with PhoneC.
+        Merge calls to conference on PhoneA.
+        Hangup on PhoneC, check call continues between AB.
+        Hangup on PhoneB, check A ends.
+
+        Args:
+            call_ab_id: call id for call_AB on PhoneA.
+            call_ac_id: call id for call_AC on PhoneA.
+
+        Returns:
+            True if succeed;
+            False if failed.
+        """
+        ads = self.android_devices
+
+        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)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 3:
+            self.log.error("Total number of call ids in {} is not 3.".
+                           format(ads[0].serial))
+            return False
+        call_conf_id = None
+        for call_id in calls:
+            if call_id != call_ab_id and call_id != call_ac_id:
+                call_conf_id = call_id
+        if not call_conf_id:
+            self.log.error("Merge call fail, no new conference call id.")
+            return False
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], True):
+            return False
+
+        # 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)))
+            return False
+
+        self.log.info("Step5: End call on PhoneC and verify call continues.")
+        ads[2].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        calls = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(calls))
+        if num_active_calls(self.log, ads[0]) != 1:
+            return False
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            return False
+        if not verify_incall_state(self.log, [ads[2]], False):
+            return False
+
+        self.log.info("Step6: End call on PhoneB and verify PhoneA end.")
+        ads[1].droid.telecomEndCall()
+        time.sleep(WAIT_TIME_IN_CALL)
+        if not verify_incall_state(self.log, [ads[0], ads[1], ads[2]], False):
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_gsm_mo_mo_add_merge_drop(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneA to PhoneB, accept on PhoneB.
+        Call from PhoneA to PhoneC, accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_gsm_mo_mo_add_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_gsm_conference_merge_drop(call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_gsm_mt_mt_add_merge_drop(self):
+        """ Test Conf Call among three phones.
+
+        Call from PhoneB to PhoneA, accept on PhoneA.
+        Call from PhoneC to PhoneA, accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_ab_id, call_ac_id = self._test_gsm_mt_mt_add_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_gsm_conference_merge_drop(call_ab_id, call_ac_id)
+
+    #SIM2 tests
+    def _reset_subscriptions_to_sim1(self, ads):
+        set_call_state_listen_level(self.log, ads[0], False,
+            self.sim_sub_ids[0][1])
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        setup_sim(self.log, ads[0], self.sim_sub_ids[0][0], True)
+        ads[0].droid.phoneSetPreferredNetworkTypeForSubscription(
+                RAT_3G, self.sim_sub_ids[0][0])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mo_mo_add_merge_drop_sim2(self):
+        """ Test Conf Call among three phones.
+
+        Set SIM2 as the default voice SIM
+        Call from PhoneA to PhoneB, accept on PhoneB.
+        Call from PhoneA to PhoneC, accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True):
+                return False
+
+            call_ab_id, call_ac_id = self._test_wcdma_mo_mo_add_swap_x(0)
+            if call_ab_id is None or call_ac_id is None:
+                return False
+
+            return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wcdma_mt_mt_add_merge_drop_sim2(self):
+        """ Test Conf Call among three phones.
+
+        Set SIM2 as the default voice SIM
+        Call from PhoneB to PhoneA, accept on PhoneA.
+        Call from PhoneC to PhoneA, accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True):
+                return False
+
+            call_ab_id, call_ac_id = self._test_wcdma_mt_mt_add_swap_x(0)
+            if call_ab_id is None or call_ac_id is None:
+                return False
+
+            return self._test_wcdma_conference_merge_drop(call_ab_id, call_ac_id)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_gsm_mo_mo_add_merge_drop_sim2(self):
+        """ Test Conf Call among three phones.
+
+        Set SIM2 as the default voice SIM
+        Call from PhoneA to PhoneB, accept on PhoneB.
+        Call from PhoneA to PhoneC, accept on PhoneC.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True):
+                return False
+
+            call_ab_id, call_ac_id = self._test_gsm_mo_mo_add_swap_x(0)
+            if call_ab_id is None or call_ac_id is None:
+                return False
+
+            return self._test_gsm_conference_merge_drop(call_ab_id, call_ac_id)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_gsm_mt_mt_add_merge_drop_sim2(self):
+        """ Test Conf Call among three phones.
+
+        Set SIM2 as the default voice SIM
+        Call from PhoneB to PhoneA, accept on PhoneA.
+        Call from PhoneC to PhoneA, accept on PhoneA.
+        On PhoneA, merge to conference call.
+        End call on PhoneC, verify call continues.
+        End call on PhoneB, verify call end on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True):
+                return False
+
+            call_ab_id, call_ac_id = self._test_gsm_mt_mt_add_swap_x(0)
+            if call_ab_id is None or call_ac_id is None:
+                return False
+
+            return self._test_gsm_conference_merge_drop(call_ab_id, call_ac_id)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_no_cep(self):
+        """ Test WFC Conference Call among three phones. No CEP.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneA (WFC APM WiFi Preferred) call PhoneC (WFC APM WiFi Preferred), accept on PhoneC.
+        3. On PhoneA, merge to conference call (No CEP).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-B continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, No_CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneA (WFC APM WiFi Preferred) call PhoneC (WFC APM WiFi Preferred), accept on PhoneC.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-B continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_merge_drop_second_call_from_host_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneA (WFC APM WiFi Preferred) call PhoneC (WFC APM WiFi Preferred), accept on PhoneC.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-B continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_merge_drop_first_call_from_participant_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneA (WFC APM WiFi Preferred) call PhoneC (WFC APM WiFi Preferred), accept on PhoneC.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-C continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mo_add_epdg_merge_drop_first_call_from_host_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneA (WFC APM WiFi Preferred) call PhoneC (WFC APM WiFi Preferred), accept on PhoneC.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-C continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mo_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_no_cep(self):
+        """ Test WFC Conference Call among three phones. No CEP.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (No CEP).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-B continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, No_CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-B continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_merge_drop_second_call_from_host_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-B continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_merge_drop_first_call_from_participant_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-C continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_merge_drop_first_call_from_host_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-C continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mt_mt_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_no_cep(self):
+        """ Test WFC Conference Call among three phones. No CEP.
+
+        Steps:
+        1. PhoneB (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (No CEP).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-B continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, No_CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mt_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mt_mt_add_epdg_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps
+        1. PhoneB (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. End call on PhoneC, verify call continues.
+        5. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-B continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mt_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mt_mt_add_epdg_merge_drop_second_call_from_host_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps:
+        1. PhoneB (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. On PhoneA disconnect call between A-C, verify call continues.
+        5. On PhoneA disconnect call between A-B, verify call continues.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-B continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mt_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mt_mt_add_epdg_merge_drop_first_call_from_participant_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps:
+        1. PhoneB (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. End call on PhoneB, verify call continues.
+        5. End call on PhoneC, verify call end on PhoneA.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-C continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mt_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mt_mt_add_epdg_merge_drop_first_call_from_host_wfc_apm_wifi_preferred_cep(self):
+        """ Test WFC Conference Call among three phones. CEP enabled.
+
+        Steps:
+        1. PhoneB (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. On PhoneA, merge to conference call (WFC CEP conference call).
+        4. On PhoneA disconnect call between A-B, verify call continues.
+        5. On PhoneA disconnect call between A-C, verify call continues.
+
+        Expected Results:
+        3. Conference merged successfully.
+        4. Drop calls succeeded. Call between A-C continues.
+        5. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mt_mt_add_epdg_swap_x(0)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_first_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_no_cep(self):
+        """ Test swap and merge features in WFC call. No CEP.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (No CEP).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        3. Swap operation succeeded.
+        4. Conference merged successfully.
+        5. Drop calls succeeded. Call between A-B continues.
+        6. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, No_CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_no_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_merge_drop_second_call_from_host_wfc_apm_wifi_preferred_cep(self):
+        """ Test swap and merge features in WFC call. CEP enabled.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (WFC CEP conference call).
+        5. On PhoneA disconnect call between A-C, verify call continues.
+        6. On PhoneA disconnect call between A-B, verify call continues.
+
+        Expected Results:
+        3. Swap operation succeeded.
+        4. Conference merged successfully.
+        5. Drop calls succeeded. Call between A-B continues.
+        6. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_host_cep(
+            call_ab_id, call_ac_id)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_epdg_mo_mt_add_epdg_swap_once_merge_drop_second_call_from_participant_wfc_apm_wifi_preferred_cep(self):
+        """ Test swap and merge features in WFC call. CEP enabled.
+
+        Steps:
+        1. PhoneA (WFC APM WiFi Preferred) call PhoneB (WFC APM WiFi Preferred), accept on PhoneB.
+        2. PhoneC (WFC APM WiFi Preferred) call PhoneA (WFC APM WiFi Preferred), accept on PhoneA.
+        3. Swap active call on PhoneA.
+        4. On PhoneA, merge to conference call (WFC CEP conference call).
+        5. End call on PhoneC, verify call continues.
+        6. End call on PhoneB, verify call end on PhoneA.
+
+        Expected Results:
+        3. Swap operation succeeded.
+        4. Conference merged successfully.
+        5. Drop calls succeeded. Call between A-B continues.
+        6. Drop calls succeeded, all call participants drop.
+
+        Returns:
+            True if pass; False if fail.
+
+        TAGS: Telephony, WFC, Conference, CEP
+        Priority: 1
+        """
+        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)),
+                 (phone_setup_iwlan, (self.log, ads[2], 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
+
+        call_ab_id, call_ac_id = self._test_epdg_mo_mt_add_epdg_swap_x(1)
+        if call_ab_id is None or call_ac_id is None:
+            return False
+
+        return self._test_ims_conference_merge_drop_second_call_from_participant_cep(
+            call_ab_id, call_ac_id)
+
+    """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveVoiceTest.py b/acts/tests/google/tel/live/TelLiveVoiceTest.py
new file mode 100644
index 0000000..082741d
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveVoiceTest.py
@@ -0,0 +1,3476 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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.utils import load_config
+
+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 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_IWLAN
+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_NW_SELECTION
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
+
+from acts.test_utils.tel.tel_test_utils import multithread_func
+from acts.test_utils.tel.tel_test_utils import get_phone_number
+from acts.test_utils.tel.tel_test_utils import phone_number_formatter
+from acts.test_utils.tel.tel_test_utils import set_phone_number
+from acts.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts.test_utils.tel.tel_test_utils import num_active_calls
+from acts.test_utils.tel.tel_test_utils import verify_incall_state
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts.test_utils.tel.tel_test_utils import WifiUtils
+from acts.test_utils.tel.tel_test_utils import wait_for_droid_not_in_network_rat
+from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts.test_utils.tel.tel_test_utils import \
+    call_voicemail_erase_all_pending_voicemail
+from acts.test_utils.tel.tel_test_utils import is_droid_in_network_rat
+from acts.test_utils.tel.tel_test_utils import set_call_state_listen_level
+from acts.test_utils.tel.tel_test_utils import setup_sim
+
+from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts.test_utils.tel.tel_voice_utils import two_phone_call_short_seq
+from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
+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_idle_csfb
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts.test_utils.tel.tel_voice_utils import two_phone_call_long_seq
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_1x
+from acts.test_utils.tel.tel_voice_utils import phone_setup_3g
+from acts.test_utils.tel.tel_voice_utils import phone_idle_3g
+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_wcdma
+from acts.test_utils.tel.tel_voice_utils import phone_setup_2g
+from acts.test_utils.tel.tel_voice_utils import phone_idle_2g
+from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts.test_utils.tel.tel_voice_utils import \
+    phone_setup_iwlan_cellular_preferred
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts.test_utils.tel.tel_voice_utils import two_phone_call_leave_voice_mail
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+class TelLiveVoiceTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      "test_call_volte_to_volte",
+                      "test_call_volte_to_csfb_3g",
+                      "test_call_volte_to_csfb_for_tmo",
+                      "test_call_volte_to_csfb_1x_long",
+                      "test_call_volte_to_csfb_long",
+                      "test_call_volte_to_3g",
+                      "test_call_volte_to_3g_1x_long",
+                      "test_call_volte_to_3g_wcdma_long",
+                      "test_call_volte_to_2g",
+                      "test_call_csfb_3g_to_csfb_3g",
+                      "test_call_3g_to_3g",
+                      "test_call_volte_to_volte_long",
+                      "test_call_csfb_3g_to_csfb_3g_long",
+                      "test_call_3g_to_3g_long",
+                      "test_call_volte_mo_hold_unhold",
+                      "test_call_volte_mt_hold_unhold",
+                      "test_call_wcdma_mo_hold_unhold",
+                      "test_call_wcdma_mt_hold_unhold",
+                      "test_call_csfb_mo_hold_unhold",
+                      "test_call_csfb_mt_hold_unhold",
+
+                      "test_call_volte_to_volte_7_digit_dialing",
+                      "test_call_volte_to_volte_10_digit_dialing",
+                      "test_call_volte_to_volte_11_digit_dialing",
+                      "test_call_volte_to_volte_12_digit_dialing",
+
+                      "test_call_volte_to_volte_loop",
+                      "test_call_csfb_3g_to_csfb_3g_loop",
+                      "test_call_3g_to_3g_loop",
+
+                      # WFC Settings Changes test
+                      "test_apm_wifi_connected_toggle_wfc",
+                      "test_nonapm_wifi_connected_toggle_wfc",
+                      "test_apm_wfc_enabled_toggle_wifi",
+                      # APM Live WFC tests
+                      # epdg, WFC, APM, WiFi only, WiFi strong
+                      "test_call_epdg_to_epdg_apm_wfc_wifi_only",
+                      "test_call_epdg_to_volte_apm_wfc_wifi_only",
+                      "test_call_epdg_to_csfb_3g_apm_wfc_wifi_only",
+                      "test_call_epdg_to_3g_apm_wfc_wifi_only",
+                      "test_call_epdg_to_epdg_long_apm_wfc_wifi_only",
+                      "test_call_epdg_to_epdg_loop_apm_wfc_wifi_only",
+                      "test_call_epdg_mo_hold_unhold_apm_wfc_wifi_only",
+                      "test_call_epdg_mt_hold_unhold_apm_wfc_wifi_only",
+                      # epdg, WFC, APM, WiFi preferred
+                      "test_call_epdg_to_epdg_apm_wfc_wifi_preferred",
+                      "test_call_epdg_to_volte_apm_wfc_wifi_preferred",
+                      "test_call_epdg_to_csfb_3g_apm_wfc_wifi_preferred",
+                      "test_call_epdg_to_3g_apm_wfc_wifi_preferred",
+                      "test_call_epdg_to_epdg_long_apm_wfc_wifi_preferred",
+                      "test_call_epdg_to_epdg_loop_apm_wfc_wifi_preferred",
+                      "test_call_epdg_mo_hold_unhold_apm_wfc_wifi_preferred",
+                      "test_call_epdg_mt_hold_unhold_apm_wfc_wifi_preferred",
+                      # epdg, WFC, APM, Cellular preferred
+                      "test_call_epdg_to_epdg_apm_wfc_cellular_preferred",
+
+                      # Non-APM Live WFC tests
+                      # epdg, WFC, WiFi only, WiFi strong, cell strong
+                      "test_call_epdg_to_epdg_wfc_wifi_only",
+                      "test_call_epdg_to_volte_wfc_wifi_only",
+                      "test_call_epdg_to_csfb_3g_wfc_wifi_only",
+                      "test_call_epdg_to_3g_wfc_wifi_only",
+                      "test_call_epdg_to_epdg_long_wfc_wifi_only",
+                      "test_call_epdg_to_epdg_loop_wfc_wifi_only",
+                      "test_call_epdg_mo_hold_unhold_wfc_wifi_only",
+                      "test_call_epdg_mt_hold_unhold_wfc_wifi_only",
+                      # epdg, WFC, WiFi preferred
+                      "test_call_epdg_to_epdg_wfc_wifi_preferred",
+                      "test_call_epdg_to_volte_wfc_wifi_preferred",
+                      "test_call_epdg_to_csfb_3g_wfc_wifi_preferred",
+                      "test_call_epdg_to_3g_wfc_wifi_preferred",
+                      "test_call_epdg_to_epdg_long_wfc_wifi_preferred",
+                      "test_call_epdg_to_epdg_loop_wfc_wifi_preferred",
+                      "test_call_epdg_mo_hold_unhold_wfc_wifi_preferred",
+                      "test_call_epdg_mt_hold_unhold_wfc_wifi_preferred",
+                      # epdg, WFC, Cellular preferred
+                      "test_call_epdg_to_epdg_wfc_cellular_preferred",
+
+                      # Voice Mail Indicator
+                      "test_erase_all_pending_voicemail",
+                      "test_voicemail_indicator_volte",
+                      "test_voicemail_indicator_lte",
+                      "test_voicemail_indicator_3g",
+                      "test_voicemail_indicator_iwlan",
+                      "test_voicemail_indicator_apm_iwlan",
+
+                      "test_call_2g_to_3g_long",
+                      "test_call_3g_to_2g_long",
+                      "test_call_2g_to_2g",
+                      "test_call_2g_to_2g_long",
+                      "test_call_gsm_mo_hold_unhold",
+                      "test_call_gsm_mt_hold_unhold",
+
+                      #SIM2 tests
+                      "test_call_3g_to_3g_sim2",
+                      "test_call_3g_to_3g_long_sim2",
+                      "test_call_wcdma_mo_hold_unhold_sim2",
+                      "test_call_wcdma_mt_hold_unhold_sim2",
+                      "test_call_2g_to_2g_sim2",
+                      "test_call_2g_to_2g_long_sim2",
+                      "test_call_2g_to_3g_long_sim2",
+                      "test_call_gsm_mo_hold_unhold_sim2",
+                      "test_call_gsm_mt_hold_unhold_sim2",
+                      )
+
+        self.simconf = load_config(self.user_params["sim_conf_file"])
+        self.stress_test_number = int(self.user_params["stress_test_number"])
+        self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+
+        try:
+            self.wifi_network_pass = self.user_params["wifi_network_pass"]
+        except KeyError:
+            self.wifi_network_pass = None
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_volte(self):
+        """ VoLTE to VoLTE call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (with VoLTE).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_volte_7_digit_dialing(self):
+        """ VoLTE to VoLTE call test, dial with 7 digit number
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (with VoLTE).
+        3. Call from PhoneA to PhoneB by 7-digit phone number, accept on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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
+
+        callee_default_number = get_phone_number(self.log, ads[1])
+        caller_dialing_number = phone_number_formatter(callee_default_number, 7)
+        try:
+            set_phone_number(self.log, ads[1], caller_dialing_number)
+            result = call_setup_teardown(self.log, ads[0], ads[1], ads[0],
+                                         is_phone_in_call_volte,
+                                         is_phone_in_call_volte,
+                                         WAIT_TIME_IN_CALL_FOR_IMS)
+        except Exception as e:
+            self.log.error("Exception happened: {}".format(e))
+        finally:
+            set_phone_number(self.log, ads[1], callee_default_number)
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_volte_10_digit_dialing(self):
+        """ VoLTE to VoLTE call test, dial with 10 digit number
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (with VoLTE).
+        3. Call from PhoneA to PhoneB by 10-digit phone number, accept on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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
+
+        callee_default_number = get_phone_number(self.log, ads[1])
+        caller_dialing_number = phone_number_formatter(callee_default_number, 10)
+        try:
+            set_phone_number(self.log, ads[1], caller_dialing_number)
+            result = call_setup_teardown(self.log, ads[0], ads[1], ads[0],
+                                         is_phone_in_call_volte,
+                                         is_phone_in_call_volte,
+                                         WAIT_TIME_IN_CALL_FOR_IMS)
+        except Exception as e:
+            self.log.error("Exception happened: {}".format(e))
+        finally:
+            set_phone_number(self.log, ads[1], callee_default_number)
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_volte_11_digit_dialing(self):
+        """ VoLTE to VoLTE call test, dial with 11 digit number
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (with VoLTE).
+        3. Call from PhoneA to PhoneB by 11-digit phone number, accept on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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
+
+        callee_default_number = get_phone_number(self.log, ads[1])
+        caller_dialing_number = phone_number_formatter(callee_default_number, 11)
+        try:
+            set_phone_number(self.log, ads[1], caller_dialing_number)
+            result = call_setup_teardown(self.log, ads[0], ads[1], ads[0],
+                                         is_phone_in_call_volte,
+                                         is_phone_in_call_volte,
+                                         WAIT_TIME_IN_CALL_FOR_IMS)
+        except Exception as e:
+            self.log.error("Exception happened: {}".format(e))
+        finally:
+            set_phone_number(self.log, ads[1], callee_default_number)
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_volte_12_digit_dialing(self):
+        """ VoLTE to VoLTE call test, dial with 12 digit number
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (with VoLTE).
+        3. Call from PhoneA to PhoneB by 12-digit phone number, accept on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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
+
+        callee_default_number = get_phone_number(self.log, ads[1])
+        caller_dialing_number = phone_number_formatter(callee_default_number, 12)
+        try:
+            set_phone_number(self.log, ads[1], caller_dialing_number)
+            result = call_setup_teardown(self.log, ads[0], ads[1], ads[0],
+                                         is_phone_in_call_volte,
+                                         is_phone_in_call_volte,
+                                         WAIT_TIME_IN_CALL_FOR_IMS)
+        except Exception as e:
+            self.log.error("Exception happened: {}".format(e))
+        finally:
+            set_phone_number(self.log, ads[1], callee_default_number)
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_csfb_3g(self):
+        """ VoLTE to CSFB 3G call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (without VoLTE).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])),
+                 (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
+
+        return two_phone_call_short_seq(
+            self.log,
+            ads[0],
+            phone_idle_volte,
+            is_phone_in_call_volte,
+            ads[1],
+            phone_idle_csfb,
+            is_phone_in_call_csfb,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_csfb_for_tmo(self):
+        """ VoLTE to CSFB 3G call test for TMobile
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (without VoLTE, CSFB to WCDMA/GSM).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])),
+                 (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
+
+        return two_phone_call_short_seq(
+            self.log,
+            ads[0],
+            phone_idle_volte,
+            None,
+            ads[1],
+            phone_idle_csfb,
+            is_phone_in_call_csfb,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_csfb_1x_long(self):
+        """ VoLTE to CSFB 1x call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (without VoLTE, CSFB to 1x).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # Make Sure PhoneB is CDMA phone.
+        if ads[1].droid.getPhoneType() != PHONE_TYPE_CDMA:
+            self.log.error("PhoneB not cdma phone, can not csfb 1x. Stop test.")
+            return False
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])),
+                 (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
+
+        return two_phone_call_long_seq(
+            self.log,
+            ads[0],
+            phone_idle_volte,
+            is_phone_in_call_volte,
+            ads[1],
+            phone_idle_csfb,
+            is_phone_in_call_1x,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_csfb_long(self):
+        """ VoLTE to CSFB WCDMA call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (without VoLTE, CSFB to WCDMA/GSM).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # Make Sure PhoneB is GSM phone.
+        if ads[1].droid.getPhoneType() != PHONE_TYPE_GSM:
+            self.log.error(
+                "PhoneB not gsm phone, can not csfb wcdma. Stop test.")
+            return False
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])),
+                 (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
+
+        return two_phone_call_long_seq(
+            self.log,
+            ads[0],
+            phone_idle_volte,
+            is_phone_in_call_volte,
+            ads[1],
+            phone_idle_csfb,
+            is_phone_in_call_csfb,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_3g(self):
+        """ VoLTE to 3G call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in 3G mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])),
+                 (phone_setup_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_volte,
+            is_phone_in_call_volte,
+            ads[1],
+            phone_idle_3g,
+            is_phone_in_call_3g,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_3g_1x_long(self):
+        """ VoLTE to 3G 1x call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in 3G 1x mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # Make Sure PhoneB is CDMA phone.
+        if ads[1].droid.getPhoneType() != PHONE_TYPE_CDMA:
+            self.log.error("PhoneB not cdma phone, can not 3g 1x. Stop test.")
+            return False
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])),
+                 (phone_setup_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_long_seq(
+            self.log,
+            ads[0],
+            phone_idle_volte,
+            is_phone_in_call_volte,
+            ads[1],
+            phone_idle_3g,
+            is_phone_in_call_1x,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_3g_wcdma_long(self):
+        """ VoLTE to 3G WCDMA call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in 3G WCDMA mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # Make Sure PhoneB is GSM phone.
+        if ads[1].droid.getPhoneType() != PHONE_TYPE_GSM:
+            self.log.error("PhoneB not gsm phone, can not 3g wcdma. Stop test.")
+            return False
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])),
+                 (phone_setup_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_long_seq(
+            self.log,
+            ads[0],
+            phone_idle_volte,
+            is_phone_in_call_volte,
+            ads[1],
+            phone_idle_3g,
+            is_phone_in_call_wcdma,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_2g(self):
+        """ VoLTE to 2G call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in 2G mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])),
+                 (phone_setup_2g, (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_2g,
+            is_phone_in_call_2g,
+            None)
+
+    def _call_epdg_to_epdg_wfc(self, ads, apm_mode, wfc_mode, wifi_ssid, wifi_pwd):
+        """ Test epdg<->epdg call functionality.
+
+        Make Sure PhoneA is set to make epdg call.
+        Make Sure PhoneB is set to make epdg call.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Args:
+            ads: list of android objects, this list should have two ad.
+            apm_mode: phones' airplane mode.
+                if True, phones are in airplane mode during test.
+                if False, phones are not in airplane mode during test.
+            wfc_mode: phones' wfc mode.
+                Valid mode includes: WFC_MODE_WIFI_ONLY, WFC_MODE_CELLULAR_PREFERRED,
+                WFC_MODE_WIFI_PREFERRED, WFC_MODE_DISABLED.
+            wifi_ssid: WiFi ssid to connect during test.
+            wifi_pwd: WiFi password.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        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.")
+            return False
+
+        return 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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_wfc_wifi_only(self):
+        """ WiFi Only, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA WFC mode: WIFI_ONLY.
+        2. Setup PhoneB WFC mode: WIFI_ONLY.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._call_epdg_to_epdg_wfc(self.android_devices, False,
+            WFC_MODE_WIFI_ONLY, self.wifi_network_ssid, self.wifi_network_pass)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_wfc_wifi_preferred(self):
+        """ WiFi Preferred, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA WFC mode: WIFI_PREFERRED.
+        2. Setup PhoneB WFC mode: WIFI_PREFERRED.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._call_epdg_to_epdg_wfc(self.android_devices, False,
+            WFC_MODE_WIFI_PREFERRED, self.wifi_network_ssid, self.wifi_network_pass)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_wfc_cellular_preferred(self):
+        """ Cellular Preferred, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA WFC mode: CELLULAR_PREFERRED.
+        2. Setup PhoneB WFC mode: CELLULAR_PREFERRED.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            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
+
+        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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_apm_wfc_wifi_only(self):
+        """ Airplane + WiFi Only, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_ONLY.
+        2. Setup PhoneB in airplane mode, WFC mode: WIFI_ONLY.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._call_epdg_to_epdg_wfc(self.android_devices, True,
+            WFC_MODE_WIFI_ONLY, self.wifi_network_ssid, self.wifi_network_pass)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_apm_wfc_wifi_preferred(self):
+        """ Airplane + WiFi Preferred, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_PREFERRED.
+        2. Setup PhoneB in airplane mode, WFC mode: WIFI_PREFERRED.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._call_epdg_to_epdg_wfc(self.android_devices, True,
+            WFC_MODE_WIFI_PREFERRED, self.wifi_network_ssid, self.wifi_network_pass)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_apm_wfc_cellular_preferred(self):
+        """ Airplane + Cellular Preferred, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA in airplane mode, WFC mode: CELLULAR_PREFERRED.
+        2. Setup PhoneB in airplane mode, WFC mode: CELLULAR_PREFERRED.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._call_epdg_to_epdg_wfc(self.android_devices, True,
+            WFC_MODE_CELLULAR_PREFERRED, self.wifi_network_ssid, self.wifi_network_pass)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_volte_wfc_wifi_only(self):
+        """ WiFi Only, WiFi calling to VoLTE test
+
+        1. Setup PhoneA WFC mode: WIFI_ONLY.
+        2. Make Sure PhoneB is in LTE mode (with VoLTE enabled).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_volte_wfc_wifi_preferred(self):
+        """ WiFi Preferred, WiFi calling to VoLTE test
+
+        1. Setup PhoneA WFC mode: WIFI_PREFERRED.
+        2. Make Sure PhoneB is in LTE mode (with VoLTE enabled).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        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_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_volte_apm_wfc_wifi_only(self):
+        """ Airplane + WiFi Only, WiFi calling to VoLTE test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_ONLY.
+        2. Make Sure PhoneB is in LTE mode (with VoLTE enabled).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_volte_apm_wfc_wifi_preferred(self):
+        """ Airplane + WiFi Preferred, WiFi calling to VoLTE test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_PREFERRED.
+        2. Make Sure PhoneB is in LTE mode (with VoLTE enabled).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        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_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_csfb_3g_wfc_wifi_only(self):
+        """ WiFi Only, WiFi calling to CSFB 3G test
+
+        1. Setup PhoneA WFC mode: WIFI_ONLY.
+        2. Make Sure PhoneB is in LTE mode (with VoLTE disabled).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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
+
+        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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_csfb_3g_wfc_wifi_preferred(self):
+        """ WiFi Preferred, WiFi calling to CSFB 3G test
+
+        1. Setup PhoneA WFC mode: WIFI_PREFERRED.
+        2. Make Sure PhoneB is in LTE mode (with VoLTE disabled).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        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_csfb, (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_csfb,
+            is_phone_in_call_csfb,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_csfb_3g_apm_wfc_wifi_only(self):
+        """ Airplane + WiFi Only, WiFi calling to CSFB 3G test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_ONLY.
+        2. Make Sure PhoneB is in LTE mode (with VoLTE disabled).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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
+
+        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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_csfb_3g_apm_wfc_wifi_preferred(self):
+        """ Airplane + WiFi Preferred, WiFi calling to CSFB 3G test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_PREFERRED.
+        2. Make Sure PhoneB is in LTE mode (with VoLTE disabled).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        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_csfb, (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_csfb,
+            is_phone_in_call_csfb,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_3g_wfc_wifi_only(self):
+        """ WiFi Only, WiFi calling to 3G test
+
+        1. Setup PhoneA WFC mode: WIFI_ONLY.
+        2. Make Sure PhoneB is in 3G mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_3g_wfc_wifi_preferred(self):
+        """ WiFi Preferred, WiFi calling to 3G test
+
+        1. Setup PhoneA WFC mode: WIFI_PREFERRED.
+        2. Make Sure PhoneB is in 3G mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        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_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_3g_apm_wfc_wifi_only(self):
+        """ Airplane + WiFi Only, WiFi calling to 3G test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_ONLY.
+        2. Make Sure PhoneB is in 3G mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], True, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_3g_apm_wfc_wifi_preferred(self):
+        """ Airplane + WiFi Preferred, WiFi calling to 3G test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_PREFERRED.
+        2. Make Sure PhoneB is in 3G mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        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_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_csfb_3g_to_csfb_3g(self):
+        """ CSFB 3G to CSFB 3G call test
+
+        1. Make Sure PhoneA is in LTE mode, VoLTE disabled.
+        2. Make Sure PhoneB is in LTE mode, VoLTE disabled.
+        3. Call from PhoneA to PhoneB, accept on PhoneA, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneA, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_csfb, (self.log, ads[0])),
+                 (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
+
+        return two_phone_call_short_seq(
+            self.log,
+            ads[0],
+            phone_idle_csfb,
+            is_phone_in_call_csfb,
+            ads[1],
+            phone_idle_csfb,
+            is_phone_in_call_csfb,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_3g_to_3g(self):
+        """ 3G to 3G call test
+
+        1. Make Sure PhoneA is in 3G mode.
+        2. Make Sure PhoneB is in 3G mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneA, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneA, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_3g, (self.log, ads[0])),
+                 (phone_setup_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_3g,
+            is_phone_in_call_3g,
+            ads[1],
+            phone_idle_3g,
+            is_phone_in_call_3g,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_volte_long(self):
+        """ VoLTE to VoLTE call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (with VoLTE).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        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_long_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_long_wfc_wifi_only(self):
+        """ WiFi Only, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA WFC mode: WIFI_ONLY.
+        2. Setup PhoneB WFC mode: WIFI_ONLY.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        return two_phone_call_long_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_long_wfc_wifi_preferred(self):
+        """ WiFi Preferred, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA WFC mode: WIFI_PREFERRED.
+        2. Setup PhoneB WFC mode: WIFI_PREFERRED.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, 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
+
+        return two_phone_call_long_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_long_apm_wfc_wifi_only(self):
+        """ Airplane + WiFi Only, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_ONLY.
+        2. Setup PhoneB in airplane mode, WFC mode: WIFI_ONLY.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, 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_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], True, WFC_MODE_WIFI_ONLY,
+                  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
+
+        return two_phone_call_long_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)
+
+    @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
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_PREFERRED.
+        2. Setup PhoneB in airplane mode, WFC mode: WIFI_PREFERRED.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, 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
+
+        return two_phone_call_long_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)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_csfb_3g_to_csfb_3g_long(self):
+        """ CSFB 3G to CSFB 3G call test
+
+        1. Make Sure PhoneA is in LTE mode, VoLTE disabled.
+        2. Make Sure PhoneB is in LTE mode, VoLTE disabled.
+        3. Call from PhoneA to PhoneB, accept on PhoneA, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneA, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_csfb, (self.log, ads[0])),
+                 (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
+
+        return two_phone_call_long_seq(
+            self.log,
+            ads[0],
+            phone_idle_csfb,
+            is_phone_in_call_csfb,
+            ads[1],
+            phone_idle_csfb,
+            is_phone_in_call_csfb,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_3g_to_3g_long(self):
+        """ 3G to 3G call test
+
+        1. Make Sure PhoneA is in 3G mode.
+        2. Make Sure PhoneB is in 3G mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneA, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneA, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_3g, (self.log, ads[0])),
+                 (phone_setup_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_long_seq(
+            self.log,
+            ads[0],
+            phone_idle_3g,
+            is_phone_in_call_3g,
+            ads[1],
+            phone_idle_3g,
+            is_phone_in_call_3g,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_volte_loop(self):
+        """ Stress test: VoLTE to VoLTE call test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is in LTE mode (with VoLTE).
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+        7. Repeat step 3~6.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        # TODO : Make this a parameter
+        MINIMUM_SUCCESS_RATE = .95
+        ads = self.android_devices
+
+        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
+
+        success_count = 0
+        fail_count = 0
+
+        for i in range(1, self.stress_test_number+1):
+
+            if two_phone_call_long_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):
+                success_count += 1
+                result_str = "Succeeded"
+
+            else:
+                fail_count += 1
+                result_str = "Failed"
+
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_loop_wfc_wifi_only(self):
+        """ Stress test: WiFi Only, WiFi calling to WiFi Calling test
+
+        1. Setup PhoneA WFC mode: WIFI_ONLY.
+        2. Setup PhoneB WFC mode: WIFI_ONLY.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+        7. Repeat step 3~6.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        # TODO : Make this a parameter
+        MINIMUM_SUCCESS_RATE = .95
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], False, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], False, WFC_MODE_WIFI_ONLY,
+                  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
+
+        success_count = 0
+        fail_count = 0
+
+        for i in range(1, self.stress_test_number+1):
+
+            if two_phone_call_long_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):
+                success_count += 1
+                result_str = "Succeeded"
+
+            else:
+                fail_count += 1
+                result_str = "Failed"
+
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_loop_wfc_wifi_preferred(self):
+        """ Stress test: WiFi Preferred, WiFi Calling to WiFi Calling test
+
+        1. Setup PhoneA WFC mode: WIFI_PREFERRED.
+        2. Setup PhoneB WFC mode: WIFI_PREFERRED.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+        7. Repeat step 3~6.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        # TODO : Make this a parameter
+        MINIMUM_SUCCESS_RATE = .95
+        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
+
+        success_count = 0
+        fail_count = 0
+
+        for i in range(1, self.stress_test_number+1):
+
+            if two_phone_call_long_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):
+                success_count += 1
+                result_str = "Succeeded"
+
+            else:
+                fail_count += 1
+                result_str = "Failed"
+
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_loop_apm_wfc_wifi_only(self):
+        """ Stress test: Airplane + WiFi Only, WiFi Calling to WiFi Calling test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_ONLY.
+        2. Setup PhoneB in airplane mode, WFC mode: WIFI_ONLY.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+        7. Repeat step 3~6.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        # TODO : Make this a parameter
+        MINIMUM_SUCCESS_RATE = .95
+        ads = self.android_devices
+
+        tasks = [(phone_setup_iwlan, (self.log, ads[0], True, WFC_MODE_WIFI_ONLY,
+                  self.wifi_network_ssid, self.wifi_network_pass)),
+                 (phone_setup_iwlan, (self.log, ads[1], True, WFC_MODE_WIFI_ONLY,
+                  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
+
+        success_count = 0
+        fail_count = 0
+
+        for i in range(1, self.stress_test_number+1):
+
+            if two_phone_call_long_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):
+                success_count += 1
+                result_str = "Succeeded"
+
+            else:
+                fail_count += 1
+                result_str = "Failed"
+
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_to_epdg_loop_apm_wfc_wifi_preferred(self):
+        """ Stress test: Airplane + WiFi Preferred, WiFi Calling to WiFi Calling test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_PREFERRED.
+        2. Setup PhoneB in airplane mode, WFC mode: WIFI_PREFERRED.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+        7. Repeat step 3~6.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        # TODO : Make this a parameter
+        MINIMUM_SUCCESS_RATE = .95
+        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
+
+        success_count = 0
+        fail_count = 0
+
+        for i in range(1, self.stress_test_number+1):
+
+            if two_phone_call_long_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):
+                success_count += 1
+                result_str = "Succeeded"
+
+            else:
+                fail_count += 1
+                result_str = "Failed"
+
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        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:
+            return True
+        else:
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_csfb_3g_to_csfb_3g_loop(self):
+        """ Stress test: CSFB 3G to CSFB 3G call test
+
+        1. Make Sure PhoneA is in LTE mode, VoLTE disabled.
+        2. Make Sure PhoneB is in LTE mode, VoLTE disabled.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+        7. Repeat step 3~6.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        # TODO : Make this a parameter
+        MINIMUM_SUCCESS_RATE = .95
+        ads = self.android_devices
+
+        tasks = [(phone_setup_csfb, (self.log, ads[0])),
+                 (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
+
+        success_count = 0
+        fail_count = 0
+
+        for i in range(1, self.stress_test_number+1):
+
+            if two_phone_call_long_seq(
+                    self.log,
+                    ads[0],
+                    phone_idle_csfb,
+                    is_phone_in_call_csfb,
+                    ads[1],
+                    phone_idle_csfb,
+                    is_phone_in_call_csfb,
+                    None):
+                success_count += 1
+                result_str = "Succeeded"
+
+            else:
+                fail_count += 1
+                result_str = "Failed"
+
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        self.log.info("Final Count - Success: {}, Failure: {}".format(
+            success_count, fail_count))
+        if success_count / (success_count+fail_count) >= MINIMUM_SUCCESS_RATE:
+            return True
+        else:
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_3g_to_3g_loop(self):
+        """ Stress test: 3G to 3G call test
+
+        1. Make Sure PhoneA is in 3G mode
+        2. Make Sure PhoneB is in 3G mode
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        6. Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+        7. Repeat step 3~6.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        # TODO : Make this a parameter
+        MINIMUM_SUCCESS_RATE = .95
+        ads = self.android_devices
+
+        tasks = [(phone_setup_3g, (self.log, ads[0])),
+                 (phone_setup_3g, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+
+        success_count = 0
+        fail_count = 0
+
+        for i in range(1, self.stress_test_number+1):
+
+            if two_phone_call_long_seq(
+                    self.log,
+                    ads[0],
+                    phone_idle_3g,
+                    is_phone_in_call_3g,
+                    ads[1],
+                    phone_idle_3g,
+                    is_phone_in_call_3g,
+                    None):
+                success_count += 1
+                result_str = "Succeeded"
+
+            else:
+                fail_count += 1
+                result_str = "Failed"
+
+            self.log.info("Iteration {} {}. Current: {} / {} passed.".
+                          format(i, result_str, success_count, self.stress_test_number))
+
+        self.log.info("Final Count - Success: {}, Failure: {}".format(
+            success_count, fail_count))
+        if success_count / (success_count+fail_count) >= MINIMUM_SUCCESS_RATE:
+            return True
+        else:
+            return False
+
+    def _hold_unhold_test(self, ads):
+        """ Test hold/unhold functionality.
+
+        PhoneA is in call with PhoneB. The call on PhoneA is active.
+        Get call list on PhoneA.
+        Hold call_id on PhoneA.
+        Check call_id state.
+        Unhold call_id on PhoneA.
+        Check call_id state.
+
+        Args:
+            ads: List of android objects.
+                This list should contain 2 android objects.
+                ads[0] is the ad to do hold/unhold operation.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        call_list = ads[0].droid.telecomCallGetCallIds()
+        self.log.info("Calls in PhoneA{}".format(call_list))
+        if num_active_calls(self.log, ads[0]) != 1:
+            return False
+        call_id = call_list[0]
+
+        if ads[0].droid.telecomCallGetCallState(call_id) != CALL_STATE_ACTIVE:
+            self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
+                           format(call_id, ads[0].droid.telecomCallGetCallState(call_id)))
+            return False
+        # TODO: Future add voice check.
+
+        self.log.info("Hold call_id {} on PhoneA".format(call_id))
+        ads[0].droid.telecomCallHold(call_id)
+        time.sleep(WAIT_TIME_IN_CALL)
+        if ads[0].droid.telecomCallGetCallState(call_id) != CALL_STATE_HOLDING:
+            self.log.error("Call_id:{}, state:{}, expected: STATE_HOLDING".
+                           format(call_id, ads[0].droid.telecomCallGetCallState(call_id)))
+            return False
+        # TODO: Future add voice check.
+
+        self.log.info("Unhold call_id {} on PhoneA".format(call_id))
+        ads[0].droid.telecomCallUnhold(call_id)
+        time.sleep(WAIT_TIME_IN_CALL)
+        if ads[0].droid.telecomCallGetCallState(call_id) != CALL_STATE_ACTIVE:
+            self.log.error("Call_id:{}, state:{}, expected: STATE_ACTIVE".
+                           format(call_id, ads[0].droid.telecomCallGetCallState(call_id)))
+            return False
+        # TODO: Future add voice check.
+
+        if not verify_incall_state(self.log, [ads[0], ads[1]], True):
+            self.log.error("Caller/Callee dropped call.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_mo_hold_unhold_wfc_wifi_only(self):
+        """ WiFi Only, WiFi calling MO call hold/unhold test
+
+        1. Setup PhoneA WFC mode: WIFI_ONLY.
+        2. Make sure PhoneB can make/receive voice call.
+        3. Call from PhoneA to PhoneB, accept on PhoneB.
+        4. Hold and unhold 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_ONLY,
+                  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
+
+        ads[0].droid.telecomClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MO Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_iwlan,
+                               verify_callee_func=None):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_mo_hold_unhold_wfc_wifi_preferred(self):
+        """ WiFi Preferred, WiFi calling MO call hold/unhold test
+
+        1. Setup PhoneA WFC mode: WIFI_PREFERRED.
+        2. Make sure PhoneB can make/receive voice call.
+        3. Call from PhoneA to PhoneB, accept on PhoneB.
+        4. Hold and unhold 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_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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MO Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_iwlan,
+                               verify_callee_func=None):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_mo_hold_unhold_apm_wfc_wifi_only(self):
+        """ Airplane + WiFi Only, WiFi calling MO call hold/unhold test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_ONLY.
+        2. Make sure PhoneB can make/receive voice call.
+        3. Call from PhoneA to PhoneB, accept on PhoneB.
+        4. Hold and unhold 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_ONLY,
+                  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
+
+        ads[0].droid.telecomClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MO Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_iwlan,
+                               verify_callee_func=None):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_mo_hold_unhold_apm_wfc_wifi_preferred(self):
+        """ Airplane + WiFi Preferred, WiFi calling MO call hold/unhold test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_PREFERRED.
+        2. Make sure PhoneB can make/receive voice call.
+        3. Call from PhoneA to PhoneB, accept on PhoneB.
+        4. Hold and unhold 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_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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MO Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_iwlan,
+                               verify_callee_func=None):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_mt_hold_unhold_wfc_wifi_only(self):
+        """ WiFi Only, WiFi calling MT call hold/unhold test
+
+        1. Setup PhoneA WFC mode: WIFI_ONLY.
+        2. Make sure PhoneB can make/receive voice call.
+        3. Call from PhoneB to PhoneA, accept on PhoneA.
+        4. Hold and unhold 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_ONLY,
+                  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
+
+        ads[0].droid.telecomClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MT Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[1], ads[0],
+                               ad_hangup=None,
+                               verify_caller_func=None,
+                               verify_callee_func=is_phone_in_call_iwlan):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_mt_hold_unhold_wfc_wifi_preferred(self):
+        """ WiFi Preferred, WiFi calling MT call hold/unhold test
+
+        1. Setup PhoneA WFC mode: WIFI_PREFERRED.
+        2. Make sure PhoneB can make/receive voice call.
+        3. Call from PhoneB to PhoneA, accept on PhoneA.
+        4. Hold and unhold 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_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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MT Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[1], ads[0],
+                               ad_hangup=None,
+                               verify_caller_func=None,
+                               verify_callee_func=is_phone_in_call_iwlan):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_mt_hold_unhold_apm_wfc_wifi_only(self):
+        """ Airplane + WiFi Only, WiFi calling MT call hold/unhold test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_ONLY.
+        2. Make sure PhoneB can make/receive voice call.
+        3. Call from PhoneB to PhoneA, accept on PhoneA.
+        4. Hold and unhold 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_ONLY,
+                  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
+
+        ads[0].droid.telecomClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MT Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[1], ads[0],
+                               ad_hangup=None,
+                               verify_caller_func=None,
+                               verify_callee_func=is_phone_in_call_iwlan):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_mt_hold_unhold_apm_wfc_wifi_preferred(self):
+        """ Airplane + WiFi Preferred, WiFi calling MT call hold/unhold test
+
+        1. Setup PhoneA in airplane mode, WFC mode: WIFI_PREFERRED.
+        2. Make sure PhoneB can make/receive voice call.
+        3. Call from PhoneB to PhoneA, accept on PhoneA.
+        4. Hold and unhold 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_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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MT Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[1], ads[0],
+                               ad_hangup=None,
+                               verify_caller_func=None,
+                               verify_callee_func=is_phone_in_call_iwlan):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_mo_hold_unhold(self):
+        """ VoLTE MO call hold/unhold test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is able to make/receive call.
+        3. Call from PhoneA to PhoneB, accept on PhoneB.
+        4. Hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_volte, (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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MO Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_volte,
+                               verify_callee_func=None):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_mt_hold_unhold(self):
+        """ VoLTE MT call hold/unhold test
+
+        1. Make Sure PhoneA is in LTE mode (with VoLTE).
+        2. Make Sure PhoneB is able to make/receive call.
+        3. Call from PhoneB to PhoneA, accept on PhoneA.
+        4. Hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_volte, (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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MT Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[1], ads[0],
+                               ad_hangup=None,
+                               verify_caller_func=None,
+                               verify_callee_func=is_phone_in_call_volte):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_wcdma_mo_hold_unhold(self):
+        """ MO WCDMA hold/unhold test
+
+        1. Make Sure PhoneA is in 3G WCDMA mode.
+        2. Make Sure PhoneB is able to make/receive call.
+        3. Call from PhoneA to PhoneB, accept on PhoneB.
+        4. Hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+            return False
+
+        tasks = [(phone_setup_3g, (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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MO Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_3g,
+                               verify_callee_func=None):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_wcdma_mt_hold_unhold(self):
+        """ MT WCDMA hold/unhold test
+
+        1. Make Sure PhoneA is in 3G WCDMA mode.
+        2. Make Sure PhoneB is able to make/receive call.
+        3. Call from PhoneB to PhoneA, accept on PhoneA.
+        4. Hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+            return False
+
+        tasks = [(phone_setup_3g, (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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MT Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[1], ads[0],
+                               ad_hangup=None,
+                               verify_caller_func=None,
+                               verify_callee_func=is_phone_in_call_3g):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_csfb_mo_hold_unhold(self):
+        """ MO CSFB WCDMA/GSM hold/unhold test
+
+        1. Make Sure PhoneA is in LTE mode (VoLTE disabled).
+        2. Make Sure PhoneB is able to make/receive call.
+        3. Call from PhoneA to PhoneB, accept on PhoneB.
+        4. Hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+            return False
+
+        tasks = [(phone_setup_csfb, (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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MO Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_csfb,
+                               verify_callee_func=None):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_csfb_mt_hold_unhold(self):
+        """ MT CSFB WCDMA/GSM hold/unhold test
+
+        1. Make Sure PhoneA is in LTE mode (VoLTE disabled).
+        2. Make Sure PhoneB is able to make/receive call.
+        3. Call from PhoneB to PhoneA, accept on PhoneA.
+        4. Hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+            return False
+
+        tasks = [(phone_setup_csfb, (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].droid.telecomCallClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MT Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[1], ads[0],
+                               ad_hangup=None,
+                               verify_caller_func=None,
+                               verify_callee_func=is_phone_in_call_csfb):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_apm_wifi_connected_toggle_wfc(self):
+        """In APM and WiFi connected, Toggling WFC Triggers RAT change
+
+        1. Make sure phone WFC disabled.
+        2. Make sure phone in APM, WiFi connected, phone not in iwlan rat.
+        3. Set phone WFC enabled, verify phone WFC available, report iwlan rat
+        4. Set phone WFC disabled, verify phone WFC unavailable, not report iwlan rat.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ad = self.android_devices[0]
+        if not set_wfc_mode(self.log, ad, WFC_MODE_DISABLED):
+            self.log.error("{} Disable WFC failed.".format(ad.serial))
+            return False
+        toggle_airplane_mode(self.log, ad, True)
+        if not ensure_wifi_connected(self.log, ad,
+                                     self.wifi_network_ssid,
+                                     self.wifi_network_pass):
+            self.log.error("{} connect to WiFi failed.".format(ad.serial))
+            return False
+        if is_droid_in_network_rat(self.log, ad, RAT_IWLAN, NETWORK_SERVICE_DATA):
+            self.log.error("{} in iwlan. Expected not in iwlan.".format(ad.serial))
+            return False
+
+        if not set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED):
+            self.log.error("{} set WFC mode failed.".format(ad.serial))
+            return False
+        if not phone_idle_iwlan(self.log, ad):
+            return False
+
+        if not set_wfc_mode(self.log, ad, WFC_MODE_DISABLED):
+            self.log.error("{} Disable WFC failed.".format(ad.serial))
+            return False
+        if not wait_for_droid_not_in_network_rat(self.log, ad,
+            RAT_IWLAN, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            self.log.error("{} in iwlan. Expected not in iwlan.".format(ad.serial))
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_nonapm_wifi_connected_toggle_wfc(self):
+        """Phone not in APM, Phone WiFI connected, Toggling WFC Triggers RAT change
+
+        1. Make sure phone WFC disabled.
+        2. Make sure phone not in APM, WiFi connected, phone not in iwlan rat.
+        3. Set phone WFC enabled, verify phone WFC available, report iwlan rat
+        4. Set phone WFC disabled, verify phone WFC unavailable, not report iwlan rat.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ad = self.android_devices[0]
+        if not set_wfc_mode(self.log, ad, WFC_MODE_DISABLED):
+            self.log.error("{} Disable WFC failed.".format(ad.serial))
+            return False
+        toggle_airplane_mode(self.log, ad, False)
+        if not ensure_wifi_connected(self.log, ad,
+                                     self.wifi_network_ssid,
+                                     self.wifi_network_pass):
+            self.log.error("{} connect to WiFi failed.".format(ad.serial))
+            return False
+        if is_droid_in_network_rat(self.log, ad, RAT_IWLAN, NETWORK_SERVICE_DATA):
+            self.log.error("{} in iwlan. Expected not in iwlan.".format(ad.serial))
+            return False
+
+        if not set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED):
+            self.log.error("{} set WFC mode failed.".format(ad.serial))
+            return False
+        if not phone_idle_iwlan(self.log, ad):
+            return False
+
+        if not set_wfc_mode(self.log, ad, WFC_MODE_DISABLED):
+            self.log.error("{} Disable WFC failed.".format(ad.serial))
+            return False
+        if not wait_for_droid_not_in_network_rat(self.log, ad,
+            RAT_IWLAN, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            self.log.error("{} in iwlan. Expected not in iwlan.".format(ad.serial))
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_apm_wfc_enabled_toggle_wifi(self):
+        """Phone in APM, WFC enabled, toggle WiFi to verify RAT change.
+
+        1. Make sure phone in APM, wifi disconnected, WFC enabled.
+        2. Connect WiFi, verify phone WFC available, report iwlan rat.
+        3. Disconnect WiFi, verify phone WFC unavailable, not report iwlan rat.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ad = self.android_devices[0]
+        if not set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED):
+            self.log.error("{} set WFC mode failed.".format(ad.serial))
+            return False
+        toggle_airplane_mode(self.log, ad, True)
+        if is_droid_in_network_rat(self.log, ad, RAT_IWLAN, NETWORK_SERVICE_DATA):
+            self.log.error("{} in iwlan. Expected not in iwlan.".format(ad.serial))
+            return False
+
+        if not ensure_wifi_connected(self.log, ad,
+                                     self.wifi_network_ssid,
+                                     self.wifi_network_pass):
+            self.log.error("{} connect to WiFi failed.".format(ad.serial))
+            return False
+        if not phone_idle_iwlan(self.log, ad):
+            return False
+
+        if not WifiUtils.wifi_toggle_state(self.log, ad, False):
+            self.log.error("{} disconnect WiFi failed.".format(ad.serial))
+            return False
+        if not wait_for_droid_not_in_network_rat(self.log, ad,
+            RAT_IWLAN, WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            self.log.error("{} in iwlan. Expected not in iwlan.".format(ad.serial))
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_erase_all_pending_voicemail(self):
+        """Script for TMO/ATT/SPT phone to erase all pending voice mail.
+        This script only works if phone have already set up voice mail options,
+        and phone should disable password protection for voice mail.
+
+        1. If phone don't have pending voice message, return True.
+        2. Dial voice mail number.
+            For TMO, the number is '123'.
+            For ATT, the number is phone's number.
+            For SPT, the number is phone's number.
+        3. Use DTMF to delete all pending voice messages.
+        4. Check getVoiceMailCount result. it should be 0.
+
+        Returns:
+            False if error happens. True is succeed.
+        """
+        ads = self.android_devices
+
+        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
+
+        return call_voicemail_erase_all_pending_voicemail(self.log,
+            self.android_devices[1])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voicemail_indicator_volte(self):
+        """Test Voice Mail notification in LTE (VoLTE enabled).
+        This script currently only works for TMO now.
+
+        1. Make sure DUT (ads[1]) in VoLTE mode. Both PhoneB (ads[0]) and DUT idle.
+        2. Make call from PhoneB to DUT, reject on DUT.
+        3. On PhoneB, leave a voice mail to DUT.
+        4. Verify DUT receive voice mail notification.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_general, (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
+        if not call_voicemail_erase_all_pending_voicemail(self.log, ads[1]):
+            self.log.error("Failed to clear voice mail.")
+            return False
+
+        return two_phone_call_leave_voice_mail(
+            self.log,
+            ads[0],
+            None,
+            None,
+            ads[1],
+            phone_idle_volte)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voicemail_indicator_lte(self):
+        """Test Voice Mail notification in LTE (VoLTE disabled).
+        This script currently only works for TMO/ATT/SPT now.
+
+        1. Make sure DUT (ads[1]) in LTE (No VoLTE) mode. Both PhoneB (ads[0]) and DUT idle.
+        2. Make call from PhoneB to DUT, reject on DUT.
+        3. On PhoneB, leave a voice mail to DUT.
+        4. Verify DUT receive voice mail notification.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_general, (self.log, ads[0])),
+                 (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
+        if not call_voicemail_erase_all_pending_voicemail(self.log, ads[1]):
+            self.log.error("Failed to clear voice mail.")
+            return False
+
+        return two_phone_call_leave_voice_mail(
+            self.log,
+            ads[0],
+            None,
+            None,
+            ads[1],
+            phone_idle_csfb)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voicemail_indicator_3g(self):
+        """Test Voice Mail notification in 3G
+        This script currently only works for TMO/ATT/SPT now.
+
+        1. Make sure DUT (ads[1]) in 3G mode. Both PhoneB (ads[0]) and DUT idle.
+        2. Make call from PhoneB to DUT, reject on DUT.
+        3. On PhoneB, leave a voice mail to DUT.
+        4. Verify DUT receive voice mail notification.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_general, (self.log, ads[0])),
+                 (phone_setup_3g, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+        if not call_voicemail_erase_all_pending_voicemail(self.log, ads[1]):
+            self.log.error("Failed to clear voice mail.")
+            return False
+
+        return two_phone_call_leave_voice_mail(
+            self.log,
+            ads[0],
+            None,
+            None,
+            ads[1],
+            phone_idle_3g)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voicemail_indicator_iwlan(self):
+        """Test Voice Mail notification in WiFI Calling
+        This script currently only works for TMO now.
+
+        1. Make sure DUT (ads[1]) in WFC mode. Both PhoneB (ads[0]) and DUT idle.
+        2. Make call from PhoneB to DUT, reject on DUT.
+        3. On PhoneB, leave a voice mail to DUT.
+        4. Verify DUT receive voice mail notification.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_general, (self.log, ads[0])),
+                 (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 call_voicemail_erase_all_pending_voicemail(self.log, ads[1]):
+            self.log.error("Failed to clear voice mail.")
+            return False
+
+        return two_phone_call_leave_voice_mail(
+            self.log,
+            ads[0],
+            None,
+            None,
+            ads[1],
+            phone_idle_iwlan)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voicemail_indicator_apm_iwlan(self):
+        """Test Voice Mail notification in WiFI Calling
+        This script currently only works for TMO now.
+
+        1. Make sure DUT (ads[1]) in APM WFC mode. Both PhoneB (ads[0]) and DUT idle.
+        2. Make call from PhoneB to DUT, reject on DUT.
+        3. On PhoneB, leave a voice mail to DUT.
+        4. Verify DUT receive voice mail notification.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_general, (self.log, ads[0])),
+                 (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 call_voicemail_erase_all_pending_voicemail(self.log, ads[1]):
+            self.log.error("Failed to clear voice mail.")
+            return False
+
+        return two_phone_call_leave_voice_mail(
+            self.log,
+            ads[0],
+            None,
+            None,
+            ads[1],
+            phone_idle_iwlan)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_2g_to_2g(self):
+        """ Test 2g<->2g call functionality.
+
+        Make Sure PhoneA is in 2g mode.
+        Make Sure PhoneB is in 2g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_2g, (self.log, ads[0])),
+                 (phone_setup_voice_2g, (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_2g,
+            is_phone_in_call_2g,
+            ads[1],
+            phone_idle_2g,
+            is_phone_in_call_2g,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_2g_to_2g_long(self):
+        """ Test 2g<->2g call functionality.
+
+        Make Sure PhoneA is in 2g mode.
+        Make Sure PhoneB is in 2g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_2g, (self.log, ads[0])),
+                 (phone_setup_voice_2g, (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_long_seq(
+            self.log,
+            ads[0],
+            phone_idle_2g,
+            is_phone_in_call_2g,
+            ads[1],
+            phone_idle_2g,
+            is_phone_in_call_2g,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_3g_to_2g_long(self):
+        """ Test 3g<->2g call functionality.
+
+        Make Sure PhoneA is in 3g mode.
+        Make Sure PhoneB is in 2g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_3g, (self.log, ads[0])),
+                 (phone_setup_voice_2g, (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_long_seq(
+            self.log,
+            ads[0],
+            phone_idle_2g,
+            is_phone_in_call_3g,
+            ads[1],
+            phone_idle_2g,
+            is_phone_in_call_2g,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_2g_to_3g_long(self):
+        """ Test 2g<->3g call functionality.
+
+        Make Sure PhoneA is in 2g mode.
+        Make Sure PhoneB is in 3g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_voice_2g, (self.log, ads[0])),
+                 (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_long_seq(
+            self.log,
+            ads[0],
+            phone_idle_2g,
+            is_phone_in_call_2g,
+            ads[1],
+            phone_idle_2g,
+            is_phone_in_call_3g,
+            None)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_gsm_mo_hold_unhold(self):
+        """ Test GSM call hold/unhold functionality.
+
+        Make Sure PhoneA is in 2g mode (GSM).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+            return False
+
+        tasks = [(phone_setup_voice_2g, (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].droid.telecomClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MO Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[0], ads[1],
+                               ad_hangup=None,
+                               verify_caller_func=is_phone_in_call_2g,
+                               verify_callee_func=None):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_gsm_mt_hold_unhold(self):
+        """ Test GSM call hold/unhold functionality.
+
+        Make Sure PhoneA is in 2g mode (GSM).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneB to PhoneA, accept on PhoneA, hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # make sure PhoneA is GSM phone before proceed.
+        if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+            return False
+
+        tasks = [(phone_setup_voice_2g, (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].droid.telecomClearCallList()
+        if num_active_calls(self.log, ads[0]) != 0:
+            self.log.error("Phone {} Call List is not empty."
+                           .format(ads[0].serial))
+            return False
+
+        self.log.info("Begin MT Call Hold/Unhold Test.")
+        if not call_setup_teardown(self.log, ads[1], ads[0],
+                               ad_hangup=None,
+                               verify_caller_func=None,
+                               verify_callee_func=is_phone_in_call_2g):
+            return False
+
+        if not self._hold_unhold_test(ads):
+            self.log.error("Hold/Unhold test fail.")
+            return False
+
+        return True
+
+    #SIM2 tests
+    def _reset_subscriptions_to_sim1(self, ads):
+        set_call_state_listen_level(self.log, ads[0], False,
+            self.sim_sub_ids[0][1])
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        setup_sim(self.log, ads[0], self.sim_sub_ids[0][0], True, True)
+        ads[0].droid.phoneSetPreferredNetworkTypeForSubscription(
+                RAT_3G, self.sim_sub_ids[0][0])
+        set_call_state_listen_level(self.log, ads[0], False,
+            self.sim_sub_ids[1][1])
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        setup_sim(self.log, ads[1], self.sim_sub_ids[1][0], True, True)
+        ads[1].droid.phoneSetPreferredNetworkTypeForSubscription(
+                RAT_3G, self.sim_sub_ids[1][0])
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_3g_to_3g_sim2(self):
+        """ Test 3g<->3g call functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 3g mode.
+        Make Sure PhoneB is in 3g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            tasks = [(phone_setup_3g, (self.log, ads[0])),
+                     (phone_setup_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_3g,
+                 is_phone_in_call_3g,
+                 ads[1],
+                 phone_idle_3g,
+                 is_phone_in_call_3g,
+                 None)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_3g_to_3g_long_sim2(self):
+        """ Test 3g<->3g call functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 3g mode.
+        Make Sure PhoneB is in 3g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            tasks = [(phone_setup_3g, (self.log, ads[0])),
+                 (phone_setup_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_long_seq(
+                self.log,
+                ads[0],
+                phone_idle_3g,
+                is_phone_in_call_3g,
+                ads[1],
+                phone_idle_3g,
+                is_phone_in_call_3g,
+                None)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_wcdma_mo_hold_unhold_sim2(self):
+        """ Test VoLTE call hold/unhold functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 3g mode (WCDMA).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            # make sure PhoneA is GSM phone before proceed.
+            if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+                return False
+
+            tasks = [(phone_setup_3g, (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].droid.telecomClearCallList()
+            if num_active_calls(self.log, ads[0]) != 0:
+                self.log.error("Phone {} Call List is not empty."
+                              .format(ads[0].serial))
+                return False
+
+            self.log.info("Begin MO Call Hold/Unhold Test.")
+            if not call_setup_teardown(self.log, ads[0], ads[1],
+                                   ad_hangup=None,
+                                   verify_caller_func=is_phone_in_call_3g,
+                                   verify_callee_func=None):
+                return False
+
+            if not self._hold_unhold_test(ads):
+                self.log.error("Hold/Unhold test fail.")
+                return False
+
+            return True
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_wcdma_mt_hold_unhold_sim2(self):
+        """ Test VoLTE call hold/unhold functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 3g mode (WCDMA).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneB to PhoneA, accept on PhoneA, hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            # make sure PhoneA is GSM phone before proceed.
+            if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+                return False
+
+            tasks = [(phone_setup_3g, (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].droid.telecomClearCallList()
+            if num_active_calls(self.log, ads[0]) != 0:
+                self.log.error("Phone {} Call List is not empty."
+                              .format(ads[0].serial))
+                return False
+
+            self.log.info("Begin MO Call Hold/Unhold Test.")
+            if not call_setup_teardown(self.log, ads[1], ads[0],
+                                   ad_hangup=None,
+                                   verify_caller_func=None,
+                                   verify_callee_func=is_phone_in_call_3g):
+                return False
+
+            if not self._hold_unhold_test(ads):
+                self.log.error("Hold/Unhold test fail.")
+                return False
+
+            return True
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_3g_to_2g_long_sim2(self):
+        """ Test 3g<->2g call functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 3g mode.
+        Make Sure PhoneB is in 2g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            tasks = [(phone_setup_voice_3g, (self.log, ads[0])),
+                     (phone_setup_voice_2g, (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_long_seq(
+                self.log,
+                ads[0],
+                phone_idle_3g,
+                is_phone_in_call_3g,
+                ads[1],
+                phone_idle_2g,
+                is_phone_in_call_2g,
+                None)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_2g_to_3g_long_sim2(self):
+        """ Test 2g<->3g call functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 2g mode.
+        Make Sure PhoneB is in 3g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            tasks = [(phone_setup_voice_2g, (self.log, ads[0])),
+                     (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_long_seq(
+                self.log,
+                ads[0],
+                phone_idle_2g,
+                is_phone_in_call_2g,
+                ads[1],
+                phone_idle_3g,
+                is_phone_in_call_3g,
+                None)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_2g_to_2g_sim2(self):
+        """ Test 2g<->2g call functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 2g mode.
+        Make Sure PhoneB is in 2g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            tasks = [(phone_setup_voice_2g, (self.log, ads[0])),
+                     (phone_setup_voice_2g, (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_2g,
+                is_phone_in_call_2g,
+                ads[1],
+                phone_idle_2g,
+                is_phone_in_call_2g,
+                None)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_2g_to_2g_long_sim2(self):
+        """ Test 2g<->2g call functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 2g mode.
+        Make Sure PhoneB is in 2g mode.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneB.
+        Call from PhoneB to PhoneA, accept on PhoneA, hang up on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            tasks = [(phone_setup_voice_2g, (self.log, ads[0])),
+                 (phone_setup_voice_2g, (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_long_seq(
+                self.log,
+                ads[0],
+                phone_idle_2g,
+                is_phone_in_call_2g,
+                ads[1],
+                phone_idle_2g,
+                is_phone_in_call_2g,
+                None)
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_gsm_mo_hold_unhold_sim2(self):
+        """ Test GSM call hold/unhold functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 2g mode (GSM).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            # make sure PhoneA is GSM phone before proceed.
+            if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+                return False
+
+            tasks = [(phone_setup_voice_2g, (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].droid.telecomCallClearCallList()
+            if num_active_calls(self.log, ads[0]) != 0:
+                self.log.error("Phone {} Call List is not empty."
+                               .format(ads[0].serial))
+                return False
+
+            self.log.info("Begin MO Call Hold/Unhold Test.")
+            if not call_setup_teardown(self.log, ads[0], ads[1],
+                                   ad_hangup=None,
+                                   verify_caller_func=is_phone_in_call_2g,
+                                   verify_callee_func=None):
+                return False
+
+            if not self._hold_unhold_test(ads):
+                self.log.error("Hold/Unhold test fail.")
+                return False
+
+            return True
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_gsm_mt_hold_unhold_sim2(self):
+        """ Test GSM call hold/unhold functionality.
+
+        Set SIM2 as the default voice SIM
+        Make Sure PhoneA is in 2g mode (GSM).
+        Make Sure PhoneB is able to make/receive call.
+        Call from PhoneB to PhoneA, accept on PhoneA, hold and unhold on PhoneA.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        try:
+            ads = self.android_devices
+            set_call_state_listen_level(self.log, ads[0], True,
+                                        self.sim_sub_ids[0][1])
+            if not setup_sim(self.log, ads[0], self.sim_sub_ids[0][1], True, True):
+                return False
+
+            # make sure PhoneA is GSM phone before proceed.
+            if (ads[0].droid.getPhoneType() != PHONE_TYPE_GSM):
+                self.log.error("Not GSM phone, abort this wcdma hold/unhold test.")
+                return False
+
+            tasks = [(phone_setup_voice_2g, (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].droid.telecomCallClearCallList()
+            if num_active_calls(self.log, ads[0]) != 0:
+                self.log.error("Phone {} Call List is not empty."
+                               .format(ads[0].serial))
+                return False
+
+            self.log.info("Begin MO Call Hold/Unhold Test.")
+            if not call_setup_teardown(self.log, ads[1], ads[0],
+                                   ad_hangup=None,
+                                   verify_caller_func=None,
+                                   verify_callee_func=is_phone_in_call_2g):
+                return False
+
+            if not self._hold_unhold_test(ads):
+                self.log.error("Hold/Unhold test fail.")
+                return False
+
+            return True
+        finally:
+            self._reset_subscriptions_to_sim1(ads)
+""" Tests End """
diff --git a/acts/tests/google/tel/live/TelPowerTest.py b/acts/tests/google/tel/live/TelPowerTest.py
new file mode 100644
index 0000000..d3db4ec
--- /dev/null
+++ b/acts/tests/google/tel/live/TelPowerTest.py
@@ -0,0 +1,775 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import math
+import os
+import time
+from queue import Empty
+
+from acts.base_test import BaseTestClass
+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_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_idle_3g
+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_voice_utils import phone_setup_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_volte
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+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_phones_idle
+from acts.test_utils.tel.tel_test_utils import is_wfc_enabled
+from acts.test_utils.tel.tel_test_utils import set_phone_screen_on
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.tel_test_utils import verify_incall_state
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts.utils import create_dir
+from acts.utils import disable_doze
+from acts.utils import get_current_human_time
+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
+
+# Monsoon output Voltage in V
+MONSOON_OUTPUT_VOLTAGE = 4.2
+# Monsoon output max current in A
+MONSOON_MAX_CURRENT = 7.8
+
+# Default power test pass criteria
+DEFAULT_POWER_PASS_CRITERIA = 999
+
+# Sampling rate in Hz
+ACTIVE_CALL_TEST_SAMPLING_RATE = 100
+# Sample duration in minute
+ACTIVE_CALL_TEST_SAMPLE_TIME = 5
+# Offset time in second
+ACTIVE_CALL_TEST_OFFSET_TIME = 180
+
+# Sampling rate in Hz
+IDLE_TEST_SAMPLING_RATE = 100
+# Sample duration in minute
+IDLE_TEST_SAMPLE_TIME = 40
+# Offset time in second
+IDLE_TEST_OFFSET_TIME = 360
+
+# For wakeup ping test, the frequency to wakeup. In unit of second.
+WAKEUP_PING_TEST_WAKEUP_FREQ = 60
+
+WAKEUP_PING_TEST_NUMBER_OF_ALARM = math.ceil(
+    (IDLE_TEST_SAMPLE_TIME * 60 + IDLE_TEST_OFFSET_TIME) / WAKEUP_PING_TEST_WAKEUP_FREQ)
+
+class TelPowerTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        self.tests = (
+            # Note: For all these power tests, please do environment calibration
+            # and baseline for pass criteria.
+            # All pass criteria information should be included in test config file.
+            # The test result will be meaning less if pass criteria is not correct.
+            "test_power_active_call_3g",
+            "test_power_active_call_volte",
+            "test_power_active_call_wfc_2g_apm",
+            "test_power_active_call_wfc_2g_lte_volte_on",
+            "test_power_active_call_wfc_5g_apm",
+            "test_power_active_call_wfc_5g_lte_volte_on",
+            "test_power_idle_baseline",
+            "test_power_idle_wfc_2g_apm",
+            "test_power_idle_wfc_2g_lte",
+            "test_power_idle_lte_volte_enabled",
+            "test_power_idle_lte_volte_disabled",
+            "test_power_idle_3g",
+            "test_power_idle_lte_volte_enabled_wakeup_ping",
+            "test_power_idle_lte_volte_disabled_wakeup_ping",
+            "test_power_idle_3g_wakeup_ping",
+            )
+        TelephonyBaseTest.__init__(self, controllers)
+
+    def setup_class(self):
+        super().setup_class()
+        self.mon = self.monsoons[0]
+        self.mon.set_voltage(MONSOON_OUTPUT_VOLTAGE)
+        self.mon.set_max_current(MONSOON_MAX_CURRENT)
+        # Monsoon phone
+        self.ad = self.android_devices[0]
+        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.wifi_network_ssid_2g = self.user_params["wifi_network_ssid_2g"]
+        self.wifi_network_pass_2g = self.user_params["wifi_network_pass_2g"]
+        self.wifi_network_ssid_5g = self.user_params["wifi_network_ssid_5g"]
+        self.wifi_network_pass_5g = self.user_params["wifi_network_pass_5g"]
+
+        self.monsoon_log_path = os.path.join(self.log_path, "MonsoonLog")
+        create_dir(self.monsoon_log_path)
+        return True
+
+    def _save_logs_for_power_test(self, test_name, monsoon_result):
+        current_time = get_current_human_time()
+        file_name = "{}_{}".format(test_name, current_time)
+        if "monsoon_log_for_power_test" in self.user_params:
+            monsoon_result.save_to_text_file([monsoon_result],
+                os.path.join(self.monsoon_log_path, file_name))
+        if "bug_report_for_power_test" in self.user_params:
+            self.take_bug_reports(test_name, current_time,
+                [self.android_devices[0]])
+
+    def _test_power_active_call(self, test_name, test_setup_func,
+        pass_criteria=DEFAULT_POWER_PASS_CRITERIA,
+        phone_check_func_after_power_test=None, *args, **kwargs):
+        average_current = 0
+        try:
+            ensure_phone_default_state(self.log, self.android_devices[0])
+            ensure_phone_default_state(self.log, self.android_devices[1])
+            if not phone_setup_voice_general(self.log, self.android_devices[1]):
+                self.log.error("PhoneB Failed to Set Up Properly.")
+                return False
+            result = self.mon.execute_sequence_and_measure(
+                ACTIVE_CALL_TEST_SAMPLING_RATE,
+                ACTIVE_CALL_TEST_SAMPLE_TIME, [test_setup_func], self.ad,
+                offset_sec=ACTIVE_CALL_TEST_OFFSET_TIME, *args, **kwargs)[0]
+            self._save_logs_for_power_test(test_name, result)
+            average_current = result.average_current
+            if not verify_incall_state(self.log,
+                                       [self.android_devices[0],
+                                        self.android_devices[1]],
+                                       True):
+                self.log.error("Call drop during power test.")
+                return False
+            if ((phone_check_func_after_power_test is not None) and
+                (not phone_check_func_after_power_test(
+                    self.log, self.android_devices[0]))):
+                self.log.error("Phone is not in correct status after power test.")
+                return False
+            return (average_current <= pass_criteria)
+        except IndexError:
+            self.log.error("Phone Setup Function failed.")
+            return False
+        finally:
+            self.android_devices[1].droid.telecomEndCall()
+            self.log.info("Result: {} mA, pass criteria: {} mA".
+                format(average_current, pass_criteria))
+
+    def _test_power_idle(self, test_name, test_setup_func,
+        pass_criteria=DEFAULT_POWER_PASS_CRITERIA,
+        phone_check_func_after_power_test=None, *args, **kwargs):
+        average_current = 0
+        try:
+            ensure_phone_default_state(self.log, self.android_devices[0])
+            result = self.mon.execute_sequence_and_measure(
+                IDLE_TEST_SAMPLING_RATE,
+                IDLE_TEST_SAMPLE_TIME, [test_setup_func], self.ad,
+                offset_sec=IDLE_TEST_OFFSET_TIME, *args, **kwargs)[0]
+            self._save_logs_for_power_test(test_name, result)
+            average_current = result.average_current
+            if ((phone_check_func_after_power_test is not None) and
+                (not phone_check_func_after_power_test(
+                    self.log, self.android_devices[0]))):
+                self.log.error("Phone is not in correct status after power test.")
+                return False
+            return (average_current <= pass_criteria)
+        except IndexError:
+            self.log.error("Phone Setup Function failed.")
+            return False
+        finally:
+            self.log.info("Result: {} mA, pass criteria: {} mA".
+                format(average_current, pass_criteria))
+
+    def _start_alarm(self):
+        # FIXME: This is temporary code to start Alarm.
+        alarm_id = self.ad.droid.phoneStartRecurringAlarm(
+            WAKEUP_PING_TEST_NUMBER_OF_ALARM,
+            1000*WAKEUP_PING_TEST_WAKEUP_FREQ,
+            "PING_GOOGLE", None)
+        if alarm_id is None:
+            self.log.error("Start alarm failed.")
+            return False
+        return True
+
+    def _setup_phone_idle_and_wakeup_ping(self, ad, phone_setup_func):
+        if not phone_setup_func(self.log, ad):
+            self.log.error("Phone failed to setup {}.".format(
+                phone_setup_func.__name__))
+            return False
+        if not self._start_alarm():
+            return False
+        ad.droid.goToSleepNow()
+        return True
+
+    def _setup_phone_active_call(self, ad, phone_setup_func,
+        phone_idle_check_func, phone_in_call_check_func):
+        if not phone_setup_func(self.log, ad):
+            self.log.error("DUT Failed to Set Up Properly: {}".format(
+                phone_setup_func.__name__))
+            return False
+        ensure_phones_idle(self.log, [ad, self.android_devices[1]])
+        if not phone_idle_check_func(self.log, ad):
+            self.log.error("DUT not in correct idle state: {}".format(
+                phone_idle_check_func.__name__))
+            return False
+        if not call_setup_teardown(self.log, ad, self.android_devices[1],
+            ad_hangup=None, verify_caller_func=phone_in_call_check_func):
+            self.log.error("Setup Call failed.")
+            return False
+        ad.droid.goToSleepNow()
+        return True
+
+    def _setup_phone_active_call_wfc(self, ad, ssid, password, airplane_mode,
+        wfc_mode, setup_volte=False):
+        if setup_volte and (not phone_setup_volte(self.log, ad)):
+            self.log.error("Phone failed to setup VoLTE.")
+            return False
+        if not phone_setup_iwlan(self.log, ad, airplane_mode, wfc_mode,
+            ssid, password):
+            self.log.error("DUT Failed to Set Up WiFi Calling")
+            return False
+        ensure_phones_idle(self.log, [ad, self.android_devices[1]])
+        if not phone_idle_iwlan(self.log, ad):
+            self.log.error("DUT not in WFC enabled state.")
+            return False
+        if not call_setup_teardown(self.log, ad, self.android_devices[1],
+            ad_hangup=None, verify_caller_func=is_phone_in_call_iwlan):
+            self.log.error("Setup Call failed.")
+            return False
+        ad.droid.goToSleepNow()
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_active_call_3g(self):
+        """Power measurement test for active CS(3G) call.
+
+        Steps:
+        1. DUT idle, in 3G mode.
+        2. Make a phone Call from DUT to PhoneB. Answer on PhoneB.
+            Make sure DUT is in CS(3G) call.
+        3. Turn off screen and wait for 3 minutes.
+            Then measure power consumption for 5 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_call_3g"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        return self._test_power_active_call("test_power_active_call_3g",
+            self._setup_phone_active_call, PASS_CRITERIA,
+            phone_check_func_after_power_test=is_phone_in_call_3g,
+            phone_setup_func=phone_setup_3g, phone_idle_check_func=phone_idle_3g,
+            phone_in_call_check_func=is_phone_in_call_3g)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_active_call_volte(self):
+        """Power measurement test for active VoLTE call.
+
+        Steps:
+        1. DUT idle, in LTE mode, VoLTE enabled.
+        2. Make a phone Call from DUT to PhoneB. Answer on PhoneB.
+            Make sure DUT is in VoLTE call.
+        3. Turn off screen and wait for 3 minutes.
+            Then measure power consumption for 5 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_call_volte"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        return self._test_power_active_call("test_power_active_call_volte",
+            self._setup_phone_active_call, PASS_CRITERIA,
+            phone_check_func_after_power_test=is_phone_in_call_volte,
+            phone_setup_func=phone_setup_volte,
+            phone_idle_check_func=phone_idle_volte,
+            phone_in_call_check_func=is_phone_in_call_volte)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_active_call_wfc_2g_apm(self):
+        """Power measurement test for active WiFi call.
+
+        Steps:
+        1. DUT idle, in Airplane mode, connect to 2G WiFi,
+            WiFi Calling enabled (WiFi-preferred mode).
+        2. Make a phone Call from DUT to PhoneB. Answer on PhoneB.
+            Make sure DUT is in WFC call.
+        3. Turn off screen and wait for 3 minutes.
+            Then measure power consumption for 5 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_call_wfc_2g_apm"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        return self._test_power_active_call("test_power_active_call_wfc_2g_apm",
+            self._setup_phone_active_call_wfc, PASS_CRITERIA,
+            phone_check_func_after_power_test=is_phone_in_call_iwlan,
+            ssid=self.wifi_network_ssid_2g, password=self.wifi_network_pass_2g,
+            airplane_mode=True, wfc_mode=WFC_MODE_WIFI_PREFERRED,
+            setup_volte=False)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_active_call_wfc_2g_lte_volte_on(self):
+        """Power measurement test for active WiFi call.
+
+        Steps:
+        1. DUT idle, LTE cellular data network, VoLTE is On, connect to 2G WiFi,
+            WiFi Calling enabled (WiFi-preferred).
+        2. Make a phone Call from DUT to PhoneB. Answer on PhoneB.
+            Make sure DUT is in WFC call.
+        3. Turn off screen and wait for 3 minutes.
+            Then measure power consumption for 5 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_call_wfc_2g_lte_volte_on"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        return self._test_power_active_call("test_power_active_call_wfc_2g_lte_volte_on",
+            self._setup_phone_active_call_wfc, PASS_CRITERIA,
+            phone_check_func_after_power_test=is_phone_in_call_iwlan,
+            ssid=self.wifi_network_ssid_2g, password=self.wifi_network_pass_2g,
+            airplane_mode=False, wfc_mode=WFC_MODE_WIFI_PREFERRED,
+            setup_volte=True)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_active_call_wfc_5g_apm(self):
+        """Power measurement test for active WiFi call.
+
+        Steps:
+        1. DUT idle, in Airplane mode, connect to 5G WiFi,
+            WiFi Calling enabled (WiFi-preferred mode).
+        2. Make a phone Call from DUT to PhoneB. Answer on PhoneB.
+            Make sure DUT is in WFC call.
+        3. Turn off screen and wait for 3 minutes.
+            Then measure power consumption for 5 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_call_wfc_5g_apm"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        return self._test_power_active_call("test_power_active_call_wfc_5g_apm",
+            self._setup_phone_active_call_wfc, PASS_CRITERIA,
+            phone_check_func_after_power_test=is_phone_in_call_iwlan,
+            ssid=self.wifi_network_ssid_5g, password=self.wifi_network_pass_5g,
+            airplane_mode=True, wfc_mode=WFC_MODE_WIFI_PREFERRED,
+            setup_volte=False)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_active_call_wfc_5g_lte_volte_on(self):
+        """Power measurement test for active WiFi call.
+
+        Steps:
+        1. DUT idle, LTE cellular data network, VoLTE is On, connect to 5G WiFi,
+            WiFi Calling enabled (WiFi-preferred).
+        2. Make a phone Call from DUT to PhoneB. Answer on PhoneB.
+            Make sure DUT is in WFC call.
+        3. Turn off screen and wait for 3 minutes.
+            Then measure power consumption for 5 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_call_wfc_5g_lte_volte_on"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        return self._test_power_active_call("test_power_active_call_wfc_5g_lte_volte_on",
+            self._setup_phone_active_call_wfc, PASS_CRITERIA,
+            phone_check_func_after_power_test=is_phone_in_call_iwlan,
+            ssid=self.wifi_network_ssid_5g, password=self.wifi_network_pass_5g,
+            airplane_mode=False, wfc_mode=WFC_MODE_WIFI_PREFERRED,
+            setup_volte=True)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_idle_baseline(self):
+        """Power measurement test for phone idle baseline.
+
+        Steps:
+        1. DUT idle, in Airplane mode. WiFi disabled, WiFi Calling disabled.
+        2. Turn off screen and wait for 6 minutes. Then measure power
+            consumption for 40 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_idle_baseline"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        def _idle_baseline(ad):
+            if not toggle_airplane_mode(self.log, ad, True):
+                self.log.error("Phone failed to turn on airplane mode.")
+                return False
+            ad.droid.goToSleepNow()
+            return True
+
+        return self._test_power_idle("test_power_idle_baseline",
+            _idle_baseline, PASS_CRITERIA)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_idle_wfc_2g_apm(self):
+        """Power measurement test for phone idle WiFi Calling Airplane Mode.
+
+        Steps:
+        1. DUT idle, in Airplane mode. Connected to 2G WiFi,
+            WiFi Calling enabled (WiFi preferred).
+        2. Turn off screen and wait for 6 minutes. Then measure power
+            consumption for 40 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_idle_wfc_2g_apm"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        def _idle_wfc_2g_apm(ad):
+            if not phone_setup_iwlan(self.log, ad, True, WFC_MODE_WIFI_PREFERRED,
+                self.wifi_network_ssid_2g, self.wifi_network_pass_2g):
+                self.log.error("Phone failed to setup WFC.")
+                return False
+            ad.droid.goToSleepNow()
+            return True
+
+        return self._test_power_idle("test_power_idle_wfc_2g_apm",
+            _idle_wfc_2g_apm, PASS_CRITERIA, is_wfc_enabled)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_idle_wfc_2g_lte(self):
+        """Power measurement test for phone idle WiFi Calling LTE VoLTE enabled.
+
+        Steps:
+        1. DUT idle, in LTE mode, VoLTE enabled. Connected to 2G WiFi,
+            WiFi Calling enabled (WiFi preferred).
+        2. Turn off screen and wait for 6 minutes. Then measure power
+            consumption for 40 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_idle_wfc_2g_lte"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        def _idle_wfc_2g_lte(ad):
+            if not phone_setup_volte(self.log, ad):
+                self.log.error("Phone failed to setup VoLTE.")
+                return False
+            if not phone_setup_iwlan(self.log, ad, False, WFC_MODE_WIFI_PREFERRED,
+                self.wifi_network_ssid_2g, self.wifi_network_pass_2g):
+                self.log.error("Phone failed to setup WFC.")
+                return False
+            ad.droid.goToSleepNow()
+            return True
+
+        return self._test_power_idle("test_power_idle_wfc_2g_lte",
+            _idle_wfc_2g_lte, PASS_CRITERIA, is_wfc_enabled)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_idle_lte_volte_enabled(self):
+        """Power measurement test for phone idle LTE VoLTE enabled.
+
+        Steps:
+        1. DUT idle, in LTE mode, VoLTE enabled. WiFi disabled,
+            WiFi Calling disabled.
+        2. Turn off screen and wait for 6 minutes. Then measure power
+            consumption for 40 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_idle_lte_volte_enabled"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        def _idle_lte_volte_enabled(ad):
+            if not phone_setup_volte(self.log, ad):
+                self.log.error("Phone failed to setup VoLTE.")
+                return False
+            ad.droid.goToSleepNow()
+            return True
+
+        return self._test_power_idle("test_power_idle_lte_volte_enabled",
+            _idle_lte_volte_enabled, PASS_CRITERIA)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_idle_lte_volte_disabled(self):
+        """Power measurement test for phone idle LTE VoLTE disabled.
+
+        Steps:
+        1. DUT idle, in LTE mode, VoLTE disabled. WiFi disabled,
+            WiFi Calling disabled.
+        2. Turn off screen and wait for 6 minutes. Then measure power
+            consumption for 40 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_idle_lte_volte_disabled"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        def _idle_lte_volte_disabled(ad):
+            if not phone_setup_csfb(self.log, ad):
+                self.log.error("Phone failed to setup CSFB.")
+                return False
+            ad.droid.goToSleepNow()
+            return True
+
+        return self._test_power_idle("test_power_idle_lte_volte_disabled",
+            _idle_lte_volte_disabled, PASS_CRITERIA)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_idle_3g(self):
+        """Power measurement test for phone idle 3G.
+
+        Steps:
+        1. DUT idle, in 3G mode. WiFi disabled, WiFi Calling disabled.
+        2. Turn off screen and wait for 6 minutes. Then measure power
+            consumption for 40 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_idle_3g"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+
+        def _idle_3g(ad):
+            if not phone_setup_3g(self.log, ad):
+                self.log.error("Phone failed to setup 3g.")
+                return False
+            ad.droid.goToSleepNow()
+            return True
+
+        return self._test_power_idle("test_power_idle_3g",
+            _idle_3g, PASS_CRITERIA)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_idle_lte_volte_enabled_wakeup_ping(self):
+        """Power measurement test for phone LTE VoLTE enabled Wakeup Ping every
+        1 minute.
+
+        Steps:
+        1. DUT idle, in LTE mode, VoLTE enabled. WiFi disabled,
+            WiFi Calling disabled.
+        2. Start script to wake up AP every 1 minute, after wakeup,
+            DUT send http Request to Google.com then go to sleep.
+        3. Turn off screen and wait for 6 minutes. Then measure power
+            consumption for 40 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_idle_lte_volte_enabled_wakeup_ping"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+        # TODO: need a SL4A API to clear all existing alarms.
+
+        result = self._test_power_idle(
+            "test_power_idle_lte_volte_enabled_wakeup_ping",
+            self._setup_phone_idle_and_wakeup_ping, PASS_CRITERIA,
+            phone_setup_func=phone_setup_volte)
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_idle_lte_volte_disabled_wakeup_ping(self):
+        """Power measurement test for phone LTE VoLTE disabled Wakeup Ping every
+        1 minute.
+
+        Steps:
+        1. DUT idle, in LTE mode, VoLTE disabled. WiFi disabled,
+            WiFi Calling disabled.
+        2. Start script to wake up AP every 1 minute, after wakeup,
+            DUT send http Request to Google.com then go to sleep.
+        3. Turn off screen and wait for 6 minutes. Then measure power
+            consumption for 40 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_idle_lte_volte_disabled_wakeup_ping"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+        # TODO: need a SL4A API to clear all existing alarms.
+
+        result = self._test_power_idle(
+            "test_power_idle_lte_volte_disabled_wakeup_ping",
+            self._setup_phone_idle_and_wakeup_ping, PASS_CRITERIA,
+            phone_setup_func=phone_setup_csfb)
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_power_idle_3g_wakeup_ping(self):
+        """Power measurement test for phone 3G Wakeup Ping every 1 minute.
+
+        Steps:
+        1. DUT idle, in 3G mode. WiFi disabled, WiFi Calling disabled.
+        2. Start script to wake up AP every 1 minute, after wakeup,
+            DUT send http Request to Google.com then go to sleep.
+        3. Turn off screen and wait for 6 minutes. Then measure power
+            consumption for 40 minutes and get average.
+
+        Expected Results:
+        Average power consumption should be within pre-defined limit.
+
+        Returns:
+        True if Pass, False if Fail.
+
+        Note: Please calibrate your test environment and baseline pass criteria.
+        Pass criteria info should be in test config file.
+        """
+        try:
+            PASS_CRITERIA = int(
+                self.user_params["pass_criteria_idle_3g_wakeup_ping"]["pass_criteria"])
+        except KeyError:
+            PASS_CRITERIA = DEFAULT_POWER_PASS_CRITERIA
+        # TODO: need SL4A API to clear all existing alarms.
+
+        result = self._test_power_idle(
+            "test_power_idle_3g_wakeup_ping",
+            self._setup_phone_idle_and_wakeup_ping, PASS_CRITERIA,
+            phone_setup_func=phone_setup_3g)
+        return result
\ No newline at end of file
diff --git a/acts/tests/google/tel/live/TelWifiDataTest.py b/acts/tests/google/tel/live/TelWifiDataTest.py
new file mode 100644
index 0000000..2a457bd
--- /dev/null
+++ b/acts/tests/google/tel/live/TelWifiDataTest.py
@@ -0,0 +1,144 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+# Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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.base_test import BaseTestClass
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_atten_utils import set_rssi
+from acts.test_utils.tel.tel_defines import MAX_RSSI_RESERVED_VALUE
+from acts.test_utils.tel.tel_defines import MIN_RSSI_RESERVED_VALUE
+from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts.test_utils.tel.tel_defines import RAT_LTE
+from acts.test_utils.tel.tel_defines import WAIT_TIME_NW_SELECTION
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+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 wait_for_cell_data_connection
+from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+
+# Attenuator name
+ATTEN_NAME_FOR_WIFI = 'wifi0'
+ATTEN_NAME_FOR_CELL = 'cell0'
+
+class TelWifiDataTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      "test_wifi_cell_switching_stress",
+                      )
+        self.stress_test_number = int(self.user_params["stress_test_number"])
+        self.live_network_ssid = self.user_params["wifi_network_ssid"]
+        try:
+            self.live_network_pwd = self.user_params["wifi_network_pass"]
+        except KeyError:
+            self.live_network_pwd = None
+
+        self.attens = {}
+        for atten in self.attenuators:
+            self.attens[atten.path] = atten
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wifi_cell_switching_stress(self):
+        """Test for data switch between WiFi and Cell. DUT go in and out WiFi
+        coverage for multiple times.
+
+        Steps:
+        1. Set WiFi and Cellular signal to good (attenuation value to MIN).
+        2. Make sure DUT get Cell data coverage (LTE) and WiFi connected.
+        3. Set WiFi RSSI to MAX (WiFi attenuator value to MIN).
+        4. Verify DUT report WiFi connected and Internet access OK.
+        5. Set WiFi RSSI to MIN (WiFi attenuator value to MAX).
+        6. Verify DUT report Cellular Data connected and Internet access OK.
+        7. Repeat Step 3~6 for stress number.
+
+        Expected Results:
+        4. DUT report WiFi connected and Internet access OK.
+        6. DUT report Cellular Data connected and Internet access OK.
+        7. Stress test should pass.
+
+        Returns:
+        True if Pass. False if fail.
+        """
+        WIFI_RSSI_CHANGE_STEP_SIZE = 2
+        WIFI_RSSI_CHANGE_DELAY_PER_STEP = 1
+        # Set Attenuator Value for WiFi and Cell to 0.
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 0, MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 0, MAX_RSSI_RESERVED_VALUE)
+
+        # Make sure DUT get Cell Data coverage (LTE).
+        toggle_airplane_mode(self.log, self.android_devices[0], False)
+        if not ensure_network_rat(
+                self.log, self.android_devices[0], RAT_LTE,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            return False
+
+        # Make sure DUT WiFi is connected.
+        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".
+                    format(self.android_devices[0].serial))
+                return False
+
+        total_iteration = self.stress_test_number
+        self.log.info("Stress test. Total iteration = {}.".
+                      format(total_iteration))
+        current_iteration = 1
+        while(current_iteration <= total_iteration):
+            self.log.info(">----Current iteration = {}/{}----<".
+                          format(current_iteration, total_iteration))
+
+            # Set WiFi RSSI to MAX.
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI], 0,
+                     MAX_RSSI_RESERVED_VALUE,
+                     WIFI_RSSI_CHANGE_STEP_SIZE, WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            # Wait for DUT report WiFi connected and Internet access OK.
+            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("Data not on WiFi")
+                break
+
+            # Set WiFi RSSI to MIN (DUT lose WiFi coverage).
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI], 0,
+                     MIN_RSSI_RESERVED_VALUE,
+                     WIFI_RSSI_CHANGE_STEP_SIZE, WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            # Wait for DUT report Cellular Data connected and Internet access OK.
+            if (not wait_for_cell_data_connection(
+                    self.log, self.android_devices[0], True) or not
+                    verify_http_connection(
+                        self.log, self.android_devices[0])):
+                self.log.error("Data not on Cell")
+                break
+
+            self.log.info(">----Iteration : {}/{} succeed.----<".
+                            format(current_iteration, total_iteration))
+            current_iteration += 1
+        if current_iteration <= total_iteration:
+            self.log.info(">----Iteration : {}/{} failed.----<".
+                            format(current_iteration, total_iteration))
+            return False
+        else:
+            return True
+
+if __name__ == "__main__":
+    raise Exception("Cannot run this class directly")
+
diff --git a/acts/tests/google/tel/live/TelWifiVoiceTest.py b/acts/tests/google/tel/live/TelWifiVoiceTest.py
new file mode 100755
index 0000000..62daf23
--- /dev/null
+++ b/acts/tests/google/tel/live/TelWifiVoiceTest.py
@@ -0,0 +1,3746 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - 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 epdg RF shield box related tests.
+"""
+
+import random
+import time
+from acts.base_test import BaseTestClass
+from queue import Empty
+from acts.test_utils.tel.tel_atten_utils import *
+from acts.test_utils.tel.tel_voice_utils import *
+from acts.test_utils.tel.tel_test_utils import *
+from acts.utils import load_config
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+# Attenuator name
+ATTEN_NAME_FOR_WIFI = 'wifi0'
+ATTEN_NAME_FOR_CELL = 'cell0'
+
+# WiFi RSSI settings for ROVE_IN test
+WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN = -60
+WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN = -70
+
+# WiFi RSSI settings for ROVE_OUT test
+WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE = -60
+WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT = -70
+WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT = -90
+
+# WiFi RSSI settings for HAND_IN test
+WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_NOT_HAND_IN = -80
+WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN = -50
+
+# WiFi RSSI settings for HAND_OUT test
+WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_NOT_HAND_OUT = -70
+WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT = -85
+
+class TelWifiVoiceTest(TelephonyBaseTest):
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.tests = (
+                      # WFC Call Routing tests.
+                      # epdg, WFC, APM, WiFi strong
+                      "test_call_epdg_wfc_wifi_only_wifi_strong_apm",
+                      "test_call_epdg_wfc_wifi_preferred_wifi_strong_apm",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_strong_apm",
+
+                      # epdg, WFC, APM, WiFi Absent
+                      "test_call_epdg_wfc_wifi_only_wifi_absent_apm",
+                      "test_call_epdg_wfc_wifi_preferred_wifi_absent_apm",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_absent_apm",
+
+                      # epdg, WFC, APM, WiFi Disabled
+                      "test_call_epdg_wfc_wifi_only_wifi_disabled_apm",
+                      "test_call_epdg_wfc_wifi_preferred_wifi_disabled_apm",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_disabled_apm",
+
+                      # epdg, WFC, cellular strong, WiFi strong
+                      "test_call_epdg_wfc_wifi_preferred_wifi_strong_cellular_strong",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_strong_cellular_strong",
+
+                      # epdg, WFC, cellular strong, WiFi weak
+                      "test_call_epdg_wfc_wifi_preferred_wifi_weak_cellular_strong",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_weak_cellular_strong",
+
+                      # epdg, WFC, cellular strong, WiFi Absent
+                      "test_call_epdg_wfc_wifi_preferred_wifi_absent_cellular_strong",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_absent_cellular_strong",
+
+                      # epdg, WFC, cellular strong, WiFi Disabled
+                      "test_call_epdg_wfc_wifi_preferred_wifi_disabled_cellular_strong",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_disabled_cellular_strong",
+
+                      # epdg, WFC, cellular weak, WiFi strong
+                      "test_call_epdg_wfc_wifi_preferred_wifi_strong_cellular_weak",
+
+                      # epdg, WFC, cellular weak, WiFi Absent=
+                      "test_call_epdg_wfc_wifi_preferred_wifi_absent_cellular_weak",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_absent_cellular_weak",
+
+                      # epdg, WFC, cellular weak, WiFi Disabled
+                      "test_call_epdg_wfc_wifi_preferred_wifi_disabled_cellular_weak",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_disabled_cellular_weak",
+
+                      # epdg, WiFI strong, WFC disabled
+                      "test_call_epdg_wfc_disabled_wifi_strong_apm",
+                      "test_call_epdg_wfc_disabled_wifi_strong_cellular_strong",
+                      "test_call_epdg_wfc_disabled_wifi_strong_cellular_weak",
+
+                      # WFC Idle-Mode Mobility
+                      # Rove-in, Rove-out test
+                      "test_rove_in_lte_wifi_preferred",
+                      "test_rove_in_lte_wifi_only",
+                      "test_rove_in_wcdma_wifi_preferred",
+                      "test_rove_in_wcdma_wifi_only",
+                      "test_rove_out_lte_wifi_preferred",
+                      "test_rove_out_lte_wifi_only",
+                      "test_rove_out_wcdma_wifi_preferred",
+                      "test_rove_out_wcdma_wifi_only",
+                      "test_rove_out_in_stress",
+
+                      # WFC Active-Mode Mobility
+                      # Hand-in, Hand-out test
+                      "test_hand_out_wifi_only",
+                      "test_hand_out_wifi_preferred",
+                      "test_hand_out_in_wifi_preferred",
+                      "test_hand_in_wifi_preferred",
+                      "test_hand_in_out_wifi_preferred",
+                      "test_hand_out_in_stress",
+
+                      # WFC test with E4G disabled
+                      "test_call_epdg_wfc_wifi_preferred_e4g_disabled",
+                      "test_call_epdg_wfc_wifi_preferred_e4g_disabled_wifi_not_connected",
+                      "test_call_epdg_wfc_wifi_preferred_e4g_disabled_leave_wifi_coverage",
+
+
+                      # FIXME
+                      # TODO(yangxliu): Below Not verified yet.
+                      # These need to set Cellular RSSI lower than -116dBm.
+                      # However, current shield box can only make Cellular RSSI
+                      # as low as ~ -116dBm. So These tests logic need to
+                      # modify/verify once shield box have the capability to
+                      # set Cellular RSSI lower.
+
+                      # ePDG Active-Mode Mobility: Hand-in, Hand-out test
+                      "test_hand_out_cellular_preferred",
+                      "test_hand_in_cellular_preferred",
+
+                      # epdg, WFC, cellular weak, WiFi strong
+                      "test_call_epdg_wfc_wifi_only_wifi_strong_cellular_weak",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_strong_cellular_weak",
+
+                      # epdg, WFC, cellular weak, WiFi weak
+                      "test_call_epdg_wfc_wifi_only_wifi_weak_cellular_weak",
+                      "test_call_epdg_wfc_wifi_preferred_wifi_weak_cellular_weak",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_weak_cellular_weak",
+
+                      # epdg, WFC, cellular weak, WiFi Absent
+                      "test_call_epdg_wfc_wifi_only_wifi_absent_cellular_weak",
+
+                      # epdg, WFC, cellular weak, WiFi Disabled
+                      "test_call_epdg_wfc_wifi_only_wifi_disabled_cellular_weak",
+
+                      # epdg, WFC, cellular absent, WiFi strong
+                      "test_call_epdg_wfc_wifi_only_wifi_strong_cellular_absent",
+                      "test_call_epdg_wfc_wifi_preferred_wifi_strong_cellular_absent",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_strong_cellular_absent",
+
+                      # epdg, WFC, cellular absent, WiFi weak
+                      "test_call_epdg_wfc_wifi_only_wifi_weak_cellular_absent",
+                      "test_call_epdg_wfc_wifi_preferred_wifi_weak_cellular_absent",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_weak_cellular_absent",
+
+                      # epdg, WFC, cellular absent, WiFi Absent
+                      "test_call_epdg_wfc_wifi_only_wifi_absent_cellular_absent",
+                      "test_call_epdg_wfc_wifi_preferred_wifi_absent_cellular_absent",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_absent_cellular_absent",
+
+                      # epdg, WFC, cellular absent, WiFi Disabled
+                      "test_call_epdg_wfc_wifi_only_wifi_disabled_cellular_absent",
+                      "test_call_epdg_wfc_wifi_preferred_wifi_disabled_cellular_absent",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_disabled_cellular_absent",
+
+                      # epdg, WiFI strong, WFC disabled
+                      "test_call_epdg_wfc_disabled_wifi_strong_cellular_absent",
+
+                      # Below test fail now, because:
+                      # 1. wifi weak not working now. (phone don't rove-in)
+                      # 2. wifi-only mode not working now.
+                      # epdg, WFC, APM, WiFi weak
+                      "test_call_epdg_wfc_wifi_only_wifi_weak_apm",
+                      "test_call_epdg_wfc_wifi_preferred_wifi_weak_apm",
+                      "test_call_epdg_wfc_cellular_preferred_wifi_weak_apm",
+
+                      # epdg, WFC, cellular strong, WiFi strong
+                      "test_call_epdg_wfc_wifi_only_wifi_strong_cellular_strong",
+
+                      # epdg, WFC, cellular strong, WiFi weak
+                      "test_call_epdg_wfc_wifi_only_wifi_weak_cellular_strong",
+
+                      # epdg, WFC, cellular strong, WiFi Absent
+                      "test_call_epdg_wfc_wifi_only_wifi_absent_cellular_strong",
+
+                      # epdg, WFC, cellular strong, WiFi Disabled
+                      "test_call_epdg_wfc_wifi_only_wifi_disabled_cellular_strong",
+
+                      # RSSI monitoring
+                      "test_rssi_monitoring",
+                      )
+
+        self.simconf = load_config(self.user_params["sim_conf_file"])
+        self.stress_test_number = int(self.user_params["stress_test_number"])
+        self.live_network_ssid = self.user_params["wifi_network_ssid"]
+
+        try:
+            self.live_network_pwd = self.user_params["wifi_network_pass"]
+        except KeyError:
+            self.live_network_pwd = None
+
+        self.attens = {}
+        for atten in self.attenuators:
+            self.attens[atten.path] = atten
+
+    def setup_class(self):
+
+        super().setup_class()
+
+        self.log.info("WFC phone: <{}> <{}>".
+                      format(self.android_devices[0].serial,
+                             get_phone_number(self.log, self.android_devices[0])))
+        self.android_devices[0].droid.startTrackingSignalStrengths()
+
+        # Do WiFi RSSI calibration.
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 0, MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 0, MAX_RSSI_RESERVED_VALUE)
+        if not ensure_network_rat(self.log, self.android_devices[0], RAT_LTE,
+            WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            self.log.error("Setup_class: phone failed to select to LTE.")
+            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".
+                    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")
+            return False
+
+        # Delay WAIT_TIME_FOR_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED after WiFi
+        # Connected to make sure WiFi RSSI reported value is correct.
+        time.sleep(WAIT_TIME_FOR_WIFI_RSSI_CALIBRATION_WIFI_CONNECTED)
+        # Turn On Screen and delay WAIT_TIME_FOR_WIFI_RSSI_CALIBRATION_SCREEN_ON
+        # then get WiFi RSSI to avoid WiFi RSSI report -127(invalid value).
+        self.android_devices[0].droid.wakeUpNow()
+        time.sleep(WAIT_TIME_FOR_WIFI_RSSI_CALIBRATION_SCREEN_ON)
+
+        setattr(self, "wifi_rssi_with_no_atten",
+            self.android_devices[0].droid.wifiGetConnectionInfo()['rssi'])
+        if self.wifi_rssi_with_no_atten == INVALID_WIFI_RSSI:
+            self.log.error("Initial WiFi RSSI calibration value is wrong: -127.")
+            return False
+        self.log.info("WiFi RSSI calibration info: atten=0, RSSI={}".
+            format(self.wifi_rssi_with_no_atten))
+        ensure_phones_default_state(self.log, [self.android_devices[0]])
+
+        # Do Cellular RSSI calibration.
+        setattr(self, "cell_rssi_with_no_atten",
+            self.android_devices[0].droid.phoneGetSignalStrengthInfo()['LteDbm'])
+        self.log.info("Cellular RSSI calibration info: atten=0, RSSI={}".
+            format(self.cell_rssi_with_no_atten))
+        return True
+
+    def teardown_class(self):
+
+        for ad in self.android_devices:
+            #TODO : Move these "always" calls to generic setup/cleanup function
+            ad.droid.phoneAdjustPreciseCallStateListenLevel(
+                PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND, False)
+            ad.droid.phoneAdjustPreciseCallStateListenLevel(
+                PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING, False)
+            ad.droid.phoneAdjustPreciseCallStateListenLevel(
+                PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND, False)
+        self.android_devices[0].droid.stopTrackingSignalStrengths()
+        return True
+
+    def teardown_test(self):
+
+        super().teardown_test()
+
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 0, MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 0, MAX_RSSI_RESERVED_VALUE)
+        return True
+
+    def _wfc_call_sequence(self, ads, mo_mt,
+                                 initial_wifi_cellular_setup_func,
+                                 wfc_phone_setup_func,
+                                 verify_wfc_initial_idle_func,
+                                 verify_wfc_in_call_state_func,
+                                 incall_wifi_cellular_setting_check_func,
+                                 expected_result):
+        """_wfc_call_sequence
+
+        Args:
+            ads: list of android devices. This list should have 2 ad.
+            mo_mt: indicating this call sequence is MO or MT.
+                Valid input: "mo" and "mt".
+            initial_wifi_cellular_setup_func: Initial WiFI router and Attenuator
+                setup function before phone setup.
+            wfc_phone_setup_func: WFC phone setup function.
+            verify_wfc_initial_idle_func: Initial WFC phone idle check function.
+            verify_wfc_in_call_state_func: WFC phone in call state check function.
+            incall_wifi_cellular_setting_check_func: During call, WiFI router and Attenuator
+                change setting  and phone status check function.
+                (for test hand-in and hand-out)
+
+            expected_result: expected test result.
+                If expect call sequence finish, this value should be set to 'True'.
+                If expect call sequence not finish (eg. setup fail, call initial fail),
+                    this value should be set to "exception string"
+                    Current supported string include:
+                        "initial_wifi_cellular_setup_func fail."
+                        "wfc_phone_setup_func fail."
+                        "phone_setup_voice_general fail."
+                        "verify_wfc_initial_idle_func fail."
+                        "initiate_call fail."
+                        "wait_and_answer_call fail."
+                        "verify_wfc_in_call_state_func fail."
+                        "PhoneB not in call."
+                        "verify_wfc_in_call_state_func fail after 30 seconds."
+                        "PhoneB not in call after 30 seconds."
+                        "incall_wifi_cellular_setting_func fail."
+                        "incall_setting_check_func fail."
+                        "hangup_call fail."
+
+        Returns:
+            if expected_result is True,
+                Return True if call sequence finish without exception. Otherwise False.
+            if expected_result is string,
+                Return True if expected exception happened. Otherwise False.
+
+        """
+        class _WfcCallSequenceException(Exception):
+            pass
+
+        if (len(ads) != 2) or (mo_mt not in ["mo", "mt"]):
+            self.log.error("Invalid parameters.")
+            return False
+
+        if mo_mt == "mo":
+            ad_caller = ads[0]
+            ad_callee = ads[1]
+        else:
+            ad_caller = ads[1]
+            ad_callee = ads[0]
+        caller_number = get_phone_number(self.log, ad_caller)
+        callee_number = get_phone_number(self.log, ad_callee)
+
+        self.log.info("-->Begin wfc_call_sequence: {} to {}<--".
+                      format(caller_number, callee_number))
+
+        try:
+            # initial setup wifi router and RF
+            if initial_wifi_cellular_setup_func and not initial_wifi_cellular_setup_func():
+                raise _WfcCallSequenceException("initial_wifi_cellular_setup_func fail.")
+
+            if wfc_phone_setup_func and not wfc_phone_setup_func():
+                raise _WfcCallSequenceException("wfc_phone_setup_func fail.")
+            if not phone_setup_voice_general(self.log, ads[1]):
+                raise _WfcCallSequenceException("phone_setup_voice_general fail.")
+            time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+            # Ensure idle status correct
+            if verify_wfc_initial_idle_func and not verify_wfc_initial_idle_func():
+                raise _WfcCallSequenceException("verify_wfc_initial_idle_func fail.")
+
+            # Make MO/MT call.
+            if not initiate_call(self.log, ad_caller, callee_number):
+                raise _WfcCallSequenceException("initiate_call fail.")
+            if not wait_and_answer_call(self.log, ad_callee, caller_number):
+                raise _WfcCallSequenceException("wait_and_answer_call fail.")
+            time.sleep(1)
+
+            # Check state, wait 30 seconds, check again.
+            if verify_wfc_in_call_state_func and not verify_wfc_in_call_state_func():
+                raise _WfcCallSequenceException("verify_wfc_in_call_state_func fail.")
+            if is_phone_not_in_call(self.log, ads[1]):
+                raise _WfcCallSequenceException("PhoneB not in call.")
+            time.sleep(WAIT_TIME_IN_CALL)
+            if verify_wfc_in_call_state_func and not verify_wfc_in_call_state_func():
+                raise _WfcCallSequenceException("verify_wfc_in_call_state_func fail after 30 seconds.")
+            if is_phone_not_in_call(self.log, ads[1]):
+                raise _WfcCallSequenceException("PhoneB not in call after 30 seconds.")
+
+            # in call change setting and check
+            if incall_wifi_cellular_setting_check_func and not incall_wifi_cellular_setting_check_func():
+                raise _WfcCallSequenceException("incall_wifi_cellular_setting_check_func fail.")
+
+            if is_phone_in_call(self.log, ads[0]):
+                # hangup call
+                if not hangup_call(self.log, ads[0]):
+                    raise _WfcCallSequenceException("hangup_call fail.")
+            else:
+                # Call drop is unexpected if 
+                # incall_wifi_cellular_setting_check_func is None
+                if incall_wifi_cellular_setting_check_func is None:
+                    raise _WfcCallSequenceException("Unexpected call drop.")
+
+        except _WfcCallSequenceException as e:
+            if str(e) == expected_result:
+                self.log.info("Expected exception happened: <{}>, return True.".format(e))
+                return True
+            else:
+                self.log.info("Unexpected exception happened: <{}>, return False.".format(e))
+                return False
+        finally:
+            ensure_phones_default_state(self.log, [ads[0], ads[1]])
+
+        self.log.info("wfc_call_sequence finished, return {}".format(expected_result is True))
+        return (expected_result is True)
+
+
+    def _phone_idle_iwlan(self):
+        return phone_idle_iwlan(self.log, self.android_devices[0])
+
+    def _phone_idle_not_iwlan(self):
+        return not self._phone_idle_iwlan()
+
+    def _phone_idle_volte(self):
+        return phone_idle_volte(self.log, self.android_devices[0])
+    def _phone_idle_csfb(self):
+        return phone_idle_csfb(self.log, self.android_devices[0])
+    def _phone_idle_3g(self):
+        return phone_idle_3g(self.log, self.android_devices[0])
+    def _phone_wait_for_not_wfc(self):
+        result = wait_for_wfc_disabled(self.log, self.android_devices[0],
+                                       WAIT_TIME_NW_SELECTION)
+        self.log.info("_phone_wait_for_not_wfc: WFC_disabled is {}".format(result))
+        if not result:
+            return False
+        # Check network RAT. Should not be iwlan
+        # TODO: Because of b/24276954, this check will fail for BH/Angler/Shamu
+        # So temporarily disable this check.
+        # Re-enable this check after b/24276954 is fixed.
+        """
+        nw_type = get_network_rat(self.log, self.android_devices[0],
+            NETWORK_SERVICE_DATA)
+        if nw_type == RAT_IWLAN:
+            self.log.error("_phone_wait_for_not_wfc Data Rat is {}, expecting not {}".
+                           format(nw_type, RAT_IWLAN))
+            return False
+        """
+        return True
+    def _phone_wait_for_wfc(self):
+        result = wait_for_wfc_enabled(self.log, self.android_devices[0],
+                                      WAIT_TIME_NW_SELECTION)
+        self.log.info("_phone_wait_for_wfc: WFC_enabled is {}".format(result))
+        if not result:
+            return False
+        nw_type = get_network_rat(self.log, self.android_devices[0],
+            NETWORK_SERVICE_DATA)
+        if nw_type != RAT_IWLAN:
+            self.log.error("_phone_wait_for_wfc Data Rat is {}, expecting {}".
+                           format(nw_type, RAT_IWLAN))
+            return False
+        return True
+    def _phone_wait_for_call_drop(self):
+        if not wait_for_droid_not_in_call(self.log, self.android_devices[0],
+                                          WAIT_TIME_CALL_DROP):
+            self.log.info("_phone_wait_for_call_drop: Call not drop.")
+            return False
+        return True
+
+    def _is_phone_in_call_iwlan(self):
+        return is_phone_in_call_iwlan(self.log, self.android_devices[0])
+    def _is_phone_in_call_not_iwlan(self):
+        return is_phone_in_call_not_iwlan(self.log, self.android_devices[0])
+    def _is_phone_not_in_call(self):
+        if is_phone_in_call(self.log, self.android_devices[0]):
+            self.log.info("{} in call.".
+                format(self.android_devices[0].serial))
+            return False
+        self.log.info("{} not in call.".format(self.android_devices[0].serial))
+        return True
+    def _is_phone_in_call_volte(self):
+        return is_phone_in_call_volte(self.log, self.android_devices[0])
+    def _is_phone_in_call_3g(self):
+        return is_phone_in_call_3g(self.log, self.android_devices[0])
+    def _is_phone_in_call_csfb(self):
+        return is_phone_in_call_csfb(self.log, self.android_devices[0])
+
+
+    def _wfc_phone_setup(self, is_airplane_mode, wfc_mode, volte_mode=True):
+        toggle_airplane_mode(self.log, self.android_devices[0], False)
+        toggle_volte(self.log, self.android_devices[0], volte_mode)
+        if not ensure_network_rat(
+                self.log, self.android_devices[0], RAT_LTE,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            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
+
+        toggle_airplane_mode(self.log, self.android_devices[0], is_airplane_mode)
+
+        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".
+                    format(self.android_devices[0].serial))
+                return False
+        return True
+
+    def _wfc_phone_setup_cellular_absent(self, wfc_mode):
+        is_exception_happened = False
+        try:
+            if not toggle_airplane_mode(self.log, self.android_devices[0], False):
+                raise Exception("Toggle APM failed.")
+            if not ensure_network_rat(
+                self.log, self.android_devices[0], RAT_LTE,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+                raise Exception("Ensure LTE failed.")
+        except Exception:
+            is_exception_happened = True
+
+        if not is_exception_happened:
+            self.log.error("_wfc_phone_setup_cellular_absent error:"
+                           "Phone on LTE, expected Phone have no cellular signal")
+            return False
+        if not toggle_volte(self.log, self.android_devices[0], True):
+            self.log.error("_wfc_phone_setup_cellular_absent: toggle VoLTE fail.")
+            raise 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".
+                    format(self.android_devices[0].serial))
+                return False
+        return True
+
+    def _wfc_phone_setup_apm_wifi_only(self):
+        return self._wfc_phone_setup(True, WFC_MODE_WIFI_ONLY)
+    def _wfc_phone_setup_apm_wifi_preferred(self):
+        return self._wfc_phone_setup(True, WFC_MODE_WIFI_PREFERRED)
+    def _wfc_phone_setup_apm_cellular_preferred(self):
+        return self._wfc_phone_setup(True, WFC_MODE_CELLULAR_PREFERRED)
+    def _wfc_phone_setup_apm_wfc_disabled(self):
+        return self._wfc_phone_setup(True, WFC_MODE_DISABLED)
+    def _wfc_phone_setup_wifi_only(self):
+        return self._wfc_phone_setup(False, WFC_MODE_WIFI_ONLY)
+    def _wfc_phone_setup_wifi_preferred(self):
+        return self._wfc_phone_setup(False, WFC_MODE_WIFI_PREFERRED)
+    def _wfc_phone_setup_cellular_preferred(self):
+        return self._wfc_phone_setup(False, WFC_MODE_CELLULAR_PREFERRED)
+    def _wfc_phone_setup_wfc_disabled(self):
+        return self._wfc_phone_setup(False, WFC_MODE_DISABLED)
+    def _wfc_phone_setup_cellular_absent_wifi_only(self):
+        return self._wfc_phone_setup_cellular_absent(WFC_MODE_WIFI_ONLY)
+    def _wfc_phone_setup_cellular_absent_wifi_preferred(self):
+        return self._wfc_phone_setup_cellular_absent(WFC_MODE_WIFI_PREFERRED)
+    def _wfc_phone_setup_cellular_absent_cellular_preferred(self):
+        return self._wfc_phone_setup_cellular_absent(WFC_MODE_CELLULAR_PREFERRED)
+    def _wfc_phone_setup_cellular_absent_wfc_disabled(self):
+        return self._wfc_phone_setup_cellular_absent(WFC_MODE_DISABLED)
+
+    def _wfc_phone_setup_wifi_preferred_e4g_disabled(self):
+        return self._wfc_phone_setup(False, WFC_MODE_WIFI_PREFERRED, False)
+
+    def _wfc_phone_setup_wifi_absent(self, is_airplane_mode, wfc_mode, volte_mode=True):
+        toggle_airplane_mode(self.log, self.android_devices[0], False)
+        toggle_volte(self.log, self.android_devices[0], volte_mode)
+        if not ensure_network_rat(
+                self.log, self.android_devices[0], RAT_LTE,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            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
+
+        toggle_airplane_mode(self.log, self.android_devices[0], is_airplane_mode)
+
+        if ensure_wifi_connected(self.log, self.android_devices[0],
+                                 self.live_network_ssid, self.live_network_pwd):
+                self.log.error("{} connect WiFI succeed, expected not succeed".
+                    format(self.android_devices[0].serial))
+                return False
+        return True
+
+    def _wfc_phone_setup_cellular_absent_wifi_absent(self, wfc_mode):
+        is_exception_happened = False
+        try:
+            if not toggle_airplane_mode(self.log, self.android_devices[0], False):
+                raise Exception("Toggle APM failed.")
+            if not ensure_network_rat(
+                self.log, self.android_devices[0], RAT_LTE,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+                raise Exception("Ensure LTE failed.")
+        except Exception:
+            is_exception_happened = True
+
+        if not is_exception_happened:
+            self.log.error("_wfc_phone_setup_cellular_absent_wifi_absent error:"
+                           "Phone on LTE, expected Phone have no cellular signal")
+            return False
+        if not toggle_volte(self.log, self.android_devices[0], True):
+            self.log.error("_wfc_phone_setup_cellular_absent: toggle VoLTE fail.")
+            raise 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 ensure_wifi_connected(self.log, self.android_devices[0],
+                                 self.live_network_ssid, self.live_network_pwd):
+                self.log.error("{} connect WiFI succeed, expected not succeed".
+                    format(self.android_devices[0].serial))
+                return False
+        return True
+
+    def _wfc_phone_setup_apm_wifi_absent_wifi_only(self):
+        return self._wfc_phone_setup_wifi_absent(True, WFC_MODE_WIFI_ONLY)
+    def _wfc_phone_setup_apm_wifi_absent_wifi_preferred(self):
+        return self._wfc_phone_setup_wifi_absent(True, WFC_MODE_WIFI_PREFERRED)
+    def _wfc_phone_setup_apm_wifi_absent_cellular_preferred(self):
+        return self._wfc_phone_setup_wifi_absent(True, WFC_MODE_CELLULAR_PREFERRED)
+    def _wfc_phone_setup_wifi_absent_wifi_only(self):
+        return self._wfc_phone_setup_wifi_absent(False, WFC_MODE_WIFI_ONLY)
+    def _wfc_phone_setup_wifi_absent_wifi_preferred(self):
+        return self._wfc_phone_setup_wifi_absent(False, WFC_MODE_WIFI_PREFERRED)
+    def _wfc_phone_setup_wifi_absent_cellular_preferred(self):
+        return self._wfc_phone_setup_wifi_absent(False, WFC_MODE_CELLULAR_PREFERRED)
+    def _wfc_phone_setup_cellular_absent_wifi_absent_wifi_only(self):
+        return self._wfc_phone_setup_cellular_absent_wifi_absent(WFC_MODE_WIFI_ONLY)
+    def _wfc_phone_setup_cellular_absent_wifi_absent_wifi_preferred(self):
+        return self._wfc_phone_setup_cellular_absent_wifi_absent(WFC_MODE_WIFI_PREFERRED)
+    def _wfc_phone_setup_cellular_absent_wifi_absent_cellular_preferred(self):
+        return self._wfc_phone_setup_cellular_absent_wifi_absent(WFC_MODE_CELLULAR_PREFERRED)
+
+    def _wfc_phone_setup_wifi_absent_wifi_preferred_e4g_disabled(self):
+        return self._wfc_phone_setup_wifi_absent(False, WFC_MODE_WIFI_PREFERRED, False)
+
+
+    def _wfc_phone_setup_wifi_disabled(self, is_airplane_mode, wfc_mode):
+        toggle_airplane_mode(self.log, self.android_devices[0], False)
+        toggle_volte(self.log, self.android_devices[0], True)
+        if not ensure_network_rat(
+                self.log, self.android_devices[0], RAT_LTE,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            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
+
+        toggle_airplane_mode(self.log, self.android_devices[0], is_airplane_mode)
+
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        return True
+
+    def _wfc_phone_setup_cellular_absent_wifi_disabled(self, wfc_mode):
+        is_exception_happened = False
+        try:
+            if not toggle_airplane_mode(self.log, self.android_devices[0], False):
+                raise Exception("Toggle APM failed.")
+            if not ensure_network_rat(
+                self.log, self.android_devices[0], RAT_LTE,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+                raise Exception("Ensure LTE failed.")
+        except Exception:
+            is_exception_happened = True
+
+        if not is_exception_happened:
+            self.log.error("_wfc_phone_setup_cellular_absent_wifi_disabled error:"
+                           "Phone on LTE, expected Phone have no cellular signal")
+            return False
+        if not toggle_volte(self.log, self.android_devices[0], True):
+            self.log.error("_wfc_phone_setup_cellular_absent: toggle VoLTE fail.")
+            raise 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
+
+        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        return True
+
+    def _wfc_phone_setup_apm_wifi_disabled_wifi_only(self):
+        return self._wfc_phone_setup_wifi_disabled(True, WFC_MODE_WIFI_ONLY)
+    def _wfc_phone_setup_apm_wifi_disabled_wifi_preferred(self):
+        return self._wfc_phone_setup_wifi_disabled(True, WFC_MODE_WIFI_PREFERRED)
+    def _wfc_phone_setup_apm_wifi_disabled_cellular_preferred(self):
+        return self._wfc_phone_setup_wifi_disabled(True, WFC_MODE_CELLULAR_PREFERRED)
+    def _wfc_phone_setup_wifi_disabled_wifi_only(self):
+        return self._wfc_phone_setup_wifi_disabled(False, WFC_MODE_WIFI_ONLY)
+    def _wfc_phone_setup_wifi_disabled_wifi_preferred(self):
+        return self._wfc_phone_setup_wifi_disabled(False, WFC_MODE_WIFI_PREFERRED)
+    def _wfc_phone_setup_wifi_disabled_cellular_preferred(self):
+        return self._wfc_phone_setup_wifi_disabled(False, WFC_MODE_CELLULAR_PREFERRED)
+    def _wfc_phone_setup_cellular_absent_wifi_disabled_wifi_only(self):
+        return self._wfc_phone_setup_cellular_absent_wifi_disabled(WFC_MODE_WIFI_ONLY)
+    def _wfc_phone_setup_cellular_absent_wifi_disabled_wifi_preferred(self):
+        return self._wfc_phone_setup_cellular_absent_wifi_disabled(WFC_MODE_WIFI_PREFERRED)
+    def _wfc_phone_setup_cellular_absent_wifi_disabled_cellular_preferred(self):
+        return self._wfc_phone_setup_cellular_absent_wifi_disabled(WFC_MODE_CELLULAR_PREFERRED)
+
+
+
+    def _wfc_set_wifi_strong_cell_strong(self):
+        self.log.info("--->Setting WiFi strong cell strong<---")
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, MAX_RSSI_RESERVED_VALUE)
+        return True
+    def _wfc_set_wifi_strong_cell_weak(self):
+        self.log.info("--->Setting WiFi strong cell weak<---")
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, CELL_WEAK_RSSI_VALUE)
+        return True
+    def _wfc_set_wifi_strong_cell_absent(self):
+        self.log.info("--->Setting WiFi strong cell absent<---")
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        return True
+
+    def _wfc_set_wifi_weak_cell_strong(self):
+        self.log.info("--->Setting WiFi weak cell strong<---")
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, WIFI_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, MAX_RSSI_RESERVED_VALUE)
+        return True
+    def _wfc_set_wifi_weak_cell_weak(self):
+        self.log.info("--->Setting WiFi weak cell weak<---")
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, CELL_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, WIFI_WEAK_RSSI_VALUE)
+        return True
+    def _wfc_set_wifi_weak_cell_absent(self):
+        self.log.info("--->Setting WiFi weak cell absent<---")
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, WIFI_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        return True
+
+    def _wfc_set_wifi_absent_cell_strong(self):
+        self.log.info("--->Setting WiFi absent cell strong<---")
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, MAX_RSSI_RESERVED_VALUE)
+        return True
+    def _wfc_set_wifi_absent_cell_weak(self):
+        self.log.info("--->Setting WiFi absent cell weak<---")
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, CELL_WEAK_RSSI_VALUE)
+        return True
+    def _wfc_set_wifi_absent_cell_absent(self):
+        self.log.info("--->Setting WiFi absent cell absent<---")
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        return True
+
+    """ Tests Begin """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_strong_apm(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI Strong, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PHoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_strong_apm(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI Strong, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PHoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_strong_apm(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI Strong, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PHoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_weak_apm(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI weak, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PHoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_apm_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_apm_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_weak_apm(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI weak, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PHoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_apm_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_apm_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_weak_apm(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI weak, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PHoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_apm_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_apm_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_absent_apm(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI absent, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PHoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_apm_wifi_absent_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_apm_wifi_absent_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_absent_apm(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI absent, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PHoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_apm_wifi_absent_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_apm_wifi_absent_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_absent_apm(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI absent, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PHoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_apm_wifi_absent_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_apm_wifi_absent_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_disabled_apm(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI disabled, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PHoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_disabled_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_disabled_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_disabled_apm(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI disabled, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PHoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_disabled_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_disabled_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_disabled_apm(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI disabled, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PHoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_disabled_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wifi_disabled_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_strong_cellular_strong(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI strong, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PHoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_strong_cellular_strong(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI strong, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PHoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_strong_cellular_strong(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI strong, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PHoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_weak_cellular_strong(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI weak, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PHoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_weak_cellular_strong(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI weak, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PHoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_weak_cellular_strong(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI strong, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PHoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_strong,
+            self._wfc_phone_setup_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_absent_cellular_strong(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI absent, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PHoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_wifi_absent_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_wifi_absent_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_absent_cellular_strong(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI absent, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PHoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_wifi_absent_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_wifi_absent_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_absent_cellular_strong(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI absent, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PHoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_wifi_absent_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_strong,
+            self._wfc_phone_setup_wifi_absent_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_disabled_cellular_strong(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI disabled, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PHoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_disabled_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_disabled_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_disabled_cellular_strong(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI disabled, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PHoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_disabled_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_disabled_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_disabled_cellular_strong(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI disabled, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PHoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_disabled_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wifi_disabled_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_strong_cellular_weak(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI strong, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_strong_cellular_weak(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI strong, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_strong_cellular_weak(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI strong, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_weak_cellular_weak(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI weak, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_weak,
+            self._wfc_phone_setup_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_weak,
+            self._wfc_phone_setup_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_weak_cellular_weak(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI weak, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_weak,
+            self._wfc_phone_setup_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_weak,
+            self._wfc_phone_setup_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_weak_cellular_weak(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI weak, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PhoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_weak,
+            self._wfc_phone_setup_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_weak,
+            self._wfc_phone_setup_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_absent_cellular_weak(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI absent, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_weak,
+            self._wfc_phone_setup_wifi_absent_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_weak,
+            self._wfc_phone_setup_wifi_absent_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_absent_cellular_weak(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI absent, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PhoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_weak,
+            self._wfc_phone_setup_wifi_absent_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_weak,
+            self._wfc_phone_setup_wifi_absent_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_absent_cellular_weak(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI absent, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PhoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_weak,
+            self._wfc_phone_setup_wifi_absent_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_weak,
+            self._wfc_phone_setup_wifi_absent_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_disabled_cellular_weak(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI disabled, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_disabled_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_disabled_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_disabled_cellular_weak(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI disabled, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PhoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_disabled_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_disabled_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_disabled_cellular_weak(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI disabled, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PhoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_disabled_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wifi_disabled_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_strong_cellular_absent(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI strong, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_strong_cellular_absent(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI strong, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_strong_cellular_absent(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI strong, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_weak_cellular_absent(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI weak, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_only,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_weak_cellular_absent(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI weak, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_weak_cellular_absent(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI weak, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on WiFi.
+        Call from PhoneB to PhoneA, call should succeed, call should on WiFi.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_weak_cell_absent,
+            self._wfc_phone_setup_cellular_absent_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_weak_cell_absent,
+            self._wfc_phone_setup_cellular_absent_cellular_preferred,
+            self._phone_idle_iwlan,
+            self._is_phone_in_call_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_absent_cellular_absent(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI absent, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_absent_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_absent_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_absent_cellular_absent(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI absent, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_absent_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_absent_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_absent_cellular_absent(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI absent, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_absent_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_absent_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_absent_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_absent_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_only_wifi_disabled_cellular_absent(self):
+        """ Test WFC MO MT, WiFI only mode, WIFI strong, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_disabled_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_disabled_wifi_only,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_wifi_disabled_cellular_absent(self):
+        """ Test WFC MO MT, WiFI preferred mode, WIFI strong, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_disabled_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_disabled_wifi_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_cellular_preferred_wifi_disabled_cellular_absent(self):
+        """ Test WFC MO MT, cellular preferred mode, WIFI strong, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_disabled_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wifi_disabled_cellular_preferred,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_disabled_wifi_strong_cellular_strong(self):
+        """ Test WFC MO MT, WFC disabled, WIFI strong, Cellular strong
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PHoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wfc_disabled,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_wfc_disabled,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_disabled_wifi_strong_cellular_weak(self):
+        """ Test WFC MO MT, WFC disabled, WIFI strong, Cellular weak
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should succeed, call should on Cellular.
+        Call from PhoneB to PHoneA, call should succeed, call should on Cellular.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wfc_disabled,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_weak,
+            self._wfc_phone_setup_wfc_disabled,
+            self._phone_idle_not_iwlan,
+            self._is_phone_in_call_not_iwlan,
+            None,
+            True)
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_disabled_wifi_strong_cellular_absent(self):
+        """ Test WFC MO MT, WFC disabled, WIFI strong, Cellular absent
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wfc_disabled,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_absent,
+            self._wfc_phone_setup_cellular_absent_wfc_disabled,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_disabled_wifi_strong_apm(self):
+        """ Test WFC MO MT, WFC disabled, WIFI strong, Phone in APM
+
+        Set WiFi/Cellular network environment.
+        Make Sure PhoneA is set correct WFC parameters.
+        Make SUre PhoneB is able to make MO/MT call.
+        Call from PhoneA to PhoneB, call should fail.
+        Call from PhoneB to PhoneA, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = [self.android_devices[0], self.android_devices[1]]
+        mo_result = self._wfc_call_sequence(ads, "mo",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wfc_disabled,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "initiate_call fail.")
+
+        mt_result = self._wfc_call_sequence(ads, "mt",
+            self._wfc_set_wifi_strong_cell_strong,
+            self._wfc_phone_setup_apm_wfc_disabled,
+            self._phone_idle_not_iwlan,
+            self._is_phone_not_in_call,
+            None,
+            "wait_and_answer_call fail.")
+
+        self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
+        return ((mo_result is True) and (mt_result is True))
+
+    def _rove_in_test(self, cellular_rat, wfc_mode):
+        """Test utility for Rove-in Tests.
+
+        Cellular strong, WiFi RSSI < -100 dBm.
+        Setup Cellular network and wfc_mode, WiFi enabled but not associated.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN in 10s,
+            PhoneA does not rove-in.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10s,
+            PhoneA rove-in.
+        Make WFC call.
+        """
+        self._wfc_set_wifi_absent_cell_strong()
+        # ensure cellular rat, wfc mode, wifi not associated
+        toggle_airplane_mode(self.log, self.android_devices[0], False)
+        toggle_volte(self.log, self.android_devices[0], True)
+        if not ensure_network_rat(
+                self.log, self.android_devices[0], cellular_rat,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            self.log.error("_rove_in_test: {} failed to be in rat: {}".
+                            format(self.android_devices[0].serial, cellular_rat))
+            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 ensure_wifi_connected(self.log, self.android_devices[0],
+                                 self.live_network_ssid, self.live_network_pwd):
+                self.log.error("{} connect WiFI succeed, expected not succeed".
+                    format(self.android_devices[0].serial))
+                return False
+
+        # set up wifi to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN in 10 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN, 5, 1)
+        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")
+            return False
+
+        if self._phone_idle_iwlan():
+            self.log.error("Phone should not report iwlan in WiFi {}Bm".
+                           format(WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN))
+            return False
+
+        # set up wifi to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN, 1, 1)
+        if not self._phone_idle_iwlan():
+            self.log.error("Phone should report iwlan in WiFi {}dBm".
+                           format(WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN))
+            return False
+
+        # make a wfc call.
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo", None, None,
+                                        self._phone_idle_iwlan,
+                                        self._is_phone_in_call_iwlan,
+                                        None,
+                                        True)
+
+    def _rove_out_test(self, cellular_rat, wfc_mode):
+        """Test utility for Rove-out Tests.
+
+        Cellular strong, WiFi RSSI WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE.
+        Setup Cellular network and wfc_mode, WiFi associated.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT in 10s,
+            PhoneA does not rove-out.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10s,
+            PhoneA rove-out.
+        Make a call.
+        """
+        # set up cell strong
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten, MAX_RSSI_RESERVED_VALUE)
+        # set up wifi WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE)
+        # 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_rat(
+                self.log, self.android_devices[0], cellular_rat,
+                WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+            self.log.error("_rove_out_test: {} failed to be in rat: {}".
+                            format(self.android_devices[0].serial, cellular_rat))
+            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")
+            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))
+            return False
+
+        # set up wifi to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT in 10 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT, 1, 1)
+        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")
+            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))
+            return False
+
+        # set up wifi to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT, 2, 1)
+        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")
+            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))
+            return False
+        # make a call.
+        if wfc_mode == WFC_MODE_WIFI_ONLY:
+            return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                            "mo", None, None,
+                                            self._phone_idle_not_iwlan,
+                                            self._is_phone_not_in_call,
+                                            None,
+                                            "initiate_call fail.")
+        elif wfc_mode == WFC_MODE_WIFI_PREFERRED:
+            if cellular_rat == RAT_LTE:
+                return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                                "mo", None, None,
+                                                self._phone_idle_volte,
+                                                self._is_phone_in_call_volte,
+                                                None,
+                                                True)
+            else:
+                return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                                "mo", None, None,
+                                                self._phone_idle_3g,
+                                                self._is_phone_in_call_3g,
+                                                None,
+                                                True)
+        else:
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_out_in_stress(self):
+        """WiFi Calling Rove out/in stress test.
+
+        Steps:
+        1. PhoneA on LTE, VoLTE enabled.
+        2. PhoneA WFC mode WiFi preferred, WiFi associated.
+        3. Cellular strong, WiFi RSSI WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE.
+        4. Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10s,
+            PhoneA rove-out.
+        5. Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10s,
+            PhoneA rove-in.
+        6. Repeat Step 4~5.
+
+        Expected Results:
+        4. Phone should rove out.
+        5. Phone should rove in.
+        6. Stress test pass rate should be higher than pre-defined limit.
+        """
+        self._wfc_set_wifi_strong_cell_strong()
+        if not self._wfc_phone_setup_wifi_preferred():
+            self.log.error("Setup WFC failed.")
+            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")
+            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))
+            return False
+        total_iteration = self.stress_test_number
+        self.log.info("Rove_out/Rove_in stress test. Total iteration = {}.".
+                      format(total_iteration))
+        current_iteration = 1
+        while(current_iteration <= total_iteration):
+            self.log.info(">----Current iteration = {}/{}----<".
+                          format(current_iteration, total_iteration))
+
+            # set up wifi to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10 seconds
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT, 2, 1)
+            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")
+                break
+            if not self._phone_wait_for_not_wfc():
+                self.log.error("Phone should rove-out in {}dBm.".
+                               format(WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT))
+                break
+            self.log.info("Rove-out succeed.")
+            # set up wifi to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10 seconds
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN, 2, 1)
+            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")
+                break
+            if not self._phone_wait_for_wfc():
+                self.log.error("Phone should rove-in in {}dBm.".
+                               format(WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN))
+                break
+            self.log.info("Rove-in succeed.")
+
+            self.log.info(">----Iteration : {}/{} succeed.----<".
+                            format(current_iteration, total_iteration))
+            current_iteration += 1
+        if current_iteration <= total_iteration:
+            self.log.info(">----Iteration : {}/{} failed.----<".
+                            format(current_iteration, total_iteration))
+            return False
+        else:
+            return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_in_out_stress(self):
+        """WiFi Calling Rove in/out stress test.
+
+        Steps:
+        1. PhoneA on LTE, VoLTE enabled.
+        2. PhoneA WFC mode WiFi preferred, WiFi associated.
+        3. Cellular strong, WiFi RSSI weak.
+        4. Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10s,
+            PhoneA rove-in.
+        5. Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10s,
+            PhoneA rove-out.
+        6. Repeat Step 4~5.
+
+        Expected Results:
+        4. Phone should rove in.
+        5. Phone should rove out.
+        6. Stress test pass rate should be higher than pre-defined limit.
+        """
+        self._wfc_set_wifi_weak_cell_strong()
+        # ensure cellular rat, wfc mode, wifi not associated
+        if not self._wfc_phone_setup_wifi_preferred():
+            self.log.error("Failed to setup for rove_in_out_stress")
+            return False
+        total_iteration = self.stress_test_number
+        self.log.info("Rove_in/Rove_out stress test. Total iteration = {}.".
+                      format(total_iteration))
+        current_iteration = 1
+        while(current_iteration <= total_iteration):
+            self.log.info(">----Current iteration = {}/{}----<".
+                          format(current_iteration, total_iteration))
+
+            # set up wifi to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10 seconds
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN, 2, 1)
+            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")
+                break
+            if not self._phone_wait_for_wfc():
+                self.log.error("Phone should rove-in in {}dBm.".
+                               format(WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN))
+                break
+            self.log.info("Rove-in succeed.")
+
+            # set up wifi to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10 seconds
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT, 2, 1)
+            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")
+                break
+            if not self._phone_wait_for_not_wfc():
+                self.log.error("Phone should rove-out in {}dBm.".
+                               format(WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT))
+                break
+            self.log.info("Rove-out succeed.")
+
+            self.log.info(">----Iteration : {}/{} succeed.----<".
+                            format(current_iteration, total_iteration))
+            current_iteration += 1
+        if current_iteration <= total_iteration:
+            self.log.info(">----Iteration : {}/{} failed.----<".
+                            format(current_iteration, total_iteration))
+            return False
+        else:
+            return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_in_lte_wifi_preferred(self):
+        """ Test WFC rove-in features.
+
+        PhoneA on LTE, VoLTE enabled.
+        PhoneA WFC mode WiFi preferred, WiFi enabled but not associated.
+        Cellular strong, WiFi RSSI < -100 dBm.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN in 10s,
+            PhoneA does not rove-in.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10s,
+            PhoneA rove-in.
+        Make WFC call.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._rove_in_test(RAT_LTE, WFC_MODE_WIFI_PREFERRED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_in_lte_wifi_only(self):
+        """ Test WFC rove-in features.
+
+        PhoneA on LTE, VoLTE enabled.
+        PhoneA WFC mode WiFi only, WiFi enabled but not associated.
+        Cellular strong, WiFi RSSI < -100 dBm.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN in 10s,
+            PhoneA does not rove-in.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10s,
+            PhoneA rove-in.
+        Make WFC call.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        return self._rove_in_test(RAT_LTE, WFC_MODE_WIFI_ONLY)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_in_wcdma_wifi_preferred(self):
+        """ Test WFC rove-in features.
+
+        PhoneA on WCDMA, VoLTE enabled.
+        PhoneA WFC mode WiFi preferred, WiFi enabled but not associated.
+        Cellular strong, WiFi RSSI < -100 dBm.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN in 10s,
+            PhoneA does not rove-in.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10s,
+            PhoneA rove-in.
+        Make WFC call.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._rove_in_test(RAT_WCDMA, WFC_MODE_WIFI_PREFERRED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_in_wcdma_wifi_only(self):
+        """ Test WFC rove-in features.
+
+        PhoneA on WCDMA, VoLTE enabled.
+        PhoneA WFC mode WiFi only, WiFi enabled but not associated.
+        Cellular strong, WiFi RSSI < -100 dBm.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN in 10s,
+            PhoneA does not rove-in.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN in 10s,
+            PhoneA rove-in.
+        Make WFC call.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        return self._rove_in_test(RAT_WCDMA, WFC_MODE_WIFI_ONLY)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_out_lte_wifi_preferred(self):
+        """ Test WFC rove-out features.
+
+        PhoneA on LTE, VoLTE enabled.
+        PhoneA WFC mode WiFi preferred, WiFi associated.
+        Cellular strong, WiFi RSSI WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT in 10s,
+            PhoneA does not rove-out.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10s,
+            PhoneA rove-out.
+        Make a call, call should succeed by cellular VoLTE.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._rove_out_test(RAT_LTE, WFC_MODE_WIFI_PREFERRED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_out_lte_wifi_only(self):
+        """ Test WFC rove-out features.
+
+        PhoneA on LTE, VoLTE enabled.
+        PhoneA WFC mode WiFi only, WiFi associated.
+        Cellular strong, WiFi RSSI WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT in 10s,
+            PhoneA does not rove-out.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10s,
+            PhoneA rove-out.
+        Make a call, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        return self._rove_out_test(RAT_LTE, WFC_MODE_WIFI_ONLY)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_out_wcdma_wifi_preferred(self):
+        """ Test WFC rove-out features.
+
+        PhoneA on WCDMA, VoLTE enabled.
+        PhoneA WFC mode WiFi preferred, WiFi associated.
+        Cellular strong, WiFi RSSI WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT in 10s,
+            PhoneA does not rove-out.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10s,
+            PhoneA rove-out.
+        Make a call, call should succeed by cellular 3g.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self._rove_out_test(RAT_WCDMA, WFC_MODE_WIFI_PREFERRED)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rove_out_wcdma_wifi_only(self):
+        """ Test WFC rove-out features.
+
+        PhoneA on WCDMA, VoLTE enabled.
+        PhoneA WFC mode WiFi only, WiFi associated.
+        Cellular strong, WiFi RSSI WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT in 10s,
+            PhoneA does not rove-out.
+        Set WiFI RSSI to WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT in 10s,
+            PhoneA rove-out.
+        Make a call, call should fail.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ###########
+        return self._rove_out_test(RAT_WCDMA, WFC_MODE_WIFI_ONLY)
+
+    def _increase_wifi_rssi_check_phone_hand_in(self):
+        """Private Test utility for hand_in test.
+
+        Increase WiFI RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_NOT_HAND_IN in 10s.
+        PhoneA should connect to WiFi and have data on WiFi.
+        PhoneA should not hand-in to iwlan.
+        Increase WiFi RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN in 10s.
+        PhoneA should hand-in to iwlan.
+        PhoneA call should remain active.
+        """
+        # Increase WiFI RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_NOT_HAND_IN in 10s
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_NOT_HAND_IN, 5, 1)
+        # Make sure WiFI connected and data OK.
+        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")
+            return False
+        # Make sure phone not hand in to iwlan.
+        if self._phone_wait_for_wfc():
+            self.log.error("Phone hand-in to wfc.")
+            return False
+        # Increase WiFI RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN in 10s
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN, 2, 1)
+        # Make sure phone hand in to iwlan.
+        if not self._phone_wait_for_wfc():
+            self.log.error("Phone failed to hand-in to wfc.")
+            return False
+        if self._is_phone_not_in_call():
+            self.log.error("Phone call dropped.")
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_hand_in_wifi_preferred(self):
+        """WiFi Hand-In Threshold - WiFi Preferred
+
+        PhoneA on LTE, VoLTE enabled, WFC WiFi preferred. WiFI not associated.
+        Cellular Strong, WiFi <-100 dBm
+        Call from PhoneA to PhoneB, PhoneA should be on VoLTE.
+        Increase WiFI RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_NOT_HAND_IN in 10s.
+        PhoneA should connect to WiFi and have data on WiFi.
+        PhoneA should not hand-in to iwlan.
+        Increase WiFi RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN in 10s.
+        PhoneA should hand-in to iwlan.
+        PhoneA call should remain active.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_absent_cell_strong,
+                                        self._wfc_phone_setup_wifi_absent_wifi_preferred,
+                                        self._phone_idle_volte,
+                                        self._is_phone_in_call_volte,
+                                        self._increase_wifi_rssi_check_phone_hand_in,
+                                        True)
+
+    def _increase_wifi_rssi_hand_in_and_decrease_wifi_rssi_hand_out(self):
+        if not self._increase_wifi_rssi_check_phone_hand_in():
+            return False
+        if not self._decrease_wifi_rssi_check_phone_hand_out():
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_hand_in_out_wifi_preferred(self):
+        """WiFi Hand-In-Out Threshold - WiFi Preferred
+
+        PhoneA on LTE, VoLTE enabled, WFC WiFi preferred. WiFI not associated.
+        Cellular Strong, WiFi <-100 dBm
+        Call from PhoneA to PhoneB, PhoneA should be on VoLTE.
+
+        Increase WiFI RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_NOT_HAND_IN in 10s.
+        PhoneA should connect to WiFi and have data on WiFi.
+        PhoneA should not hand-in to iwlan.
+        Increase WiFi RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN in 10s.
+        PhoneA should hand-in to iwlan.
+        PhoneA call should remain active.
+
+        Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_NOT_HAND_OUT, in 10s.
+        PhoneA should still be in call.
+        PhoneA should not hand-out, PhoneA should have data on WiFi.
+        Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT, in 10s.
+        PhoneA should still be in call. PhoneA should hand-out to LTE.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_absent_cell_strong,
+                                        self._wfc_phone_setup_wifi_absent_wifi_preferred,
+                                        self._phone_idle_volte,
+                                        self._is_phone_in_call_volte,
+                                        self._increase_wifi_rssi_hand_in_and_decrease_wifi_rssi_hand_out,
+                                        True)
+
+    def _decrease_lte_rssi_check_phone_not_hand_in(self):
+        """Private Test utility for hand_in test.
+
+        Decrease Cellular RSSI to CELL_WEAK_RSSI_VALUE in 30s.
+        PhoneA should not hand-in to WFC.
+        PhoneA should either drop or hands over to 3g/2g.
+        """
+        # Decrease LTE RSSI to CELL_WEAK_RSSI_VALUE in 30 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten,
+                 CELL_WEAK_RSSI_VALUE, 1, 1)
+        # Make sure phone not hand in to iwlan.
+        if self._phone_wait_for_wfc():
+            self.log.error("Phone hand-in to wfc.")
+            return False
+
+        # TODO(yangxliu) Currently shield box can set Cell RSSI as low as -116dBm
+        # In such RSSI, call will not drop nor hand over to 3g.
+        # Need to fix this logic once shield box can set Cell RSSI lower.
+        return True
+        # FIXME
+        # Make sure phone either drop or hands over to 2g/3g.
+        if is_phone_not_in_call(self.log, self.android_devices[0]):
+            self.log.info("Call drop.")
+            return True
+        if self._is_phone_in_call_csfb():
+            self.log.info("Call hands over to 2g/3g.")
+            return True
+        return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_hand_in_cellular_preferred(self):
+        """WiFi Hand-In Not Attempted - Cellular Preferred
+
+        PhoneA on LTE, VoLTE enabled, WFC cellular preferred. WiFI associated.
+        Cellular and WiFi signal strong.
+        Call from PhoneA to PhoneB, PhoneA should be on VoLTE.
+        Decrease Cellular RSSI to CELL_WEAK_RSSI_VALUE in 30s.
+        PhoneA should not hand-in to WFC.
+        PhoneA should either drop or hands over to 3g/2g.
+        """
+        # TODO(yangxliu) Currently shield box can set Cell RSSI as low as -116dBm
+        # In such RSSI, call will not drop nor hand over to 3g.
+        # Need to fix this logic once shield box can set Cell RSSI lower.
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_strong_cell_strong,
+                                        self._wfc_phone_setup_cellular_preferred,
+                                        self._phone_idle_volte,
+                                        self._is_phone_in_call_volte,
+                                        self._decrease_lte_rssi_check_phone_not_hand_in,
+                                        True)
+
+    def _decrease_wifi_rssi_check_phone_hand_out(self):
+        """Private Test utility for hand_out test.
+
+        Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_NOT_HAND_OUT in 10s.
+        PhoneA should still be in call.
+        PhoneA should not hand-out, PhoneA should have data on WiFi.
+        Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT in 10s.
+        PhoneA should still be in call. PhoneA should hand-out to LTE.
+        PhoneA should have data on WiFi.
+        """
+        # Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_NOT_HAND_OUT
+        # in 10 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_NOT_HAND_OUT, 2, 1)
+        # Make sure WiFi still connected and have data.
+        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")
+            return False
+        # Make sure phone not hand-out, not drop call
+        if self._phone_wait_for_not_wfc():
+            self.log.error("Phone should not hand out.")
+            return False
+        if self._is_phone_not_in_call():
+            self.log.error("Phone should not drop call.")
+            return False
+
+        # Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT
+        # in 10 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT, 2, 1)
+        # Make sure WiFi still connected and have data.
+        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")
+            return False
+        # Make sure phone hand-out, not drop call
+        if not self._phone_wait_for_not_wfc():
+            self.log.error("Phone should hand out.")
+            return False
+        if not self._is_phone_in_call_volte():
+            self.log.error("Phone should be in volte call.")
+            return False
+
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_hand_out_wifi_preferred(self):
+        """WiFi Hand-Out Threshold - WiFi Preferred
+
+        PhoneA on LTE, VoLTE enabled, WFC WiFi preferred, WiFi associated.
+        Cellular and WiFi signal strong.
+        Call from PhoneA to PhoneB, PhoneA should be on iwlan.
+        Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_NOT_HAND_OUT in 10s.
+        PhoneA should still be in call.
+        PhoneA should not hand-out, PhoneA should have data on WiFi.
+        Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT in 10s.
+        PhoneA should still be in call. PhoneA should hand-out to LTE.
+        PhoneA should have data on WiFi.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_strong_cell_strong,
+                                        self._wfc_phone_setup_wifi_preferred,
+                                        self._phone_idle_iwlan,
+                                        self._is_phone_in_call_iwlan,
+                                        self._decrease_wifi_rssi_check_phone_hand_out,
+                                        True)
+
+    def _decrease_wifi_rssi_hand_out_and_increase_wifi_rssi_hand_in(self):
+        if not self._decrease_wifi_rssi_check_phone_hand_out():
+            return False
+        if not self._increase_wifi_rssi_check_phone_hand_in():
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_hand_out_in_wifi_preferred(self):
+        """WiFi Hand-Out Threshold - WiFi Preferred
+
+        PhoneA on LTE, VoLTE enabled, WFC WiFi preferred, WiFi associated.
+        Cellular and WiFi signal strong.
+        Call from PhoneA to PhoneB, PhoneA should be on iwlan.
+
+        Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_NOT_HAND_OUT in 10s.
+        PhoneA should still be in call.
+        PhoneA should not hand-out, PhoneA should have data on WiFi.
+        Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT in 10s.
+        PhoneA should still be in call. PhoneA should hand-out to LTE.
+        PhoneA should have data on WiFi.
+
+        Increase WiFI RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_NOT_HAND_IN in 10s.
+        PhoneA should connect to WiFi and have data on WiFi.
+        PhoneA should not hand-in to iwlan.
+        Increase WiFi RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN in 10s.
+        PhoneA should hand-in to iwlan.
+        PhoneA call should remain active.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_strong_cell_strong,
+                                        self._wfc_phone_setup_wifi_preferred,
+                                        self._phone_idle_iwlan,
+                                        self._is_phone_in_call_iwlan,
+                                        self._decrease_wifi_rssi_hand_out_and_increase_wifi_rssi_hand_in,
+                                        True)
+
+    def _hand_out_hand_in_stress(self):
+        total_iteration = self.stress_test_number
+        self.log.info("Hand_out/Hand_in stress test. Total iteration = {}.".
+                      format(total_iteration))
+        current_iteration = 1
+        if self._phone_wait_for_call_drop():
+            self.log.error("Call Drop.")
+            return False
+        while(current_iteration <= total_iteration):
+            self.log.info(">----Current iteration = {}/{}----<".
+                          format(current_iteration, total_iteration))
+
+            # Decrease WiFi RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT
+            # in 10 seconds
+            self.log.info("Decrease WiFi RSSI to hand out.")
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT, 2, 1)
+            # Make sure WiFi still connected and have data.
+            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")
+                break
+            # Make sure phone hand-out, not drop call
+            if not self._phone_wait_for_not_wfc():
+                self.log.error("Phone failed to hand-out in RSSI {}.".
+                               format(WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT))
+                break
+            if self._phone_wait_for_call_drop():
+                self.log.error("Call Drop.")
+                break
+            # Increase WiFI RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN in 10s
+            self.log.info("Increase WiFi RSSI to hand in.")
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN, 2, 1)
+            # Make sure WiFi still connected and have data.
+            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")
+                break
+            # Make sure phone hand in to iwlan.
+            if not self._phone_wait_for_wfc():
+                self.log.error("Phone failed to hand-in to wfc.")
+                break
+            if self._phone_wait_for_call_drop():
+                self.log.error("Call Drop.")
+                break
+
+            self.log.info(">----Iteration : {}/{} succeed.----<".
+                            format(current_iteration, total_iteration))
+            current_iteration += 1
+        if current_iteration <= total_iteration:
+            self.log.info(">----Iteration : {}/{} failed.----<".
+                            format(current_iteration, total_iteration))
+            return False
+        else:
+            return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_hand_out_in_stress(self):
+        """WiFi Calling Hand out/in stress test.
+
+        Steps:
+        1. PhoneA on LTE, VoLTE enabled.
+        2. PhoneA WFC mode WiFi preferred, WiFi associated.
+        3. Cellular strong, WiFi RSSI strong. Call from PhoneA to PhoneB,
+            call should be on WFC.
+        4. Set WiFI RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT in 10s,
+            PhoneA hand-out.
+        5. Set WiFI RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN in 10s,
+            PhoneA hand-in.
+        6. Repeat Step 4~5. Call should not drop.
+
+        Expected Results:
+        4. Phone should hand out.
+        5. Phone should hand in.
+        6. Stress test pass rate should be higher than pre-defined limit.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_strong_cell_strong,
+                                        self._wfc_phone_setup_wifi_preferred,
+                                        self._phone_idle_iwlan,
+                                        self._is_phone_in_call_iwlan,
+                                        self._hand_out_hand_in_stress,
+                                        True)
+
+    def _hand_in_hand_out_stress(self):
+        total_iteration = self.stress_test_number
+        self.log.info("Hand_in/Hand_out stress test. Total iteration = {}.".
+                      format(total_iteration))
+        current_iteration = 1
+        if self._phone_wait_for_call_drop():
+            self.log.error("Call Drop.")
+            return False
+        while(current_iteration <= total_iteration):
+            self.log.info(">----Current iteration = {}/{}----<".
+                          format(current_iteration, total_iteration))
+
+            # Increase WiFi RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN
+            # in 10 seconds
+            self.log.info("Increase WiFi RSSI to hand in.")
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN, 2, 1)
+            # Make sure WiFi still connected and have data.
+            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")
+                break
+            # Make sure phone hand in to iwlan.
+            if not self._phone_wait_for_wfc():
+                self.log.error("Phone failed to hand-in to wfc.")
+                break
+            if self._phone_wait_for_call_drop():
+                self.log.error("Call Drop.")
+                break
+
+            # Decrease WiFI RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT in 10s
+            self.log.info("Decrease WiFi RSSI to hand out.")
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT, 2, 1)
+            # Make sure WiFi still connected and have data.
+            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")
+                break
+            # Make sure phone hand-out, not drop call
+            if not self._phone_wait_for_not_wfc():
+                self.log.error("Phone failed to hand-out in RSSI {}.".
+                               format(WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT))
+                break
+            if self._phone_wait_for_call_drop():
+                self.log.error("Call Drop.")
+                break
+
+            self.log.info(">----Iteration : {}/{} succeed.----<".
+                            format(current_iteration, total_iteration))
+            current_iteration += 1
+        if current_iteration <= total_iteration:
+            self.log.info(">----Iteration : {}/{} failed.----<".
+                            format(current_iteration, total_iteration))
+            return False
+        else:
+            return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_hand_in_out_stress(self):
+        """WiFi Calling Hand in/out stress test.
+
+        Steps:
+        1. PhoneA on LTE, VoLTE enabled.
+        2. PhoneA WFC mode WiFi preferred, WiFi associated.
+        3. Cellular strong, WiFi RSSI weak. Call from PhoneA to PhoneB,
+            call should be on VoLTE.
+        4. Set WiFI RSSI to WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN in 10s,
+            PhoneA hand-in.
+        5. Set WiFI RSSI to WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT in 10s,
+            PhoneA hand-out.
+        6. Repeat Step 4~5. Call should not drop.
+
+        Expected Results:
+        4. Phone should hand in.
+        5. Phone should hand out.
+        6. Stress test pass rate should be higher than pre-defined limit.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_weak_cell_strong,
+                                        self._wfc_phone_setup_wifi_preferred,
+                                        self._phone_idle_volte,
+                                        self._is_phone_in_call_volte,
+                                        self._hand_in_hand_out_stress,
+                                        True)
+
+    def _increase_cellular_rssi_check_phone_hand_out(self):
+        """Private Test utility for hand_out test.
+
+        Increase Cellular RSSI to CELL_STRONG_RSSI_VALUE, in 30s.
+        PhoneA should still be in call. PhoneA should hand-out to LTE.
+        PhoneA should have data on WiFi.
+        """
+        # Increase Cellular RSSI to CELL_STRONG_RSSI_VALUE in 30 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+                 self.cell_rssi_with_no_atten,
+                 CELL_STRONG_RSSI_VALUE, 1, 1)
+        # Make sure phone hand-out, not drop call
+        if not self._phone_wait_for_not_wfc():
+            self.log.error("Phone should hand out.")
+            return False
+        if not self._is_phone_in_call_volte():
+            self.log.error("Phone should be in volte call.")
+            return False
+        # Make sure WiFi still connected and have data.
+        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")
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_hand_out_cellular_preferred(self):
+        """WiFi Hand-Out Threshold - Cellular Preferred
+
+        Cellular signal absent, WiFi signal strong.
+        PhoneA VoLTE enabled, WFC Cellular preferred, WiFi associated.
+        Call from PhoneA to PhoneB, PhoneA should be on iwlan.
+        Increase Cellular RSSI to CELL_STRONG_RSSI_VALUE, in 30s.
+        PhoneA should still be in call. PhoneA should hand-out to LTE.
+        PhoneA should have data on WiFi.
+        """
+        # TODO(yangxliu) Currently shield box can set Cell RSSI as low as -116dBm
+        # In such RSSI, phone (cellular preferred mode) will not select to iwlan.
+        # Need to fix this logic once shield box can set Cell RSSI lower.
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_strong_cell_absent,
+                                        self._wfc_phone_setup_cellular_absent_cellular_preferred,
+                                        self._phone_idle_iwlan,
+                                        self._is_phone_in_call_iwlan,
+                                        self._increase_cellular_rssi_check_phone_hand_out,
+                                        True)
+
+    def _decrease_wifi_rssi_check_phone_not_hand_out(self):
+        """Private Test utility for hand_out test.
+
+        Decrease WiFi RSSI to <-100dBm, in 30s.
+        PhoneA should drop call. PhoneA should not report LTE as voice RAT.
+        PhoneA data should be on LTE.
+        """
+        # Decrease WiFi RSSI to <-100dBm in 30 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        # Make sure PhoneA data is on LTE.
+        if (not wait_for_cell_data_connection(
+                self.log, self.android_devices[0], True) or not
+                verify_http_connection(
+                    self.log, self.android_devices[0])):
+            self.log.error("Data not on Cell.")
+            return False
+        # Make sure phone drop.
+        self.log.info("Wait for call drop.")
+        if not self._phone_wait_for_call_drop():
+            self.log.error("Phone should drop call.")
+            return False
+        # Make sure Voice RAT is not LTE.
+        if RAT_LTE == get_network_rat(self.log, self.android_devices[0],
+                                      NETWORK_SERVICE_VOICE):
+            self.log.error("Phone should not report lte as voice rat.")
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_hand_out_wifi_only(self):
+        """WiFi Hand-Out Not Attempted - WiFi Only
+
+        PhoneA on LTE, VoLTE enabled, WFC WiFi only, WiFi associated.
+        Cellular and WiFi signal strong.
+        Call from PhoneA to PhoneB, PhoneA should be on iwlan.
+        Decrease WiFi RSSI to <-100dBm, in 30s.
+        PhoneA should drop call. PhoneA should not report LTE as voice RAT.
+        PhoneA data should be on LTE.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_strong_cell_strong,
+                                        self._wfc_phone_setup_wifi_only,
+                                        self._phone_idle_iwlan,
+                                        self._is_phone_in_call_iwlan,
+                                        self._decrease_wifi_rssi_check_phone_not_hand_out,
+                                        True)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_e4g_disabled(self):
+        """WiFi Calling with E4G disabled.
+
+        PhoneA on LTE, VoLTE disabled, WFC WiFi preferred, WiFi associated.
+        Cellular and WiFi signal strong.
+        Call from PhoneA to PhoneB, PhoneA should be on iwlan.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_strong_cell_strong,
+                                        self._wfc_phone_setup_wifi_preferred_e4g_disabled,
+                                        self._phone_idle_iwlan,
+                                        self._is_phone_in_call_iwlan,
+                                        None,
+                                        True)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_e4g_disabled_wifi_not_connected(self):
+        """WiFi Calling with E4G disabled.
+
+        PhoneA on LTE, VoLTE disabled, WFC WiFi preferred, WiFi not associated.
+        Cellular signal strong, WiFi absent.
+        Call from PhoneA to PhoneB, PhoneA should be on CSFB.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_absent_cell_strong,
+                                        self._wfc_phone_setup_wifi_absent_wifi_preferred_e4g_disabled,
+                                        self._phone_idle_not_iwlan,
+                                        self._is_phone_in_call_csfb,
+                                        None,
+                                        True)
+
+    def _decrease_wifi_rssi_check_phone_drop(self):
+        """Private Test utility for e4g_disabled_wfc test.
+
+        Decrease WiFi RSSI to make sure WiFI not connected. Call should Drop.
+        """
+        # Decrease WiFi RSSI to <-100dBm in 30 seconds
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        # Make sure PhoneA data is on cellular.
+        if (not wait_for_cell_data_connection(
+                self.log, self.android_devices[0], True) or not
+                verify_http_connection(
+                    self.log, self.android_devices[0])):
+            self.log.error("Data not on Cell.")
+            return False
+        # Make sure phone drop.
+        self.log.info("Wait for call drop.")
+        if not self._phone_wait_for_call_drop():
+            self.log.error("Phone should drop call.")
+            return False
+        return True
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_epdg_wfc_wifi_preferred_e4g_disabled_leave_wifi_coverage(self):
+        """WiFi Calling with E4G disabled.
+
+        PhoneA on LTE, VoLTE disabled, WFC WiFi preferred, WiFi associated.
+        Cellular and WiFi signal strong.
+        Call from PhoneA to PhoneB, PhoneA should be on iwlan.
+        Decrease WiFi RSSI to make sure WiFI not connected. Call should Drop.
+        """
+        return self._wfc_call_sequence([self.android_devices[0], self.android_devices[1]],
+                                        "mo",
+                                        self._wfc_set_wifi_strong_cell_strong,
+                                        self._wfc_phone_setup_wifi_preferred_e4g_disabled,
+                                        self._phone_idle_iwlan,
+                                        self._is_phone_in_call_iwlan,
+                                        self._decrease_wifi_rssi_check_phone_drop,
+                                        True)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_rssi_monitoring(self):
+        """Test WiFi RSSI Monitoring API and Callback function.
+
+        Steps:
+        1. Set WiFi RSSI to INITIAL_RSSI (-60dBm), connect WiFi on DUT.
+        2. Start WiFi RSSI Monitoring for HIGHER_RSSI_THRESHOLD (-50dBm) and
+            LOWER_RSSI_THRESHOLD (-70dBm)
+        3. Increase WiFi RSSI to HIGHER_RSSI_THRESHOLD+5dBm
+        4. Decrease WiFi RSSI to HIGHER_RSSI_THRESHOLD-5dBm
+        5. Decrease WiFi RSSI to LOWER_RSSI_THRESHOLD-5dBm
+        6. Increase WiFi RSSI to LOWER_RSSI_THRESHOLD+5dBm
+
+        Expected Results:
+        1. WiFi Connected successfully.
+        2. DUT report LOWER_RSSI_THRESHOLD available.
+        3. DUT report HIGHER_RSSI_THRESHOLD available.
+        4. DUT report HIGHER_RSSI_THRESHOLD lost.
+        5. DUT report LOWER_RSSI_THRESHOLD lost.
+        6. DUT report LOWER_RSSI_THRESHOLD available.
+        """
+        INITIAL_RSSI = -60
+        HIGHER_RSSI_THRESHOLD = -50
+        LOWER_RSSI_THRESHOLD = -70
+        RSSI_THRESHOLD_MARGIN = 5
+
+        WIFI_RSSI_CHANGE_STEP_SIZE = 2
+        WIFI_RSSI_CHANGE_DELAY_PER_STEP = 1
+
+        ad = self.android_devices[0]
+
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                 self.wifi_rssi_with_no_atten, INITIAL_RSSI)
+        if not ensure_wifi_connected(self.log, ad,
+                                     self.live_network_ssid, self.live_network_pwd):
+                self.log.error("{} connect WiFI failed".format(ad.serial))
+                return False
+        try:
+            rssi_monitoring_id_higher = ad.droid.connectivitySetRssiThresholdMonitor(
+                HIGHER_RSSI_THRESHOLD)
+            rssi_monitoring_id_lower = ad.droid.connectivitySetRssiThresholdMonitor(
+                LOWER_RSSI_THRESHOLD)
+
+            self.log.info("Initial RSSI: {},"
+                          "rssi_monitoring_id_lower should be available.".
+                          format(INITIAL_RSSI))
+            try:
+                event = ad.ed.wait_for_event(NetworkCallBack,
+                            is_network_call_back_event_match,
+                            sub_event=NetworkCallBackAvailable,
+                            network_callback_id=rssi_monitoring_id_lower)
+                self.log.info("Received Event: {}".format(event))
+            except Empty:
+                self.log.error("No {} event for id {}".
+                    format(NetworkCallBackAvailable, rssi_monitoring_id_lower))
+                return False
+
+            self.log.info("Set RSSI to HIGHER_RSSI_THRESHOLD+5,"
+                          "rssi_monitoring_id_higher should be available.")
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     HIGHER_RSSI_THRESHOLD + RSSI_THRESHOLD_MARGIN,
+                     WIFI_RSSI_CHANGE_STEP_SIZE, WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            try:
+                event = ad.ed.wait_for_event(NetworkCallBack,
+                            is_network_call_back_event_match,
+                            sub_event=NetworkCallBackAvailable,
+                            network_callback_id=rssi_monitoring_id_higher)
+                self.log.info("Received Event: {}".format(event))
+            except Empty:
+                self.log.error("No {} event for id {}".
+                    format(NetworkCallBackAvailable, rssi_monitoring_id_higher))
+                return False
+
+            self.log.info("Set RSSI to HIGHER_RSSI_THRESHOLD-5,"
+                          "rssi_monitoring_id_higher should be lost.")
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     HIGHER_RSSI_THRESHOLD - RSSI_THRESHOLD_MARGIN,
+                     WIFI_RSSI_CHANGE_STEP_SIZE, WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            try:
+                event = ad.ed.wait_for_event(NetworkCallBack,
+                            is_network_call_back_event_match,
+                            sub_event=NetworkCallBackLost,
+                            network_callback_id=rssi_monitoring_id_higher)
+                self.log.info("Received Event: {}".format(event))
+            except Empty:
+                self.log.error("No {} event for id {}".
+                    format(NetworkCallBackLost, rssi_monitoring_id_higher))
+                return False
+
+            self.log.info("Set RSSI to LOWER_RSSI_THRESHOLD-5,"
+                          "rssi_monitoring_id_lower should be lost.")
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     LOWER_RSSI_THRESHOLD - RSSI_THRESHOLD_MARGIN,
+                     WIFI_RSSI_CHANGE_STEP_SIZE, WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            try:
+                event = ad.ed.wait_for_event(NetworkCallBack,
+                            is_network_call_back_event_match,
+                            sub_event=NetworkCallBackLost,
+                            network_callback_id=rssi_monitoring_id_lower)
+                self.log.info("Received Event: {}".format(event))
+            except Empty:
+                self.log.error("No {} event for id {}".
+                    format(NetworkCallBackLost, rssi_monitoring_id_lower))
+                return False
+
+            self.log.info("Set RSSI to LOWER_RSSI_THRESHOLD+5,"
+                          "rssi_monitoring_id_lower should be available.")
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+                     self.wifi_rssi_with_no_atten,
+                     LOWER_RSSI_THRESHOLD + RSSI_THRESHOLD_MARGIN,
+                     WIFI_RSSI_CHANGE_STEP_SIZE, WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            try:
+                event = ad.ed.wait_for_event(NetworkCallBack,
+                            is_network_call_back_event_match,
+                            sub_event=NetworkCallBackAvailable,
+                            network_callback_id=rssi_monitoring_id_lower)
+                self.log.info("Received Event: {}".format(event))
+            except Empty:
+                self.log.error("No {} event for id {}".
+                    format(NetworkCallBackAvailable, rssi_monitoring_id_lower))
+                return False
+        finally:
+            ad.droid.connectivityStopRssiThresholdMonitor(rssi_monitoring_id_higher)
+            ad.droid.connectivityStopRssiThresholdMonitor(rssi_monitoring_id_lower)
+        return True
+
+
+""" Tests End """
diff --git a/acts/tests/google/wifi/WifiAutoJoinTest.py b/acts/tests/google/wifi/WifiAutoJoinTest.py
new file mode 100755
index 0000000..de90f2b
--- /dev/null
+++ b/acts/tests/google/wifi/WifiAutoJoinTest.py
@@ -0,0 +1,458 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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.base_test import BaseTestClass
+from acts.test_utils.wifi_test_utils import wifi_forget_network
+from acts.test_utils.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi_test_utils import WifiEnums
+from acts.test_utils.wifi_test_utils import start_wifi_connection_scan
+from acts.test_utils.wifi_test_utils import check_internet_connection
+from acts.test_utils.wifi_test_utils import track_connection
+
+NETWORK_ID_ERROR = "Network don't have ID"
+NETWORK_ERROR = "Device is not connected to reference network"
+
+class WifiAutoJoinTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_autojoin_out_of_range",
+            "test_autojoin_Ap1_2g",
+            "test_autojoin_Ap1_2gto5g",
+            "test_autojoin_in_AP1_5gto2g",
+            "test_autojoin_swtich_AP1toAp2",
+            "test_autojoin_Ap2_2gto5g",
+            "test_autojoin_Ap2_5gto2g",
+            "test_autojoin_out_of_range",
+            "test_autojoin_Ap2_2g",
+            "test_autojoin_Ap2_2gto5g",
+            "test_autojoin_in_Ap2_5gto2g",
+            "test_autojoin_swtich_AP2toAp1",
+            "test_autojoin_Ap1_2gto5g",
+            "test_autojoin_Ap1_5gto2g",
+            "test_autojoin_swtich_to_blacklist_AP",
+            "test_autojoin_in_blacklist_AP",
+            "test_autojoin_back_from_blacklist_AP",
+            )
+
+    def setup_class(self):
+        """It will setup the required dependencies from config file and configure
+           the required networks for auto-join testing. Configured networks will
+           not be removed. If networks are already configured it will skip
+           configuring the networks
+
+        Returns:
+            True if successfully configured the requirements for testing.
+        """
+        self.dut = self.android_devices[0]
+        wifi_test_device_init(self.dut)
+        req_params = ("reference_networks", "other_network", "atten_val",
+                      "ping_addr", "max_bugreports" )
+        assert self.unpack_userparams(req_params)
+        self.log.debug("Connect networks :: {}".format(self.other_network))
+        configured_networks = self.droid.wifiGetConfiguredNetworks()
+        self.log.debug("Configured networks :: {}".format(configured_networks))
+        count_confnet = 0
+        result = False
+        if self.reference_networks[0]['2g']['ssid'] == self.reference_networks[0]['5g']['ssid']:
+            self.ref_ssid_count = 1
+        else:
+            self.ref_ssid_count = 2 # Different SSID for 2g and 5g
+        for confnet in configured_networks:
+            if confnet[WifiEnums.SSID_KEY] == self.reference_networks[0]['2g']['ssid']:
+                count_confnet += 1
+            elif confnet[WifiEnums.SSID_KEY] == self.reference_networks[0]['5g']['ssid']:
+                count_confnet += 1
+        self.log.info("count_confnet {}".format(count_confnet))
+        if count_confnet == self.ref_ssid_count:
+            return True
+        else:
+            self.log.info("Configured networks for testing")
+            self.attenuators[0].set_atten(0)
+            self.attenuators[1].set_atten(90)
+            self.attenuators[2].set_atten(90)
+            wait_time = 15
+            self.droid.wakeLockAcquireBright()
+            self.droid.wakeUpNow()
+            try:
+                self.droid.wifiPriorityConnect(self.reference_networks[0]['2g'])
+                connect_result = self.ed.pop_event("WifiManagerPriorityConnectOnSuccess", 1)
+                self.log.info(connect_result)
+                time.sleep(wait_time)
+                if self.ref_ssid_count == 2: #add 5g network as well
+                    self.droid.wifiPriorityConnect(self.reference_networks[0]['5g'])
+                    connect_result = self.ed.pop_event("WifiManagerPriorityConnectOnSuccess", 1)
+                    self.log.info(connect_result)
+                    time.sleep(wait_time)
+                self.droid.wifiPriorityConnect(self.other_network)
+                connect_result = self.ed.pop_event("WifiManagerPriorityConnectOnSuccess")
+                self.log.info(connect_result)
+                track_connection(self.dut, self.other_network["ssid"], 1)
+                wifi_forget_network(self.dut, self.other_network["ssid"])
+                time.sleep(wait_time)
+                current_network = self.droid.wifiGetConnectionInfo()
+                self.log.info("Current network: {}".format(current_network))
+                self.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
+                self.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR)
+                self.ip_address = self.droid.wifiGetConfigFile();
+                self.log.info("IP info: {}".format(self.ip_address))
+            finally:
+                self.droid.wifiLockRelease()
+                self.droid.goToSleepNow()
+        return True
+
+    def check_connection(self, network_bssid):
+        """Check current wifi connection networks.
+        Args:
+            network_bssid: Network bssid to which connection.
+        Returns:
+            True if connection to given network happen, else return False.
+        """
+        time.sleep(40) #time for connection state to be updated
+        self.log.info("Check network for {}".format(network_bssid))
+        current_network = self.droid.wifiGetConnectionInfo()
+        self.log.debug("Current network:  {}".format(current_network))
+        if WifiEnums.BSSID_KEY in current_network:
+            return current_network[WifiEnums.BSSID_KEY] == network_bssid
+        return False
+
+    def set_attn_and_validate_connection(self, attn_value, bssid):
+        """Validate wifi connection status on different attenuation setting.
+
+        Args:
+            attn_value: Attenuation value for different APs signal.
+            bssid: Bssid of excepted network.
+
+        Returns:
+            True if bssid of current network match, else false.
+        """
+        self.attenuators[0].set_atten(attn_value[0])
+        self.attenuators[1].set_atten(attn_value[1])
+        self.attenuators[2].set_atten(attn_value[2])
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        try:
+            self.assert_true(self.check_connection(bssid),
+                    "Device is not connected to required bssid {}".format(bssid))
+            time.sleep(10) #wait for connection to be active
+            self.assert_true(check_internet_connection(self.dut, self.ping_addr),
+                             "Error, No Internet connection for current bssid {}".
+                             format(bssid))
+        finally:
+            self.droid.wifiLockRelease()
+            self.droid.goToSleepNow()
+
+    def on_fail(self, test_name, begin_time):
+        if self.max_bugreports > 0:
+            self.take_bug_reports(test_name, begin_time, self.android_devices)
+            self.max_bugreports -= 1
+
+    """ Tests Begin """
+    def test_autojoin_Ap1_2g(self):
+        """Test wifi auto join functionality move in range of AP1.
+
+         1. Attenuate the signal to low range of AP1 and Ap2 not visible at all.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable
+            connection to BSSID in range.
+        """
+        att0,att1,att2 =  self.atten_val["Ap1_2g"]
+        variance = 5
+        attenuations = ([att0+variance*2, att1, att2], [att0+variance, att1, att2],
+                        [att0, att1, att2], [att0-variance, att1, att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_Ap1_2g_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[0]["2g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_Ap1_2g failed {}".
+                         format(len(failed)))
+
+    def test_autojoin_Ap1_2gto5g(self):
+        """Test wifi auto join functionality move to high range.
+
+         1. Attenuate the signal to high range of AP1.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable
+            connection to BSSID in range.
+        """
+        att0,att1,att2 =  self.atten_val["Ap1_2gto5g"]
+        variance = 5
+        attenuations = ([att0+variance*2, att1, att2], [att0+variance, att1, att2],
+                        [att0, att1, att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_Ap1_2gto5g_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1],att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[0]["5g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_Ap1_2gto5g failed {}".
+                         format(len(failed)))
+
+
+    def test_autojoin_in_AP1_5gto2g(self):
+        """Test wifi auto join functionality move to low range toward AP2.
+
+         1. Attenuate the signal to medium range of AP1 and low range of AP2.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable
+            connection to BSSID in range.
+        """
+        att0,att1,att2 =  self.atten_val["In_AP1_5gto2g"]
+        variance = 5
+        attenuations = ([att0-variance, att1+variance, att2], [att0, att1, att2],
+                        [att0+variance, att1-variance, att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_in_AP1_5gto2g_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1],att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[0]["2g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_in_AP1_5gto2g failed {}".
+                         format(len(failed)))
+
+    def test_autojoin_swtich_AP1toAp2(self):
+        """Test wifi auto join functionality move from low range of AP1 to better
+           range of AP2.
+
+         1. Attenuate the signal to low range of AP1 and medium range of AP2.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable
+            connection to BSSID in range.
+        """
+        att0,att1,att2 =  self.atten_val["Swtich_AP1toAp2"]
+        variance = 5
+        attenuations = ([att0-variance, att1+variance, att2], [att0, att1, att2],
+                        [att0+variance, att1-variance, att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_swtich_AP1toAp2_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1],att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[1]["2g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_swtich_AP1toAp2 failed {}".
+                         format(len(failed)))
+
+    def test_autojoin_Ap2_2gto5g(self):
+        """Test wifi auto join functionality move to high range of AP2.
+
+         1. Attenuate the signal to out range of AP1 and high range of AP2.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable
+            connection to BSSID in range.
+        """
+        att0,att1,att2 =  self.atten_val["Ap2_2gto5g"]
+        variance = 5
+        attenuations = ([att0-variance, att1+variance*2, att2],
+                        [att0, att1+variance, att2], [att0, att1, att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_Ap2_2gto5g_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[1]["5g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_Ap2_2gto5g failed {}".
+                         format(len(failed)))
+
+    def test_autojoin_Ap2_5gto2g(self):
+        """Test wifi auto join functionality move to low range of AP2.
+
+         1. Attenuate the signal to low range of AP2.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable.
+        """
+        att0,att1,att2 =  self.atten_val["Ap2_5gto2g"]
+        variance = 5
+        attenuations = ([att0, att1-variance, att2], [att0, att1, att2],
+                        [att0, att1+variance, att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_Ap2_5gto2g_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[1]["2g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_Ap2_5gto2g failed {}".
+                         format(len(failed)))
+
+    def test_autojoin_out_of_range(self):
+        """Test wifi auto join functionality move to low range.
+
+         1. Attenuate the signal to out of range.
+         2. Wake up the device.
+         3. Start the scan.
+         4. Check that device is not connected to any network.
+        """
+        self.attenuators[0].set_atten(90)
+        self.attenuators[1].set_atten(90)
+        self.attenuators[2].set_atten(90)
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        try:
+            start_wifi_connection_scan(self.droid, self.ed)
+            wifi_results = self.droid.wifiGetScanResults()
+            self.log.debug("Scan result {}".format(wifi_results))
+            time.sleep(20)
+            current_network = self.droid.wifiGetConnectionInfo()
+            self.log.info("Current network: {}".format(current_network))
+            self.assert_true(('network_id' in current_network and
+                              current_network['network_id'] == -1),
+                             "Device is connected to network {}".format(current_network))
+        finally:
+            self.droid.wifiLockRelease()
+            self.droid.goToSleepNow()
+
+    def test_autojoin_Ap2_2g(self):
+        """Test wifi auto join functionality move in low range of AP2.
+
+         1. Attenuate the signal to move in range of AP2 and Ap1 not visible at all.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable
+            connection to BSSID in range.
+        """
+        att0,att1,att2 =  self.atten_val["Ap2_2g"]
+        variance = 5
+        attenuations = ([att0,att1+variance*2,att2],
+                        [att0,att1+variance,att2],[att0,att1,att2],
+                        [att0,att1-variance,att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_Ap2_2g_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1],att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[1]["2g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_Ap2_2g failed {}".
+                         format(len(failed)))
+
+    def test_autojoin_in_Ap2_5gto2g(self):
+        """Test wifi auto join functionality move to medium range of Ap2 and
+           low range of AP1.
+
+         1. Attenuate the signal to move in medium range of AP2 and low range of AP1.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable
+            connection to BSSID in range.
+        """
+        att0,att1,att2 =  self.atten_val["In_Ap2_5gto2g"]
+        variance = 5
+        attenuations = ([att0,att1-variance,att2],[att0,att1,att2],
+                        [att0,att1+variance,att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_in_Ap2_5gto2g_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1],att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[1]["2g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_in_Ap2_5gto2g failed {}".
+                         format(len(failed)))
+
+    def test_autojoin_swtich_AP2toAp1(self):
+        """Test wifi auto join functionality move from low range of AP2 to better
+           range of AP1.
+
+         1. Attenuate the signal to low range of AP2 and medium range of AP1.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable
+            connection to BSSID in range.
+        """
+        att0,att1,att2 =  self.atten_val["Swtich_AP2toAp1"]
+        variance = 5
+        attenuations = ([att0+variance,att1-variance,att2],[att0,att1,att2],
+                        [att0-variance,att1+variance,att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_swtich_AP2toAp1_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1],att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[0]["2g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_swtich_AP2toAp1 failed {}".
+                         format(len(failed)))
+
+    def test_autojoin_Ap1_5gto2g(self):
+        """Test wifi auto join functionality move to medium range of AP1.
+
+         1. Attenuate the signal to medium range of AP1.
+         2. Wake up the device.
+         3. Check that device is connected to right BSSID and maintain stable
+            connection to BSSID in range.
+        """
+        att0,att1,att2 =  self.atten_val["Ap1_5gto2g"]
+        variance = 5
+        attenuations = ([att0,att1,att2], [att0+variance,att1,att2],
+                        [att0+variance*2,att1,att2])
+        name_func = lambda att_value, bssid : ("test_autojoin_Ap1_5gto2g_AP1_{}_AP2"
+                     "_{}_AP3_{}").format(att_value[0], att_value[1],att_value[2])
+        failed = self.run_generated_testcases(
+                        self.set_attn_and_validate_connection,
+                        attenuations, self.reference_networks[0]["2g"]['bssid'],
+                        name_func = name_func)
+        self.assert_true(not failed, "Number of test_autojoin_Ap1_5gto2g failed {}".
+                         format(len(failed)))
+
+    def test_autojoin_swtich_to_blacklist_AP(self):
+        """Test wifi auto join functionality in medium range of blacklist BSSID.
+
+         1. Attenuate the signal to low range of AP1 and medium range of AP3.
+         2. Wake up the device.
+         3. Check that device is connected to AP1 BSSID and maintain stable
+            connection to BSSID.
+        """
+        self.set_attn_and_validate_connection(self.atten_val["Swtich_to_blacklist"],
+                                              self.reference_networks[0]["2g"]['bssid'])
+
+    def test_autojoin_in_blacklist_AP(self):
+        """Test wifi auto join functionality in high range of blacklist BSSID.
+
+         1. Attenuate the signal to out of range of AP1 and full range of AP3.
+         2. Wake up the device.
+         3. Check that device is disconnected form all AP.
+        """
+        attn0, attn1, attn2 = self.atten_val["In_blacklist"]
+        self.attenuators[0].set_atten(attn0)
+        self.attenuators[1].set_atten(attn1)
+        self.attenuators[2].set_atten(attn2)
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        try:
+            start_wifi_connection_scan(self.droid, self.ed)
+            wifi_results = self.droid.wifiGetScanResults()
+            self.log.debug("Scan result {}".format(wifi_results))
+            time.sleep(20)
+            current_network = self.droid.wifiGetConnectionInfo()
+            self.log.info("Current network: {}".format(current_network))
+            self.assert_true(('network_id' in current_network and
+                              current_network['network_id'] == -1),
+                             "Device is still connected to blacklisted network {}".
+                             format(current_network))
+        finally:
+            self.droid.wifiLockRelease()
+            self.droid.goToSleepNow()
+
+    def test_autojoin_back_from_blacklist_AP(self):
+        """Test wifi auto join functionality in medium range of blacklist BSSID.
+
+         1. Attenuate the signal to medium of range of AP1 and low range of AP3.
+         2. Wake up the device.
+         3. Check that device is disconnected form all AP.
+        """
+        self.set_attn_and_validate_connection(self.atten_val["Back_from_blacklist"],
+                                              self.reference_networks[0]["2g"]['bssid'])
+    """ Tests End """
+if __name__ == "__main__":
+    pass
diff --git a/acts/tests/google/wifi/WifiEnterpriseRoamingTest.py b/acts/tests/google/wifi/WifiEnterpriseRoamingTest.py
new file mode 100644
index 0000000..45d6133
--- /dev/null
+++ b/acts/tests/google/wifi/WifiEnterpriseRoamingTest.py
@@ -0,0 +1,222 @@
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import pprint
+import time
+
+from random import shuffle
+
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi_test_utils import eap_connect
+from acts.test_utils.wifi_test_utils import expand_enterprise_config_by_phase2
+from acts.test_utils.wifi_test_utils import reset_droid_wifi
+from acts.test_utils.wifi_test_utils import verify_wifi_connection_info
+from acts.test_utils.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi_test_utils import WifiEnums
+
+# EAP Macros
+EAP = WifiEnums.Eap
+EapPhase2 = WifiEnums.EapPhase2
+
+# Enterprise Config Macros
+Ent = WifiEnums.Enterprise
+
+class WifiEnterpriseRoamingTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_roaming_with_different_auth_method",
+        )
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wifi_test_device_init(self.dut)
+        req_params = (
+            "ent_roaming_ssid",
+            "bssid_a",
+            "bssid_b",
+            "attn_vals",
+            # Expected time within which roaming should finish, in seconds.
+            "roam_interval",
+            "ca_cert",
+            "client_cert",
+            "client_key",
+            "eap_identity",
+            "eap_password",
+            "device_password"
+        )
+        assert self.unpack_userparams(req_params)
+        self.config_peap = {
+            Ent.EAP: EAP.PEAP,
+            Ent.CA_CERT: self.ca_cert,
+            Ent.IDENTITY: self.eap_identity,
+            Ent.PASSWORD: self.eap_password,
+            Ent.PHASE2: EapPhase2.MSCHAPV2,
+            WifiEnums.SSID_KEY: self.ent_roaming_ssid
+        }
+        self.config_tls = {
+            Ent.EAP: EAP.TLS,
+            Ent.CA_CERT: self.ca_cert,
+            WifiEnums.SSID_KEY: self.ent_roaming_ssid,
+            Ent.CLIENT_CERT: self.client_cert,
+            Ent.PRIVATE_KEY_ID: self.client_key,
+            Ent.IDENTITY: self.eap_identity,
+        }
+        self.config_ttls = {
+            Ent.EAP: EAP.TTLS,
+            Ent.CA_CERT: self.ca_cert,
+            Ent.IDENTITY: self.eap_identity,
+            Ent.PASSWORD: self.eap_password,
+            Ent.PHASE2: EapPhase2.MSCHAPV2,
+            WifiEnums.SSID_KEY: self.ent_roaming_ssid
+        }
+        self.config_sim = {
+            Ent.EAP: EAP.SIM,
+            WifiEnums.SSID_KEY: self.ent_roaming_ssid,
+        }
+        self.attn_a = self.attenuators[0]
+        self.attn_b = self.attenuators[1]
+        # Set screen lock password so ConfigStore is unlocked.
+        self.droid.setDevicePassword(self.device_password)
+        self.set_attns("default")
+        return True
+
+    def teardown_class(self):
+        reset_droid_wifi(self.droid, self.ed)
+        self.droid.disableDevicePassword()
+        self.ed.clear_all_events()
+        self.set_attns("default")
+
+    def setup_test(self):
+        self.droid.wifiStartTrackingStateChange()
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        reset_droid_wifi(self.droid, self.ed)
+        self.ed.clear_all_events()
+        return True
+
+    def teardown_test(self):
+        self.droid.wakeLockRelease()
+        self.droid.goToSleepNow()
+        self.droid.wifiStopTrackingStateChange()
+        self.set_attns("default")
+
+    def set_attns(self, attn_val_name):
+        """Sets attenuation values on attenuators used in this test.
+
+        Args:
+            attn_val_name: Name of the attenuation value pair to use.
+        """
+        msg = "Set attenuation values to %s" % self.attn_vals[attn_val_name]
+        self.log.info(msg)
+        try:
+            self.attn_a.set_atten(self.attn_vals[attn_val_name][0])
+            self.attn_b.set_atten(self.attn_vals[attn_val_name][1])
+        except:
+            msg = "Failed to set attenuation values %s." % attn_val_name
+            self.log.error(msg)
+            raise
+
+    def gen_eap_configs(self):
+        """Generates configurations for different EAP authentication types.
+
+        Returns:
+            A list of dicts each representing an EAP configuration.
+        """
+        configs = [self.config_tls]
+                   # self.config_sim
+        configs += expand_enterprise_config_by_phase2(self.config_ttls)
+        configs += expand_enterprise_config_by_phase2(self.config_peap)
+        return configs
+
+    def gen_eap_roaming_test_name(self, config):
+        """Generates a test case name based on an EAP configuration.
+
+        Args:
+            config: A dict representing an EAP credential.
+
+        Returns:
+            A string representing the name of a generated EAP test case.
+        """
+        name = "test_roaming-%s" % config[Ent.EAP].name
+        if Ent.PHASE2 in config:
+            name += "-{}".format(config[Ent.PHASE2].name)
+        return name
+
+    def trigger_roaming_and_validate(self, attn_val_name, expected_con):
+        """Sets attenuators to trigger roaming and validate the DUT connected
+        to the BSSID expected.
+
+        Args:
+            attn_val_name: Name of the attenuation value pair to use.
+            expected_con: The expected info of the network to we expect the DUT
+                to roam to.
+        """
+        self.set_attns(attn_val_name)
+        self.log.info("Wait %ss for roaming to finish." % self.roam_interval)
+        time.sleep(self.roam_interval)
+        try:
+            self.droid.wakeLockAcquireBright()
+            self.droid.wakeUpNow()
+            verify_wifi_connection_info(self.dut, expected_con)
+            expected_bssid = expected_con[WifiEnums.BSSID_KEY]
+            self.log.info("Roamed to %s successfully" % expected_bssid)
+        finally:
+            self.droid.wifiLockRelease()
+            self.droid.goToSleepNow()
+
+    def roaming_between_a_and_b_logic(self, config):
+        """Test roaming between two enterprise APs.
+
+        Steps:
+        1. Make bssid_a visible, bssid_b not visible.
+        2. Connect to ent_roaming_ssid. Expect DUT to connect to bssid_a.
+        3. Make bssid_a not visible, bssid_b visible.
+        4. Expect DUT to roam to bssid_b.
+        5. Make bssid_a visible, bssid_b not visible.
+        6. Expect DUT to roam back to bssid_a.
+        """
+        expected_con_to_a = {
+            WifiEnums.SSID_KEY: self.ent_roaming_ssid,
+            WifiEnums.BSSID_KEY: self.bssid_a,
+        }
+        expected_con_to_b = {
+            WifiEnums.SSID_KEY: self.ent_roaming_ssid,
+            WifiEnums.BSSID_KEY: self.bssid_b,
+        }
+        self.set_attns("a_on_b_off")
+        assert eap_connect(config, self.dut, validate_con=False)
+        verify_wifi_connection_info(self.dut, expected_con_to_a)
+        self.log.info("Roaming from %s to %s" % (self.bssid_a, self.bssid_b))
+        self.trigger_roaming_and_validate("b_on_a_off", expected_con_to_b)
+        self.log.info("Roaming from %s to %s" % (self.bssid_b, self.bssid_a))
+        self.trigger_roaming_and_validate("a_on_b_off", expected_con_to_a)
+        return True
+
+    """ Tests Begin """
+    def test_roaming_with_different_auth_method(self):
+        eap_configs = self.gen_eap_configs()
+        self.log.info("Testing %d different configs." % len(eap_configs))
+        shuffle(eap_configs)
+        failed = self.run_generated_testcases(
+            self.roaming_between_a_and_b_logic,
+            eap_configs,
+            name_func=self.gen_eap_roaming_test_name)
+        msg = ("The following configs failed enterprise roaming test: %s" %
+               pprint.pformat(failed))
+        self.assert_true(len(failed) == 0, msg)
+        return True
+    """ Tests End """
diff --git a/acts/tests/google/wifi/WifiEnterpriseTest.py b/acts/tests/google/wifi/WifiEnterpriseTest.py
new file mode 100755
index 0000000..c0f8d9f
--- /dev/null
+++ b/acts/tests/google/wifi/WifiEnterpriseTest.py
@@ -0,0 +1,447 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2015 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import pprint
+import time
+
+from queue import Empty
+from random import shuffle
+
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi_test_utils import eap_connect
+from acts.test_utils.wifi_test_utils import expand_enterprise_config_by_phase2
+from acts.test_utils.wifi_test_utils import reset_droid_wifi
+from acts.test_utils.wifi_test_utils import start_wifi_connection_scan
+from acts.test_utils.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi_test_utils import WifiEnums
+from acts.test_utils.wifi_test_utils import WifiEventNames
+
+# EAP Macros
+EAP = WifiEnums.Eap
+EapPhase2 = WifiEnums.EapPhase2
+
+# Enterprise Config Macros
+Ent = WifiEnums.Enterprise
+# Event name macros
+Event = WifiEventNames
+
+
+class WifiEnterpriseTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_eap_connect",
+            "test_eap_connect_negative",
+            # "test_passpoint_connect",
+            # "test_passpoint_connect_negative"
+        )
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wifi_test_device_init(self.dut)
+        required_userparam_names = (
+            "ca_cert",
+            "client_cert",
+            "client_key",
+            "passpoint_ca_cert",
+            "passpoint_client_cert",
+            "passpoint_client_key",
+            "eap_identity",
+            "eap_password",
+            "invalid_ca_cert",
+            "invalid_client_cert",
+            "invalid_client_key",
+            "fqdn",
+            "provider_friendly_name",
+            "realm",
+            "ssid_peap",
+            "ssid_tls",
+            "ssid_ttls",
+            "ssid_sim",
+            "ssid_passpoint",
+            "device_password",
+            "ping_addr"
+        )
+        optional_userparam_names = (
+            "roaming_consortium_ids",
+            "plmn"
+        )
+        assert self.unpack_userparams(required_userparam_names,
+                    opt_param_names = optional_userparam_names)
+        # Default configs for EAP networks.
+        self.config_peap = {
+            Ent.EAP: EAP.PEAP,
+            Ent.CA_CERT: self.ca_cert,
+            Ent.IDENTITY: self.eap_identity,
+            Ent.PASSWORD: self.eap_password,
+            Ent.PHASE2: EapPhase2.MSCHAPV2,
+            WifiEnums.SSID_KEY: self.ssid_peap
+        }
+        self.config_tls = {
+            Ent.EAP: EAP.TLS,
+            Ent.CA_CERT: self.ca_cert,
+            WifiEnums.SSID_KEY: self.ssid_tls,
+            Ent.CLIENT_CERT: self.client_cert,
+            Ent.PRIVATE_KEY_ID: self.client_key,
+            Ent.IDENTITY: self.eap_identity,
+        }
+        self.config_ttls = {
+            Ent.EAP: EAP.TTLS,
+            Ent.CA_CERT: self.ca_cert,
+            Ent.IDENTITY: self.eap_identity,
+            Ent.PASSWORD: self.eap_password,
+            Ent.PHASE2: EapPhase2.MSCHAPV2,
+            WifiEnums.SSID_KEY: self.ssid_ttls
+        }
+        self.config_sim = {
+            Ent.EAP: EAP.SIM,
+            WifiEnums.SSID_KEY: self.ssid_sim,
+        }
+
+        # Base config for passpoint networks.
+        self.config_passpoint = {
+            Ent.FQDN: self.fqdn,
+            Ent.FRIENDLY_NAME: self.provider_friendly_name,
+            Ent.REALM: self.realm,
+            Ent.CA_CERT: self.passpoint_ca_cert
+        }
+        if hasattr(self, "plmn"):
+            self.config_passpoint[Ent.PLMN] = self.plmn
+        if hasattr(self, "roaming_consortium_ids"):
+            self.config_passpoint[Ent.ROAMING_IDS] = self.roaming_consortium_ids
+
+        # Default configs for passpoint networks.
+        self.config_passpoint_tls = dict(self.config_tls)
+        self.config_passpoint_tls.update(self.config_passpoint)
+        self.config_passpoint_tls[Ent.CLIENT_CERT] = self.passpoint_client_cert
+        self.config_passpoint_tls[Ent.PRIVATE_KEY_ID] = self.passpoint_client_key
+        del self.config_passpoint_tls[WifiEnums.SSID_KEY]
+        self.config_passpoint_ttls = dict(self.config_ttls)
+        self.config_passpoint_ttls.update(self.config_passpoint)
+        del self.config_passpoint_ttls[WifiEnums.SSID_KEY]
+        # Set screen lock password so ConfigStore is unlocked.
+        self.droid.setDevicePassword(self.device_password)
+        return True
+
+    def teardown_class(self):
+        reset_droid_wifi(self.droid, self.ed)
+        self.droid.disableDevicePassword()
+        self.ed.clear_all_events()
+
+    def setup_test(self):
+        self.droid.wifiStartTrackingStateChange()
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        reset_droid_wifi(self.droid, self.ed)
+        self.ed.clear_all_events()
+        return True
+
+    def teardown_test(self):
+        self.droid.wakeLockRelease()
+        self.droid.goToSleepNow()
+        self.droid.wifiStopTrackingStateChange()
+
+    """Helper Functions"""
+
+    def eap_negative_connect_logic(self, config, ad):
+        """Tries to connect to an enterprise network with invalid credentials
+        and expect a failure.
+
+        Args:
+            config: A dict representing an invalid EAP credential.
+
+        Returns:
+            True if connection failed as expected, False otherwise.
+        """
+        verdict = eap_connect(config, ad)
+        self.assert_true(not verdict, "Connection should have failed.")
+        self.log.info("Connection failed as expected.")
+        return True
+
+    def expand_config_by_phase2(self, config):
+        """Take an enterprise config and generate a list of configs, each with
+        a different phase2 auth type.
+
+        Args:
+            config: A dict representing enterprise config.
+
+        Returns
+            A list of enterprise configs.
+        """
+        results = []
+        for phase2_type in EapPhase2:
+            # Skip a special case for passpoint TTLS.
+            if Ent.FQDN in config and phase2_type == EapPhase2.GTC:
+                continue
+            c = dict(config)
+            c[Ent.PHASE2] = phase2_type
+            results.append(c)
+        return results
+
+    def gen_eap_configs(self):
+        """Generates configurations for different EAP authentication types.
+
+        Returns:
+            A list of dicts each representing an EAP configuration.
+        """
+        configs = [self.config_tls]
+                   # self.config_sim
+        configs += expand_enterprise_config_by_phase2(self.config_ttls)
+        configs += expand_enterprise_config_by_phase2(self.config_peap)
+        return configs
+
+    def gen_passpoint_configs(self):
+        """Generates passpoint configurations for different EAP authentication
+        types.
+
+        Returns:
+            A list of dicts each representing an EAP configuration for
+            passpoint networks.
+        """
+        configs = [self.config_passpoint_tls]
+        configs += expand_enterprise_config_by_phase2(self.config_passpoint_ttls)
+        return configs
+
+    def gen_negative_configs(self, configs, neg_params):
+        """Generic function used to generate negative configs.
+
+        For all the valid configurations, if a param in the neg_params also
+        exists in a config, a copy of the config is made with an invalid value
+        of the param.
+
+        Args:
+            configs: A list of valid configurations.
+            neg_params: A dict that has all the invalid values.
+
+        Returns:
+            A list of invalid configurations generated based on the valid
+            configurations. Each invalid configuration has a different invalid
+            field.
+        """
+        results = []
+        for c in configs:
+            for k, v in neg_params.items():
+                # Skip negative test for TLS's identity field since it's not
+                # used for auth.
+                if c[Ent.EAP] == EAP.TLS and k == Ent.IDENTITY:
+                    continue
+                if k in c:
+                    nc = dict(c)
+                    nc[k] = v
+                    nc["invalid_field"] = k
+                    results.append(nc)
+        return results
+
+    def gen_negative_eap_configs(self):
+        """Generates invalid configurations for different EAP authentication
+        types.
+
+        For all the valid EAP configurations, if a param that is part of the
+        authentication info exists in a config, a copy of the config is made
+        with an invalid value of the param.
+
+        Returns:
+            A list of dicts each representing an invalid EAP configuration.
+        """
+        neg_params = {
+            Ent.CLIENT_CERT: self.invalid_client_cert,
+            Ent.CA_CERT: self.invalid_ca_cert,
+            Ent.PRIVATE_KEY_ID: self.invalid_client_key,
+            Ent.IDENTITY: "fake_identity",
+            Ent.PASSWORD: "wrong_password"
+        }
+        configs = self.gen_eap_configs()
+        return self.gen_negative_configs(configs, neg_params)
+
+    def gen_negative_passpoint_configs(self):
+        """Generates invalid configurations for different EAP authentication
+        types with passpoint support.
+
+        Returns:
+            A list of dicts each representing an invalid EAP configuration
+            with passpoint fields.
+        """
+        neg_params = {
+            Ent.CLIENT_CERT: self.invalid_client_cert,
+            Ent.CA_CERT: self.invalid_ca_cert,
+            Ent.PRIVATE_KEY_ID: self.invalid_client_key,
+            Ent.IDENTITY: "fake_identity",
+            Ent.PASSWORD: "wrong_password",
+            Ent.FQDN: "fake_fqdn",
+            Ent.REALM: "where_no_one_has_gone_before",
+            Ent.PLMN: "fake_plmn",
+            Ent.ROAMING_IDS: [1234567890, 9876543210]
+        }
+        configs = self.gen_passpoint_configs()
+        return self.gen_negative_configs(configs, neg_params)
+
+    def gen_eap_test_name(self, config, ad):
+        """Generates a test case name based on an EAP configuration.
+
+        Args:
+            config: A dict representing an EAP credential.
+            ad: Discarded. This is here because name function signature needs
+                to be consistent with logic function signature for generated
+                test cases.
+
+        Returns:
+            A string representing the name of a generated EAP test case.
+        """
+        name = "test_connect-%s" % config[Ent.EAP].name
+        if Ent.PHASE2 in config:
+            name += "-{}".format(config[Ent.PHASE2].name)
+        return name
+
+    def gen_passpoint_test_name(self, config, ad):
+        """Generates a test case name based on an EAP passpoint configuration.
+
+        Args:
+            config: A dict representing an EAP passpoint credential.
+            ad: Discarded. This is here because name function signature needs
+                to be consistent with logic function signature for generated
+                test cases.
+
+        Returns:
+            A string representing the name of a generated EAP passpoint connect
+            test case.
+        """
+        name = self.gen_eap_test_name(config, ad)
+        name = name.replace("connect", "passpoint_connect")
+        return name
+
+    """Tests"""
+    def test_eap_connect(self):
+        """Test connecting to enterprise networks of different authentication
+        types.
+
+        The authentication types tested are:
+            EAP-TLS
+            EAP-PEAP with different phase2 types.
+            EAP-TTLS with different phase2 types.
+
+        Procedures:
+            For each enterprise wifi network
+            1. Connect to the network.
+            2. Send a GET request to a website and check response.
+
+        Expect:
+            Successful connection and Internet access through the enterprise
+            networks.
+        """
+        eap_configs = self.gen_eap_configs()
+        self.log.info("Testing %d different configs." % len(eap_configs))
+        shuffle(eap_configs)
+        failed = self.run_generated_testcases(
+            eap_connect,
+            eap_configs,
+            self.dut,
+            name_func=self.gen_eap_test_name)
+        msg = ("The following configs failed EAP connect test: %s" %
+               pprint.pformat(failed))
+        self.assert_true(len(failed) == 0, msg)
+        return True
+
+    def test_eap_connect_negative(self):
+        """Test connecting to enterprise networks.
+
+        Procedures:
+            For each enterprise wifi network
+            1. Connect to the network with invalid credentials.
+
+        Expect:
+            Fail to establish connection.
+        """
+        neg_eap_configs = self.gen_negative_eap_configs()
+        self.log.info("Testing %d different configs." % len(neg_eap_configs))
+        shuffle(neg_eap_configs)
+        def name_gen(config, ad):
+            name = self.gen_eap_test_name(config, ad)
+            name += "-with_wrong-{}".format(config["invalid_field"])
+            return name
+        failed = self.run_generated_testcases(
+            self.eap_negative_connect_logic,
+            neg_eap_configs,
+            self.dut,
+            name_func=name_gen)
+        msg = ("The following configs failed negative EAP connect test: %s" %
+               pprint.pformat(failed))
+        self.assert_true(len(failed) == 0, msg)
+        return True
+
+    def test_passpoint_connect(self):
+        """Test connecting to enterprise networks of different authentication
+        types with passpoint support.
+
+        The authentication types tested are:
+            EAP-TLS
+            EAP-TTLS with MSCHAPV2 as phase2.
+
+        Procedures:
+            For each enterprise wifi network
+            1. Connect to the network.
+            2. Send a GET request to a website and check response.
+
+        Expect:
+            Successful connection and Internet access through the enterprise
+            networks with passpoint support.
+        """
+        self.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+            "Passpoint is not supported on device %s" % self.dut.model)
+        passpoint_configs = self.gen_passpoint_configs()
+        self.log.info("Testing %d different configs." % len(passpoint_configs))
+        shuffle(passpoint_configs)
+        failed = self.run_generated_testcases(
+            eap_connect,
+            passpoint_configs,
+            self.dut,
+            name_func=self.gen_passpoint_test_name)
+        msg = ("The following configs failed passpoint connect test: %s" %
+               pprint.pformat(failed))
+        self.assert_true(len(failed) == 0, msg)
+        return True
+
+    def test_passpoint_connect_negative(self):
+        """Test connecting to enterprise networks.
+
+        Procedures:
+            For each enterprise wifi network
+            1. Connect to the network with invalid credentials.
+
+        Expect:
+            Fail to establish connection.
+        """
+        self.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+            "Passpoint is not supported on device %s" % self.dut.model)
+        neg_passpoint_configs = self.gen_negative_passpoint_configs()
+        self.log.info("Testing %d different configs." % len(neg_passpoint_configs))
+        shuffle(neg_passpoint_configs)
+        def name_gen(config, ad):
+            name = self.gen_passpoint_test_name(config, ad)
+            name += "-with_wrong-{}".format(config["invalid_field"])
+            return name
+        failed = self.run_generated_testcases(
+            self.eap_negative_connect_logic,
+            neg_passpoint_configs,
+            self.dut,
+            name_func=name_gen)
+        msg = ("The following configs failed negative passpoint connect test: "
+               "%s") % pprint.pformat(failed)
+        self.assert_true(len(failed) == 0, msg)
+        return True
+
+if __name__ == "__main__":
+    pass
diff --git a/acts/tests/google/wifi/WifiManagerTest.py b/acts/tests/google/wifi/WifiManagerTest.py
new file mode 100755
index 0000000..cb3197f
--- /dev/null
+++ b/acts/tests/google/wifi/WifiManagerTest.py
@@ -0,0 +1,272 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import itertools
+import pprint
+import time
+import traceback
+
+from queue import Empty
+
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi_test_utils import match_networks
+from acts.test_utils.wifi_test_utils import reset_droid_wifi
+from acts.test_utils.wifi_test_utils import start_wifi_connection_scan
+from acts.test_utils.wifi_test_utils import wifi_forget_network
+from acts.test_utils.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi_test_utils import wifi_toggle_state
+from acts.test_utils.wifi_test_utils import WifiEnums
+from acts.test_utils.wifi_test_utils import WifiEventNames
+from acts.utils import find_field
+from acts.utils import trim_model_name
+
+class WifiManagerTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_toggle_state",
+            "test_toggle_with_screen",
+            "test_scan",
+            "test_add_network",
+            "test_forget_network",
+            "test_tdls_supported",
+            "test_energy_info",
+            "test_iot_with_password",
+            )
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wifi_test_device_init(self.dut)
+        req_params = (
+            "iot_networks",
+            "open_network",
+            "iperf_server_address",
+            "tdls_models",
+            "energy_info_models"
+            )
+        self.assert_true(self.unpack_userparams(req_params),
+            "Failed to unpack user params")
+        self.assert_true(len(self.iot_networks) > 0,
+            "Need at least one iot network with psk.")
+        self.assert_true(wifi_toggle_state(self.droid, self.ed, True),
+            "Failed to turn on wifi before tests.")
+        self.iot_networks = self.iot_networks + [self.open_network]
+        self.iperf_server = self.iperf_servers[0]
+        return True
+
+    def setup_test(self):
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        self.iperf_server.start()
+        return True
+
+    def teardown_test(self):
+        self.droid.wakeLockRelease()
+        self.droid.goToSleepNow()
+        reset_droid_wifi(self.droid, self.ed)
+        self.iperf_server.stop()
+
+    """Helper Functions"""
+    def connect_to_wifi_network_with_password(self, params):
+        """Connection logic for open and psk wifi networks.
+
+        Logic steps are
+        1. Connect to the network.
+        2. Run iperf traffic.
+
+        Args:
+            params: A tuple of network info and AndroidDevice object.
+
+        Returns:
+            True if successful, False otherwise.
+        """
+        result = False
+        wait_time = 5
+        network, ad = params
+        droid = ad.droid
+        ed = ad.ed
+        SSID = network[WifiEnums.SSID_KEY]
+        try:
+            ed.clear_all_events()
+            start_wifi_connection_scan(droid, ed)
+            droid.wifiStartTrackingStateChange()
+            self.assert_true(droid.wifiConnect(network),
+                "wifi connect returned false.")
+            connect_result = ed.pop_event(WifiEventNames.WIFI_CONNECTED)
+            self.log.debug(connect_result)
+            result = connect_result['data'][WifiEnums.SSID_KEY] == SSID
+            if result:
+                self.log.info("Starting iperf traffic through {}".format(SSID))
+                time.sleep(wait_time)
+                port_arg = "-p {}".format(self.iperf_server.port)
+                result, data = ad.run_iperf_client(self.iperf_server_address,
+                                                   port_arg)
+                self.log.debug(pprint.pformat(data))
+        except Empty:
+            self.log.error("Failed to connect to {}".format(SSID))
+            self.log.debug(traceback.format_exc())
+        finally:
+            droid.wifiStopTrackingStateChange()
+        return result
+
+    def run_iperf(self, iperf_args):
+        if "iperf_server_address" not in self.user_params:
+            self.log.error(("Missing iperf_server_address. "
+                "Provide one in config."))
+        else:
+            iperf_addr = self.user_params["iperf_server_address"]
+            self.log.info("Running iperf client.")
+            result, data = self.dut.run_iperf_client(iperf_addr,
+                iperf_args)
+            self.log.debug(data)
+
+    def run_iperf_rx_tx(self, time, omit=10):
+        args = "-p {} -t {} -O 10".format(self.iperf_server.port, time, omit)
+        self.log.info("Running iperf client {}".format(args))
+        self.run_iperf(args)
+        args = "-p {} -t {} -O 10 -R".format(self.iperf_server.port, time, omit)
+        self.log.info("Running iperf client {}".format(args))
+        self.run_iperf(args)
+
+    """Tests"""
+    def test_toggle_state(self):
+        """Test toggling wifi"""
+        self.log.debug("Going from on to off.")
+        assert wifi_toggle_state(self.droid, self.ed, False)
+        self.log.debug("Going from off to on.")
+        assert wifi_toggle_state(self.droid, self.ed, True)
+        return True
+
+    def test_toggle_with_screen(self):
+        """Test toggling wifi with screen on/off"""
+        wait_time = 5
+        self.log.debug("Screen from off to on.")
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        time.sleep(wait_time)
+        self.log.debug("Going from on to off.")
+        try:
+            assert wifi_toggle_state(self.droid, self.ed, False)
+            time.sleep(wait_time)
+            self.log.debug("Going from off to on.")
+            assert wifi_toggle_state(self.droid, self.ed, True)
+        finally:
+            self.droid.wakeLockRelease()
+            time.sleep(wait_time)
+            self.droid.goToSleepNow()
+        return True
+
+    def test_scan(self):
+        """Test wifi connection scan can start and find expected networks."""
+        wifi_toggle_state(self.droid, self.ed, True)
+        self.log.debug("Start regular wifi scan.")
+        start_wifi_connection_scan(self.droid, self.ed)
+        wifi_results = self.droid.wifiGetScanResults()
+        self.log.debug("Scan results: %s" % wifi_results)
+        condition = {WifiEnums.SSID_KEY: self.open_network[WifiEnums.SSID_KEY]}
+        assert match_networks(condition, wifi_results)
+        return True
+
+    def test_add_network(self):
+        """Test wifi connection scan."""
+        ssid = self.open_network[WifiEnums.SSID_KEY]
+        nId = self.droid.wifiAddNetwork(self.open_network)
+        self.assert_true(nId > -1, "Failed to add network.")
+        configured_networks = self.droid.wifiGetConfiguredNetworks()
+        self.log.debug(("Configured networks after adding: %s" %
+                        configured_networks))
+        condition = {WifiEnums.SSID_KEY: ssid}
+        assert match_networks(condition, configured_networks)
+        return True
+
+    def test_forget_network(self):
+        self.assert_true(self.test_add_network(), "Failed to add network.")
+        ssid = self.open_network[WifiEnums.SSID_KEY]
+        wifi_forget_network(self.dut, ssid)
+        configured_networks = self.droid.wifiGetConfiguredNetworks()
+        for nw in configured_networks:
+            self.assert_true(nw[WifiEnums.BSSID_KEY] != ssid,
+                "Found forgotten network %s in configured networks." % ssid)
+        return True
+
+    def test_iot_with_password(self):
+        params = list(itertools.product(self.iot_networks, self.android_devices))
+        name_gen = lambda p : "test_connection_to-%s" % p[0][WifiEnums.SSID_KEY]
+        failed = self.run_generated_testcases(
+            self.connect_to_wifi_network_with_password,
+            params,
+            name_func=name_gen)
+        self.assert_true(not failed, "Failed ones: {}".format(failed))
+        return True
+
+    def test_tdls_supported(self):
+        model = trim_model_name(self.dut.model)
+        self.log.debug("Model is %s" % model)
+        if model in self.tdls_models:
+            assert self.droid.wifiIsTdlsSupported()
+        else:
+            assert not self.droid.wifiIsTdlsSupported()
+        return True
+
+    # TODO(angli): Actually connect to a network and do an http request between
+    # iterations.
+    def test_energy_info(self):
+        """Verify the WiFi energy info reporting feature.
+
+        Steps:
+            1. Check that the WiFi energy info reporting support on this device
+               is as expected (support or not).
+            2. If the device does not support energy info reporting as
+               expected, skip the test.
+            3. Call API to get WiFi energy info.
+            4. Verify the values of "ControllerEnergyUsed" and
+               "ControllerIdleTimeMillis" in energy info don't decrease.
+            5. Repeat from Step 3 for 10 times.
+        """
+        # Check if dut supports energy info reporting.
+        actual_support = self.dut.droid.wifiIsEnhancedPowerReportingSupported()
+        model = self.dut.model
+        expected_support = model in self.energy_info_models
+        msg = "Expect energy info support to be %s on %s, got %s." % (
+              expected_support, model, actual_support)
+        self.assert_true(actual_support == expected_support, msg)
+        if not actual_support:
+            self.skip(("Device %s does not support energy info reporting as "
+                       "expected.") % model)
+        # Verify reported values don't decrease.
+        self.log.info(("Device %s supports energy info reporting, verify that "
+                       "the reported values don't decrease.") % model)
+        energy = 0
+        idle_time = 0
+        for i in range(10):
+            info = self.droid.wifiGetControllerActivityEnergyInfo()
+            self.log.debug("Iteration %d, got energy info: %s" % (i, info))
+            new_energy = info["ControllerEnergyUsed"]
+            new_idle_time = info["ControllerIdleTimeMillis"]
+            self.assert_true(new_energy >= energy,
+                "Energy value decreased: previous %d, now %d" % (energy,
+                    new_energy))
+            energy = new_energy
+            self.assert_true(new_idle_time >= idle_time,
+                "Idle time decreased: previous %d, now %d" % (idle_time,
+                    new_idle_time))
+            idle_time = new_idle_time
+            start_wifi_connection_scan(self.droid, self.ed)
+        return True
+
+if __name__ == "__main__":
+    pass
diff --git a/acts/tests/google/wifi/WifiPowerTest.py b/acts/tests/google/wifi/WifiPowerTest.py
new file mode 100644
index 0000000..23f82d5
--- /dev/null
+++ b/acts/tests/google/wifi/WifiPowerTest.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 json
+import time
+import traceback
+
+from queue import Empty
+
+from acts.base_test import BaseTestClass
+from acts.utils import find_field
+from acts.utils import rand_ascii_str
+from acts.utils import sync_device_time
+
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.wifi_test_utils import reset_droid_wifi
+from acts.test_utils.wifi_test_utils import start_wifi_connection_scan
+from acts.test_utils.wifi_test_utils import wifi_connect
+from acts.test_utils.wifi_test_utils import wifi_toggle_state
+from acts.test_utils.wifi_test_utils import WifiEnums
+
+class WifiPowerTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        self.tests = (
+            "test_power",
+            )
+        BaseTestClass.__init__(self, controllers)
+
+    def setup_class(self):
+        self.mon = self.monsoons[0]
+        self.mon.set_voltage(4.2)
+        self.mon.set_max_current(7.8)
+        self.ad = self.android_devices[0]
+        required_userparam_names = (
+            # These two params should follow the format of
+            # {"SSID": <SSID>, "password": <Password>}
+            "network_2g",
+            "network_5g"
+        )
+        assert self.unpack_userparams(required_userparam_names)
+        sync_device_time(self.ad)
+        reset_droid_wifi(self.ad.droid, self.ad.ed)
+        return True
+
+    def test_power(self):
+        toggle_airplane_mode(self.log, self.ad, True)
+        # 30min for each measurement.
+        durations = 1
+        start_pmc = ("am start -n com.android.pmc/com.android.pmc."
+            "PMCMainActivity")
+        pmc_base_cmd = "am broadcast -a com.android.pmc.action.AUTOPOWER --es PowerAction "
+        pmc_start_connect_scan = pmc_base_cmd + "StartConnectivityScan"
+        pmc_stop_connect_scan = pmc_base_cmd + "StopConnectivityScan"
+        pmc_start_gscan_no_dfs = pmc_base_cmd + "StartGScanBand"
+        pmc_start_gscan_specific_channels = pmc_base_cmd + "StartGScanChannel"
+        pmc_stop_gscan = pmc_base_cmd + "StopGScan"
+        pmc_start_1MB_download = pmc_base_cmd + "Download1MB"
+        pmc_stop_1MB_download = pmc_base_cmd + "StopDownload"
+        # Start pmc app.
+        self.ad.adb.shell(start_pmc)
+        def wifi_off(ad):
+            assert wifi_toggle_state(ad.droid, ad.ed, False)
+            return True
+        def wifi_on(ad):
+            assert wifi_toggle_state(ad.droid, ad.ed, True)
+            return True
+        def wifi_on_with_connectivity_scan(ad):
+            ad.adb.shell(pmc_start_connect_scan)
+            self.log.info("Started connectivity scan.")
+            return True
+        def connected_to_2g(ad):
+            ad.adb.shell(pmc_stop_connect_scan)
+            self.log.info("Stoped connectivity scan.")
+            reset_droid_wifi(ad.droid, ad.ed)
+            ssid = self.network_2g["SSID"]
+            pwd = self.network_2g["password"]
+            msg = "Failed to connect to %s" % ssid
+            assert wifi_connect(ad, ssid, pwd), msg
+            self.log.info("Connected to %s" % ssid)
+            return True
+        def connected_to_2g_download_1MB(ad):
+            self.log.info("Start downloading 1MB file consecutively.")
+            ad.adb.shell(pmc_start_1MB_download)
+            return True
+        def connected_to_5g(ad):
+            ad.adb.shell(pmc_stop_1MB_download)
+            self.log.info("Stopped downloading 1MB file.")
+            reset_droid_wifi(ad.droid, ad.ed)
+            ssid = self.network_5g["SSID"]
+            pwd = self.network_5g["password"]
+            msg = "Failed to connect to %s" % ssid
+            assert wifi_connect(ad, ssid, pwd), msg
+            self.log.info("Connected to %s" % ssid)
+            return True
+        def connected_to_5g_download_1MB(ad):
+            self.log.info("Start downloading 1MB file consecutively.")
+            ad.adb.shell(pmc_start_1MB_download)
+            return True
+        def gscan_with_three_channels(ad):
+            reset_droid_wifi(ad.droid, ad.ed)
+            self.log.info("Disconnected from wifi.")
+            ad.adb.shell(pmc_stop_1MB_download)
+            self.log.info("Stopped downloading 1MB file.")
+            ad.adb.shell(pmc_start_gscan_specific_channels)
+            self.log.info("Started gscan for the three main 2G channels.")
+            return True
+        def gscan_with_all_channels(ad):
+            ad.adb.shell(pmc_stop_gscan)
+            self.log.info("Stopped gscan with channels.")
+            ad.adb.shell(pmc_start_gscan_no_dfs)
+            self.log.info("Started gscan for all but DFS channels.")
+            return True
+        def clean_up(ad):
+            ad.adb.shell(pmc_stop_gscan)
+            reset_droid_wifi(ad.droid, ad.ed)
+            return False
+        funcs = (
+            wifi_off,
+            wifi_on,
+            wifi_on_with_connectivity_scan,
+            connected_to_2g,
+            connected_to_2g_download_1MB,
+            connected_to_5g,
+            connected_to_5g_download_1MB,
+            gscan_with_three_channels,
+            gscan_with_all_channels
+        )
+        results = self.mon.execute_sequence_and_measure(10, durations, funcs, self.ad, offset_sec=30)
+        assert len(results) == len(funcs), "Did not get enough results!"
+        return True
diff --git a/acts/tests/google/wifi/WifiRttManagerTest.py b/acts/tests/google/wifi/WifiRttManagerTest.py
new file mode 100644
index 0000000..b4bdc5c
--- /dev/null
+++ b/acts/tests/google/wifi/WifiRttManagerTest.py
@@ -0,0 +1,571 @@
+#!/usr/bin/python3.4
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import pprint
+
+from queue import Empty
+from acts.base_test import BaseTestClass
+from acts.controllers.android import SL4AAPIError
+from acts.test_utils.wifi_test_utils import match_networks
+from acts.test_utils.wifi_test_utils import SPEED_OF_LIGHT
+from acts.test_utils.wifi_test_utils import start_wifi_single_scan
+from acts.test_utils.wifi_test_utils import start_wifi_connection_scan
+from acts.test_utils.wifi_test_utils import trim_model_name
+from acts.test_utils.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi_test_utils import WifiEnums
+
+# Macros for RttParam keywords
+RttParam = WifiEnums.RttParam
+# Macros for RttManager
+Rtt = WifiEnums.Rtt
+RttBW = WifiEnums.RttBW
+RttPreamble = WifiEnums.RttPreamble
+RttPeerType = WifiEnums.RttPeerType
+RttType = WifiEnums.RttType
+
+ScanResult = WifiEnums.ScanResult
+RTT_MARGIN_OF_ERROR = WifiEnums.RTT_MARGIN_OF_ERROR
+
+class WifiRTTRangingError (Exception):
+     """Error in WifiScanner Rtt."""
+
+class WifiRttManagerTest(BaseTestClass):
+    """Tests for wifi's RttManager APIs."""
+    tests = None
+    MAX_RTT_AP = 10
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.tests = (
+            "test_support_check",
+            "test_invalid_params",
+            "test_capability_check",
+            "test_rtt_ranging_single_AP_stress",
+            "test_regular_scan_then_rtt_ranging_stress",
+            "test_gscan_then_rtt_ranging_stress"
+        )
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wifi_test_device_init(self.dut)
+        required_params = (
+            "support_models",
+            "stress_num",
+            "vht80_5g",
+            # "no_11mc",
+            # "ht40_2g"
+            "actual_distance"
+        )
+        msg = "Failed to unpack user params."
+        self.assert_true(self.unpack_userparams(required_params), msg)
+        self.assert_true(self.actual_distance >= 5,
+            "Actual distance should be no shorter than 5 meters.")
+        self.visible_networks = (
+            self.vht80_5g,
+            # self.no_11mc,
+            # self.ht40_2g
+        )
+        self.default_rtt_params = {
+            RttParam.request_type: RttType.TYPE_TWO_SIDED,
+            RttParam.device_type: RttPeerType.PEER_TYPE_AP,
+            RttParam.preamble: RttPreamble.PREAMBLE_HT,
+            RttParam.bandwidth: RttBW.BW_80_SUPPORT
+        }
+        # Expected capability for devices that don't support RTT.
+        rtt_cap_neg = {
+            'lcrSupported': False,
+            'bwSupported': 0,
+            'twoSided11McRttSupported': False,
+            'preambleSupported': 0,
+            'oneSidedRttSupported': False,
+            'lciSupported': False
+        }
+        rtt_cap_shamu = {
+            'lcrSupported': False,
+            'bwSupported': 0x1C,
+            'twoSided11McRttSupported': True,
+            'preambleSupported': 6,
+            'oneSidedRttSupported': False,
+            'lciSupported': False
+        }
+        rtt_cap_bullhead = {
+            'lcrSupported': True,
+            'bwSupported': 0x1C,
+            'twoSided11McRttSupported': True,
+            'preambleSupported': 7,
+            'oneSidedRttSupported': True,
+            'lciSupported': True
+        }
+        rtt_cap_angler = {
+            'lcrSupported': True,
+            'bwSupported': 0x1C,
+            'twoSided11McRttSupported': True,
+            'preambleSupported': 6,
+            'oneSidedRttSupported': False,
+            'lciSupported': True
+        }
+        self.rtt_cap_table = {
+            "hammerhead": rtt_cap_neg,
+            "shamu": rtt_cap_shamu,
+            "volantis": rtt_cap_neg,
+            "volantisg": rtt_cap_neg,
+            "bullhead": rtt_cap_bullhead,
+            "angler": rtt_cap_angler
+        }
+        return True
+
+    """Helper Functions"""
+    def invalid_params_logic(self, rtt_params):
+        try:
+            self.droid.wifiRttStartRanging([rtt_params])
+        except SL4AAPIError as e:
+            e_str = str(e)
+            self.assert_true("IllegalArgumentException" in e_str,
+                "Missing IllegalArgumentException in %s." % e_str)
+            msg = "Got expected exception with invalid param %s." % rtt_params
+            self.log.info(msg)
+
+    def get_rtt_results(self, rtt_params):
+        """Starts RTT ranging and get results.
+
+        Args:
+            rtt_params: A list of dicts each representing an RttParam.
+
+        Returns:
+            Rtt ranging results.
+        """
+        self.log.debug("Start ranging with:\n%s" % pprint.pformat(rtt_params))
+        idx = self.droid.wifiRttStartRanging(rtt_params)
+        event = None
+        try:
+            event = self.ed.pop_events("WifiRttRanging%d" % idx, 30)
+            if event[0]["name"].endswith("onSuccess"):
+                results = event[0]["data"]["Results"]
+                result_len = len(results)
+                param_len = len(rtt_params)
+                self.assert_true(result_len == param_len,
+                    "Expected %d results, got %d." % (param_len, result_len))
+                # Add acceptable margin of error to results, which will be used
+                # during result processing.
+                for i, r in enumerate(results):
+                    bw_mode = rtt_params[i][RttParam.bandwidth]
+                    r[RttParam.margin] = RTT_MARGIN_OF_ERROR[bw_mode]
+            self.log.debug(pprint.pformat(event))
+            return event
+        except Empty:
+            self.log.error("Waiting for RTT event timed out.")
+            return None
+
+    def network_selector(self, network_info):
+        """Decides if a network should be used for rtt ranging.
+
+        There are a few conditions:
+        1. This network supports 80211mc.
+        2. This network's info matches certain conditions.
+
+        This is added to better control which networks to range against instead
+        of blindly use all 80211mc networks in air.
+
+        Args:
+            network_info: A dict representing a WiFi network.
+
+        Returns:
+            True if the input network should be used for ranging, False
+            otherwise.
+        """
+        target_params = {
+            "is80211McRTTResponder": True,
+            WifiEnums.BSSID_KEY: self.vht80_5g[WifiEnums.BSSID_KEY],
+        }
+        for k, v in target_params.items():
+            if k not in network_info:
+                return False
+            if type(network_info[k]) is str:
+                network_info[k] = network_info[k].lower()
+                v = v.lower()
+            if network_info[k] != v:
+                return False
+        return True
+
+    def regular_scan_for_rtt_networks(self):
+        """Scans for 11mc-capable WiFi networks using regular wifi scan.
+
+        Networks are selected based on self.network_selector.
+
+        Returns:
+            A list of networks that have RTTResponders.
+        """
+        start_wifi_connection_scan(self.droid, self.ed)
+        networks = self.droid.wifiGetScanResults()
+        rtt_networks = []
+        for nw in networks:
+            if self.network_selector(nw):
+                rtt_networks.append(nw)
+        return rtt_networks
+
+    def gscan_for_rtt_networks(self):
+        """Scans for 11mc-capable WiFi networks using wifi gscan.
+
+        Networks are selected based on self.network_selector.
+
+        Returns:
+            A list of networks that have RTTResponders.
+        """
+        s = {
+            "reportEvents" : WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+            "band": WifiEnums.WIFI_BAND_BOTH,
+            "periodInMs": 10000,
+            "numBssidsPerScan": 32
+        }
+        idx = start_wifi_single_scan(self.android_devices[0], s)["Index"]
+        self.log.info("Scan index is %d" % idx)
+        event_name = "WifiScannerScan%donFullResult" % idx
+        def condition(event):
+            nw = event["data"]["Results"][0]
+            return self.network_selector(nw)
+        rtt_networks = []
+        try:
+            for i in range(len(self.visible_networks)):
+                event = self.ed.wait_for_event(event_name, condition, 30)
+                rtt_networks.append(event["data"]["Results"][0])
+            self.log.info("Waiting for gscan to finish.")
+            event_name = "WifiScannerScan%donResults" % idx
+            event = self.ed.pop_event(event_name, 300)
+            total_network_cnt = len(event["data"]["Results"][0]["ScanResults"])
+            self.log.info("Found %d networks in total." % total_network_cnt)
+            self.log.debug(rtt_networks)
+            return rtt_networks
+        except Empty:
+            self.log.error("Timed out waiting for gscan result.")
+
+    def process_rtt_events(self, events):
+        """Processes rtt ranging events.
+
+        Validates RTT event types.
+        Validates RTT response status and measured RTT values.
+        Enforces success rate.
+
+        Args:
+            events: A list of callback results from RTT ranging.
+        """
+        total = aborted = failure = invalid = out_of_range = 0
+        for e in events:
+            if e["name"].endswith("onAborted"):
+                aborted += 1
+            if e["name"].endswith("onFailure"):
+                failure += 1
+            if e["name"].endswith("onSuccess"):
+                results = e["data"]["Results"]
+                for r in results:
+                    total += 1
+                    # Status needs to be "success".
+                    status = r["status"]
+                    if status != Rtt.STATUS_SUCCESS:
+                        self.log.warning("Got error status %d." % status)
+                        invalid += 1
+                        continue
+                    # RTT value should be positive.
+                    value = r["rtt"]
+                    if value <= 0:
+                        self.log.warning("Got error RTT value %d." % value)
+                        invalid += 1
+                        continue
+                    # Vadlidate values in successful responses.
+                    acd = self.actual_distance
+                    margin = r[RttParam.margin]
+                    # If the distance is >= 0, check distance only.
+                    d = r["distance"] / 100.0
+                    if d > 0:
+                        # Distance should be in acceptable range.
+                        is_d_valid = (acd - margin) <= d <= acd + (margin)
+                        if not is_d_valid:
+                            self.log.warning(("Reported distance %.2fm is out of the"
+                                " acceptable range %.2f±%.2fm.") % (d, acd, margin))
+                            out_of_range += 1
+                        continue
+                    # Check if the RTT value is in range.
+                    d = (value / 2) / 1E10 * SPEED_OF_LIGHT
+                    is_rtt_valid = (acd - margin) <= d <= (acd + margin)
+                    if not is_rtt_valid:
+                        self.log.warning(
+                           ("Distance calculated from RTT value %d - %.2fm is "
+                            "out of the acceptable range %.2f±%dm.") % (
+                            value, d, acd, margin))
+                        out_of_range += 1
+                        continue
+                    # Check if the RSSI value is in range.
+                    rssi = r["rssi"]
+                    # average rssi in 0.5dB steps, e.g. 143 implies -71.5dB,
+                    # so the valid range is 0 to 200
+                    is_rssi_valid = 0 <= rssi <= 200
+                    if not is_rssi_valid:
+                        self.log.warning(("Reported RSSI %d is out of the"
+                                " acceptable range 0-200") % rssi)
+                        out_of_range += 1
+                        continue
+        self.log.info(("Processed %d RTT events. %d aborted, %s failed. Among"
+            " the %d responses in successful callbacks, %s are invalid, %s has"
+            " RTT values that are out of range.") % (
+            len(events), aborted, failure, total, invalid, out_of_range))
+        self.assert_true(total > 0, "No RTT response received.")
+        # Percentage of responses that are valid should be >= 90%.
+        valid_total = float(total - invalid)
+        valid_response_rate = valid_total / total
+        self.log.info("%.2f%% of the responses are valid." % (
+                      valid_response_rate * 100))
+        self.assert_true(valid_response_rate >= 0.9,
+                         "Valid response rate is below 90%%.")
+        # Among the valid responses, the percentage of having an in-range RTT
+        # value should be >= 67%.
+        valid_value_rate = (total - invalid - out_of_range) / valid_total
+        self.log.info("%.2f%% of valid responses have in-range RTT value" % (
+                      valid_value_rate * 100))
+        msg = "In-range response rate is below 67%%."
+        self.assert_true(valid_value_rate >= 0.67, msg)
+
+    def scan_then_rtt_ranging_stress_logic(self, scan_func):
+        """Test logic to scan then do rtt ranging based on the scan results.
+
+        Steps:
+        1. Start scan and get scan results.
+        2. Filter out the networks that support rtt in scan results.
+        3. Start rtt ranging against those networks that support rtt.
+        4. Repeat
+        5. Process RTT events.
+
+        Args:
+            scan_func: A function that does a wifi scan and only returns the
+                networks that support rtt in the scan results.
+
+        Returns:
+            True if rtt behaves as expected, False otherwise.
+        """
+        total = self.stress_num
+        failed = 0
+        all_results = []
+        for i in range(total):
+            self.log.info("Iteration %d" % i)
+            rtt_networks = scan_func()
+            if not rtt_networks:
+                self.log.warning("Found no rtt network, skip this iteration.")
+                failed += 1
+                continue
+            self.log.debug("Found rtt networks:%s" % rtt_networks)
+            rtt_params = []
+            for rn in rtt_networks:
+                rtt_params.append(self.rtt_config_from_scan_result(rn))
+            results = self.get_rtt_results(rtt_params)
+            if results:
+                self.log.debug(results)
+                all_results += results
+        self.process_rtt_events(all_results)
+
+    def rtt_config_from_scan_result(self, scan_result):
+        """Creates an Rtt configuration based on the scan result of a network.
+        """
+        scan_result_channel_width_to_rtt = {
+            ScanResult.CHANNEL_WIDTH_20MHZ: RttBW.BW_20_SUPPORT,
+            ScanResult.CHANNEL_WIDTH_40MHZ: RttBW.BW_40_SUPPORT,
+            ScanResult.CHANNEL_WIDTH_80MHZ: RttBW.BW_80_SUPPORT,
+            ScanResult.CHANNEL_WIDTH_160MHZ: RttBW.BW_160_SUPPORT,
+            ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: RttBW.BW_160_SUPPORT
+        }
+        p = {}
+        freq = scan_result[RttParam.frequency]
+        p[RttParam.frequency] = freq
+        p[RttParam.BSSID] = scan_result[WifiEnums.BSSID_KEY]
+        if freq > 5000:
+            p[RttParam.preamble] = RttPreamble.PREAMBLE_VHT
+        else:
+            p[RttParam.preamble] = RttPreamble.PREAMBLE_HT
+        cf0 = scan_result[RttParam.center_freq0]
+        if cf0 > 0:
+            p[RttParam.center_freq0] = cf0
+        cf1 = scan_result[RttParam.center_freq1]
+        if cf1 > 0:
+            p[RttParam.center_freq1] = cf1
+        cw = scan_result["channelWidth"]
+        p[RttParam.channel_width] = cw
+        p[RttParam.bandwidth] = scan_result_channel_width_to_rtt[cw]
+        if scan_result["is80211McRTTResponder"]:
+            p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
+        else:
+            p[RttParam.request_type] = RttType.TYPE_ONE_SIDED
+        return p
+
+    """Tests"""
+    def test_invalid_params(self):
+        """Tests the sanity check function in RttManager.
+        """
+        param_list = [
+            {RttParam.device_type: 3},
+            {RttParam.device_type: 1, RttParam.request_type:3},
+            {
+                RttParam.device_type: 1,
+                RttParam.request_type:1,
+                RttParam.BSSID: None
+            },
+            {RttParam.BSSID: "xxxxxxxx", RttParam.number_burst: 1},
+            {RttParam.number_burst: 0, RttParam.num_samples_per_burst: -1},
+            {RttParam.num_samples_per_burst:32},
+            {
+                RttParam.num_samples_per_burst:5,
+                RttParam.num_retries_per_measurement_frame: -1
+            },
+            {RttParam.num_retries_per_measurement_frame: 4 },
+            {
+                RttParam.num_retries_per_measurement_frame: 2,
+                RttParam.num_retries_per_FTMR: -1
+            },
+            {RttParam.num_retries_per_FTMR: 4}
+        ]
+        for param in param_list:
+            self.invalid_params_logic(param)
+        return True
+
+    def test_support_check(self):
+        """No device supports device-to-device RTT; only shamu and volantis
+        devices support device-to-ap RTT.
+        """
+        model = trim_model_name(self.dut.model)
+        self.assert_true(not self.droid.wifiIsDeviceToDeviceRttSupported(),
+            "Device to device is not supposed to be supported.")
+        if any([model in m for m in self.support_models]):
+            self.assert_true(self.droid.wifiIsDeviceToApRttSupported(),
+                "%s should support device-to-ap RTT." % model)
+            self.log.info("%s supports device-to-ap RTT as expected." % model)
+        else:
+            self.assert_true(not self.droid.wifiIsDeviceToApRttSupported(),
+                "%s should not support device-to-ap RTT." % model)
+            self.log.info(("%s does not support device-to-ap RTT as expected."
+                ) % model)
+            self.abort_class("Device %s does not support RTT, abort." % model)
+        return True
+
+    def test_capability_check(self):
+        """Checks the capabilities params are reported as expected.
+        """
+        caps = self.droid.wifiRttGetCapabilities()
+        self.assert_true(caps, "Unable to get rtt capabilities.")
+        self.log.debug("Got rtt capabilities %s" % caps)
+        model = trim_model_name(self.dut.model)
+        self.assert_true(model in self.rtt_cap_table, "Unknown model %s" % model)
+        expected_caps = self.rtt_cap_table[model]
+        for k, v in expected_caps.items():
+            self.assert_true(k in caps, "%s missing in capabilities." % k)
+            self.assert_true(v == caps[k],
+                "Expected %s for %s, got %s." % (v, k, caps[k]))
+        return True
+
+    def test_discovery(self):
+        """Make sure all the expected 11mc BSSIDs are discovered properly, and
+        they are all reported as 802.11mc Rtt Responder.
+
+        Procedures:
+            1. Scan for wifi networks.
+
+        Expect:
+            All the RTT networks show up in scan results and their
+            "is80211McRTTResponder" is True.
+            All the non-RTT networks show up in scan results and their
+            "is80211McRTTResponder" is False.
+        """
+        start_wifi_connection_scan(self.droid, self.ed)
+        scan_results = self.droid.wifiGetScanResults()
+        self.log.debug(scan_results)
+        for n in visible_networks:
+            self.assert_true(match_networks(n, scan_results),
+                "Network %s was not discovered properly." % n)
+        return True
+
+    def test_missing_bssid(self):
+        """Start Rtt ranging with a config that does not have BSSID set.
+        Should not get onSuccess.
+        """
+        p = {}
+        p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
+        p[RttParam.device_type]  = RttPeerType.PEER_TYPE_AP
+        p[RttParam.preamble]     = RttPreamble.PREAMBLE_VHT
+        p[RttParam.bandwidth]    = RttBW.BW_80_SUPPORT
+        p[RttParam.frequency] = self.vht80_5g[WifiEnums.frequency_key]
+        p[RttParam.center_freq0] = self.vht80_5g[RttParam.center_freq0]
+        results = self.get_rtt_results([p])
+        self.assert_true(results, "Did not get any result.")
+        self.log.info(pprint.pformat(results))
+        return True
+
+    def test_rtt_ranging_single_AP_stress(self):
+        """Stress test for Rtt against one AP.
+
+        Steps:
+            1. Do RTT ranging against the self.vht80_5g BSSID.
+            2. Repeat self.stress_num times.
+            3. Verify RTT results.
+        """
+        p = {}
+        p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
+        p[RttParam.device_type]  = RttPeerType.PEER_TYPE_AP
+        p[RttParam.preamble]     = RttPreamble.PREAMBLE_VHT
+        p[RttParam.bandwidth]    = RttBW.BW_80_SUPPORT
+        p[RttParam.BSSID] = self.vht80_5g[WifiEnums.BSSID_KEY]
+        p[RttParam.frequency] = self.vht80_5g[WifiEnums.frequency_key]
+        p[RttParam.center_freq0] = self.vht80_5g[RttParam.center_freq0]
+        p[RttParam.channel_width] = ScanResult.CHANNEL_WIDTH_80MHZ
+        all_results = []
+        for i in range(self.stress_num):
+            self.log.info("RTT Ranging iteration %d" % (i + 1))
+            results = self.get_rtt_results([p])
+            if results:
+                all_results += results
+            else:
+                self.log.warning("Did not get result for iteration %d." % i)
+        frate = self.process_rtt_events(all_results)
+        return True
+
+    def test_regular_scan_then_rtt_ranging_stress(self):
+        """Stress test for regular scan then start rtt ranging against the RTT
+        compatible networks found by the scan.
+
+        Steps:
+            1. Start a WiFi connection scan.
+            2. Get scan results.
+            3. Find all the 11mc capable BSSIDs and choose the ones to use
+               (self.network_selector)
+            4. Do RTT ranging against the selected BSSIDs, with the info from
+               the scan results.
+            5. Repeat self.stress_num times.
+            6. Verify RTT results.
+        """
+        scan_func = self.regular_scan_for_rtt_networks
+        self.scan_then_rtt_ranging_stress_logic(scan_func)
+        return True
+
+    def test_gscan_then_rtt_ranging_stress(self):
+        """Stress test for gscan then start rtt ranging against the RTT
+        compatible networks found by the scan.
+
+        Steps:
+            1. Start a WifiScanner single shot scan on all channels.
+            2. Wait for full scan results of the expected 11mc capable BSSIDs.
+            3. Wait for single shot scan to finish on all channels.
+            4. Do RTT ranging against the selected BSSIDs, with the info from
+               the scan results.
+            5. Repeat self.stress_num times.
+            6. Verify RTT results.
+        """
+        scan_func = self.gscan_for_rtt_networks
+        self.scan_then_rtt_ranging_stress_logic(scan_func)
+        return True
diff --git a/acts/tests/google/wifi/WifiScannerBssidTest.py b/acts/tests/google/wifi/WifiScannerBssidTest.py
new file mode 100755
index 0000000..5953a10
--- /dev/null
+++ b/acts/tests/google/wifi/WifiScannerBssidTest.py
@@ -0,0 +1,438 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 os
+import itertools
+
+from queue import Empty
+from acts.base_test import BaseTestClass
+from acts.utils import load_config
+from acts.test_utils.wifi_test_utils import start_wifi_track_bssid
+from acts.test_utils.wifi_test_utils import start_wifi_background_scan
+from acts.test_utils.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi_test_utils import WifiChannelUS
+from acts.test_utils.wifi_test_utils import WifiEnums
+from acts.test_utils.wifi_test_utils import get_scan_time_and_channels
+
+
+BSSID_EVENT_WAIT = 30
+
+BSSID_EVENT_TAG = "WifiScannerBssid"
+SCAN_EVENT_TAG = "WifiScannerScan"
+SCANTIME = 10000 #framework support only 10s as minimum scan interval
+
+class WifiScannerBssidError(Exception):
+    pass
+
+class WifiScannerBssidTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        # A list of all test cases to be executed in this class.
+        self.tests = ("test_wifi_track_bssid_sanity",
+                      "test_wifi_track_bssid_found",
+                      "test_wifi_track_bssid_lost",
+                      "test_wifi_track_bssid_for_2g_while_scanning_5g_channels",
+                      "test_wifi_track_bssid_for_5g_while_scanning_2g_channels",)
+        self.default_scan_setting = {
+            "band": WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+            "periodInMs": SCANTIME,
+            "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+            'numBssidsPerScan': 32
+        }
+        self.leeway = 5
+        self.stime_channel = 47 #dwell time plus 2ms
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wifi_test_device_init(self.dut)
+        self.assert_true(self.dut.droid.wifiIsScannerSupported(),
+            "Device %s doesn't support WifiScanner, abort." % self.dut.model)
+        """It will setup the required dependencies and fetch the user params from
+          config file"""
+        self.attenuators[0].set_atten(0)
+        self.attenuators[1].set_atten(0)
+        req_params = ("bssid_2g", "bssid_5g", "bssid_dfs", "attenuator_id",
+                      "max_bugreports")
+        self.wifi_chs = WifiChannelUS(self.dut.model)
+        userparam_status = self.unpack_userparams(req_params)
+        return userparam_status
+
+    def on_fail(self, test_name, begin_time):
+        if self.max_bugreports > 0:
+            self.take_bug_reports(test_name, begin_time, self.android_devices)
+            self.max_bugreports -= 1
+
+    """ Helper Functions Begin """
+    def fetch_scan_result(self, scan_idx, scan_setting):
+        """Fetch the scan result for provider listener index.
+
+        This function calculate the time required for scanning based on scan setting
+        and wait for scan result event, on triggering of event process the scan result.
+
+        Args:
+          scan_idx: Index of the scan listener.
+          scan_setting: Setting used for starting the scan.
+
+        Returns:
+          scan_results: if scan result available.
+        """
+        #generating event wait time from scan setting plus leeway
+        self.log.debug(scan_setting)
+        scan_time, scan_channels = get_scan_time_and_channels(self.wifi_chs,
+                                                              scan_setting,
+                                                              self.stime_channel)
+        scan_time += scan_setting['periodInMs'] #add scan period delay for next cycle
+        if scan_setting["reportEvents"] == WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN:
+            waittime = int(scan_time/1000) + self.leeway
+        else:
+            time_cache = scan_setting['periodInMs'] * 10 #default cache
+            waittime = int((time_cache + scan_time )/1000) + self.leeway
+        event_name = "{}{}onResults".format(SCAN_EVENT_TAG, scan_idx)
+        self.log.info("Waiting for the scan result event {}".format(event_name))
+        event = self.ed.pop_event(event_name, waittime)
+        results = event["data"]["Results"]
+        if len(results) > 0 and "ScanResults" in results[0]:
+            return results[0]["ScanResults"]
+
+    def start_scan_and_validate_environment(self, scan_setting, bssid_settings):
+        """Validate environment for test using current scan result for provided
+           settings.
+
+        This function start the scan for given setting and verify that interested
+        Bssids are in scan result or not.
+
+        Args:
+            scan_setting: Setting used for starting the scan.
+            bssid_settings: list of bssid settings.
+
+        Returns:
+            True, if bssid not found in scan result.
+        """
+        try:
+            data = start_wifi_background_scan(self.dut, scan_setting)
+            self.scan_idx = data["Index"]
+            results = self.fetch_scan_result(self.scan_idx, scan_setting)
+            self.log.debug("scan result {}".format(results))
+            self.assert_true(results, "Device is not able to fetch the scan results")
+            for result in results:
+                for bssid_setting in bssid_settings:
+                    if bssid_setting[WifiEnums.BSSID_KEY] == result[WifiEnums.BSSID_KEY]:
+                        self.log.error("Bssid {} already exist in current scan results".
+                                   format(result[WifiEnums.BSSID_KEY]))
+                        return False
+        except Empty as error:
+            self.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+            raise AssertionError("OnResult event did not triggered for scanner\n{}".
+                                 format(error))
+        return True
+
+    def check_bssid_in_found_result(self, bssid_settings, found_results):
+        """look for any tracked bssid in reported result of found bssids.
+
+        Args:
+            bssid_settings:Setting used for tracking bssids.
+            found_results: Result reported in found event.
+
+        Returns:
+            True if bssid is present in result.
+        """
+        for bssid_setting in bssid_settings:
+            for found_result in found_results:
+                if found_result[WifiEnums.BSSID_KEY] == bssid_setting[WifiEnums.
+                                                                      BSSID_KEY]:
+                    return True
+        return False
+
+    def track_bssid_with_vaild_scan_for_found(self, track_setting):
+        """Common logic for tracking a bssid for Found event.
+
+         1. Starts Wifi Scanner bssid tracking for interested bssids in track_setting.
+         2. Start Wifi Scanner scan with default scan settings.
+         3. Validate the environment to check AP is not in range.
+         4. Attenuate the signal to make AP in range.
+         5. Verified that onFound event is triggered for interested bssids in
+            track setting.
+
+        Args:
+            track_setting: Setting for bssid tracking.
+
+        Returns:
+            True if found event occur for interested BSSID.
+        """
+        self.attenuators[self.attenuator_id].set_atten(90)
+        data = start_wifi_track_bssid(self.dut, track_setting)
+        idx = data["Index"]
+        valid_env = self.start_scan_and_validate_environment(
+                           self.default_scan_setting, track_setting["bssidInfos"])
+        try:
+            self.assert_true(valid_env,
+                             "Test environment is not valid, AP is in range")
+            self.attenuators[self.attenuator_id].set_atten(0)
+            event_name = "{}{}onFound".format(BSSID_EVENT_TAG, idx)
+            self.log.info("Waiting for the BSSID event {}".format(event_name))
+            event = self.ed.pop_event(event_name, BSSID_EVENT_WAIT)
+            self.log.debug(event)
+            found = self.check_bssid_in_found_result(track_setting["bssidInfos"],
+                                                      event["data"]["Results"])
+            self.assert_true(found,
+                             "Test fail because Bssid is not found in event results")
+        except Empty as error:
+            self.log.error("{}".format(error))
+            # log scan result for debugging
+            results = self.fetch_scan_result(self.scan_idx,
+                                             self.default_scan_setting)
+            self.log.debug("scan result {}".format(results))
+            raise AssertionError("Event {} did not triggered for {}\n{}".
+                                 format(event_name, track_setting["bssidInfos"],
+                                        error))
+        finally:
+            self.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+            self.droid.wifiScannerStopTrackingBssids(idx)
+
+    def track_bssid_with_vaild_scan_for_lost(self, track_setting):
+        """Common logic for tracking a bssid for Lost event.
+
+         1. Start Wifi Scanner scan with default scan settings.
+         2. Validate the environment to check AP is not in range.
+         3. Starts Wifi Scanner bssid tracking for interested bssids in track_setting.
+         4. Attenuate the signal to make Bssids in range.
+         5. Verified that onFound event is triggered for interested bssids in
+            track setting.
+         6. Attenuate the signal to make Bssids out of range.
+         7. Verified that onLost event is triggered.
+
+        Args:
+            track_setting: Setting for bssid tracking.
+            scan_setting: Setting used for starting the scan.
+
+        Returns:
+            True if Lost event occur for interested BSSID.
+        """
+        self.attenuators[self.attenuator_id].set_atten(90)
+        valid_env = self.start_scan_and_validate_environment(
+                          self.default_scan_setting, track_setting["bssidInfos"])
+        idx = None
+        found = False
+        try:
+            self.assert_true(valid_env,
+                             "Test environment is not valid, AP is in range")
+            data = start_wifi_track_bssid(self.dut, track_setting)
+            idx = data["Index"]
+            self.attenuators[self.attenuator_id].set_atten(0)
+            #onFound event should be occurre before tracking for onLost event
+            event_name = "{}{}onFound".format(BSSID_EVENT_TAG, idx)
+            self.log.info("Waiting for the BSSID event {}".format(event_name))
+            event = self.ed.pop_event(event_name, BSSID_EVENT_WAIT)
+            self.log.debug(event)
+            found = self.check_bssid_in_found_result(track_setting["bssidInfos"],
+                                                      event["data"]["Results"])
+            self.assert_true(found,
+                             "Test fail because Bssid is not found in event results")
+            if found:
+                self.attenuators[self.attenuator_id].set_atten(90)
+                # log scan result for debugging
+                for i in range(1, track_setting["apLostThreshold"]):
+                    results = self.fetch_scan_result(self.scan_idx,
+                                                     self.default_scan_setting)
+                    self.log.debug("scan result {} {}".format(i, results))
+                event_name = "{}{}onLost".format(BSSID_EVENT_TAG, idx)
+                self.log.info("Waiting for the BSSID event {}".format(event_name))
+                event = self.ed.pop_event(event_name, BSSID_EVENT_WAIT)
+                self.log.debug(event)
+        except Empty as error:
+            raise AssertionError("Event {} did not triggered for {}\n{}".
+                                 format(event_name, track_setting["bssidInfos"], error))
+        finally:
+            self.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+            if idx:
+                self.droid.wifiScannerStopTrackingBssids(idx)
+
+    def wifi_generate_track_bssid_settings(self, isLost):
+        """Generates all the combinations of different track setting parameters.
+
+        Returns:
+            A list of dictionaries each representing a set of track settings.
+        """
+        bssids = [[self.bssid_2g], [self.bssid_5g],
+                  [self.bssid_2g, self.bssid_5g]]
+        if self.dut.model != "hammerhead":
+            bssids.append([self.bssid_dfs])
+        if isLost:
+            apthreshold = (3,5)
+        else:
+            apthreshold = (1,)
+        # Create track setting strings based on the combinations
+        setting_combinations = list(itertools.product(bssids, apthreshold))
+        # Create scan setting strings based on the combinations
+        track_settings = []
+        for combo in setting_combinations:
+            s = {}
+            s["bssidInfos"] = combo[0]
+            s["apLostThreshold"] = combo[1]
+            track_settings.append(s)
+        return track_settings
+
+    def track_setting_to_string(self, track_setting):
+        """Convert track setting to string for Bssids in that"""
+        string = ""
+        for bssid_setting in track_setting:
+            string += bssid_setting[WifiEnums.BSSID_KEY]
+            string += "_"
+        return string
+
+    """ Helper Functions End """
+
+    """ Tests Begin """
+    def test_wifi_track_bssid_found(self):
+        """Test bssid track for event found with a list of different settings.
+
+         1. Starts Wifi Scanner bssid tracking for interested bssids in track_setting.
+         2. Start Wifi Scanner scan with default scan settings.
+         3. Validate the environment to check AP is not in range.
+         4. Attenuate the signal to make AP in range.
+         5. Verified that onFound event is triggered for interested bssids in
+            track setting.
+        """
+        track_settings = self.wifi_generate_track_bssid_settings(False)
+        name_func = (lambda track_setting :
+                     "test_wifi_track_found_bssidInfos_{}apLostThreshold_{}".
+                     format(self.track_setting_to_string(track_setting["bssidInfos"]),
+                            track_setting["apLostThreshold"]))
+        failed = self.run_generated_testcases( self.track_bssid_with_vaild_scan_for_found,
+                                               track_settings, name_func = name_func)
+        self.assert_true(not failed,
+                         "Track bssid found failed with these bssids: {}".
+                         format(failed))
+
+    def test_wifi_track_bssid_lost(self):
+        """Test bssid track for event lost with a list of different settings.
+
+         1. Start Wifi Scanner scan with default scan settings.
+         2. Validate the environment to check AP is not in range.
+         3. Starts Wifi Scanner bssid tracking for interested bssids in track_setting.
+         4. Attenuate the signal to make Bssids in range.
+         5. Verified that onFound event is triggered for interested bssids in
+            track setting.
+         6. Attenuate the signal to make Bssids out of range.
+         7. Verified that onLost event is triggered.
+        """
+        track_settings = self.wifi_generate_track_bssid_settings(True)
+        name_func = (lambda track_setting :
+                     "test_wifi_track_lost_bssidInfos_{}apLostThreshold_{}".
+                     format(self.track_setting_to_string(track_setting["bssidInfos"]),
+                            track_setting["apLostThreshold"]))
+        failed = self.run_generated_testcases( self.track_bssid_with_vaild_scan_for_lost,
+                                               track_settings, name_func = name_func)
+        self.assert_true(not failed,
+                         "Track bssid lost failed with these bssids: {}".format(failed))
+
+    def test_wifi_track_bssid_sanity(self):
+        """Test bssid track for event found and lost with default settings.
+
+         1. Start WifiScanner scan for default scan settings.
+         2. Start Bssid track for "bssid_2g" AP.
+         3. Attenuate the signal to move in AP range.
+         4. Verify that onFound event occur.
+         5. Attenuate the signal to move out of range
+         6. Verify that onLost event occur.
+        """
+        track_setting = {"bssidInfos":[self.bssid_2g], "apLostThreshold":3}
+        self.track_bssid_with_vaild_scan_for_lost(track_setting)
+
+    def test_wifi_track_bssid_for_2g_while_scanning_5g_channels(self):
+      """Test bssid track for 2g bssids while scanning 5g channels.
+
+         1. Starts Wifi Scanner bssid tracking for 2g bssids in track_setting.
+         2. Start Wifi Scanner scan for 5G Band only.
+         3. Validate the environment to check AP is not in range.
+         4. Attenuate the signal to make AP in range.
+         5. Verified that onFound event isn't triggered for 2g bssids.
+      """
+      self.attenuators[self.attenuator_id].set_atten(90)
+      scan_setting = { "band": WifiEnums.WIFI_BAND_5_GHZ,
+                       "periodInMs": SCANTIME,
+                       "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                       "numBssidsPerScan": 32}
+      track_setting = {"bssidInfos":[self.bssid_2g], "apLostThreshold":3}
+      valid_env = self.start_scan_and_validate_environment(scan_setting,
+                                                     track_setting["bssidInfos"])
+      idx = None
+      try:
+          self.assert_true(valid_env,
+                               "Test environment is not valid, AP is in range")
+          data = start_wifi_track_bssid(self.dut, track_setting)
+          idx = data["Index"]
+          self.attenuators[self.attenuator_id].set_atten(0)
+          event_name = "{}{}onFound".format(BSSID_EVENT_TAG, idx)
+          self.log.info("Waiting for the BSSID event {}".format(event_name))
+          #waiting for 2x time to make sure
+          event = self.ed.pop_event(event_name, BSSID_EVENT_WAIT * 2)
+          self.log.debug(event)
+          found = self.check_bssid_in_found_result(track_setting["bssidInfos"],
+                                                    event["data"]["Results"])
+          self.assert_true(not found,
+                             "Test fail because Bssid onFound event is triggered")
+      except Empty as error:
+          self.log.info("As excepted event didn't occurred with different scan setting")
+      finally:
+          self.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+          if idx:
+              self.droid.wifiScannerStopTrackingBssids(idx)
+
+    def test_wifi_track_bssid_for_5g_while_scanning_2g_channels(self):
+        """Test bssid track for 5g bssids while scanning 2g channels.
+
+           1. Starts Wifi Scanner bssid tracking for 5g bssids in track_setting.
+           2. Start Wifi Scanner scan for 2G Band only.
+           3. Validate the environment to check AP is not in range.
+           4. Attenuate the signal to make AP in range.
+           5. Verified that onFound event isn't triggered for 5g bssids.
+        """
+        self.attenuators[self.attenuator_id].set_atten(90)
+        scan_setting = { "band": WifiEnums.WIFI_BAND_24_GHZ,
+                         "periodInMs": SCANTIME,
+                         "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                         "numBssidsPerScan": 32}
+        track_setting = {"bssidInfos":[self.bssid_5g], "apLostThreshold":3}
+        data = start_wifi_track_bssid(self.dut, track_setting)
+        idx = data["Index"]
+        valid_env = self.start_scan_and_validate_environment(scan_setting,
+                                                       track_setting["bssidInfos"])
+        idx = None
+        try:
+            self.assert_true(valid_env,
+                               "Test environment is not valid, AP is in range")
+            self.attenuators[self.attenuator_id].set_atten(0)
+            event_name = "{}{}onFound".format(BSSID_EVENT_TAG, idx)
+            self.log.info("Waiting for the BSSID event {}".format(event_name))
+            #waiting for 2x time to make sure
+            event = self.ed.pop_event(event_name, BSSID_EVENT_WAIT * 2)
+            self.log.debug(event)
+            found = self.check_bssid_in_found_result(track_setting["bssidInfos"],
+                                                      event["data"]["Results"])
+            self.assert_true(not found,
+                             "Test fail because Bssid onFound event is triggered")
+        except Empty as error:
+            self.log.info("As excepted event didn't occurred with different scan setting")
+        finally:
+            self.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+            if idx:
+                self.droid.wifiScannerStopTrackingBssids(idx)
+
+    """ Tests End """
diff --git a/acts/tests/google/wifi/WifiScannerChangeTest.py b/acts/tests/google/wifi/WifiScannerChangeTest.py
new file mode 100755
index 0000000..e874db3
--- /dev/null
+++ b/acts/tests/google/wifi/WifiScannerChangeTest.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 os
+from queue import Empty
+
+from acts.controllers.ap.access_point import AP
+from acts.base_test import BaseTestClass
+from acts.utils import load_config
+from acts.test_utils.wifi_test_utils import start_wifi_tracking_change
+from acts.test_utils.wifi_test_utils import WifiEnums
+from acts.test_utils.wifi_test_utils import wifi_toggle_state
+
+SCANCHANNEL = [2412,2437,2457,2462,5180,5200,5220,5745]
+SCANTIME = 5000
+SHORT_TIMEOUT = 30
+EVENT_TAG = "WifiScannerChange"
+
+class WifiScannerScanError(Exception):
+  pass
+
+class WifiScannerChangeTest(BaseTestClass):
+  tests = None
+  current_path = os.path.dirname(os.path.abspath(__file__))
+
+  def __init__(self, controllers):
+    BaseTestClass.__init__(self, controllers)
+    # A list of all test cases to be executed in this class.
+    self.tests = (
+        "test_wifi_track_change_turn_off_two",
+        "test_wifi_start_track_change_with_wifi_off"
+        )
+
+  def setup_class(self):
+    if hasattr(self, "access_points"):
+      self.config = load_config(self.current_path
+                                + "/WifiScannerTests.config")
+      # Initialize APs with config file.
+      for item in self.config["AP"]:
+        self.log.info("Setting up AP " + str(item["index"]))
+        self.access_points[item["index"]].apply_configs(item)
+      return True
+
+  """ Helper Functions Begin """
+  def start_wifi_track_change_expect_failure(self):
+    try:
+      idx = self.droid.wifiScannerStartTrackingChange()
+      event = self.ed.pop_event(''.join((EVENT_TAG, str(idx), "onFailure")),
+                                SHORT_TIMEOUT)
+    except Empty:
+      events = self.ed.pop_events(EVENT_TAG, SHORT_TIMEOUT)
+      self.log.error("Did not get expected onFailure. Got\n" + str(events))
+      return False
+    self.log.debug("Got expected onFailure:\n" + str(event))
+    return True
+
+  def verify_one_changing_results(self, expected, actual):
+    for k,v in expected.items():
+      if k not in actual:
+        self.log.error(' '.join(("Missing", k, "in", actual)))
+        return False
+      if actual[k] != v:
+        self.log.error(' '.join(("Missmatch: expected", v, "got", actual[k])))
+        return False
+    return True
+
+  def verify_onChanging_results(self, expected, actuals):
+    # Create a result lookup table by bssid.
+    actual_results = {}
+    for a in actuals:
+      actual_results[a["bssid"]] = a
+    status = True
+    for exp in expected:
+      if exp["bssid"] not in actual_results:
+        self.log.error("Missing " + str(exp))
+        continue
+      a_result = actual_results[exp["bssid"]]
+      if not self.verify_one_changing_results(exp, a_result):
+        status = False
+    return status
+  """ Helper Functions End """
+
+  """ Tests Begin """
+  def test_wifi_track_change_turn_off_two(self):
+    # Get bssid infos.
+    ap = self.access_points[0]
+    bssids0 = ap.get_active_bssids_info("radio0", "frequency", "ssid")
+    bssids1 = ap.get_active_bssids_info("radio1", "frequency", "ssid")
+    bssids = bssids0 + bssids1
+    idx = start_wifi_tracking_change(self.droid, self.ed)
+    self.log.debug("Wait for onQuiescence.")
+    event = self.ed.pop_event(EVENT_TAG + str(idx) + "onQuiescence", 120)
+    self.log.debug("Tuning off " + str(bssids))
+    ap.toggle_radio_state("radio0", False)
+    ap.toggle_radio_state("radio1", False)
+    self.log.debug("Waiting for onChanging.")
+    event = self.ed.pop_event(''.join((EVENT_TAG, str(idx), "onChanging")), 60)
+    self.log.debug("Got:\n" + str(event))
+    self.droid.wifiScannerStopTrackingChange(idx)
+    return self.verify_onChanging_results(bssids, event["data"]["Results"])
+
+  def test_wifi_start_track_change_with_wifi_off(self):
+    self.log.debug("Make sure wifi is off.")
+    wifi_toggle_state(self.droid, self.ed, False)
+    status = self.start_wifi_track_change_expect_failure()
+    self.log.debug("Turning wifi back on.")
+    wifi_toggle_state(self.droid, self.ed, True)
+    return status
+  """ Tests End """
+
+if __name__ == "__main__":
+  tester = WifiScannerChangeTest()
+  tester.run()
+
diff --git a/acts/tests/google/wifi/WifiScannerScanTest.py b/acts/tests/google/wifi/WifiScannerScanTest.py
new file mode 100755
index 0000000..47d67f4
--- /dev/null
+++ b/acts/tests/google/wifi/WifiScannerScanTest.py
@@ -0,0 +1,1082 @@
+#!/usr/bin/python3.4
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+#   Copyright 2014 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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
+from queue import Empty
+import threading, time, traceback
+
+from acts.base_test import BaseTestClass
+from acts.utils import load_config
+from acts.test_utils.wifi_test_utils import get_scan_time_and_channels
+from acts.test_utils.wifi_test_utils import check_internet_connection
+from acts.test_utils.wifi_test_utils import start_wifi_background_scan
+from acts.test_utils.wifi_test_utils import start_wifi_single_scan
+from acts.test_utils.wifi_test_utils import track_connection
+from acts.test_utils.wifi_test_utils import wifi_test_device_init
+from acts.test_utils.wifi_test_utils import WifiEnums
+from acts.test_utils.wifi_test_utils import WifiChannelUS
+from acts.test_utils.wifi_test_utils import wifi_forget_network
+from acts.test_utils.wifi_test_utils import wifi_toggle_state
+
+SCANTIME = 10000 #framework support only 10s as minimum scan interval
+NUMBSSIDPERSCAN = 8
+EVENT_TAG = "WifiScannerScan"
+SCAN_TIME_PASSIVE = 47 # dwell time plus 2ms
+SCAN_TIME_ACTIVE = 32 # dwell time plus 2ms
+SHORT_TIMEOUT = 30
+NETWORK_ID_ERROR = "Network don't have ID"
+NETWORK_ERROR = "Device is not connected to reference network"
+INVALID_RESULT = "Test fail because scan result reported are not valid"
+EMPTY_RESULT = "Test fail because empty scan result reported"
+KEY_RET = "ResultElapsedRealtime"
+
+class WifiScannerScanError(Exception):
+    pass
+
+class WifiScannerScanTest(BaseTestClass):
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.failed_scan_settings = None
+        # A list of all test cases to be executed in this class.
+        self.tests = (
+            "test_available_channels_generated",
+            "test_wifi_scanner_single_scan_channel_sanity",
+            "test_wifi_scanner_with_wifi_off",
+            "test_single_scan_report_each_scan_for_channels_with_enumerated_params",
+            "test_single_scan_report_each_scan_for_band_with_enumerated_params",
+            "test_wifi_scanner_batch_scan_channel_sanity",
+            "test_wifi_scanner_batch_scan_period_too_short",
+            "test_batch_scan_report_buffer_full_for_channels_with_enumerated_params",
+            "test_batch_scan_report_buffer_full_for_band_with_enumerated_params",
+            "test_batch_scan_report_each_scan_for_channels_with_enumerated_params",
+            "test_batch_scan_report_each_scan_for_band_with_enumerated_params",
+            "test_single_scan_report_full_scan_for_channels_with_enumerated_params",
+            "test_single_scan_report_full_scan_for_band_with_enumerated_params",
+            "test_batch_scan_report_full_scan_for_channels_with_enumerated_params",
+            "test_batch_scan_report_full_scan_for_band_with_enumerated_params",
+            "test_wifi_connection_while_single_scan",
+            "test_single_scan_while_pno",
+            "test_wifi_connection_and_pno_while_batch_scan",
+            "test_wifi_scanner_single_scan_in_isolated",
+            "test_wifi_scanner_with_invalid_numBssidsPerScan"
+            )
+        self.leeway = 10
+        self.stime_channel = SCAN_TIME_PASSIVE
+        self.default_scan_setting = {
+                        "band": WifiEnums.WIFI_BAND_BOTH,
+                        "periodInMs": SCANTIME,
+                        "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN
+                        }
+        self.default_batch_scan_setting = {
+                        "band": WifiEnums.WIFI_BAND_BOTH,
+                        "periodInMs": SCANTIME,
+                        "reportEvents": WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL
+                        }
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wifi_test_device_init(self.dut)
+        req_params = ("connect_network", "run_extended_test", "ping_addr",
+                      "max_bugreports")
+        userparam_status = self.unpack_userparams(req_params)
+        self.assert_true(userparam_status, "Required user parameter")
+        self.log.debug("Run extended test: {}".format(self.run_extended_test))
+        self.wifi_chs = WifiChannelUS(self.dut.model)
+        self.assert_true(self.dut.droid.wifiIsScannerSupported(),
+            "Device %s doesn't support WifiScanner, abort." % self.dut.model)
+        self.attenuators[0].set_atten(0)
+        self.attenuators[1].set_atten(0)
+        return True
+
+    def teardown_test(self):
+        BaseTestClass.teardown_test(self)
+        self.log.debug("Shut down all wifi scanner activities.")
+        self.droid.wifiScannerShutdown()
+
+    def on_fail(self, test_name, begin_time):
+        if self.max_bugreports > 0:
+            self.take_bug_reports(test_name, begin_time, self.android_devices)
+            self.max_bugreports -= 1
+
+    """ Helper Functions Begin """
+
+    def wifi_generate_scanner_scan_settings(self, extended, scan_type, report_result):
+        """Generates all the combinations of different scan setting parameters.
+
+        Args:
+          extended: True for extended setting
+          scan_type: key for type of scan
+          report_result: event type of report scan results
+
+        Returns:
+          A list of dictionaries each representing a set of scan settings.
+        """
+        base_scan_time = [SCANTIME*2]
+        if scan_type == "band":
+            scan_types_setting = [WifiEnums.WIFI_BAND_BOTH]
+        else:
+            scan_types_setting = [self.wifi_chs.MIX_CHANNEL_SCAN]
+        num_of_bssid = [NUMBSSIDPERSCAN*4]
+        max_scan_cache = [0]
+        if extended:
+            base_scan_time.append(SCANTIME)
+            if scan_type == "band":
+                scan_types_setting.extend([WifiEnums.WIFI_BAND_24_GHZ,
+                                           WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS,
+                                           WifiEnums.WIFI_BAND_BOTH_WITH_DFS])
+            else:
+                scan_types_setting.extend([self.wifi_chs.NONE_DFS_5G_FREQUENCIES,
+                                           self.wifi_chs.ALL_2G_FREQUENCIES,
+                                           self.wifi_chs.DFS_5G_FREQUENCIES,
+                                           self.wifi_chs.ALL_5G_FREQUENCIES])
+            num_of_bssid.append(NUMBSSIDPERSCAN*3)
+            max_scan_cache.append(5)
+            # Generate all the combinations of report types and scan types
+        if report_result == WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT:
+            report_types = {"reportEvents" : report_result}
+            setting_combinations = list(itertools.product(scan_types_setting,
+                                                          base_scan_time))
+            # Create scan setting strings based on the combinations
+            scan_settings = []
+            for combo in setting_combinations:
+                s = dict(report_types)
+                s[scan_type] = combo[0]
+                s["periodInMs"] = combo[1]
+                scan_settings.append(s)
+        else:
+            report_types = {"reportEvents" : report_result}
+            setting_combinations = list(itertools.product(scan_types_setting,
+                                                          base_scan_time, num_of_bssid,
+                                                          max_scan_cache))
+            # Create scan setting strings based on the combinations
+            scan_settings = []
+            for combo in setting_combinations:
+                s = dict(report_types)
+                s[scan_type] = combo[0]
+                s["periodInMs"] = combo[1]
+                s["numBssidsPerScan"] = combo[2]
+                s["maxScansToCache"] = combo[3]
+                scan_settings.append(s)
+        return scan_settings
+
+    def proces_and_valid_batch_scan_result(self, scan_resutls, scan_rt,
+                                           result_rt, scan_setting):
+        """This function process scan results and validate against settings used
+        while starting the scan.
+
+        There are two steps for the verification. First it checks that all the
+        wifi networks in results are of the correct frequencies set by scan setting
+        params. Then it checks that the delta between the batch of scan results less
+        than the time required for scanning channel set by scan setting params.
+
+        Args:
+            scan_results: scan results reported.
+            scan_rt: Elapsed real time on start scan.
+            result_rt: Elapsed ral time on results reported.
+            scan_setting: The params for the single scan.
+
+        Returns:
+            bssids: total number of bssids scan result have
+            validity: True if the all scan result are valid.
+        """
+        bssids = 0
+        validity = True
+        scan_time_mic = 0
+        scan_channels = []
+        scan_time, scan_channels = get_scan_time_and_channels(self.wifi_chs,
+                                                              scan_setting,
+                                                              self.stime_channel)
+        scan_time_mic = scan_time * 1000
+        for i, batch in enumerate(scan_resutls, start=1):
+            max_scan_interval =  batch["ScanResults"][0]["timestamp"] + scan_time_mic
+            self.log.debug("max_scan_interval: {}".format(max_scan_interval) )
+            for result in batch["ScanResults"]:
+              if (result["frequency"] not in scan_channels
+                      or result["timestamp"] > max_scan_interval
+                      or result["timestamp"] < scan_rt*1000
+                      or result["timestamp"] > result_rt*1000) :
+                  self.log.error("Result didn't match requirement: {}".
+                                 format(result) )
+                  validity = False
+            self.log.info("Number of scan result in batch {} :: {}".format(i,
+                                                     len(batch["ScanResults"])))
+            bssids += len(batch["ScanResults"])
+        return bssids, validity
+
+    def pop_scan_result_events(self, event_name):
+        """Function to pop all the scan result events.
+
+        Args:
+            event_name: event name.
+
+        Returns:
+            results: list  of scan result reported in events
+        """
+        results = []
+        try:
+            events = self.ed.pop_all(event_name)
+            for event in events:
+                results.append(event["data"]["Results"])
+        except Empty as error:
+            self.log.debug("Number of Full scan results {}".format(len(results)))
+        return results
+
+    def wifi_scanner_single_scan(self, scan_setting):
+        """Common logic for an enumerated wifi scanner single scan test case.
+
+         1. Start WifiScanner single scan for scan_setting.
+         2. Wait for the scan result event, wait time depend on scan settings
+            parameter.
+         3. Verify that scan results match with scan settings parameters.
+         4. Also verify that only one scan result event trigger.
+
+        Args:
+            scan_setting: The params for the single scan.
+        """
+        data = start_wifi_single_scan(self.dut, scan_setting)
+        idx = data["Index"]
+        scan_rt = data["ScanElapsedRealtime"]
+        self.log.info("Wifi single shot scan started index: {} at real time: {}".
+                      format(idx, scan_rt))
+        results = []
+        #generating event wait time from scan setting plus leeway
+        scan_time, scan_channels = get_scan_time_and_channels(self.wifi_chs,
+                                                              scan_setting,
+                                                              self.stime_channel)
+        wait_time = int(scan_time/1000) + self.leeway
+        validity = False
+        #track number of result received
+        result_received = 0
+        try:
+            for snumber in range(1,3):
+                event_name = "{}{}onResults".format(EVENT_TAG, idx)
+                self.log.debug("Waiting for event: {} for time {}".
+                               format(event_name, wait_time))
+                event = self.ed.pop_event(event_name, wait_time)
+                self.log.debug("Event received: {}".format(event ))
+                results = event["data"]["Results"]
+                result_received += 1
+                bssids, validity = self.proces_and_valid_batch_scan_result(
+                                                          results, scan_rt,
+                                                          event["data"][KEY_RET],
+                                                          scan_setting)
+                self.assert_true(len(results) == 1,
+                                 "Test fail because number of scan result {}"
+                                 .format(len(results)))
+                self.assert_true(bssids > 0, EMPTY_RESULT)
+                self.assert_true(validity, INVALID_RESULT)
+                self.log.info("Scan number Buckets: {}\nTotal BSSID: {}".
+                              format(len(results), bssids))
+        except Empty as error:
+            self.assert_true(result_received >= 1,
+                             "Event did not triggered for single shot {}".
+                             format(error))
+        finally:
+            self.droid.wifiScannerStopScan(idx)
+            #For single shot number of result received and length of result should be one
+            self.assert_true(result_received == 1,
+                             "Test fail because received result {}".
+                             format(result_received))
+
+    def wifi_scanner_single_scan_full(self, scan_setting):
+        """Common logic for single scan test case for full scan result.
+
+        1. Start WifiScanner single scan with scan_setting for full scan result.
+        2. Wait for the scan result event, wait time depend on scan settings
+           parameter.
+        3. Pop all full scan result events occurred earlier.
+        4. Verify that full scan results match with normal scan results.
+
+        Args:
+            scan_setting: The parameters for the single scan.
+        """
+        self.ed.clear_all_events()
+        data = start_wifi_single_scan(self.dut, scan_setting)
+        idx = data["Index"]
+        scan_rt = data["ScanElapsedRealtime"]
+        self.log.info("Wifi single shot scan started with index: {}".format(idx))
+        #generating event wait time from scan setting plus leeway
+        scan_time, scan_channels = get_scan_time_and_channels(self.wifi_chs,
+                                                              scan_setting,
+                                                              self.stime_channel)
+        wait_time = int(scan_time/1000) + self.leeway
+        results = []
+        validity = False
+        try:
+            event_name = "{}{}onResults".format(EVENT_TAG, idx)
+            self.log.debug("Waiting for event: {} for time {}".
+                           format(event_name, wait_time))
+            event = self.ed.pop_event(event_name, wait_time)
+            self.log.info("Event received: {}".format(event))
+            bssids, validity = (self.proces_and_valid_batch_scan_result(
+                                                event["data"]["Results"], scan_rt,
+                                                event["data"][KEY_RET],
+                                                scan_setting))
+            self.assert_true(bssids > 0, EMPTY_RESULT)
+            self.assert_true(validity, INVALID_RESULT)
+            event_name = "{}{}onFullResult".format(EVENT_TAG, idx)
+            results = self.pop_scan_result_events(event_name)
+            self.assert_true(len(results) >= bssids,
+                             "Full single shot result don't match {}".
+                             format(len(results)))
+        except Empty as error:
+            raise AssertionError("Event did not triggered for single shot {}".
+                                 format(error))
+        finally:
+            self.droid.wifiScannerStopScan(idx)
+
+    def wifi_scanner_batch_scan_full(self, scan_setting):
+        """Common logic for batch scan test case for full scan result.
+
+        1. Start WifiScanner batch scan with scan_setting for full scan result.
+        2. Wait for the scan result event, wait time depend on scan settings
+           parameter.
+        3. Pop all full scan result events occurred earlier.
+        4. Verify that full scan results match with scan results.
+
+        Args:
+            scan_setting: The params for the batch scan.
+        """
+        self.ed.clear_all_events()
+        data = start_wifi_background_scan(self.dut, scan_setting)
+        idx = data["Index"]
+        scan_rt = data["ScanElapsedRealtime"]
+        self.log.info("Wifi batch shot scan started with index: {}".format(idx))
+        #generating event wait time from scan setting plus leeway
+        scan_time, scan_channels = get_scan_time_and_channels(self.wifi_chs,
+                                                              scan_setting,
+                                                              self.stime_channel)
+        scan_time += scan_setting['periodInMs'] #add scan period delay for next cycle
+        wait_time = int(scan_time/1000) + self.leeway
+        validity = False
+        try:
+            for snumber in range(1,3):
+                results = []
+                event_name = "{}{}onResults".format(EVENT_TAG, idx)
+                self.log.debug("Waiting for event: {} for time {}".
+                               format(event_name, wait_time))
+                event = self.ed.pop_event(event_name, wait_time)
+                self.log.debug("Event received: {}".format(event))
+                bssids, validity = self.proces_and_valid_batch_scan_result(
+                                                      event["data"]["Results"],
+                                                      scan_rt,
+                                                      event["data"][KEY_RET],
+                                                      scan_setting)
+                event_name = "{}{}onFullResult".format(EVENT_TAG, idx)
+                results = self.pop_scan_result_events(event_name)
+                self.assert_true(len(results) >= bssids,
+                                 "Full single shot result don't match {}".
+                                 format(len(results)))
+                self.assert_true(bssids > 0, EMPTY_RESULT)
+                self.assert_true(validity, INVALID_RESULT)
+        except Empty as error:
+             raise AssertionError("Event did not triggered for batch scan {}".
+                                  format(error))
+        finally:
+            self.droid.wifiScannerStopBackgroundScan(idx)
+            self.ed.clear_all_events()
+
+    def wifi_scanner_batch_scan(self, scan_setting):
+        """Common logic for an enumerated wifi scanner batch scan test case.
+
+        1. Start WifiScanner batch scan for given scan_setting.
+        2. Wait for the scan result event, wait time depend on scan settings
+           parameter.
+        3. Verify that scan results match with scan settings parameters.
+        4. Also verify that multiple scan result events trigger.
+
+        Args:
+            scan_setting: The parameters for the batch scan.
+        """
+        data = start_wifi_background_scan(self.dut, scan_setting)
+        idx = data["Index"]
+        scan_rt = data["ScanElapsedRealtime"]
+        self.log.info("Wifi background scan started with index: {} real time {}"
+                      .format(idx, scan_rt))
+        scan_time, scan_channels = get_scan_time_and_channels(self.wifi_chs,
+                                                              scan_setting,
+                                                              self.stime_channel)
+        #generating event wait time from scan setting plus leeway
+        time_cache = 0
+        number_bucket = 1 #bucket for Report result on each scan
+        check_get_result = False
+        if scan_setting['reportEvents'] == WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL:
+            check_get_result = True
+            if ('maxScansToCache' in scan_setting and
+                                    scan_setting['maxScansToCache'] != 0) :
+                time_cache = (scan_setting['maxScansToCache'] *
+                              scan_setting['periodInMs'])
+                number_bucket = scan_setting['maxScansToCache']
+            else:
+                time_cache = 10 * scan_setting['periodInMs'] #10 as default max scan cache
+                number_bucket = 10
+        else:
+            time_cache = scan_setting['periodInMs'] #need while waiting for seconds resutls
+        wait_time = int((time_cache + scan_time)/1000) + self.leeway
+        validity = False
+        try:
+            for snumber in range(1,3):
+                event_name = "{}{}onResults".format(EVENT_TAG, idx)
+                self.log.info("Waiting for event: {} for time {}".
+                              format(event_name,wait_time))
+                event = self.ed.pop_event(event_name, wait_time)
+                self.log.debug("Event received: {}".format(event ))
+                results = event["data"]["Results"]
+                bssids, validity = (self.proces_and_valid_batch_scan_result(
+                                                          results, scan_rt,
+                                                          event["data"][KEY_RET],
+                                                          scan_setting))
+                self.log.info("Scan number: {}\n Buckets: {}\n  BSSID: {}".
+                              format(snumber, len(results), bssids))
+                self.assert_true(len(results) == number_bucket,
+                                     "Test fail because number_bucket {}".
+                                     format(len(results)))
+                self.assert_true(bssids >= 1, EMPTY_RESULT)
+                self.assert_true(validity, INVALID_RESULT)
+                if snumber%2 == 1 and check_get_result :
+                    self.log.info("Get Scan result using GetScanResult API")
+                    time.sleep(wait_time/number_bucket)
+                    if self.droid.wifiScannerGetScanResults():
+                        event = self.ed.pop_event(event_name, 1)
+                        self.log.debug("Event onResults: {}".format(event))
+                        results = event["data"]["Results"]
+                        bssids, validity = (self.proces_and_valid_batch_scan_result(
+                                                          results, scan_rt,
+                                                          event["data"][KEY_RET],
+                                                          scan_setting))
+                        self.log.info("Got Scan result number: {} BSSID: {}".
+                                      format(snumber, bssids))
+                        self.assert_true(bssids >= 1, EMPTY_RESULT)
+                        self.assert_true(validity, INVALID_RESULT)
+                    else:
+                        self.log.error("Error while fetching the scan result")
+        except Empty as error:
+            raise AssertionError("Event did not triggered for batch scan {}".
+                                 format(error))
+        finally:
+            self.droid.wifiScannerStopBackgroundScan(idx)
+            self.ed.clear_all_events()
+
+    def start_wifi_scanner_single_scan_expect_failure(self, scan_setting):
+        """Common logic to test wif scanner single scan with invalid settings
+           or environment
+
+         1. Start WifiScanner batch scan for setting parameters.
+         2. Verify that scan is not started.
+
+         Args:
+            scan_setting: The params for the single scan.
+        """
+        try:
+            idx = self.droid.wifiScannerStartScan(scan_setting)
+            event = self.ed.pop_event("{}{}onFailure".format(EVENT_TAG, idx),
+                                      SHORT_TIMEOUT)
+        except Empty as error:
+            raise AssertionError("Did not get expected onFailure {}".format(error))
+
+    def start_wifi_scanner_background_scan_expect_failure(self, scan_setting):
+        """Common logic to test wif scanner batch scan with invalid settings
+           or environment
+
+         1. Start WifiScanner batch scan for setting parameters.
+         2. Verify that scan is not started.
+
+         Args:
+          scan_setting: The params for the single scan.
+        """
+        try:
+          idx = self.droid.wifiScannerStartBackgroundScan(scan_setting)
+          event = self.ed.pop_event("{}{}onFailure".format(EVENT_TAG, idx),
+                                    SHORT_TIMEOUT)
+        except Empty as error:
+          raise AssertionError("Did not get expected onFailure {}".format(error))
+
+    def check_get_available_channels_with_one_band(self, band):
+        """Common logic to check available channels for a band.
+
+         1. Get available channels for band.
+         2. Verify that channels match with supported channels for band.
+
+         Args:
+            band: wifi band."""
+
+        r = self.droid.wifiScannerGetAvailableChannels(band)
+        self.log.debug(band)
+        self.log.debug(r)
+        expected = self.wifi_chs.band_to_freq(band)
+        self.assert_true(set(r) == set(expected),
+                         "Band {} failed. Expected {}, got {}".
+                         format(band, expected, r))
+
+    def connect_to_reference_network(self):
+        """Connect to reference network and make sure that connection happen"""
+        self.droid.wakeLockAcquireBright()
+        self.droid.wakeUpNow()
+        try:
+            self.droid.wifiPriorityConnect(self.connect_network)
+            connect_result = self.ed.pop_event("WifiManagerPriorityConnectOnSuccess",
+                                               SHORT_TIMEOUT)
+            self.log.info(connect_result)
+            return track_connection(self.dut, self.connect_network["ssid"], 1)
+        except Exception as error:
+            self.log.exception(traceback.format_exc())
+            self.log.error("Connection to network fail because {}".format(error))
+            return False
+        finally:
+            self.droid.wifiLockRelease()
+            self.droid.goToSleepNow()
+
+    """ Helper Functions End """
+
+    """ Tests Begin """
+    def test_available_channels_generated(self):
+        """Test available channels for different bands.
+
+         1. Get available channels for different bands.
+         2. Verify that channels match with supported channels for respective band.
+        """
+        bands = (1,2,3,4,6,7)
+        name_func = lambda band : "test_get_channel_band_{}".format(band)
+        failed = self.run_generated_testcases(
+                                self.check_get_available_channels_with_one_band,
+                                bands, name_func = name_func)
+        self.assert_true(not failed,
+                         "Number of test_get_channel_band failed {}".
+                         format(len(failed)))
+
+    def test_single_scan_report_each_scan_for_channels_with_enumerated_params(self):
+        """Test WiFi scanner single scan for channels with enumerated settings.
+
+         1. Start WifiScanner single scan for different channels with enumerated
+            scan settings.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                          self.run_extended_test, "channels",
+                                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+        self.log.debug("Scan settings:{}\n{}".format(len(scan_settings),
+                                                     scan_settings))
+        name_func = (lambda scan_setting :
+                     ("test_single_scan_report_each_scan_for_channels_{}"
+                      "_numBssidsPerScan_{}_maxScansToCache_{}_period_{}").
+                      format(scan_setting["channels"],
+                             scan_setting["numBssidsPerScan"],
+                             scan_setting["maxScansToCache"],
+                             scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_single_scan,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_single_scan_report_each_scan_for_channels"
+                          " failed {}").format(len(failed)))
+
+    def test_single_scan_report_each_scan_for_band_with_enumerated_params(self):
+        """Test WiFi scanner single scan for bands with enumerated settings.
+
+         1. Start WifiScanner single scan for different bands with enumerated
+            scan settings.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                          self.run_extended_test,"band",
+                                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+        self.log.debug("Scan settings:{}\n{}".format(len(scan_settings),
+                                                     scan_settings))
+        name_func = (lambda scan_setting :
+                     ("test_single_scan_report_each_scan_for_band_{}"
+                      "_numBssidsPerScan_{}_maxScansToCache_{}_period_{}").
+                     format(scan_setting["band"],
+                            scan_setting["numBssidsPerScan"],
+                            scan_setting["maxScansToCache"],
+                            scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_single_scan,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_single_scan_report_each_scan_for_band"
+                          " failed {}").format(len(failed)))
+
+    def test_batch_scan_report_buffer_full_for_channels_with_enumerated_params(self):
+        """Test WiFi scanner batch scan using channels with enumerated settings
+           to report buffer full scan results.
+
+         1. Start WifiScanner batch scan using different channels with enumerated
+            scan settings to report buffer full scan results.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                      self.run_extended_test, "channels",
+                                      WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL)
+        self.log.debug("Scan settings:{}\n{}".format(len(scan_settings),
+                                                     scan_settings))
+        name_func = (lambda scan_setting :
+                     ("test_batch_scan_report_buffer_full_for_channels_{}"
+                      "_numBssidsPerScan_{}_maxScansToCache_{}_periodInMs_{}").
+                     format(scan_setting["channels"],
+                            scan_setting["numBssidsPerScan"],
+                            scan_setting["maxScansToCache"],
+                            scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_batch_scan,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_batch_scan_report_buffer_full_for_channels"
+                          " failed {}").format(len(failed)))
+
+    def test_batch_scan_report_buffer_full_for_band_with_enumerated_params(self):
+        """Test WiFi scanner batch scan using band with enumerated settings
+           to report buffer full scan results.
+
+         1. Start WifiScanner batch scan using different bands with enumerated
+            scan settings to report buffer full scan results.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                       self.run_extended_test,"band",
+                                       WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL)
+        self.log.debug("Scan settings:{}\n{}".format(len(scan_settings),
+                                                     scan_settings))
+        name_func = (lambda scan_setting :
+                     ("test_batch_scan_report_buffer_full_for_band_{}"
+                      "_numBssidsPerScan_{}_maxScansToCache_{}_periodInMs_{}").
+                     format(scan_setting["band"],
+                            scan_setting["numBssidsPerScan"],
+                            scan_setting["maxScansToCache"],
+                            scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_batch_scan,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_batch_scan_report_buffer_full_for_band"
+                          " failed {}").format(len(failed)))
+
+    def test_batch_scan_report_each_scan_for_channels_with_enumerated_params(self):
+        """Test WiFi scanner batch scan using channels with enumerated settings
+           to report each scan results.
+
+         1. Start WifiScanner batch scan using different channels with enumerated
+            scan settings to report each scan results.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                        self.run_extended_test, "channels",
+                                        WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+        self.log.debug("Scan settings:{}\n{}".
+                       format(len(scan_settings),scan_settings))
+        name_func = (lambda scan_setting :
+                     ("test_batch_scan_report_each_scan_for_channels_{}"
+                      "_numBssidsPerScan_{}_maxScansToCache_{}_periodInMs_{}").
+                     format(scan_setting["channels"],
+                            scan_setting["numBssidsPerScan"],
+                            scan_setting["maxScansToCache"],
+                            scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_batch_scan,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_batch_scan_report_each_scan_for_channels"
+                          " failed {}").format(len(failed)))
+
+    def test_batch_scan_report_each_scan_for_band_with_enumerated_params(self):
+        """Test WiFi scanner batch scan using band with enumerated settings
+           to report each scan results.
+
+         1. Start WifiScanner batch scan using different bands with enumerated
+            scan settings to report each scan results.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                        self.run_extended_test, "band",
+                                        WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+        self.log.debug("Scan settings:{}\n{}".format(len(scan_settings),
+                                                     scan_settings))
+        name_func = (lambda scan_setting :
+                     ("test_batch_scan_report_each_scan_for_band_{}"
+                      "_numBssidsPerScan_{}_maxScansToCache_{}_periodInMs_{}").
+                     format(scan_setting["band"],
+                            scan_setting["numBssidsPerScan"],
+                            scan_setting["maxScansToCache"],
+                            scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_batch_scan,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_batch_scan_report_each_scan_for_band"
+                          " failed {}").format(len(failed)))
+
+    def test_single_scan_report_full_scan_for_channels_with_enumerated_params(self):
+        """Test WiFi scanner single scan using channels with enumerated settings
+           to report full scan results.
+
+         1. Start WifiScanner single scan using different channels with enumerated
+            scan settings to report full scan results.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                        self.run_extended_test, "channels",
+                                        WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT)
+        self.log.debug("Full Scan settings:{}\n{}".format(len(scan_settings),
+                                                          scan_settings))
+        name_func = (lambda scan_setting :
+                     "test_single_scan_report_full_scan_for_channels_{}_periodInMs_{}".
+                     format(scan_setting["channels"],scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_single_scan_full,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_single_scan_report_full_scan_for_channels"
+                          " failed {}").format(len(failed)))
+
+    def test_single_scan_report_full_scan_for_band_with_enumerated_params(self):
+        """Test WiFi scanner single scan using band with enumerated settings
+           to report full scan results.
+
+         1. Start WifiScanner single scan using different bands with enumerated
+            scan settings to report full scan results.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                        self.run_extended_test, "band",
+                                        WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT)
+        self.log.debug("Full Scan settings:{}\n{}".format(len(scan_settings),
+                                                          scan_settings))
+        name_func = (lambda scan_setting :
+                     "test_single_scan_report_full_scan_for_band_{}_periodInMs_{}".
+                     format(scan_setting["band"],scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_single_scan_full,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_single_scan_report_full_scan_for_band"
+                          " failed {}").format(len(failed)))
+
+    def test_batch_scan_report_full_scan_for_channels_with_enumerated_params(self):
+        """Test WiFi scanner batch scan using channels with enumerated settings
+           to report full scan results.
+
+         1. Start WifiScanner batch scan using different channels with enumerated
+            scan settings to report full scan results.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                        self.run_extended_test, "channels",
+                                        WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT)
+        self.log.debug("Full Scan settings:{}\n{}".format(len(scan_settings),
+                                                          scan_settings))
+        name_func = (lambda scan_setting :
+                     ("test_batch_scan_report_full_scan_for_channels"
+                      "_{}_periodInMs_{}").format(scan_setting["channels"],
+                                                  scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_batch_scan_full,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_batch_scan_report_full_scan_for_channels"
+                          " failed {}").format(len(failed)))
+
+    def test_batch_scan_report_full_scan_for_band_with_enumerated_params(self):
+        """Test WiFi scanner batch scan using channels with enumerated settings
+           to report full scan results.
+
+         1. Start WifiScanner batch scan using different channels with enumerated
+            scan settings to report full scan results.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_settings = self.wifi_generate_scanner_scan_settings(
+                                        self.run_extended_test, "band",
+                                        WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT)
+        self.log.debug("Full Scan settings:{}\n{}".format(len(scan_settings),
+                                                          scan_settings))
+        name_func = (lambda scan_setting :
+                     ("test_batch_scan_report_full_scan_for_band"
+                      "_{}_periodInMs_{}").format(scan_setting["band"],
+                                                  scan_setting["periodInMs"]))
+        failed = self.run_generated_testcases(self.wifi_scanner_batch_scan_full,
+                                              scan_settings,
+                                              name_func = name_func)
+        self.assert_true(not failed,
+                         ("Number of test_batch_scan_report_full_scan_for_band"
+                          " failed {}").format(len(failed)))
+
+    def test_wifi_connection_while_single_scan(self):
+        """Test configuring a connection parallel to wifi scanner single scan.
+
+         1. Start WifiScanner single scan for both band with default scan settings.
+         2. Configure a connection to reference network.
+         3. Verify that connection to reference network occurred.
+         2. Verify that scanner report single scan results.
+        """
+        self.attenuators[self.connect_network["attenuator"]].set_atten(0)
+        data = start_wifi_single_scan(self.dut, self.default_scan_setting)
+        idx = data["Index"]
+        scan_rt = data["ScanElapsedRealtime"]
+        self.log.info("Wifi single shot scan started with index: {}".format(idx))
+        self.assert_true(self.connect_to_reference_network(), NETWORK_ERROR)
+        time.sleep(10) #wait for connection to be active
+        self.assert_true(check_internet_connection(self.dut, self.ping_addr),
+                         "Error, No internet connection for current network")
+        #generating event wait time from scan setting plus leeway
+        scan_time, scan_channels = get_scan_time_and_channels(self.wifi_chs,
+                                                      self.default_scan_setting,
+                                                      self.stime_channel)
+        wait_time = int(scan_time/1000) + self.leeway
+        validity = False
+        try:
+            event_name = "{}{}onResults".format(EVENT_TAG, idx)
+            self.log.debug("Waiting for event: {} for time {}".
+                           format(event_name, wait_time))
+            event = self.ed.pop_event(event_name, wait_time)
+            self.log.debug("Event received: {}".format(event ))
+            results = event["data"]["Results"]
+            bssids, validity = self.proces_and_valid_batch_scan_result(
+                                                      results, scan_rt,
+                                                      event["data"][KEY_RET],
+                                                      self.default_scan_setting)
+            self.log.info("Scan number Buckets: {}\nTotal BSSID: {}".
+                                                    format(len(results), bssids))
+            self.assert_true(len(results) == 1 and bssids >= 1, EMPTY_RESULT)
+        except Empty as error:
+            raise AssertionError("Event did not triggered for single scan {}".
+                                 format(error))
+
+    def test_single_scan_while_pno(self):
+        """Test wifi scanner single scan parallel to PNO connection.
+
+         1. Check device have a saved network.
+         2. Trigger PNO by attenuate the signal to move out of range.
+         3. Start WifiScanner single scan for both band with default scan settings.
+         4. Verify that scanner report single scan results.
+         5. Attenuate the signal to move in range.
+         6. Verify connection occurred through PNO.
+        """
+        self.log.info("Check connection through PNO for reference network")
+        current_network = self.droid.wifiGetConnectionInfo()
+        self.log.info("Current network: {}".format(current_network))
+        self.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
+        self.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR)
+        self.log.info("Kicking PNO for reference network")
+        self.attenuators[self.connect_network["attenuator"]].set_atten(90)
+        time.sleep(10) #wait for PNO to be kicked
+        self.log.info("Starting single scan while PNO")
+        self.wifi_scanner_single_scan(self.default_scan_setting)
+        self.attenuators[self.connect_network["attenuator"]].set_atten(0)
+        self.log.info("Check connection through PNO for reference network")
+        time.sleep(30) #wait for connection through PNO
+        current_network = self.droid.wifiGetConnectionInfo()
+        self.log.info("Current network: {}".format(current_network))
+        self.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
+        self.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR)
+        time.sleep(10) #wait for IP to be assigned
+        self.assert_true(check_internet_connection(self.dut, self.ping_addr),
+                         "Error, No internet connection for current network")
+        wifi_forget_network(self.dut, self.connect_network["ssid"])
+
+    def test_wifi_connection_and_pno_while_batch_scan(self):
+        """Test configuring a connection and PNO connection parallel to wifi
+           scanner batch scan.
+
+         1. Start WifiScanner batch scan with default batch scan settings.
+         2. Wait for scan result event for a time depend on scan settings.
+         3. Verify reported batch scan results.
+         4. Configure a connection to reference network.
+         5. Verify that connection to reference network occurred.
+         6. Wait for scan result event for a time depend on scan settings.
+         7. Verify reported batch scan results.
+         8. Trigger PNO by attenuate the signal to move out of range.
+         9. Wait for scan result event for a time depend on scan settings.
+         10. Verify reported batch scan results.
+         11. Attenuate the signal to move in range.
+         12. Verify connection occurred through PNO.
+        """
+        self.attenuators[self.connect_network["attenuator"]].set_atten(0)
+        data = start_wifi_background_scan(self.dut, self.default_batch_scan_setting)
+        idx = data["Index"]
+        scan_rt = data["ScanElapsedRealtime"]
+        self.log.info("Wifi background scan started with index: {} rt {}".
+                      format(idx, scan_rt))
+        #generating event wait time from scan setting plus leeway
+        scan_time, scan_channels = get_scan_time_and_channels(
+                                                self.wifi_chs,
+                                                self.default_batch_scan_setting,
+                                                self.stime_channel)
+        #default number buckets
+        number_bucket = 10
+        time_cache = self.default_batch_scan_setting['periodInMs'] * number_bucket #default cache
+        #add 2 seconds extra time for switch between the channel for connection scan
+        wait_time = int((time_cache + scan_time )/1000) + self.leeway + 2
+        result_flag = 0
+        try:
+          for snumber in range(1,7):
+            event_name = "{}{}onResults".format(EVENT_TAG, idx)
+            self.log.info("Waiting for event: {}".format(event_name ))
+            event = self.ed.pop_event(event_name, wait_time)
+            self.log.debug("Event onResults: {}".format(event ))
+            results = event["data"]["Results"]
+            bssids, validity = self.proces_and_valid_batch_scan_result(
+                                               results, scan_rt,
+                                               event["data"][KEY_RET],
+                                               self.default_batch_scan_setting)
+            self.log.info("Scan number: {}\n Buckets: {}\n BSSID: {}".
+                                         format(snumber, len(results), bssids))
+            self.assert_true(bssids >= 1, "Not able to fetch scan result")
+            if snumber == 1:
+                self.log.info("Try to connect AP while waiting for event: {}".
+                              format(event_name ))
+                self.assert_true(self.connect_to_reference_network(), NETWORK_ERROR)
+                time.sleep(10) #wait for connection to be active
+                self.assert_true(check_internet_connection(self.dut, self.ping_addr),
+                                 "Error, No internet connection for current network")
+            elif snumber == 3:
+                self.log.info("Kicking PNO for reference network")
+                self.attenuators[self.connect_network["attenuator"]].set_atten(90)
+            elif snumber == 4:
+                self.log.info("Bring back device for PNO connection")
+                current_network = self.droid.wifiGetConnectionInfo()
+                self.log.info("Current network: {}".format(current_network))
+                self.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
+                self.assert_true(current_network['network_id'] == -1,
+                                 "Device is still connected to network  {}".
+                                 format(current_network[WifiEnums.SSID_KEY]))
+                self.attenuators[self.connect_network["attenuator"]].set_atten(0)
+                time.sleep(10) #wait for connection to take place before waiting for scan result
+            elif snumber == 6:
+                self.log.info("Check connection through PNO for reference network")
+                current_network = self.droid.wifiGetConnectionInfo()
+                self.log.info("Current network: {}".format(current_network))
+                self.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
+                self.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR)
+                time.sleep(10) #wait for connection to be active
+                self.assert_true(check_internet_connection(self.dut, self.ping_addr),
+                                 "Error, No internet connection for current network")
+                wifi_forget_network(self.dut, self.connect_network["ssid"])
+        except Empty as error:
+            raise AssertionError("Event did not triggered for batch scan {}".
+                                 format(error))
+        finally:
+            self.droid.wifiScannerStopBackgroundScan(idx)
+            self.ed.clear_all_events()
+
+    def test_wifi_scanner_single_scan_channel_sanity(self):
+        """Test WiFi scanner single scan for mix channel with default setting
+           parameters.
+
+         1. Start WifiScanner single scan for mix channels with default setting
+            parameters.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_setting = { "channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+                         "periodInMs": SCANTIME,
+                         "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN }
+        self.wifi_scanner_single_scan(scan_setting)
+
+    def test_wifi_scanner_batch_scan_channel_sanity(self):
+        """Test WiFi scanner batch scan for mix channel with default setting
+           parameters to report the result on buffer full.
+
+         1. Start WifiScanner batch scan for mix channels with default setting
+            parameters.
+         2. Verify that scan results match with respective scan settings.
+        """
+        scan_setting = { "channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+                         "periodInMs": SCANTIME,
+                         "reportEvents": WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL}
+        self.wifi_scanner_batch_scan(scan_setting)
+
+    def test_wifi_scanner_batch_scan_period_too_short(self):
+        """Test WiFi scanner batch scan for band with too short period time.
+
+         1. Start WifiScanner batch scan for both band with interval period as 5s.
+         2. Verify that scan is not started."""
+        scan_setting = { "band": WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+                         "periodInMs": 5000,
+                         "reportEvents": WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL}
+        self.start_wifi_scanner_background_scan_expect_failure(scan_setting);
+
+    def test_wifi_scanner_single_scan_in_isolated(self):
+        """Test WiFi scanner in isolated environment with default scan settings.
+
+         1. Created isolated environment by attenuating the single by 90db
+         2. Start WifiScanner single scan for mix channels with default setting
+            parameters.
+         3. Verify that empty scan results reported.
+        """
+        self.attenuators[0].set_atten(90)
+        self.attenuators[1].set_atten(90)
+        data = start_wifi_single_scan(self.dut, self.default_scan_setting)
+        idx = data["Index"]
+        scan_rt = data["ScanElapsedRealtime"]
+        self.log.info("Wifi single shot scan started with index: {}".format(idx))
+        results = []
+        #generating event wait time from scan setting plus leeway
+        scan_time, scan_channels = get_scan_time_and_channels(self.wifi_chs,
+                                                      self.default_scan_setting,
+                                                      self.stime_channel)
+        wait_time = int(scan_time/1000) + self.leeway
+        try:
+            event_name = "{}{}onResults".format(EVENT_TAG, idx)
+            self.log.debug("Waiting for event: {} for time {}".format(event_name,
+                                                                      wait_time))
+            event = self.ed.pop_event(event_name, wait_time)
+            self.log.debug("Event received: {}".format(event))
+            results = event["data"]["Results"]
+            bssids, validity = (self.proces_and_valid_batch_scan_result(
+                                                      results, scan_rt,
+                                                      event["data"][KEY_RET],
+                                                      self.default_scan_setting))
+            self.log.info("Scan number Buckets: {}\nTotal BSSID: {}".
+                          format(len(results), bssids))
+            self.assert_true(bssids == 0, ("Test fail because report scan "
+                                              "results reported are not empty"))
+        except Empty as error:
+            raise AssertionError("Event did not triggered for in isolated environment {}".
+                   format(error))
+        finally:
+            self.ed.clear_all_events()
+            self.attenuators[0].set_atten(0)
+            self.attenuators[1].set_atten(0)
+
+    def test_wifi_scanner_with_wifi_off(self):
+        """Test WiFi scanner single scan when wifi is off.
+
+         1. Toggle wifi state to off.
+         2. Start WifiScanner single scan for both band with default scan settings.
+         3. Verify that scan is not started.
+        """
+        self.log.debug("Make sure wifi is off.")
+        wifi_toggle_state(self.droid, self.ed, False)
+        self.start_wifi_scanner_single_scan_expect_failure(self.default_scan_setting)
+        self.log.debug("Turning wifi back on.")
+        wifi_toggle_state(self.droid, self.ed, True)
+
+    def test_wifi_scanner_with_invalid_numBssidsPerScan(self):
+        """Test WiFi scanner single scan with invalid number of bssids reported
+           per scan.
+
+         1. Start WifiScanner single scan with invalid number of bssids reported
+            per scan.
+         2. Verify that scan results triggered for default supported number of
+            bssids per scan.
+        """
+        scan_setting = {
+            "band": WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+            "periodInMs": SCANTIME,
+            "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+            'numBssidsPerScan': 33
+        }
+        self.wifi_scanner_single_scan(scan_setting)
+    """ Tests End """
diff --git a/acts/tests/google/wifi/WifiScannerTests.config b/acts/tests/google/wifi/WifiScannerTests.config
new file mode 100755
index 0000000..85d599c
--- /dev/null
+++ b/acts/tests/google/wifi/WifiScannerTests.config
@@ -0,0 +1,28 @@
+{
+  "_description": "Default wireless network setup for APs used in the test.",
+  "AP": [{"index": 0,
+          "radio0": {"settings": {"channel": 1}, "wifi-iface" : [{"ssid": "Test_1", "key": "hahahaha", "encryption": "psk2"},{"ssid": "Test_1.1", "key": "hahahaha", "encryption": "psk2"},
+                                                                 {"ssid": "Test_1.2", "key": "hahahaha", "encryption": "psk2"},{"ssid": "Test_1.3", "key": "hahahaha", "encryption": "psk2"}]},
+          "radio1": {"settings": {"channel": 40}, "wifi-iface" : [{"ssid": "Test_40", "key": "hahahaha", "encryption": "psk2"},{"ssid": "Test_40.1", "key": "hahahaha", "encryption": "psk2"},
+                                                                  {"ssid": "Test_40.2", "key": "hahahaha", "encryption": "psk2"},{"ssid": "Test_40.3", "key": "hahahaha", "encryption": "psk2"}]}
+         },
+         {"index": 1,
+            "radio0": {"settings": {"channel": 6}, "wifi-iface" : [{"ssid": "Test_6", "key": "hahahaha", "encryption": "psk"}, {"ssid": "Test_6.1", "key": "hahahaha", "encryption": "psk2"},
+                                                                   {"ssid": "Test_6.2", "key": "hahahaha", "encryption": "psk2"},  {"ssid": "Test_6.3", "key": "hahahaha", "encryption": "psk2"}]},
+            "radio1": {"settings": {"channel": 40}, "wifi-iface" : [{"ssid": "Test_40", "key": "hahahaha", "encryption": "psk"}, {"ssid": "Test_40.1", "key": "hahahaha", "encryption": "psk2"},
+                                                                    {"ssid": "Test_40.3", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_40.2", "key": "hahahaha", "encryption": "psk2"}]}
+         },
+         {"index": 2,
+            "radio0": {"settings": {"channel": 10}, "wifi-iface" : [{"ssid": "Test_10", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_10.1", "key": "hahahaha", "encryption": "psk2"},
+                                                                    {"ssid": "Test_10.2", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_10.3", "key": "hahahaha", "encryption": "psk2"}]},
+            "radio1": {"settings": {"channel": 44}, "wifi-iface" : [{"ssid": "Test_44", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_44.1", "key": "hahahaha", "encryption": "psk2"},
+                                                                    {"ssid": "Test_44.2", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_44.3", "key": "hahahaha", "encryption": "psk2"}]}
+         },
+         {"index": 3,
+            "radio0": {"settings": {"channel": 11}, "wifi-iface" : [{"ssid": "Test_11", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_11.1", "key": "hahahaha", "encryption": "psk2"},
+                                                                    {"ssid": "Test_11.2", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_11.3", "key": "hahahaha", "encryption": "psk2"}]},
+            "radio1": {"settings": {"channel": 149}, "wifi-iface" : [{"ssid": "Test_149", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_149.1", "key": "hahahaha", "encryption": "psk2"},
+                                                                    {"ssid": "Test_149.2", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_149.3", "key": "hahahaha", "encryption": "psk2"}]}
+         }
+        ]
+}
\ No newline at end of file