Added decoractors for extra test info
am: 7ad0951d1b  -s ours

Change-Id: I2071785001238b4a339e8644c8a4243e7da5556b
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..838f4b9
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,20 @@
+[Hook Scripts]
+acts_base_class_test = ./acts/framework/tests/acts_base_class_test.py
+acts_android_device_test = ./acts/framework/tests/acts_android_device_test.py
+acts_asserts_test = ./acts/framework/tests/acts_asserts_test.py
+acts_base_class_test = ./acts/framework/tests/acts_base_class_test.py
+acts_logger_test = ./acts/framework/tests/acts_logger_test.py
+acts_records_test = ./acts/framework/tests/acts_records_test.py
+acts_sl4a_client_test = ./acts/framework/tests/acts_sl4a_client_test.py
+acts_job_test = ./acts/framework/tests/acts_job_test.py
+acts_test_runner_test = ./acts/framework/tests/acts_test_runner_test.py
+acts_unittest_suite = ./acts/framework/tests/acts_unittest_suite.py
+acts_utils_test = ./acts/framework/tests/acts_utils_test.py
+acts_host_utils_test = ./acts/framework/tests/acts_host_utils_test.py
+acts_import_test_utils_test = ./acts/framework/tests/acts_import_test_utils_test.py
+acts_import_unit_test = ./acts/framework/tests/acts_import_unit_test.py
+yapf_hook = ./tools/yapf_checker.py
+
+[Builtin Hooks]
+commit_msg_bug_field = true
+jsonlint = true
diff --git a/acts/.gitignore b/acts/.gitignore
index f345b57..c436601 100644
--- a/acts/.gitignore
+++ b/acts/.gitignore
@@ -1,3 +1,4 @@
+.DS_Store
 # Byte-compiled / optimized / DLL files
 __pycache__/
 *.py[cod]
diff --git a/acts/README.md b/acts/README.md
index dca9f6d..bf0ec48 100644
--- a/acts/README.md
+++ b/acts/README.md
@@ -1,8 +1,20 @@
 # Android Comms Test Suite
-ACTS is a python-based test framework that is designed to be lightweight,
-pluggable, and easy to use. It initializes equipment and services associated
-to a test run, provides those resources to test classes, executes test cases,
-and generates test reports.
+The Android Comms Test Suite, is a lightweight Python-based automation tool set
+that is used to perform automated testing of current and upcoming Android
+devices. It provides a simple execution interface; a set of pluggable libraries
+for accessing commercially avilable devices, Android devices, and a collection
+of utility functions to further ease test development. It is an ideal desktop
+tool for a wireless stack developer or integrator whether exercising a new code
+path, performing sanity testing, or running extended regression test suites.
+
+Included in the tests/google directory are a bundle of tests, many of which can
+be run with as little as one or two Android devices with wifi, cellular, or
+bluetooth connectivity, including:
+1. Wifi tests for access point interopability, enterprise server integration,
+WiFi scanning, WiFi auto join, and round trip time.
+2. Bluetooth tests for low energy, GATT, SPP, and bonding.
+3. Cellular tests for circuit switch and IMS calling, data connectivity,
+messaging, network switching, and WiFi hotspot.
 
 ACTS follows the Google Open-source
 [Python Style Guide](https://google.github.io/styleguide/pyguide.html), and
@@ -10,9 +22,11 @@
 
 ## ACTS Execution Flow Overview
 Below is a high level view of the ACTS flow:
+
 1. Read configuration files
 2. Create controllers
 3. Sequentially execute test classes
+
 ```
 FooTest.setup_class()
 FooTest.setup_test()
@@ -26,6 +40,7 @@
 BarTest.setup_class()
 ....
 ```
+
 4. Destroy controllers
 
 ## Preparing an Android Device
@@ -42,28 +57,28 @@
 displayed. Check the "Always" box and click "Yes".
 
 ## ACTS Setup
-1. ACTS requires three python dependencies:
-- Python3.4
-- The setuptools package
-- The pyserial package
-2. From the ACTS directory, run setup
-- `$ sudo python3 setup.py develop`
+From the ACTS directory, run setup
 
-After installation, `act.py` and `flashutil.py` will be in usr/bin and can be
-called as command line utilities. Components in ACTS are importable under the
-package "acts." in Python3.4, for example:
+- `$ sudo python setup.py develop`
+
+After installation, `act.py` will be in usr/bin and can be called as command
+line utilities. Components in ACTS are importable under the package "acts."
+in Python, for example:
+
 ```
-$ python3
+$ python
 >>> from acts.controllers import android_device
 >>> device_list = android_device.get_all_instances()
 ```
 
 ## Verifying Setup
 To verify the host and device are ready, from the frameworks folder run:
+
 - `$ act.py -c sample_config.json -tb SampleTestBed -tc SampleTest`
 
 If the above command executed successfully, the ouput should look something
 similar to following:
+
 ```
 [SampleTestBed] 07-22 15:23:50.323 INFO ==========> SampleTest <==========
 [SampleTestBed] 07-22 15:23:50.327 INFO [Test Case] test_make_toast
@@ -74,6 +89,7 @@
 SampleTestBed@07-22-2015_1-23-44-096: Requested 1, Executed 1, Passed 1,
 Failed 0, Skipped 0
 ```
+
 By default, all logs are saved in `/tmp/logs`
 
 ## Breaking Down the Example
@@ -90,6 +106,7 @@
 Each specifies the hardware, services, the path to the logs directory, and
 a list of paths where the python test case files are located. Below are the
 contents of a sample configuration file:
+
 ```
 {   "_description": "This is an example skeleton test configuration file.",
     "testbed":
@@ -118,6 +135,7 @@
 Test classes must also contain an iterable member self.tests that lists the
 test case names within the class.  More on this is discussed after the following
 code snippet.
+
 ```
 from acts.base_test import BaseTestClass
 
@@ -140,9 +158,11 @@
 specific tests within a test class.
 
 The following will run a single test, test_make_toast:
+
 `$ act.py -c sample_config.txt -tb SampleTestBed -tc SampleTest:test_make_toast`
 
 Multiple tests may be specified with a comma-delimited list. The following
 will execute test_make_toast and test_make_bagel:
+
 - `$ act.py -c sample_config.txt -tb SampleTestBed -tc
 SampleTest:test_make_toast,test_make_bagel`
diff --git a/acts/etc/autotest_campaigns/bt_and_ble_sanity b/acts/etc/autotest_campaigns/bt_and_ble_sanity
new file mode 100644
index 0000000..cb0bdcd
--- /dev/null
+++ b/acts/etc/autotest_campaigns/bt_and_ble_sanity
@@ -0,0 +1,23 @@
+BtPreFlightTest
+FilteringTest
+UniqueFilteringTest
+ConcurrentBleAdvertisingTest
+ConcurrentBleScanningTest
+GattConnectTest
+GattReadTest
+GattWriteTest
+GattNotifyTest
+BleStressTest
+BleScanApiTest
+BleAdvertiseApiTest
+GattApiTest
+BtBasicFunctionalityTest
+BleOpportunisticScanTest
+BleOnLostOnFoundTest
+GattOverBrEdrTest
+BtStressTest
+BtKillProcessTest
+BtAirplaneModeTest
+BtFactoryResetTest
+RfcommTest
+RfcommStressTest
diff --git a/acts/etc/autotest_campaigns/bt_auto_test b/acts/etc/autotest_campaigns/bt_auto_test
new file mode 100644
index 0000000..60bdd3e
--- /dev/null
+++ b/acts/etc/autotest_campaigns/bt_auto_test
@@ -0,0 +1,14 @@
+BtPreFlightTest
+BtCarBasicFunctionalityTest
+BtCarHfpConferenceTest
+BtCarHfpConnectionTest
+BtCarHfpFuzzTest
+BtCarHfpTest
+BtCarMapMceTest
+BtCarMediaConnectionTest
+BtCarMediaPassthroughTest
+BtCarPairedConnectDisconnectTest
+BtCarPairingTest
+BtCarPbapTest
+BtCarToggleTest
+
diff --git a/acts/etc/autotest_campaigns/bt_funhaus_test b/acts/etc/autotest_campaigns/bt_funhaus_test
new file mode 100644
index 0000000..46f134d
--- /dev/null
+++ b/acts/etc/autotest_campaigns/bt_funhaus_test
@@ -0,0 +1,2 @@
+BtPreFlightTest
+BtFunhausTest
diff --git a/acts/etc/autotest_campaigns/bt_gatt_longevity b/acts/etc/autotest_campaigns/bt_gatt_longevity
new file mode 100644
index 0000000..38e3c03
--- /dev/null
+++ b/acts/etc/autotest_campaigns/bt_gatt_longevity
@@ -0,0 +1,2 @@
+BtPreFlightTest
+GattLongevityTest
diff --git a/acts/etc/autotest_campaigns/bt_power_test b/acts/etc/autotest_campaigns/bt_power_test
new file mode 100644
index 0000000..2c0109e
--- /dev/null
+++ b/acts/etc/autotest_campaigns/bt_power_test
@@ -0,0 +1 @@
+BleScanPowerTest
diff --git a/acts/etc/autotest_campaigns/bt_rfcomm_longevity b/acts/etc/autotest_campaigns/bt_rfcomm_longevity
new file mode 100644
index 0000000..0547b04
--- /dev/null
+++ b/acts/etc/autotest_campaigns/bt_rfcomm_longevity
@@ -0,0 +1,2 @@
+BtPreFlightTest
+RfcommLongevityTest
diff --git a/acts/etc/autotest_campaigns/telephony_att_basic b/acts/etc/autotest_campaigns/telephony_att_basic
new file mode 100644
index 0000000..f439e50
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_att_basic
@@ -0,0 +1,61 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceTest:
+test_call_csfb_3g_to_csfb_3g,
+test_call_3g_to_3g,
+test_call_wcdma_mo_hold_unhold,
+test_call_wcdma_mt_hold_unhold,
+test_call_csfb_mo_hold_unhold,
+test_call_csfb_mt_hold_unhold
+
+TelLiveDataTest:
+test_airplane_mode,
+test_4g,
+test_tethering_4g_to_5gwifi,
+test_tethering_4g_to_2gwifi_2clients,
+test_toggle_data_during_active_wifi_tethering,
+test_disable_wifi_tethering_resume_connected_wifi,
+test_3g,
+test_wcdma_multi_bearer,
+test_tethering_3g_to_5gwifi,
+test_tethering_3g_to_2gwifi,
+test_lte_wifi_switching,
+test_tethering_entitlement_check
+
+TelLiveSmsTest:
+test_sms_mo_4g,
+test_sms_mt_4g,
+test_sms_mo_in_call_csfb,
+test_sms_mt_in_call_csfb,
+test_sms_mo_3g,
+test_sms_mt_3g,
+test_sms_mo_in_call_wcdma,
+test_sms_mt_in_call_wcdma
+
+TelLiveVoiceConfTest:
+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_mo_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_mt_mt_add_merge_unmerge_swap_drop,
+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
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_spt_basic b/acts/etc/autotest_campaigns/telephony_spt_basic
new file mode 100644
index 0000000..f223efe
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_spt_basic
@@ -0,0 +1,55 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceTest:
+test_call_csfb_3g_to_csfb_3g,
+test_call_3g_to_3g
+
+TelLiveDataTest:
+test_airplane_mode,
+test_4g,
+test_tethering_4g_to_5gwifi,
+test_tethering_4g_to_2gwifi_2clients,
+test_toggle_data_during_active_wifi_tethering,
+test_disable_wifi_tethering_resume_connected_wifi,
+test_3g,
+test_tethering_3g_to_2gwifi,
+test_tethering_3g_to_5gwifi,
+test_lte_wifi_switching,
+test_tethering_entitlement_check
+
+TelLiveSmsTest:
+test_sms_mo_4g,
+test_sms_mt_4g,
+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_1x,
+test_sms_mt_in_call_1x
+
+TelLiveVoiceConfTest:
+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,
+test_1x_mo_mo_add_merge_drop_from_participant,
+test_1x_mo_mo_add_merge_drop_from_host,
+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
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_tmo_basic b/acts/etc/autotest_campaigns/telephony_tmo_basic
new file mode 100644
index 0000000..0484813
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_tmo_basic
@@ -0,0 +1,95 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceTest:
+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_mo_hold_unhold,
+test_call_volte_mt_hold_unhold,
+test_call_volte_to_csfb_for_tmo,
+test_call_volte_to_3g,
+test_voicemail_indicator_volte,
+test_voicemail_indicator_lte,
+test_call_csfb_3g_to_csfb_3g,
+test_call_csfb_mo_hold_unhold,
+test_call_csfb_mt_hold_unhold,
+test_call_3g_to_3g,
+test_voicemail_indicator_3g,
+test_call_wcdma_mo_hold_unhold,
+test_call_wcdma_mt_hold_unhold
+
+TelLiveDataTest:
+test_airplane_mode,
+test_4g,
+test_lte_multi_bearer,
+test_tethering_4g_to_5gwifi,
+test_tethering_4g_to_2gwifi_2clients,
+test_toggle_data_during_active_wifi_tethering,
+test_disable_wifi_tethering_resume_connected_wifi,
+test_3g,
+test_wcdma_multi_bearer,
+test_tethering_3g_to_5gwifi,
+test_tethering_3g_to_2gwifi,
+test_lte_wifi_switching,
+test_tethering_entitlement_check
+
+TelLiveSmsTest:
+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_3g,
+test_sms_mt_3g,
+test_sms_mo_in_call_wcdma,
+test_sms_mt_in_call_wcdma
+
+TelLiveVoiceTest:
+test_call_epdg_to_epdg_wfc_cellular_preferred,
+test_call_epdg_to_epdg_apm_wfc_cellular_preferred,
+test_call_epdg_to_epdg_wfc_wifi_preferred,
+test_call_epdg_to_epdg_long_wfc_wifi_preferred,
+test_call_epdg_to_epdg_apm_wfc_wifi_preferred,
+test_call_epdg_to_epdg_long_apm_wfc_wifi_preferred,
+test_call_epdg_to_volte_wfc_wifi_preferred,
+test_call_epdg_to_volte_apm_wfc_wifi_preferred,
+test_call_epdg_to_csfb_3g_wfc_wifi_preferred,
+test_call_epdg_to_csfb_3g_apm_wfc_wifi_preferred,
+test_call_epdg_to_3g_wfc_wifi_preferred,
+test_call_epdg_to_3g_apm_wfc_wifi_preferred,
+test_call_epdg_mo_hold_unhold_wfc_wifi_preferred,
+test_call_epdg_mo_hold_unhold_apm_wfc_wifi_preferred,
+test_call_epdg_mt_hold_unhold_wfc_wifi_preferred,
+test_call_epdg_mt_hold_unhold_apm_wfc_wifi_preferred,
+test_voicemail_indicator_iwlan,
+test_voicemail_indicator_apm_iwlan
+
+TelLiveSettingsTest:
+test_lte_volte_wifi_connected_toggle_wfc,
+test_lte_wifi_connected_toggle_wfc,
+test_3g_wifi_connected_toggle_wfc,
+test_apm_wifi_connected_toggle_wfc,
+test_lte_volte_wfc_enabled_toggle_wifi,
+test_lte_wfc_enabled_toggle_wifi,
+test_3g_wfc_enabled_toggle_wifi,
+test_apm_wfc_enabled_toggle_wifi,
+test_lte_wfc_enabled_wifi_connected_toggle_volte,
+test_lte_volte_wfc_wifi_preferred_to_cellular_preferred,
+test_apm_wfc_wifi_preferred_to_cellular_preferred,
+test_lte_volte_wfc_cellular_preferred_to_wifi_preferred,
+test_apm_wfc_cellular_preferred_to_wifi_preferred,
+test_apm_wfc_wifi_preferred_turn_off_apm,
+test_apm_wfc_cellular_preferred_turn_off_apm
+
+TelLiveSmsTest:
+test_sms_mo_iwlan,
+test_sms_mt_iwlan,
+test_sms_mo_in_call_iwlan,
+test_sms_mt_in_call_iwlan
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_tmo_conf_cep b/acts/etc/autotest_campaigns/telephony_tmo_conf_cep
new file mode 100644
index 0000000..63fb773
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_tmo_conf_cep
@@ -0,0 +1,92 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceConfTest:
+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_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_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_first_call_from_host_wfc_apm_wifi_preferred_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_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_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_call_volte_add_mt_video_accept_as_voice_merge_drop,
+test_call_video_add_mo_voice_swap_downgrade_merge_drop,
+test_call_volte_add_mo_video_downgrade_merge_drop,
+test_call_volte_add_mt_video_downgrade_merge_drop
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_tmo_conf_nocep b/acts/etc/autotest_campaigns/telephony_tmo_conf_nocep
new file mode 100644
index 0000000..74552b7
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_tmo_conf_nocep
@@ -0,0 +1,42 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceConfTest:
+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_mo_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_mt_mt_add_merge_unmerge_swap_drop,
+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,
+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_wcdma_merge_drop_second_call_from_participant_no_cep,
+test_volte_mo_mo_add_volte_merge_drop_second_call_from_participant_no_cep,
+test_volte_mo_mo_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_mt_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_no_cep
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_tmo_data b/acts/etc/autotest_campaigns/telephony_tmo_data
new file mode 100644
index 0000000..757a503
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_tmo_data
@@ -0,0 +1,24 @@
+TelLiveDataTest:
+test_airplane_mode,
+test_4g,
+test_lte_wifi_switching,
+test_tethering_entitlement_check,
+test_tethering_4g_to_2gwifi,
+test_toggle_data_during_active_wifi_tethering,
+test_disable_wifi_tethering_resume_connected_wifi
+
+TelLiveSmsTest:
+test_sms_mo_4g,
+test_sms_mt_4g
+
+TelLiveDataTest:
+test_3g,
+test_tethering_3g_to_5gwifi
+
+TelLiveSmsTest:
+test_sms_mo_3g,
+test_sms_mt_3g
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_tmo_nexus b/acts/etc/autotest_campaigns/telephony_tmo_nexus
new file mode 100644
index 0000000..dd1e769
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_tmo_nexus
@@ -0,0 +1,63 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceTest:
+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_mo_hold_unhold,
+test_call_volte_mt_hold_unhold,
+test_call_volte_to_csfb_for_tmo,
+test_call_volte_to_3g,
+test_call_csfb_3g_to_csfb_3g,
+test_call_csfb_mo_hold_unhold,
+test_call_csfb_mt_hold_unhold,
+test_call_3g_to_3g,
+test_call_wcdma_mo_hold_unhold,
+test_call_wcdma_mt_hold_unhold,
+test_call_epdg_to_epdg_wfc_cellular_preferred,
+test_call_epdg_to_epdg_apm_wfc_cellular_preferred,
+test_call_epdg_to_epdg_wfc_wifi_preferred,
+test_call_epdg_to_epdg_apm_wfc_wifi_preferred,
+test_call_epdg_mo_hold_unhold_apm_wfc_wifi_preferred,
+test_call_epdg_to_volte_apm_wfc_wifi_preferred,
+test_call_epdg_to_3g_wfc_wifi_preferred
+
+TelLiveDataTest:
+test_airplane_mode,
+test_4g,
+test_lte_multi_bearer,
+test_tethering_4g_to_2gwifi,
+test_toggle_data_during_active_wifi_tethering,
+test_disable_wifi_tethering_resume_connected_wifi,
+test_3g,
+test_wcdma_multi_bearer,
+test_tethering_3g_to_5gwifi,
+test_lte_wifi_switching,
+test_tethering_entitlement_check
+
+TelLiveSmsTest:
+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_3g,
+test_sms_mt_3g,
+test_sms_mo_in_call_wcdma,
+test_sms_mt_in_call_wcdma,
+test_sms_mo_iwlan,
+test_sms_mt_iwlan,
+test_sms_mo_in_call_iwlan,
+test_sms_mt_in_call_iwlan
+
+TelLiveSettingsTest:
+test_lte_volte_wifi_connected_toggle_wfc,
+test_apm_wifi_connected_toggle_wfc,
+test_apm_wfc_enabled_toggle_wifi
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_tmo_vt b/acts/etc/autotest_campaigns/telephony_tmo_vt
new file mode 100644
index 0000000..a8653d0
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_tmo_vt
@@ -0,0 +1,34 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVideoTest:
+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_video_accept_as_voice_mo_upgrade_bidirectional,
+test_call_volte_to_volte_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,
+test_call_volte_add_mo_video_accept_as_voice_merge_drop,
+test_call_video_add_mo_voice,
+test_call_volte_add_mo_video,
+test_call_volte_add_mt_video,
+test_call_video_add_mo_video,
+test_call_mt_video_add_mo_video,
+test_disable_data_vt_unavailable
+
+TelLiveVideoDataTest:
+test_internet_access_during_video_call
+
+TelLiveSmsTest:
+test_sms_mo_in_call_vt,
+test_sms_mt_in_call_vt
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_vzw_basic b/acts/etc/autotest_campaigns/telephony_vzw_basic
new file mode 100644
index 0000000..d3e2fca
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_vzw_basic
@@ -0,0 +1,43 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceTest:
+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_mo_hold_unhold,
+test_call_volte_mt_hold_unhold,
+test_call_volte_to_csfb_3g,
+test_call_volte_to_3g,
+test_call_csfb_3g_to_csfb_3g,
+test_call_3g_to_3g
+
+TelLiveDataTest:
+test_airplane_mode,
+test_4g,
+test_lte_multi_bearer,
+test_tethering_4g_to_5gwifi,
+test_tethering_4g_to_2gwifi_2clients,
+test_toggle_data_during_active_wifi_tethering,
+test_disable_wifi_tethering_resume_connected_wifi,
+test_3g,
+test_tethering_3g_to_5gwifi,
+test_tethering_3g_to_2gwifi,
+test_lte_wifi_switching,
+test_tethering_entitlement_check
+
+TelLiveSmsTest:
+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_1x,
+test_sms_mt_in_call_csfb_1x,
+test_sms_mo_3g,
+test_sms_mt_3g,
+test_sms_mo_in_call_1x,
+test_sms_mt_in_call_1x
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_vzw_conf b/acts/etc/autotest_campaigns/telephony_vzw_conf
new file mode 100644
index 0000000..a400f1e
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_vzw_conf
@@ -0,0 +1,39 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveDataTest:
+test_tethering_4g_to_2gwifi_2clients
+
+TelLiveVoiceConfTest:
+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,
+test_1x_mo_mo_add_merge_drop_from_participant,
+test_1x_mo_mo_add_merge_drop_from_host,
+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_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
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_vzw_conf_cep b/acts/etc/autotest_campaigns/telephony_vzw_conf_cep
new file mode 100644
index 0000000..188b4d7
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_vzw_conf_cep
@@ -0,0 +1,44 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceConfTest:
+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_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
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_vzw_conf_nocep b/acts/etc/autotest_campaigns/telephony_vzw_conf_nocep
new file mode 100644
index 0000000..254790c
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_vzw_conf_nocep
@@ -0,0 +1,12 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceConfTest:
+test_volte_mo_mo_add_volte_merge_drop_second_call_from_participant_no_cep,
+test_volte_mo_mo_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_mt_mt_add_volte_swap_twice_merge_drop_second_call_from_participant_no_cep
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_vzw_data b/acts/etc/autotest_campaigns/telephony_vzw_data
new file mode 100644
index 0000000..ea1cbd0
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_vzw_data
@@ -0,0 +1,17 @@
+TelLiveDataTest:
+test_airplane_mode,
+test_4g,
+test_lte_wifi_switching,
+test_tethering_entitlement_check,
+test_tethering_4g_to_2gwifi,
+test_tethering_4g_to_5gwifi,
+test_toggle_data_during_active_wifi_tethering,
+test_disable_wifi_tethering_resume_connected_wifi
+
+TelLiveSmsTest:
+test_sms_mo_4g,
+test_sms_mt_4g
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_vzw_nexus b/acts/etc/autotest_campaigns/telephony_vzw_nexus
new file mode 100644
index 0000000..d90788d
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_vzw_nexus
@@ -0,0 +1,49 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVoiceTest:
+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_mo_hold_unhold,
+test_call_volte_mt_hold_unhold,
+test_call_volte_to_csfb_for_tmo,
+test_call_volte_to_3g,
+test_call_csfb_3g_to_csfb_3g,
+test_call_3g_to_3g,
+test_call_epdg_to_epdg_apm_wfc_cellular_preferred,
+test_call_volte_to_csfb_3g
+
+TelLiveDataTest:
+test_airplane_mode,
+test_4g,
+test_lte_multi_bearer,
+test_tethering_4g_to_2gwifi,
+test_toggle_data_during_active_wifi_tethering,
+test_disable_wifi_tethering_resume_connected_wifi,
+test_3g,
+test_tethering_3g_to_5gwifi,
+test_lte_wifi_switching,
+test_tethering_entitlement_check
+
+TelLiveSmsTest:
+test_sms_mo_4g,
+test_sms_mt_4g,
+test_sms_mo_in_call_volte,
+test_sms_mt_in_call_volte,
+test_sms_mo_3g,
+test_sms_mt_3g,
+test_sms_mo_iwlan,
+test_sms_mt_iwlan,
+test_sms_mo_in_call_csfb_1x,
+test_sms_mt_in_call_csfb_1x,
+test_sms_mo_in_call_1x,
+test_sms_mt_in_call_1x
+
+TelLiveSettingsTest:
+test_apm_wfc_enabled_toggle_wifi
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/telephony_vzw_vt b/acts/etc/autotest_campaigns/telephony_vzw_vt
new file mode 100644
index 0000000..7872268
--- /dev/null
+++ b/acts/etc/autotest_campaigns/telephony_vzw_vt
@@ -0,0 +1,51 @@
+TelLivePreflightTest:
+test_pre_flight_check
+
+TelLiveVideoTest:
+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_video_accept_as_voice_mo_upgrade_bidirectional,
+test_call_volte_to_volte_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,
+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,
+test_call_video_add_mo_video,
+test_call_video_add_mt_video,
+test_call_mt_video_add_mo_video,
+test_call_mt_video_add_mt_video,
+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,
+test_disable_data_vt_unavailable
+
+TelLiveVideoDataTest:
+test_internet_access_during_video_call
+
+TelLiveSettingsTest:
+test_apm_wfc_cellular_preferred_turn_off_apm
+
+TelLiveVoiceTest:
+test_call_epdg_to_epdg_apm_wfc_cellular_preferred,
+test_call_epdg_to_epdg_wfc_cellular_preferred
+
+TelLiveSmsTest:
+test_sms_mo_in_call_vt,
+test_sms_mt_in_call_vt
+
+TelLivePreflightTest:
+test_check_crash
+
diff --git a/acts/etc/autotest_campaigns/wifi_iot_ent b/acts/etc/autotest_campaigns/wifi_iot_ent
new file mode 100644
index 0000000..b24d355
--- /dev/null
+++ b/acts/etc/autotest_campaigns/wifi_iot_ent
@@ -0,0 +1,2 @@
+WifiEnterpriseTest
+WifiManagerTest
diff --git a/acts/etc/autotest_campaigns/wifi_sanity b/acts/etc/autotest_campaigns/wifi_sanity
new file mode 100644
index 0000000..4910198
--- /dev/null
+++ b/acts/etc/autotest_campaigns/wifi_sanity
@@ -0,0 +1,4 @@
+WifiManagerTest
+WifiNewSetupAutoJoinTest
+WifiScannerScanTest
+WifiScannerBssidTest
diff --git a/acts/framework/README b/acts/framework/README
index 1c5faaf..43c98aa 100644
--- a/acts/framework/README
+++ b/acts/framework/README
@@ -6,14 +6,16 @@
   - python3.4-setuptools
 
 Python dependencies (installed automatically by setup.py):
-  - contextlib2
   - future
   - pyserial
 
+To run unit tests:
+$ python3 setup.py test
+$ python setup.py test
 
 Setup:
   1. Install the system dependencies.
      On Ubuntu, sudo apt-get install python3.4 python3-setuptools
   2. Run "python3.4 setup.py install" with elevated permissions
   3. To verify ACTS is ready to go, at the location for README, and run:
-     cd tests/ && ./test_acts
+     cd tests/ && act.py -c acts_sanity_test_config.json -tc IntegrationTest
diff --git a/acts/framework/acts/asserts.py b/acts/framework/acts/asserts.py
index 4fe63df..939ff9c 100644
--- a/acts/framework/acts/asserts.py
+++ b/acts/framework/acts/asserts.py
@@ -19,6 +19,7 @@
 
 from acts import signals
 
+
 # Have an instance of unittest.TestCase so we could reuse some logic from
 # python's own unittest.
 # _ProxyTest is required because py2 does not allow instantiating
@@ -26,8 +27,11 @@
 class _ProxyTest(unittest.TestCase):
     def runTest(self):
         pass
+
+
 _pyunit_proxy = _ProxyTest()
 
+
 def assert_equal(first, second, msg=None, extras=None):
     """Assert an expression evaluates to true, otherwise fail the test.
 
@@ -42,12 +46,49 @@
     """
     try:
         _pyunit_proxy.assertEqual(first, second)
-    except AssertionError as e:
+    except Exception as e:
+        # We have to catch all here for py2/py3 compatibility.
+        # In py2, assertEqual throws exceptions.AssertionError, which does not
+        # exist in py3. In py3, it throws unittest.case.failureException, which
+        # does not exist in py2. To accommodate using explicit catch complicates
+        # the code like hell, so I opted to catch all instead.
         my_msg = str(e)
         if msg:
             my_msg = "%s %s" % (my_msg, msg)
         fail(my_msg, extras=extras)
 
+
+def assert_almost_equal(first,
+                        second,
+                        places=7,
+                        msg=None,
+                        delta=None,
+                        extras=None):
+    """
+    Assert FIRST to be within +/- DELTA to SECOND, otherwise fail the
+    test.
+    :param first: The first argument, LHS
+    :param second: The second argument, RHS
+    :param places: For floating points, how many decimal places to look into
+    :param msg: Message to display on failure
+    :param delta: The +/- first and second could be apart from each other
+    :param extras: Extra object passed to test failure handler
+    :return:
+    """
+    try:
+        if delta:
+            _pyunit_proxy.assertAlmostEqual(
+                first, second, msg=msg, delta=delta)
+        else:
+            _pyunit_proxy.assertAlmostEqual(
+                first, second, places=places, msg=msg)
+    except Exception as e:
+        my_msg = str(e)
+        if msg:
+            my_msg = "%s %s" % (my_msg, msg)
+        fail(my_msg, extras=extras)
+
+
 def assert_raises(expected_exception, extras=None, *args, **kwargs):
     """Assert that an exception is raised when a function is called.
 
@@ -67,7 +108,11 @@
     context = _AssertRaisesContext(expected_exception, extras=extras)
     return context
 
-def assert_raises_regex(expected_exception, expected_regex, extras=None, *args,
+
+def assert_raises_regex(expected_exception,
+                        expected_regex,
+                        extras=None,
+                        *args,
                         **kwargs):
     """Assert that an exception is raised when a function is called.
 
@@ -86,10 +131,11 @@
         extras: An optional field for extra information to be included in
                 test result.
     """
-    context = _AssertRaisesContext(expected_exception, expected_regex,
-                                   extras=extras)
+    context = _AssertRaisesContext(
+        expected_exception, expected_regex, extras=extras)
     return context
 
+
 def assert_true(expr, msg, extras=None):
     """Assert an expression evaluates to true, otherwise fail the test.
 
@@ -102,6 +148,20 @@
     if not expr:
         fail(msg, extras)
 
+
+def assert_false(expr, msg, extras=None):
+    """Assert an expression evaluates to false, otherwise fail the test.
+
+    Args:
+        expr: The expression that is evaluated.
+        msg: A string explaining the details in case of failure.
+        extras: An optional field for extra information to be included in
+                test result.
+    """
+    if expr:
+        fail(msg, extras)
+
+
 def skip(reason, extras=None):
     """Skip a test case.
 
@@ -115,6 +175,7 @@
     """
     raise signals.TestSkip(reason, extras)
 
+
 def skip_if(expr, reason, extras=None):
     """Skip a test case if expression evaluates to True.
 
@@ -127,6 +188,7 @@
     if expr:
         skip(reason, extras)
 
+
 def abort_class(reason, extras=None):
     """Abort all subsequent test cases within the same test class in one
     iteration.
@@ -145,6 +207,7 @@
     """
     raise signals.TestAbortClass(reason, extras)
 
+
 def abort_class_if(expr, reason, extras=None):
     """Abort all subsequent test cases within the same test class in one
     iteration, if expression evaluates to True.
@@ -165,6 +228,7 @@
     if expr:
         abort_class(reason, extras)
 
+
 def abort_all(reason, extras=None):
     """Abort all subsequent test cases, including the ones not in this test
     class or iteration.
@@ -179,6 +243,7 @@
     """
     raise signals.TestAbortAll(reason, extras)
 
+
 def abort_all_if(expr, reason, extras=None):
     """Abort all subsequent test cases, if the expression evaluates to
     True.
@@ -195,6 +260,7 @@
     if expr:
         abort_all(reason, extras)
 
+
 def fail(msg, extras=None):
     """Explicitly fail a test case.
 
@@ -208,6 +274,7 @@
     """
     raise signals.TestFailure(msg, extras)
 
+
 def explicit_pass(msg, extras=None):
     """Explicitly pass a test case.
 
@@ -225,6 +292,7 @@
     """
     raise signals.TestPass(msg, extras)
 
+
 class _AssertRaisesContext(object):
     """A context manager used to implement TestCase.assertRaises* methods."""
 
@@ -243,12 +311,12 @@
                 exc_name = self.expected.__name__
             except AttributeError:
                 exc_name = str(self.expected)
-            raise signals.TestFailure("{} not raised".format(exc_name),
-                                      extras=self.extras)
+            raise signals.TestFailure(
+                "{} not raised".format(exc_name), extras=self.extras)
         if not issubclass(exc_type, self.expected):
             # let unexpected exceptions pass through
             return False
-        self.exception = exc_value # store for later retrieval
+        self.exception = exc_value  # store for later retrieval
         if self.expected_regexp is None:
             return True
 
@@ -256,7 +324,8 @@
         if isinstance(expected_regexp, str):
             expected_regexp = re.compile(expected_regexp)
         if not expected_regexp.search(str(exc_value)):
-            raise signals.TestFailure('"%s" does not match "%s"' %
-                     (expected_regexp.pattern, str(exc_value)),
-                      extras=self.extras)
+            raise signals.TestFailure(
+                '"%s" does not match "%s"' %
+                (expected_regexp.pattern, str(exc_value)),
+                extras=self.extras)
         return True
diff --git a/acts/framework/acts/base_test.py b/acts/framework/acts/base_test.py
old mode 100644
new mode 100755
index 6452b6c..c6e4a00
--- a/acts/framework/acts/base_test.py
+++ b/acts/framework/acts/base_test.py
@@ -15,22 +15,26 @@
 # limitations under the License.
 
 import os
+import time
+import traceback
 
 from acts import asserts
 from acts import keys
 from acts import logger
 from acts import records
 from acts import signals
-from acts import test_runner
+from acts import tracelogger
 from acts import utils
 
 # Macro strings for test result reporting
 TEST_CASE_TOKEN = "[Test Case]"
 RESULT_LINE_TEMPLATE = TEST_CASE_TOKEN + " %s %s"
 
-class BaseTestError(Exception):
+
+class Error(Exception):
     """Raised for exceptions that occured in BaseTestClass."""
 
+
 class BaseTestClass(object):
     """Base class for all test classes to inherit from.
 
@@ -63,6 +67,12 @@
             setattr(self, name, value)
         self.results = records.TestResult()
         self.current_test_name = None
+        self.log = tracelogger.TraceLogger(self.log)
+        if 'android_devices' in self.__dict__:
+            for ad in self.android_devices:
+                if ad.droid:
+                    utils.set_location_service(ad, False)
+                    utils.sync_device_time(ad)
 
     def __enter__(self):
         return self
@@ -70,16 +80,24 @@
     def __exit__(self, *args):
         self._exec_func(self.clean_up)
 
-    def unpack_userparams(self, req_param_names=[], opt_param_names=[],
+    def unpack_userparams(self,
+                          req_param_names=[],
+                          opt_param_names=[],
                           **kwargs):
-        """Unpacks user defined parameters in test config into individual
-        variables.
+        """An optional function that unpacks user defined parameters into
+        individual variables.
 
-        Instead of accessing the user param with self.user_params["xxx"], the
-        variable can be directly accessed with self.xxx.
+        After unpacking, the params can be directly accessed with self.xxx.
 
-        A missing required param will raise an exception. If an optional param
-        is missing, an INFO line will be logged.
+        If a required param is not provided, an exception is raised. If an
+        optional param is not provided, a warning line will be logged.
+
+        To provide a param, add it in the config file or pass it in as a kwarg.
+        If a param appears in both the config file and kwarg, the value in the
+        config file is used.
+
+        User params from the config file can also be directly accessed in
+        self.user_params.
 
         Args:
             req_param_names: A list of names of the required user params.
@@ -90,22 +108,42 @@
                      required_list or opt_list.
 
         Raises:
-            BaseTestError is raised if a required user params is missing from
-            test config.
+            Error is raised if a required user params is not provided.
         """
         for k, v in kwargs.items():
+            if k in self.user_params:
+                v = self.user_params[k]
             setattr(self, k, v)
         for name in req_param_names:
+            if hasattr(self, name):
+                continue
             if name not in self.user_params:
-                raise BaseTestError(("Missing required user param '%s' in test"
-                    " configuration.") % name)
+                raise Error(("Missing required user param '%s' in test "
+                             "configuration.") % name)
             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:
+            if hasattr(self, name):
+                continue
+            if name in self.user_params:
                 setattr(self, name, self.user_params[name])
+            else:
+                self.log.warning(("Missing optional user param '%s' in "
+                                  "configuration, continue."), name)
+
+        capablity_of_devices = utils.CapablityPerDevice
+        if "additional_energy_info_models" in self.user_params:
+            self.energy_info_models = (capablity_of_devices.energy_info_models
+                                       + self.additional_energy_info_models)
+        else:
+            self.energy_info_models = capablity_of_devices.energy_info_models
+        self.user_params["energy_info_models"] = self.energy_info_models
+
+        if "additional_tdls_models" in self.user_params:
+            self.tdls_models = (capablity_of_devices.energy_info_models +
+                                self.additional_tdls_models)
+        else:
+            self.tdls_models = capablity_of_devices.energy_info_models
+        self.user_params["tdls_models"] = self.tdls_models
 
     def _setup_class(self):
         """Proxy function to guarantee the base implementation of setup_class
@@ -186,7 +224,8 @@
                     case.
         """
         test_name = record.test_name
-        self.log.error(record.details)
+        if record.details:
+            self.log.error(record.details)
         begin_time = logger.epoch_to_log_line_timestamp(record.begin_time)
         self.log.info(RESULT_LINE_TEMPLATE, test_name, record.result)
         self.on_fail(test_name, begin_time)
@@ -251,6 +290,28 @@
             begin_time: Logline format timestamp taken when the test started.
         """
 
+    def _on_blocked(self, record):
+        """Proxy function to guarantee the base implementation of on_blocked
+        is called.
+
+        Args:
+            record: The records.TestResultRecord object for the blocked 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 block: %s", record.details)
+        self.on_blocked(test_name, begin_time)
+
+    def on_blocked(self, test_name, begin_time):
+        """A function that is executed upon a test begin skipped.
+
+        Args:
+            test_name: Name of the test that triggered this function.
+            begin_time: Logline format timestamp taken when the test started.
+        """
+
     def _on_exception(self, record):
         """Proxy function to guarantee the base implementation of on_exception
         is called.
@@ -262,7 +323,6 @@
         test_name = record.test_name
         self.log.exception(record.details)
         begin_time = logger.epoch_to_log_line_timestamp(record.begin_time)
-        self.log.info(RESULT_LINE_TEMPLATE, test_name, record.result)
         self.on_exception(test_name, begin_time)
 
     def on_exception(self, test_name, begin_time):
@@ -318,17 +378,29 @@
         self.log.info("%s %s", TEST_CASE_TOKEN, test_name)
         verdict = None
         try:
-            ret = self._setup_test(test_name)
-            asserts.assert_true(ret is not False,
-                                "Setup for %s failed." % test_name)
             try:
+                if hasattr(self, 'android_devices'):
+                    for ad in self.android_devices:
+                        if not ad.is_adb_logcat_on:
+                            ad.start_adb_logcat(cont_logcat_file=True)
+                ret = self._setup_test(test_name)
+                asserts.assert_true(ret is not False,
+                                    "Setup for %s failed." % test_name)
                 if args or kwargs:
                     verdict = test_func(*args, **kwargs)
                 else:
                     verdict = test_func()
             finally:
-                self._teardown_test(test_name)
+                try:
+                    self._teardown_test(test_name)
+                except signals.TestAbortAll:
+                    raise
+                except Exception as e:
+                    self.log.error(traceback.format_exc())
+                    tr_record.add_error("teardown_test", e)
+                    self._exec_procedure_func(self._on_exception, tr_record)
         except (signals.TestFailure, AssertionError) as e:
+            self.log.error(traceback.format_exc())
             tr_record.test_fail(e)
             self._exec_procedure_func(self._on_fail, tr_record)
         except signals.TestSkip as e:
@@ -347,7 +419,11 @@
             # This is a trigger test for generated tests, suppress reporting.
             is_generate_trigger = True
             self.results.requested.remove(test_name)
+        except signals.TestBlocked as e:
+            tr_record.test_blocked(e)
+            self._exec_procedure_func(self._on_blocked, tr_record)
         except Exception as e:
+            self.log.error(traceback.format_exc())
             # Exception happened during test.
             tr_record.test_unknown(e)
             self._exec_procedure_func(self._on_exception, tr_record)
@@ -368,9 +444,14 @@
             if not is_generate_trigger:
                 self.results.add_record(tr_record)
 
-    def run_generated_testcases(self, test_func, settings,
-                                args=None, kwargs=None,
-                                tag="", name_func=None):
+    def run_generated_testcases(self,
+                                test_func,
+                                settings,
+                                args=None,
+                                kwargs=None,
+                                tag="",
+                                name_func=None,
+                                format_args=False):
         """Runs generated test cases.
 
         Generated test cases are not written down as functions, but as a list
@@ -392,6 +473,8 @@
                        proper test name. The test name should be shorter than
                        utils.MAX_FILENAME_LEN. Names over the limit will be
                        truncated.
+            format_args: If True, args will be appended as the first argument
+                         in the args list passed to test_func.
 
         Returns:
             A list of settings that did not pass.
@@ -399,22 +482,35 @@
         args = args or ()
         kwargs = kwargs or {}
         failed_settings = []
-        for s in settings:
-            test_name = "{} {}".format(tag, s)
+
+        for setting in settings:
+            test_name = "{} {}".format(tag, setting)
+
             if name_func:
                 try:
-                    test_name = name_func(s, *args, **kwargs)
+                    test_name = name_func(setting, *args, **kwargs)
                 except:
                     self.log.exception(("Failed to get test name from "
                                         "test_func. Fall back to default %s"),
                                        test_name)
+
             self.results.requested.append(test_name)
+
             if len(test_name) > utils.MAX_FILENAME_LEN:
                 test_name = test_name[:utils.MAX_FILENAME_LEN]
+
             previous_success_cnt = len(self.results.passed)
-            self.exec_one_testcase(test_name, test_func, (s,) + args, **kwargs)
+
+            if format_args:
+                self.exec_one_testcase(test_name, test_func,
+                                       args + (setting, ), **kwargs)
+            else:
+                self.exec_one_testcase(test_name, test_func,
+                                       (setting, ) + args, **kwargs)
+
             if len(self.results.passed) - previous_success_cnt != 1:
-                failed_settings.append(s)
+                failed_settings.append(setting)
+
         return failed_settings
 
     def _exec_func(self, func, *args):
@@ -464,28 +560,61 @@
             name, function is the actual test case function.
 
         Raises:
-            test_runner.USERError is raised if the test name does not follow
+            Error is raised if the test name does not follow
             naming convention "test_*". This can only be caused by user input
             here.
         """
         test_funcs = []
         for test_name in test_names:
-            if not test_name.startswith("test_"):
-                msg = ("Test case name %s does not follow naming convention "
-                       "test_*, abort.") % test_name
-                raise test_runner.USERError(msg)
-            try:
-                test_funcs.append((test_name, getattr(self, test_name)))
-            except AttributeError:
-                def test_skip_func(*args, **kwargs):
-                    raise signals.TestSkip("Test %s does not exist in %s" %
-                                           (test_name, self.TAG))
-                test_funcs.append((test_name, test_skip_func))
-            except BaseTestError as e:
-                self.log.warning(str(e))
+            test_funcs.append(self._get_test_func(test_name))
+
         return test_funcs
 
-    def run(self, test_names=None):
+    def _get_test_func(self, test_name):
+        """Obtain the actual function of test cases based on the test name.
+
+        Args:
+            test_name: String, The name of the test.
+
+        Returns:
+            A tuples of (string, function). String is the test case
+            name, function is the actual test case function.
+
+        Raises:
+            Error is raised if the test name does not follow
+            naming convention "test_*". This can only be caused by user input
+            here.
+        """
+        if not test_name.startswith("test_"):
+            raise Error(("Test case name %s does not follow naming "
+                         "convention test_*, abort.") % test_name)
+        try:
+            return test_name, getattr(self, test_name)
+        except:
+
+            def test_skip_func(*args, **kwargs):
+                raise signals.TestSkip("Test %s does not exist" % test_name)
+
+            self.log.info("Test case %s not found in %s.", test_name, self.TAG)
+            return test_name, test_skip_func
+
+    def _block_all_test_cases(self, tests):
+        """
+        Block all passed in test cases.
+        Args:
+            tests: The tests to block.
+        """
+        for test_name, test_func in tests:
+            signal = signals.TestBlocked("Failed class setup")
+            record = records.TestResultRecord(test_name, self.TAG)
+            record.test_begin()
+            if hasattr(test_func, 'gather'):
+                signal.extras = test_func.gather()
+            record.test_blocked(signal)
+            self.results.add_record(record)
+            self._on_blocked(record)
+
+    def run(self, test_names=None, test_case_iterations=1):
         """Runs test cases within a test class by the order they appear in the
         execution list.
 
@@ -516,19 +645,23 @@
                 test_names = self._get_all_test_names()
         self.results.requested = test_names
         tests = self._get_test_funcs(test_names)
+        # A TestResultRecord used for when setup_class fails.
         # Setup for the class.
         try:
             if self._setup_class() is False:
-                raise signals.TestFailure("Failed to setup %s." % self.TAG)
+                self.log.error("Failed to setup %s.", self.TAG)
+                self._block_all_test_cases(tests)
+                return self.results
         except Exception as e:
             self.log.exception("Failed to setup %s.", self.TAG)
-            self.results.fail_class(self.TAG, e)
             self._exec_func(self.teardown_class)
+            self._block_all_test_cases(tests)
             return self.results
         # Run tests in order.
         try:
             for test_name, test_func in tests:
-                self.exec_one_testcase(test_name, test_func, self.cli_args)
+                for _ in range(test_case_iterations):
+                    self.exec_one_testcase(test_name, test_func, self.cli_args)
             return self.results
         except signals.TestAbortClass:
             return self.results
@@ -549,3 +682,42 @@
         This function should clean up objects initialized in the constructor by
         user.
         """
+
+    def _take_bug_report(self, test_name, begin_time):
+        if "no_bug_report_on_fail" in self.user_params:
+            return
+
+        # 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_report(test_name, begin_time)
+                tombstone_path = os.path.join(
+                    ad.log_path, "BugReports", "{},{}".format(
+                        begin_time, ad.serial).replace(' ', '_'))
+                utils.create_dir(tombstone_path)
+                ad.adb.pull('/data/tombstones/', tombstone_path, timeout=1200)
+            except Exception as e:
+                ad.log.error(
+                    "Failed to take a bug report for %s with error %s",
+                    test_name, e)
+
+    def _reboot_device(self, ad):
+        ad.log.info("Rebooting device.")
+        ad = ad.reboot()
+
+    def _cleanup_logger_sessions(self):
+        for (logger, session) in self.logger_sessions:
+            self.log.info("Resetting a diagnostic session %s, %s", logger,
+                          session)
+            logger.reset()
+        self.logger_sessions = []
+
+    def _pull_diag_logs(self, test_name, begin_time):
+        for (logger, session) in self.logger_sessions:
+            self.log.info("Pulling diagnostic session %s", logger)
+            logger.stop(session)
+            diag_path = os.path.join(self.log_path, begin_time)
+            utils.create_dir(diag_path)
+            logger.pull(session, diag_path)
diff --git a/acts/framework/acts/bin/act.py b/acts/framework/acts/bin/act.py
index bb49d65..dac9fcf 100755
--- a/acts/framework/acts/bin/act.py
+++ b/acts/framework/acts/bin/act.py
@@ -23,284 +23,136 @@
 import sys
 import traceback
 
-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
-
-# An environment variable defining the base location for ACTS logs.
-_ENV_ACTS_LOGPATH = 'ACTS_LOGPATH'
-
-# An environment variable defining the test search paths for ACTS.
-_ENV_ACTS_TESTPATHS = 'ACTS_TESTPATHS'
-_PATH_SEPARATOR = ':'
+from acts import config_parser
+from acts import keys
+from acts import signals
+from acts import test_runner
 
 
-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 not isinstance(name, 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.
-
-    Args:
-        testbed_configs: A list of testbed configuration json objects.
-
-    Raises:
-        If any part of the configuration is invalid, USERError is raised.
-    """
-    seen_names = set()
-    # 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.add(name)
-
-
-def _verify_test_class_name(test_cls_name):
-    if not test_cls_name.endswith("Test"):
-        raise USERError(("Requested test class '%s' does not follow the test "
-                         "class naming convention *Test.") % test_cls_name)
-
-
-def _parse_one_test_specifier(item):
-    """Parse one test specifier from command line input.
-
-    This also verifies that the test class name and test case names follow
-    ACTS's naming conventions. A test class name has to end with "Test"; a test
-    case name has to start with "test".
-
-    Args:
-        item: A string that specifies a test class or test cases in one test
-            class to run.
-
-    Returns:
-        A tuple of a string and a list of strings. The string is the test class
-        name, the list of strings is a list of test case names. The list can be
-        None.
-    """
-    tokens = item.split(':')
-    if len(tokens) > 2:
-        raise USERError("Syntax error in test specifier %s" % item)
-    if len(tokens) == 1:
-        # This should be considered a test class name
-        test_cls_name = tokens[0]
-        _verify_test_class_name(test_cls_name)
-        return (test_cls_name, None)
-    elif len(tokens) == 2:
-        # This should be considered a test class name followed by
-        # a list of test case names.
-        test_cls_name, test_case_names = tokens
-        clean_names = []
-        _verify_test_class_name(test_cls_name)
-        for elem in test_case_names.split(','):
-            test_case_name = elem.strip()
-            if not test_case_name.startswith("test_"):
-                raise USERError(("Requested test case '%s' in test class "
-                                 "'%s' does not follow the test case "
-                                 "naming convention test_*.") %
-                                (test_case_name, test_cls_name))
-            clean_names.append(test_case_name)
-        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, test_paths, log_path):
-    """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.
-        tb_filters: A subset of test bed names to be pulled from the
-            config file. If None, then all test beds will be selected.
-        test_paths: A list of command-line default for the test path.
-            If None, then the paths must be specified within the config file.
-        log_path: A command-line default for the log path. If None, then the
-            log path must be specified in the config file.
-
-    Returns:
-        A list of test configuration json objects to be passed to TestRunner.
-    """
-    try:
-        configs = load_config(test_config_path)
-        if test_paths:
-            configs[Config.key_test_paths.value] = test_paths
-        if log_path:
-            configs[Config.key_log_path.value] = log_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
-
-        if (not Config.key_log_path.value in configs and
-                _ENV_ACTS_LOGPATH in os.environ):
-            print('Using environment log path: %s' %
-                  (os.environ[_ENV_ACTS_LOGPATH]))
-            configs[Config.key_log_path.value] = os.environ[_ENV_ACTS_LOGPATH]
-        if (not Config.key_test_paths.value in configs and
-                _ENV_ACTS_TESTPATHS in os.environ):
-            print('Using environment test paths: %s' %
-                  (os.environ[_ENV_ACTS_TESTPATHS]))
-            configs[Config.key_test_paths.value] = os.environ[
-                _ENV_ACTS_TESTPATHS].split(_PATH_SEPARATOR)
-
-        _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])
-        config_path, _ = os.path.split(abs_path(test_config_path))
-        configs[Config.key_config_path] = config_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 = []
-    # TODO: See if there is a better way to do this: b/29836695
-    config_path, _ = os.path.split(abs_path(test_config_path))
-    configs[Config.key_config_path] = config_path
-    for original_bed_config in beds:
-        new_test_config = dict(configs)
-        new_test_config[Config.key_testbed.value] = original_bed_config
-        # Keys in each test bed config will be copied to a level up 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.
-        new_test_config.update(original_bed_config)
-        config_jsons.append(new_test_config)
-    return config_jsons
-
-
-def _run_test(test_runner, repeat=1):
-    """Instantiate and runs TestRunner.
+def _run_test(parsed_config, test_identifiers, repeat=1):
+    """Instantiate and runs test_runner.TestRunner.
 
     This is the function to start separate processes with.
 
     Args:
-        test_runner: The test_runner instance to be executed.
+        parsed_config: A dict that is a set of configs for one
+                       test_runner.TestRunner.
+        test_identifiers: A list of tuples, each identifies what test case to
+                          run on what test class.
         repeat: Number of times to iterate the specified tests.
+
+    Returns:
+        True if all tests passed without any error, False otherwise.
     """
+    runner = _create_test_runner(parsed_config, test_identifiers)
     try:
         for i in range(repeat):
-            test_runner.run()
-    except TestAbortAll:
-        return
+            runner.run()
+        return runner.results.is_all_pass
+    except signals.TestAbortAll:
+        return True
     except:
-        print("Exception when executing {}, iteration {}.".format(
-            test_runner.testbed_name, i))
+        print("Exception when executing %s, iteration %s." %
+              (runner.testbed_name, i))
         print(traceback.format_exc())
-        return False
     finally:
-        test_runner.stop()
+        runner.stop()
 
 
-def _gen_term_signal_handler(test_runners):
-    def termination_sig_handler(signal_num, frame):
-        for t in test_runners:
-            t.stop()
+def _create_test_runner(parsed_config, test_identifiers):
+    """Instantiates one test_runner.TestRunner object and register termination
+    signal handlers that properly shut down the test_runner.TestRunner run.
+
+    Args:
+        parsed_config: A dict that is a set of configs for one
+                       test_runner.TestRunner.
+        test_identifiers: A list of tuples, each identifies what test case to
+                          run on what test class.
+
+    Returns:
+        A test_runner.TestRunner object.
+    """
+    try:
+        t = test_runner.TestRunner(parsed_config, test_identifiers)
+    except:
+        print("Failed to instantiate test runner, abort.")
+        print(traceback.format_exc())
         sys.exit(1)
-
-    return termination_sig_handler
+    # Register handler for termination signals.
+    handler = config_parser.gen_term_signal_handler([t])
+    signal.signal(signal.SIGTERM, handler)
+    signal.signal(signal.SIGINT, handler)
+    return t
 
 
-def _run_tests_parallel(process_args):
-    print("Executing {} concurrent test runs.".format(len(process_args)))
-    results = concurrent_exec(_run_test, process_args)
+def _run_tests_parallel(parsed_configs, test_identifiers, repeat):
+    """Executes requested tests in parallel.
+
+    Each test run will be in its own process.
+
+    Args:
+        parsed_config: A list of dicts, each is a set of configs for one
+                       test_runner.TestRunner.
+        test_identifiers: A list of tuples, each identifies what test case to
+                          run on what test class.
+        repeat: Number of times to iterate the specified tests.
+
+    Returns:
+        True if all test runs executed successfully, False otherwise.
+    """
+    print("Executing {} concurrent test runs.".format(len(parsed_configs)))
+    arg_list = [(c, test_identifiers, repeat) for c in parsed_configs]
+    results = []
+    with multiprocessing.Pool(processes=len(parsed_configs)) as pool:
+        # Can't use starmap for py2 compatibility. One day, one day......
+        for args in arg_list:
+            results.append(pool.apply_async(_run_test, args))
+        pool.close()
+        pool.join()
     for r in results:
-        if r is False or isinstance(r, Exception):
+        if r.get() is False or isinstance(r, Exception):
             return False
 
 
-def _run_tests_sequential(process_args):
+def _run_tests_sequential(parsed_configs, test_identifiers, repeat):
+    """Executes requested tests sequentially.
+
+    Requested test runs will commence one after another according to the order
+    of their corresponding configs.
+
+    Args:
+        parsed_config: A list of dicts, each is a set of configs for one
+                       test_runner.TestRunner.
+        test_identifiers: A list of tuples, each identifies what test case to
+                          run on what test class.
+        repeat: Number of times to iterate the specified tests.
+
+    Returns:
+        True if all test runs executed successfully, False otherwise.
+    """
     ok = True
-    for args in process_args:
-        if _run_test(*args) is False:
-            ok = False
+    for c in parsed_configs:
+        try:
+            ret = _run_test(c, test_identifiers, repeat)
+            ok = ok and ret
+        except:
+            print("Exception occurred when executing test bed %s" %
+                  c[keys.Config.key_testbed.value])
     return ok
 
 
-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
-
-
 def main(argv):
+    """This is a sample implementation of a cli entry point for ACTS test
+    execution.
+
+    Alternatively, you could directly invoke an ACTS test script:
+
+        python3 MyTest.py -c my_config.json
+
+    See acts.test_runner.main for more details.
+    Or you could implement your own cli entry point using acts.config_parser
+    functions and acts.test_runner.execute_one_test_class.
+    """
     parser = argparse.ArgumentParser(description=(
         "Specify tests to run. If "
         "nothing specified, run all test cases found."))
@@ -320,22 +172,20 @@
         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',
+        '-ci',
+        '--campaign_iterations',
+        metavar="<CAMPAIGN_ITERATIONS>",
+        nargs='?',
         type=int,
-        metavar="<NUMBER>",
-        help="Number of times to run the specified test cases.")
+        const=1,
+        default=1,
+        help="Number of times to run the campaign or a group of test cases.")
     parser.add_argument(
         '-tb',
         '--testbed',
@@ -348,14 +198,14 @@
         '--logpath',
         type=str,
         metavar="<PATH>",
-        help=("Root path under which all logs will be placed."))
+        help="Root path under which all logs will be placed.")
     parser.add_argument(
         '-tp',
         '--testpaths',
         nargs='*',
         type=str,
         metavar="<PATH> <PATH>",
-        help=("One or more non-recursive test class search paths."))
+        help="One or more non-recursive test class search paths.")
 
     group = parser.add_mutually_exclusive_group(required=True)
     group.add_argument(
@@ -373,45 +223,38 @@
         metavar="<PATH>",
         help=("Path to a file containing a comma delimited list of test "
               "classes to run."))
+    parser.add_argument(
+        '-r',
+        '--random',
+        action="store_true",
+        help=("If set, tests will be executed in random order."))
+    parser.add_argument(
+        '-ti',
+        '--test_case_iterations',
+        metavar="<TEST_CASE_ITERATIONS>",
+        nargs='?',
+        type=int,
+        help="Number of times to run every test case.")
 
     args = parser.parse_args(argv)
     test_list = None
-    repeat = 1
     if args.testfile:
-        test_list = _parse_test_file(args.testfile[0])
+        test_list = config_parser.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,
-            args.testpaths, args.logpath)
-    if not parsed_configs:
-        print("Encountered error when parsing the config file, abort!")
-        sys.exit(1)
+    parsed_configs = config_parser.load_test_config_file(
+        args.config[0], args.testbed, args.testpaths, args.logpath,
+        args.test_args, args.random, args.test_case_iterations)
     # 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)
+    test_identifiers = config_parser.parse_test_list(test_list)
+
     # Execute test runners.
-    if args.parallel and len(process_args) > 1:
-        exec_result = _run_tests_parallel(process_args)
+    if args.parallel and len(parsed_configs) > 1:
+        exec_result = _run_tests_parallel(parsed_configs, test_identifiers,
+                                          args.campaign_iterations)
     else:
-        exec_result = _run_tests_sequential(process_args)
+        exec_result = _run_tests_sequential(parsed_configs, test_identifiers,
+                                            args.campaign_iterations)
     if exec_result is False:
         sys.exit(1)
     sys.exit(0)
diff --git a/acts/framework/acts/config_parser.py b/acts/framework/acts/config_parser.py
new file mode 100755
index 0000000..57c3a5c
--- /dev/null
+++ b/acts/framework/acts/config_parser.py
@@ -0,0 +1,306 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from builtins import str
+
+import os
+import random
+import sys
+
+from acts import keys
+from acts import utils
+
+# An environment variable defining the base location for ACTS logs.
+_ENV_ACTS_LOGPATH = 'ACTS_LOGPATH'
+
+# An environment variable defining the test search paths for ACTS.
+_ENV_ACTS_TESTPATHS = 'ACTS_TESTPATHS'
+_PATH_SEPARATOR = ':'
+
+
+class ActsConfigError(Exception):
+    """Raised when there is a problem in test configuration file."""
+
+
+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 keys.Config.reserved_keys.value:
+        if k not in test_config:
+            raise ActsConfigError(
+                "Required key %s missing in test config." % 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, ActsConfigError is raised.
+    """
+    if not name:
+        raise ActsConfigError("Test bed names can't be empty.")
+    if not isinstance(name, str):
+        raise ActsConfigError("Test bed names have to be string.")
+    for l in name:
+        if l not in utils.valid_filename_chars:
+            raise ActsConfigError(
+                "Char '%s' is not allowed in test bed names." % l)
+
+
+def _validate_testbed_configs(testbed_configs):
+    """Validates the testbed configurations.
+
+    Args:
+        testbed_configs: A list of testbed configuration json objects.
+
+    Raises:
+        If any part of the configuration is invalid, ActsConfigError is raised.
+    """
+    seen_names = set()
+    # 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[keys.Config.key_testbed_name.value]
+        _validate_testbed_name(name)
+        # Test bed names should be unique.
+        if name in seen_names:
+            raise ActsConfigError("Duplicate testbed name %s found." % name)
+        seen_names.add(name)
+
+
+def _verify_test_class_name(test_cls_name):
+    if not test_cls_name.endswith("Test"):
+        raise ActsConfigError(
+            ("Requested test class '%s' does not follow the test class naming "
+             "convention *Test.") % test_cls_name)
+
+
+def gen_term_signal_handler(test_runners):
+    def termination_sig_handler(signal_num, frame):
+        for t in test_runners:
+            t.stop()
+        sys.exit(1)
+
+    return termination_sig_handler
+
+
+def _parse_one_test_specifier(item):
+    """Parse one test specifier from command line input.
+
+    This also verifies that the test class name and test case names follow
+    ACTS's naming conventions. A test class name has to end with "Test"; a test
+    case name has to start with "test".
+
+    Args:
+        item: A string that specifies a test class or test cases in one test
+            class to run.
+
+    Returns:
+        A tuple of a string and a list of strings. The string is the test class
+        name, the list of strings is a list of test case names. The list can be
+        None.
+    """
+    tokens = item.split(':')
+    if len(tokens) > 2:
+        raise ActsConfigError("Syntax error in test specifier %s" % item)
+    if len(tokens) == 1:
+        # This should be considered a test class name
+        test_cls_name = tokens[0]
+        _verify_test_class_name(test_cls_name)
+        return (test_cls_name, None)
+    elif len(tokens) == 2:
+        # This should be considered a test class name followed by
+        # a list of test case names.
+        test_cls_name, test_case_names = tokens
+        clean_names = []
+        _verify_test_class_name(test_cls_name)
+        for elem in test_case_names.split(','):
+            test_case_name = elem.strip()
+            if not test_case_name.startswith("test_"):
+                raise ActsConfigError(
+                    ("Requested test case '%s' in test class "
+                     "'%s' does not follow the test case "
+                     "naming convention test_*.") % (test_case_name,
+                                                     test_cls_name))
+            clean_names.append(test_case_name)
+        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 test_randomizer(test_identifiers, test_case_iterations=10):
+    """Generate test lists by randomizing user provided test list.
+
+    Args:
+        test_identifiers: A list of test classes/cases.
+        random_iterations: The range of random iterations for each case.
+    Returns:
+        A list of randomized test cases.
+    """
+    random_tests = []
+    preflight_tests = []
+    postflight_tests = []
+    for test_class, test_cases in test_identifiers:
+        if "Preflight" in test_class:
+            preflight_tests.append((test_class, test_cases))
+        elif "Postflight" in test_class:
+            postflight_tests.append((test_class, test_cases))
+        else:
+            for test_case in test_cases:
+                random_tests.append((test_class,
+                                     [test_case] * random.randrange(
+                                         1, test_case_iterations + 1)))
+    random.shuffle(random_tests)
+    new_tests = []
+    previous_class = None
+    for test_class, test_cases in random_tests:
+        if test_class == previous_class:
+            previous_cases = new_tests[-1][1]
+            previous_cases.extend(test_cases)
+        else:
+            new_tests.append((test_class, test_cases))
+        previous_class = test_class
+    return preflight_tests + new_tests + postflight_tests
+
+
+def load_test_config_file(test_config_path,
+                          tb_filters=None,
+                          override_test_path=None,
+                          override_log_path=None,
+                          override_test_args=None,
+                          override_random=None,
+                          override_test_case_iterations=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.
+        tb_filters: A subset of test bed names to be pulled from the config
+                    file. If None, then all test beds will be selected.
+        override_test_path: If not none the test path to use instead.
+        override_log_path: If not none the log path to use instead.
+        override_test_args: If not none the test args to use instead.
+        override_random: If not None, override the config file value.
+        override_test_case_iterations: If not None, override the config file
+                                       value.
+
+    Returns:
+        A list of test configuration json objects to be passed to
+        test_runner.TestRunner.
+    """
+    configs = utils.load_config(test_config_path)
+    if override_test_path:
+        configs[keys.Config.key_test_paths.value] = override_test_path
+    if override_log_path:
+        configs[keys.Config.key_log_path.value] = override_log_path
+    if override_test_args:
+        configs[keys.Config.ikey_cli_args.value] = override_test_args
+    if override_random:
+        configs[keys.Config.key_random.value] = override_random
+    if override_test_case_iterations:
+        configs[keys.Config.key_test_case_iterations.value] = \
+            override_test_case_iterations
+    if tb_filters:
+        tbs = []
+        for tb in configs[keys.Config.key_testbed.value]:
+            if tb[keys.Config.key_testbed_name.value] in tb_filters:
+                tbs.append(tb)
+        if len(tbs) != len(tb_filters):
+            raise ActsConfigError(
+                ("Expect to find %d test bed configs, found %d. Check if"
+                 " you have the correct test bed names.") % (len(tb_filters),
+                                                             len(tbs)))
+        configs[keys.Config.key_testbed.value] = tbs
+
+    if (not keys.Config.key_log_path.value in configs and
+            _ENV_ACTS_LOGPATH in os.environ):
+        print('Using environment log path: %s' %
+              (os.environ[_ENV_ACTS_LOGPATH]))
+        configs[keys.Config.key_log_path.value] = os.environ[_ENV_ACTS_LOGPATH]
+    if (not keys.Config.key_test_paths.value in configs and
+            _ENV_ACTS_TESTPATHS in os.environ):
+        print('Using environment test paths: %s' %
+              (os.environ[_ENV_ACTS_TESTPATHS]))
+        configs[keys.Config.key_test_paths.value] = os.environ[
+            _ENV_ACTS_TESTPATHS].split(_PATH_SEPARATOR)
+
+    _validate_test_config(configs)
+    _validate_testbed_configs(configs[keys.Config.key_testbed.value])
+    k_log_path = keys.Config.key_log_path.value
+    configs[k_log_path] = utils.abs_path(configs[k_log_path])
+    config_path, _ = os.path.split(utils.abs_path(test_config_path))
+    configs[keys.Config.key_config_path] = config_path
+    tps = configs[keys.Config.key_test_paths.value]
+    # Unpack testbeds into separate json objects.
+    beds = configs.pop(keys.Config.key_testbed.value)
+    config_jsons = []
+    # TODO: See if there is a better way to do this: b/29836695
+    config_path, _ = os.path.split(utils.abs_path(test_config_path))
+    configs[keys.Config.key_config_path] = config_path
+    for original_bed_config in beds:
+        new_test_config = dict(configs)
+        new_test_config[keys.Config.key_testbed.value] = original_bed_config
+        # Keys in each test bed config will be copied to a level up 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.
+        new_test_config.update(original_bed_config)
+        config_jsons.append(new_test_config)
+    return config_jsons
+
+
+def parse_test_file(fpath):
+    """Parses a test file that contains test specifiers.
+
+    Args:
+        fpath: A string that is the path to the test file to parse.
+
+    Returns:
+        A list of strings, each is a test specifier.
+    """
+    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
diff --git a/acts/framework/acts/controllers/__init__.py b/acts/framework/acts/controllers/__init__.py
index e2f5b49..d0e411c 100644
--- a/acts/framework/acts/controllers/__init__.py
+++ b/acts/framework/acts/controllers/__init__.py
@@ -22,12 +22,7 @@
         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
+    "android_device", "attenuator", "monsoon", "access_point", "iperf_server"
+]
diff --git a/acts/framework/acts/controllers/access_point.py b/acts/framework/acts/controllers/access_point.py
index 3cca2c7..c91275a 100755
--- a/acts/framework/acts/controllers/access_point.py
+++ b/acts/framework/acts/controllers/access_point.py
@@ -14,500 +14,293 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import acts.jsonrpc as jsonrpc
-from acts.test_utils.wifi.wifi_test_utils import WifiEnums
+import collections
+import ipaddress
+import logging
 
-ACTS_CONTROLLER_CONFIG_NAME = "AP"
-ACTS_CONTROLLER_REFERENCE_NAME = "access_points"
+from acts.controllers.ap_lib import dhcp_config
+from acts.controllers.ap_lib import dhcp_server
+from acts.controllers.ap_lib import hostapd
+from acts.controllers.ap_lib import hostapd_config
+from acts.controllers.utils_lib.commands import ip
+from acts.controllers.utils_lib.commands import route
+from acts.controllers.utils_lib.commands import shell
+from acts.controllers.utils_lib.ssh import connection
+from acts.controllers.utils_lib.ssh import settings
 
-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
+ACTS_CONTROLLER_CONFIG_NAME = 'AccessPoint'
+ACTS_CONTROLLER_REFERENCE_NAME = 'access_points'
 
-def destroy(objs):
-    return
 
-class ServerError(Exception):
-    pass
+def create(configs):
+    """Creates ap controllers from a json config.
 
-class ClientError(Exception):
-    pass
+    Creates an ap controller from either a list, or a single
+    element. The element can either be just the hostname or a dictionary
+    containing the hostname and username of the ap to connect to over ssh.
 
-"""
-Controller for OpenWRT routers.
-"""
-class AP():
-    """Interface to OpenWRT using the LuCI interface.
+    Args:
+        The json configs that represent this controller.
 
-    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")
+    Returns:
+        A new AccessPoint.
     """
-    IFACE_DEFAULTS = {"mode": "ap", "disabled": "0",
-                      "encryption": "psk2", "network": "lan"}
-    RADIO_DEFAULTS = {"disabled": "0"}
+    return [
+        AccessPoint(settings.from_config(c['ssh_config'])) for c in configs
+    ]
 
-    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.
+def destroy(aps):
+    """Destroys a list of access points.
 
-        Finds the section ids of the sections that have the specified key:value
-        pair in them.
+    Args:
+        aps: The list of access points to destroy.
+    """
+    for ap in aps:
+        ap.close()
+
+
+def get_info(aps):
+    """Get information on a list of access points.
+
+    Args:
+        aps: A list of AccessPoints.
+
+    Returns:
+        A list of all aps hostname.
+    """
+    return [ap.ssh_settings.hostname for ap in aps]
+
+
+class Error(Exception):
+    """Error raised when there is a problem with the access point."""
+
+
+_ApInstance = collections.namedtuple('_ApInstance', ['hostapd', 'subnet'])
+
+# We use these today as part of a hardcoded mapping of interface name to
+# capabilities.  However, medium term we need to start inspecting
+# interfaces to determine their capabilities.
+_AP_2GHZ_INTERFACE = 'wlan0'
+_AP_5GHZ_INTERFACE = 'wlan1'
+# These ranges were split this way since each physical radio can have up
+# to 8 SSIDs so for the 2GHz radio the DHCP range will be
+# 192.168.1 - 8 and the 5Ghz radio will be 192.168.9 - 16
+_AP_2GHZ_SUBNET_STR = '192.168.1.0/24'
+_AP_5GHZ_SUBNET_STR = '192.168.9.0/24'
+_AP_2GHZ_SUBNET = dhcp_config.Subnet(ipaddress.ip_network(_AP_2GHZ_SUBNET_STR))
+_AP_5GHZ_SUBNET = dhcp_config.Subnet(ipaddress.ip_network(_AP_5GHZ_SUBNET_STR))
+
+
+class AccessPoint(object):
+    """An access point controller.
+
+    Attributes:
+        ssh: The ssh connection to this ap.
+        ssh_settings: The ssh settings being used by the ssh conneciton.
+        dhcp_settings: The dhcp server settings being used.
+    """
+
+    def __init__(self, ssh_settings):
+        """
+        Args:
+            ssh_settings: acts.controllers.utils_lib.ssh.SshSettings instance.
+        """
+        self.ssh_settings = ssh_settings
+        self.ssh = connection.SshConnection(self.ssh_settings)
+
+        # Singleton utilities for running various commands.
+        self._ip_cmd = ip.LinuxIpCommand(self.ssh)
+        self._route_cmd = route.LinuxRouteCommand(self.ssh)
+
+        # A map from network interface name to _ApInstance objects representing
+        # the hostapd instance running against the interface.
+        self._aps = dict()
+
+    def __del__(self):
+        self.close()
+
+    def start_ap(self, hostapd_config, additional_parameters=None):
+        """Starts as an ap using a set of configurations.
+
+        This will start an ap on this host. To start an ap the controller
+        selects a network interface to use based on the configs given. It then
+        will start up hostapd on that interface. Next a subnet is created for
+        the network interface and dhcp server is refreshed to give out ips
+        for that subnet for any device that connects through that interface.
 
         Args:
-            cfg_name: Name of the configuration file to look in.
-            key: Key of the pair.
-            value: Value of the pair.
+            hostapd_config: hostapd_config.HostapdConfig, The configurations
+                            to use when starting up the ap.
+            additional_parameters: A dicitonary of parameters that can sent
+                                   directly into the hostapd config file.  This
+                                   can be used for debugging and or adding one
+                                   off parameters into the config.
 
         Returns:
-            A list of the section ids found.
+            An identifier for the ap being run. This identifier can be used
+            later by this controller to control the ap.
+
+        Raises:
+            Error: When the ap can't be brought up.
         """
-        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
+        # Right now, we hardcode that a frequency maps to a particular
+        # network interface.  This is true of the hardware we're running
+        # against right now, but in general, we'll want to do some
+        # dynamic discovery of interface capabilities.  See b/32582843
+        if hostapd_config.frequency < 5000:
+            interface = _AP_2GHZ_INTERFACE
+            subnet = _AP_2GHZ_SUBNET
+        else:
+            interface = _AP_5GHZ_INTERFACE
+            subnet = _AP_5GHZ_SUBNET
 
-    def _section_option_lookup(self, cfg_name, conditions, *target_keys):
-        """Looks up values of options in sections that match the conditions.
+        # In order to handle dhcp servers on any interface, the initiation of
+        # the dhcp server must be done after the wlan interfaces are figured
+        # out as opposed to being in __init__
+        self._dhcp = dhcp_server.DhcpServer(self.ssh, interface=interface)
 
-        To match a condition, a section needs to have all the key:value pairs
-        specified in conditions.
+        # For multi bssid configurations the mac address
+        # of the wireless interface needs to have enough space to mask out
+        # up to 8 different mac addresses.  The easiest way to do this
+        # is to set the last byte to 0.  While technically this could
+        # cause a duplicate mac address it is unlikely and will allow for
+        # one radio to have up to 8 APs on the interface.  The check ensures
+        # backwards compatibility since if someone has set the bssid on purpose
+        # the bssid will not be changed from what the user set.
+        interface_mac_orig = None
+        if not hostapd_config.bssid:
+            cmd = "ifconfig %s|grep ether|awk -F' ' '{print $2}'" % interface
+            interface_mac_orig = self.ssh.run(cmd)
+            interface_mac = interface_mac_orig.stdout[:-1] + '0'
+            hostapd_config.bssid = interface_mac
+
+        if interface in self._aps:
+            raise ValueError('No WiFi interface available for AP on '
+                             'channel %d' % hostapd_config.channel)
+
+        apd = hostapd.Hostapd(self.ssh, interface)
+        new_instance = _ApInstance(hostapd=apd, subnet=subnet)
+        self._aps[interface] = new_instance
+
+        # Turn off the DHCP server, we're going to change its settings.
+        self._dhcp.stop()
+        # Clear all routes to prevent old routes from interfering.
+        self._route_cmd.clear_routes(net_interface=interface)
+
+        if hostapd_config.bss_lookup:
+            # The dhcp_bss dictionary is created to hold the key/value
+            # pair of the interface name and the ip scope that will be
+            # used for the particular interface.  The a, b, c, d
+            # variables below are the octets for the ip address.  The
+            # third octet is then incremented for each interface that
+            # is requested.  This part is designed to bring up the
+            # hostapd interfaces and not the DHCP servers for each
+            # interface.
+            dhcp_bss = {}
+            counter = 1
+            for bss in hostapd_config.bss_lookup:
+                if not hostapd_config.bss_lookup[bss].bssid:
+                    if interface_mac_orig:
+                        hostapd_config.bss_lookup[
+                            bss].bssid = interface_mac_orig.stdout[:-1] + str(
+                                counter)
+                self._route_cmd.clear_routes(net_interface=str(bss))
+                if interface is _AP_2GHZ_INTERFACE:
+                    starting_ip_range = _AP_2GHZ_SUBNET_STR
+                else:
+                    starting_ip_range = _AP_5GHZ_SUBNET_STR
+                a, b, c, d = starting_ip_range.split('.')
+                dhcp_bss[bss] = dhcp_config.Subnet(
+                    ipaddress.ip_network('%s.%s.%s.%s' % (a, b, str(
+                        int(c) + counter), d)))
+                counter = counter + 1
+
+        apd.start(hostapd_config, additional_parameters=additional_parameters)
+
+        # The DHCP serer requires interfaces to have ips and routes before
+        # the server will come up.
+        interface_ip = ipaddress.ip_interface(
+            '%s/%s' % (subnet.router, subnet.network.netmask))
+        self._ip_cmd.set_ipv4_address(interface, interface_ip)
+        if hostapd_config.bss_lookup:
+            # This loop goes through each interface that was setup for
+            # hostapd and assigns the DHCP scopes that were defined but
+            # not used during the hostapd loop above.  The k and v
+            # variables represent the interface name, k, and dhcp info, v.
+            for k, v in dhcp_bss.items():
+                bss_interface_ip = ipaddress.ip_interface(
+                    '%s/%s' %
+                    (dhcp_bss[k].router, dhcp_bss[k].network.netmask))
+                self._ip_cmd.set_ipv4_address(str(k), bss_interface_ip)
+
+        # Restart the DHCP server with our updated list of subnets.
+        configured_subnets = [x.subnet for x in self._aps.values()]
+        if hostapd_config.bss_lookup:
+            for k, v in dhcp_bss.items():
+                configured_subnets.append(v)
+
+        self._dhcp.start(config=dhcp_config.DhcpConfig(configured_subnets))
+
+        return interface
+
+    def get_bssid_from_ssid(self, ssid):
+        """Gets the BSSID from a provided SSID
 
         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.
+            ssid: An SSID string
+        Returns: The BSSID if on the AP or None is SSID could not be 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
+        cmd = "iw dev %s info|grep addr|awk -F' ' '{print $2}'" % str(ssid)
+        iw_output = self.ssh.run(cmd)
+        if 'command failed: No such device' in iw_output.stderr:
+            return None
+        else:
+            return iw_output.stdout
 
-    def run(self, *cmd):
-        """Executes a terminal command on the AP.
+    def stop_ap(self, identifier):
+        """Stops a running ap on this controller.
 
         Args:
-            cmd: A tuple of command strings.
-
-        Returns:
-            The terminal output of the command.
+            identifier: The identify of the ap that should be taken down.
         """
-        return self._client.sys("exec", *cmd)
 
-    def apply_configs(self, ap_config):
-        """Applies configurations to the access point.
+        if identifier not in self._aps:
+            raise ValueError('Invalid identifer %s given' % identifier)
 
-        Reads the configuration file, adds wifi interfaces, and sets parameters
-        based on the configuration file.
+        instance = self._aps.get(identifier)
 
-        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.
+        instance.hostapd.stop()
+        self._dhcp.stop()
+        self._ip_cmd.clear_ipv4_addresses(identifier)
+
+        # DHCP server needs to refresh in order to tear down the subnet no
+        # longer being used. In the event that all interfaces are torn down
+        # then an exception gets thrown. We need to catch this exception and
+        # check that all interfaces should actually be down.
+        configured_subnets = [x.subnet for x in self._aps.values()]
+        if configured_subnets:
+            self._dhcp.start(dhcp_config.DhcpConfig(configured_subnets))
+
+    def stop_all_aps(self):
+        """Stops all running aps on this device."""
+
+        for ap in self._aps.keys():
+            try:
+                self.stop_ap(ap)
+            except dhcp_server.NoInterfaceError as e:
                 pass
-        self.apply_wifi_changes()
 
-    def _apply_radio_configs(self, radio_id, radio_config):
-        """Applies conigurations on a radio of the AP.
+    def close(self):
+        """Called to take down the entire access point.
 
-        Sets the options in the radio config.
-        Adds wifi-ifaces to this radio based on the configurations.
+        When called will stop all aps running on this host, shutdown the dhcp
+        server, and stop the ssh conneciton.
         """
-        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)
+        if self._aps:
+            self.stop_all_aps()
+            self._dhcp.stop()
 
-    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)
+        self.ssh.close()
diff --git a/acts/framework/acts/controllers/adb.py b/acts/framework/acts/controllers/adb.py
index 2b04d9a..2a1b69d 100644
--- a/acts/framework/acts/controllers/adb.py
+++ b/acts/framework/acts/controllers/adb.py
@@ -16,77 +16,50 @@
 
 from builtins import str
 
+import logging
 import random
+import re
+import shellescape
 import socket
-import subprocess
 import time
 
+from acts.controllers.utils_lib import host_utils
+from acts.controllers.utils_lib.ssh import connection
+from acts.libs.proc import job
+
+DEFAULT_ADB_TIMEOUT = 60
+DEFAULT_ADB_PULL_TIMEOUT = 180
+
+
+def parsing_parcel_output(output):
+    """Parsing the adb output in Parcel format.
+
+    Parsing the adb output in format:
+      Result: Parcel(
+        0x00000000: 00000000 00000014 00390038 00340031 '........8.9.1.4.'
+        0x00000010: 00300038 00300030 00300030 00340032 '8.0.0.0.0.0.2.4.'
+        0x00000020: 00350034 00330035 00320038 00310033 '4.5.5.3.8.2.3.1.'
+        0x00000030: 00000000                            '....            ')
+    """
+    output = ''.join(re.findall(r"'(.*)'", output))
+    return re.sub(r'[.\s]', '', output)
+
+
 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 "
-    "--ei com.googlecode.android_scripting.extra.USE_SERVICE_PORT {} "
-    "com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher" )
+    def __init__(self, cmd, stdout, stderr, ret_code):
+        self.cmd = cmd
+        self.stdout = stdout
+        self.stderr = stderr
+        self.ret_code = ret_code
 
-def get_available_host_port():
-    """Gets a host port number available for adb forward.
+    def __str__(self):
+        return ("Error executing adb cmd '%s'. ret: %d, stdout: %s, stderr: %s"
+                ) % (self.cmd, self.ret_code, self.stdout, self.stderr)
 
-    Returns:
-        An integer representing a port number on the host available for adb
-        forward.
-    """
-    while True:
-        port = random.randint(1024, 9900)
-        if is_port_available(port):
-            return port
 
-def is_port_available(port):
-    """Checks if a given port number is available on the system.
-
-    Args:
-        port: An integer which is the port number to check.
-
-    Returns:
-        True if the port is available; False otherwise.
-    """
-    # Make sure adb is not using this port so we don't accidentally interrupt
-    # ongoing runs by trying to bind to the port.
-    if port in list_occupied_adb_ports():
-        return False
-    s = None
-    try:
-        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        s.bind(('localhost', port))
-        return True
-    except socket.error:
-        return False
-    finally:
-        if s:
-            s.close()
-
-def list_occupied_adb_ports():
-    """Lists all the host ports occupied by adb forward.
-
-    This is useful because adb will silently override the binding if an attempt
-    to bind to a port already used by adb was made, instead of throwing binding
-    error. So one should always check what ports adb is using before trying to
-    bind to a port with adb.
-
-    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
-
-class AdbProxy():
+class AdbProxy(object):
     """Proxy class for ADB.
 
     For syntactic reasons, the '-' in adb commands need to be replaced with
@@ -95,15 +68,43 @@
     >> adb.start_server()
     >> adb.devices() # will return the console output of "adb devices".
     """
-    def __init__(self, serial="", log=None):
-        self.serial = serial
-        if serial:
-            self.adb_str = "adb -s {}".format(serial)
-        else:
-            self.adb_str = "adb"
-        self.log = log
 
-    def _exec_cmd(self, cmd):
+    _SERVER_LOCAL_PORT = None
+
+    def __init__(self, serial="", ssh_connection=None):
+        """Construct an instance of AdbProxy.
+
+        Args:
+            serial: str serial number of Android device from `adb devices`
+            ssh_connection: SshConnection instance if the Android device is
+                            conected to a remote host that we can reach via SSH.
+        """
+        self.serial = serial
+        adb_path = self._exec_cmd("which adb")
+        adb_cmd = [adb_path]
+        if serial:
+            adb_cmd.append("-s %s" % serial)
+        if ssh_connection is not None and not AdbProxy._SERVER_LOCAL_PORT:
+            # Kill all existing adb processes on the remote host (if any)
+            # Note that if there are none, then pkill exits with non-zero status
+            ssh_connection.run("pkill adb", ignore_status=True)
+            # Copy over the adb binary to a temp dir
+            temp_dir = ssh_connection.run("mktemp -d").stdout.strip()
+            ssh_connection.send_file(adb_path, temp_dir)
+            # Start up a new adb server running as root from the copied binary.
+            remote_adb_cmd = "%s/adb %s root" % (temp_dir, "-s %s" % serial
+                                                 if serial else "")
+            ssh_connection.run(remote_adb_cmd)
+            # Proxy a local port to the adb server port
+            local_port = ssh_connection.create_ssh_tunnel(5037)
+            AdbProxy._SERVER_LOCAL_PORT = local_port
+
+        if AdbProxy._SERVER_LOCAL_PORT:
+            adb_cmd.append("-P %d" % local_port)
+        self.adb_str = " ".join(adb_cmd)
+        self._ssh_connection = ssh_connection
+
+    def _exec_cmd(self, cmd, ignore_status=False, timeout=DEFAULT_ADB_TIMEOUT):
         """Executes adb commands in a new shell.
 
         This is specific to executing adb binary because stderr is not a good
@@ -118,62 +119,92 @@
         Raises:
             AdbError is raised if the adb command exit code is not 0.
         """
-        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
-        (out, err) = proc.communicate()
-        ret = proc.returncode
-        total_output = "{} cmd '{}' stdout: {}, stderr: {}, ret: {}".format(
-              self.serial, cmd, out, err, ret)
-        # TODO(angli): Fix this when global logger is done.
-        if self.log:
-            self.log.debug(total_output)
-        if ret == 0:
-            return out
-        else:
-            raise AdbError(total_output)
+        result = job.run(cmd, ignore_status=True, timeout=timeout)
+        ret, out, err = result.exit_status, result.stdout, result.stderr
 
-    def _exec_adb_cmd(self, name, arg_str):
-        return self._exec_cmd(' '.join((self.adb_str, name, arg_str)))
+        logging.debug("cmd: %s, stdout: %s, stderr: %s, ret: %s", cmd, out,
+                      err, ret)
+
+        if ret == 0 or ignore_status:
+            if "Result: Parcel" in out:
+                return parsing_parcel_output(out)
+            else:
+                return out
+        else:
+            raise AdbError(cmd=cmd, stdout=out, stderr=err, ret_code=ret)
+
+    def _exec_adb_cmd(self, name, arg_str, **kwargs):
+        return self._exec_cmd(' '.join((self.adb_str, name, arg_str)),
+                              **kwargs)
 
     def tcp_forward(self, host_port, device_port):
-        """Starts tcp forwarding.
+        """Starts tcp forwarding from localhost to this android device.
 
         Args:
-            host_port: Port number to use on the computer.
+            host_port: Port number to use on localhost
             device_port: Port number to use on the android device.
         """
-        self.forward("tcp:{} tcp:{}".format(host_port, device_port))
+        if self._ssh_connection:
+            # We have to hop through a remote host first.
+            #  1) Find some free port on the remote host's localhost
+            #  2) Setup forwarding between that remote port and the requested
+            #     device port
+            remote_port = self._ssh_connection.find_free_port()
+            self._ssh_connection.create_ssh_tunnel(
+                remote_port, local_port=host_port)
+            host_port = remote_port
+        self.forward("tcp:%d tcp:%d" % (host_port, device_port))
 
-    def start_sl4a(self, port=8080):
-        """Starts sl4a server on the android device.
+    def remove_tcp_forward(self, host_port):
+        """Stop tcp forwarding a port from localhost to this android device.
 
         Args:
-            port: Port number to use on the android device.
+            host_port: Port number to use on localhost
         """
-        MAX_SL4A_WAIT_TIME = 10
-        print(self.shell(SL4A_LAUNCH_CMD.format(port)))
-
-        for _ in range(MAX_SL4A_WAIT_TIME):
-            time.sleep(1)
-            if self.is_sl4a_running():
+        if self._ssh_connection:
+            remote_port = self._ssh_connection.close_ssh_tunnel(host_port)
+            if remote_port is None:
+                logging.warning("Cannot close unknown forwarded tcp port: %d",
+                                host_port)
                 return
-        raise AdbError(
-                "com.googlecode.android_scripting process never started.")
+            # The actual port we need to disable via adb is on the remote host.
+            host_port = remote_port
+        self.forward("--remove tcp:%d" % host_port)
 
-    def is_sl4a_running(self):
-        """Checks if the sl4a app is running on an android device.
+    def getprop(self, prop_name):
+        """Get a property of the device.
+
+        This is a convenience wrapper for "adb shell getprop xxx".
+
+        Args:
+            prop_name: A string that is the name of the property to get.
 
         Returns:
-            True if the sl4a app is running, False otherwise.
+            A string that is the value of the property, or None if the property
+            doesn't exist.
         """
-        #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
+        return self.shell("getprop %s" % prop_name)
+
+    # TODO: This should be abstracted out into an object like the other shell
+    # command.
+    def shell(self, command, ignore_status=False, timeout=DEFAULT_ADB_TIMEOUT):
+        return self._exec_adb_cmd(
+            'shell',
+            shellescape.quote(command),
+            ignore_status=ignore_status,
+            timeout=timeout)
+
+    def pull(self,
+             command,
+             ignore_status=False,
+             timeout=DEFAULT_ADB_PULL_TIMEOUT):
+        return self._exec_adb_cmd(
+            'pull', command, ignore_status=ignore_status, timeout=timeout)
 
     def __getattr__(self, name):
-        def adb_call(*args):
+        def adb_call(*args, **kwargs):
             clean_name = name.replace('_', '-')
             arg_str = ' '.join(str(elem) for elem in args)
-            return self._exec_adb_cmd(clean_name, arg_str)
+            return self._exec_adb_cmd(clean_name, arg_str, **kwargs)
+
         return adb_call
diff --git a/acts/framework/acts/controllers/android.py b/acts/framework/acts/controllers/android.py
deleted file mode 100644
index defa3a4..0000000
--- a/acts/framework/acts/controllers/android.py
+++ /dev/null
@@ -1,127 +0,0 @@
-#/usr/bin/env 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.
-"""
-
-from builtins import str
-
-import json
-import os
-import socket
-import threading
-import time
-
-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()
-
-    _SOCKET_CONNECT_TIMEOUT = 60
-
-    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
-        timeout_time = time.time() + self._SOCKET_CONNECT_TIMEOUT
-        while True:
-            try:
-                self.conn = socket.create_connection(
-                        (addr, port), max(1,timeout_time - time.time()))
-                self.conn.settimeout(timeout)
-                break
-            except (TimeoutError, socket.timeout):
-                print("Failed to create socket connection!")
-                raise
-            except (socket.error, IOError):
-                # TODO: optimize to only forgive some errors here
-                # error values are OS-specific so this will require
-                # additional tuning to fail faster
-                if time.time() + 1 >= timeout_time:
-                    print("Failed to create socket connection!")
-                    raise
-                time.sleep(1)
-
-        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(
-                "RPC call %s failed with error %s" % (method, 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
old mode 100644
new mode 100755
index cdb1f3b..b5f00da
--- a/acts/framework/acts/controllers/android_device.py
+++ b/acts/framework/acts/controllers/android_device.py
@@ -16,18 +16,25 @@
 
 from builtins import str
 from builtins import open
+from datetime import datetime
 
+import logging
 import os
+import re
+import socket
 import time
-import traceback
 
 from acts import logger as acts_logger
 from acts import signals
+from acts import tracelogger
 from acts import utils
 from acts.controllers import adb
-from acts.controllers import android
 from acts.controllers import event_dispatcher
 from acts.controllers import fastboot
+from acts.controllers import sl4a_client
+from acts.controllers.utils_lib import host_utils
+from acts.controllers.utils_lib.ssh import connection
+from acts.controllers.utils_lib.ssh import settings
 
 ACTS_CONTROLLER_CONFIG_NAME = "AndroidDevice"
 ACTS_CONTROLLER_REFERENCE_NAME = "android_devices"
@@ -37,31 +44,49 @@
 ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param"
 ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
 ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!"
+CRASH_REPORT_PATHS = ("/data/tombstones/", "/data/ramdumps/", "/data/ramdump/")
+CRASH_REPORT_SKIPS = ("RAMDUMP_RESERVED", "RAMDUMP_STATUS")
+BUG_REPORT_TIMEOUT = 1200
+PULL_TIMEOUT = 300
+PORT_RETRY_COUNT = 3
+IPERF_TIMEOUT = 60
+SL4A_APK_NAME = "com.googlecode.android_scripting"
+
 
 class AndroidDeviceError(signals.ControllerError):
     pass
 
+
 class DoesNotExistError(AndroidDeviceError):
     """Raised when something that does not exist is referenced.
     """
 
-def create(configs, logger):
+
+def create(configs):
+    """Creates AndroidDevice controller objects.
+
+    Args:
+        configs: A list of dicts, each representing a configuration for an
+                 Android device.
+
+    Returns:
+        A list of AndroidDevice objects.
+    """
     if not configs:
         raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG)
     elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN:
-        ads = get_all_instances(logger=logger)
+        ads = get_all_instances()
     elif not isinstance(configs, list):
         raise AndroidDeviceError(ANDROID_DEVICE_NOT_LIST_CONFIG_MSG)
     elif isinstance(configs[0], str):
         # Configs is a list of serials.
-        ads = get_instances(configs, logger)
+        ads = get_instances(configs)
     else:
         # Configs is a list of dicts.
-        ads = get_instances_with_configs(configs, logger)
-    connected_ads = list_adb_devices()
+        ads = get_instances_with_configs(configs)
 
     for ad in ads:
-        if ad.serial not in connected_ads:
+        if not ad.is_connected():
             raise DoesNotExistError(("Android device %s is specified in config"
                                      " but is not attached.") % ad.serial)
     _start_services_on_ads(ads)
@@ -110,13 +135,20 @@
     running_ads = []
     for ad in ads:
         running_ads.append(ad)
+        if not ad.is_sl4a_installed():
+            ad.log.info("sl4a.apk is not installed")
+            if not ad.skip_sl4a:
+                ad.log.exception("The required sl4a.apk is not installed")
+                destroy(running_ads)
+                raise
         try:
-            ad.start_services(skip_sl4a=getattr(ad, "skip_sl4a", False))
+            ad.start_services(skip_sl4a=ad.skip_sl4a)
         except:
             ad.log.exception("Failed to start some services, abort!")
             destroy(running_ads)
             raise
 
+
 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.
@@ -128,13 +160,8 @@
     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
+    return re.findall(r"(\S+)\t%s" % key, device_list_str)
+
 
 def list_adb_devices():
     """List all android devices connected to the computer that are detected by
@@ -146,6 +173,7 @@
     out = adb.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.
@@ -156,22 +184,23 @@
     out = fastboot.FastbootProxy().devices()
     return _parse_device_list(out, "fastboot")
 
-def get_instances(serials, logger=None):
+
+def get_instances(serials):
     """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))
+        results.append(AndroidDevice(s))
     return results
 
-def get_instances_with_configs(configs, logger=None):
+
+def get_instances_with_configs(configs):
     """Create AndroidDevice instances from a list of json configs.
 
     Each config should have the required key-value pair "serial".
@@ -179,7 +208,6 @@
     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.
@@ -189,19 +217,25 @@
         try:
             serial = c.pop("serial")
         except KeyError:
-            raise AndroidDeviceError(('Required value "serial" is missing in '
-                'AndroidDevice config %s.') % c)
-        ad = AndroidDevice(serial, logger=logger)
+            raise AndroidDeviceError(
+                "Required value 'serial' is missing in AndroidDevice config %s."
+                % c)
+        ssh_config = c.pop("ssh_config", None)
+        ssh_connection = None
+        if ssh_config is not None:
+            ssh_settings = settings.from_config(ssh_config)
+            ssh_connection = connection.SshConnection(ssh_settings)
+        ad = AndroidDevice(serial, ssh_connection=ssh_connection)
         ad.load_config(c)
         results.append(ad)
     return results
 
-def get_all_instances(include_fastboot=False, logger=None):
+
+def get_all_instances(include_fastboot=False):
     """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
@@ -209,8 +243,9 @@
     """
     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)
+        return get_instances(serial_list)
+    return get_instances(list_adb_devices())
+
 
 def filter_devices(ads, func):
     """Finds the AndroidDevice instances from a list that match certain
@@ -230,6 +265,7 @@
             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.
@@ -249,6 +285,7 @@
         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):
@@ -256,16 +293,19 @@
             elif getattr(ad, k) != v:
                 return False
         return True
+
     filtered = filter_devices(ads, _get_device_filter)
     if not filtered:
-        raise AndroidDeviceError(("Could not find a target device that matches"
-                                  " condition: %s.") % kwargs)
+        raise AndroidDeviceError(
+            "Could not find a target device that matches condition: %s." %
+            kwargs)
     elif len(filtered) == 1:
         return filtered[0]
     else:
         serials = [ad.serial for ad in filtered]
         raise AndroidDeviceError("More than one device matched: %s" % serials)
 
+
 def take_bug_reports(ads, test_name, begin_time):
     """Takes bug reports on a list of android devices.
 
@@ -280,11 +320,14 @@
         begin_time: Logline format timestamp taken when the test started.
     """
     begin_time = acts_logger.normalize_log_line_timestamp(begin_time)
+
     def take_br(test_name, begin_time, ad):
         ad.take_bug_report(test_name, begin_time)
+
     args = [(test_name, begin_time, ad) for ad in ads]
     utils.concurrent_exec(take_br, args)
 
+
 class AndroidDevice:
     """Class representing an android device.
 
@@ -299,9 +342,10 @@
                 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.
         log_path: A string that is the path where all logs collected on this
                   android device should be stored.
+        log: A logger adapted from root logger with added token specific to an
+             AndroidDevice instance.
         adb_logcat_process: A process that collects the adb logcat.
         adb_logcat_file_path: A string that's the full path to the adb logcat
                               file collected, if any.
@@ -310,22 +354,32 @@
                   via fastboot.
     """
 
-    def __init__(self, serial="", host_port=None, device_port=8080,
-                 logger=None):
+    def __init__(self,
+                 serial="",
+                 host_port=None,
+                 device_port=sl4a_client.DEFAULT_DEVICE_SIDE_PORT,
+                 ssh_connection=None):
         self.serial = serial
         self.h_port = host_port
         self.d_port = device_port
-        self.log = acts_logger.LoggerProxy(logger)
-        lp = self.log.log_path
-        self.log_path = os.path.join(lp, "AndroidDevice%s" % serial)
+        # logging.log_path only exists when this is used in an ACTS test run.
+        log_path_base = getattr(logging, "log_path", "/tmp/logs")
+        self.log_path = os.path.join(log_path_base, "AndroidDevice%s" % serial)
+        self.log = tracelogger.TraceLogger(
+            AndroidDeviceLoggerAdapter(logging.getLogger(),
+                                       {"serial": self.serial}))
         self._droid_sessions = {}
         self._event_dispatchers = {}
         self.adb_logcat_process = None
         self.adb_logcat_file_path = None
-        self.adb = adb.AdbProxy(serial)
-        self.fastboot = fastboot.FastbootProxy(serial)
+        self.adb = adb.AdbProxy(serial, ssh_connection=ssh_connection)
+        self.fastboot = fastboot.FastbootProxy(
+            serial, ssh_connection=ssh_connection)
         if not self.is_bootloader:
             self.root_adb()
+        self._ssh_connection = ssh_connection
+        self.skip_sl4a = False
+        self.crash_report = None
 
     def clean_up(self):
         """Cleans up the AndroidDevice object and releases any resources it
@@ -333,7 +387,10 @@
         """
         self.stop_services()
         if self.h_port:
-            self.adb.forward("--remove tcp:%d" % self.h_port)
+            self.adb.remove_tcp_forward(self.h_port)
+            self.h_port = None
+        if self._ssh_connection:
+            self._ssh_connection.close()
 
     # TODO(angli): This function shall be refactored to accommodate all services
     # and not have hard coded switch for SL4A when b/29157104 is done.
@@ -364,9 +421,15 @@
 
         Stop adb logcat and terminate sl4a sessions if exist.
         """
-        if self.adb_logcat_process:
+        if self.is_adb_logcat_on:
             self.stop_adb_logcat()
         self.terminate_all_sessions()
+        self.stop_sl4a()
+
+    def is_connected(self):
+        out = self.adb.devices()
+        devices = _parse_device_list(out, "device")
+        return self.serial in devices
 
     @property
     def build_info(self):
@@ -380,12 +443,12 @@
             device is in bootloader mode.
         """
         if self.is_bootloader:
+            self.log.error("Device is in fastboot mode, could not get build "
+                           "info.")
             return
         info = {}
-        info["build_id"] = self.adb.shell("getprop ro.build.id").decode(
-            "utf-8").strip()
-        info["build_type"] = self.adb.shell("getprop ro.build.type").decode(
-            "utf-8").strip()
+        info["build_id"] = self.adb.getprop("ro.build.id")
+        info["build_type"] = self.adb.getprop("ro.build.type")
         return info
 
     @property
@@ -399,11 +462,11 @@
         """True if adb is running as root for this device.
         """
         try:
-            return "0" == self.adb.shell("id -u").decode("utf-8").strip()
+            return "0" == self.adb.shell("id -u")
         except adb.AdbError:
             # Wait a bit and retry to work around adb flakiness for this cmd.
             time.sleep(0.2)
-            return "0" == self.adb.shell("id -u").decode("utf-8").strip()
+            return "0" == self.adb.shell("id -u")
 
     @property
     def model(self):
@@ -414,20 +477,17 @@
             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)
+            lines = out.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()
+        model = self.adb.getprop("ro.build.product").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
+            return self.adb.getprop("ro.product.name").lower()
 
     @property
     def droid(self):
@@ -481,7 +541,20 @@
         """Whether there is an ongoing adb logcat collection.
         """
         if self.adb_logcat_process:
-            return True
+            try:
+                ret = self.adb_logcat_process.poll()
+            except AttributeError as e:
+                #This happens if the logcat process is not a Popen object as
+                #would be expected if connected to a real android device.  This
+                #solves the mock test problem.
+                ret = None
+            if ret is None:
+                return True
+            else:
+                if self.droid:
+                    self.droid.logI('Logcat died')
+                self.log.error('Logcat died on %s' % self.adb_logcat_file_path)
+                return False
         return False
 
     def load_config(self, config):
@@ -495,9 +568,11 @@
             an existing attribute.
         """
         for k, v in config.items():
-            if hasattr(self, k):
-                raise AndroidDeviceError(("Attempting to set existing "
-                    "attribute %s on %s") % (k, self.serial))
+            # skip_sl4a value can be reset from config file
+            if hasattr(self, k) and k != "skip_sl4a":
+                raise AndroidDeviceError(
+                    "Attempting to set existing attribute %s on %s" %
+                    (k, self.serial))
             setattr(self, k, v)
 
     def root_adb(self):
@@ -535,14 +610,35 @@
             >>> ad = AndroidDevice()
             >>> droid, ed = ad.get_droid()
         """
-        if not self.h_port or not adb.is_port_available(self.h_port):
-            self.h_port = adb.get_available_host_port()
-        self.adb.tcp_forward(self.h_port, self.d_port)
+        forward_success = False
+        last_error = None
+        for _ in range(PORT_RETRY_COUNT):
+            if not self.h_port or not host_utils.is_port_available(
+                    self.h_port):
+                self.h_port = host_utils.get_available_host_port()
+            try:
+                self.adb.tcp_forward(self.h_port, self.d_port)
+                forward_success = True
+                break
+            except adb.AdbError as e:
+                last_error = e
+                pass
+        if not forward_success:
+            raise last_error
+
+        # TODO(bpeake) b/33470152 Fixup SL4A connection code
+        if self.is_sl4a_running():
+            self.stop_sl4a()
+            time.sleep(30)
+        self.start_sl4a()
         try:
             droid = self.start_new_session()
         except:
-            self.adb.start_sl4a()
+            self.stop_sl4a()
+            time.sleep(30)
+            self.start_sl4a()
             droid = self.start_new_session()
+
         if handle_event:
             ed = self.get_dispatcher(droid)
             return droid, ed
@@ -584,9 +680,9 @@
                 period.
         """
         if not self.adb_logcat_file_path:
-            raise AndroidDeviceError(("Attempting to cat adb log when none has"
-                                      " been collected on Android device %s."
-                                      ) % self.serial)
+            raise AndroidDeviceError(
+                ("Attempting to cat adb log when none has"
+                 " been collected on Android device %s.") % self.serial)
         end_time = acts_logger.get_log_line_timestamp()
         self.log.debug("Extracting adb log from logcat.")
         adb_excerpt_path = os.path.join(self.log_path, "AdbLogExcerpts")
@@ -614,7 +710,7 @@
                     if not acts_logger.is_valid_logline_timestamp(line_time):
                         continue
                     if self._is_timestamp_in_range(line_time, begin_time,
-                        end_time):
+                                                   end_time):
                         in_range = True
                         if not line.endswith('\n'):
                             line += '\n'
@@ -623,23 +719,38 @@
                         if in_range:
                             break
 
-    def start_adb_logcat(self):
+    def start_adb_logcat(self, cont_logcat_file=False):
         """Starts a standing adb logcat collection in separate subprocesses and
         save the logcat in a file.
+
+        Args:
+            cont_logcat_file: Specifies whether to continue the previous logcat
+                              file.  This allows for start_adb_logcat to act
+                              as a restart logcat function if it is noticed
+                              logcat is no longer running.
         """
         if self.is_adb_logcat_on:
             raise AndroidDeviceError(("Android device {} already has an adb "
-                                     "logcat thread going on. Cannot start "
-                                     "another one.").format(self.serial))
-        # Disable adb log spam filter.
+                                      "logcat thread going on. Cannot start "
+                                      "another one.").format(self.serial))
+        # Disable adb log spam filter. Have to stop and clear settings first
+        # because 'start' doesn't support --clear option before Android N.
+        self.adb.shell("logpersist.stop --clear")
         self.adb.shell("logpersist.start")
-        f_name = "adblog,{},{}.txt".format(self.model, self.serial)
-        utils.create_dir(self.log_path)
-        logcat_file_path = os.path.join(self.log_path, f_name)
+        if cont_logcat_file:
+            if self.droid:
+                self.droid.logI('Restarting logcat')
+            self.log.warning(
+                'Restarting logcat on file %s' % self.adb_logcat_file_path)
+            logcat_file_path = self.adb_logcat_file_path
+        else:
+            f_name = "adblog,{},{}.txt".format(self.model, self.serial)
+            utils.create_dir(self.log_path)
+            logcat_file_path = os.path.join(self.log_path, f_name)
         try:
             extra_params = self.adb_logcat_param
         except AttributeError:
-            extra_params = ""
+            extra_params = "-b all"
         cmd = "adb -s {} logcat -v threadtime {} >> {}".format(
             self.serial, extra_params, logcat_file_path)
         self.adb_logcat_process = utils.start_standing_subprocess(cmd)
@@ -649,12 +760,86 @@
         """Stops the adb logcat collection subprocess.
         """
         if not self.is_adb_logcat_on:
-            raise AndroidDeviceError(("Android device {} does not have an "
-                                      "ongoing adb logcat collection."
-                                      ).format(self.serial))
+            raise AndroidDeviceError(
+                "Android device %s does not have an ongoing adb logcat collection."
+                % self.serial)
         utils.stop_standing_subprocess(self.adb_logcat_process)
         self.adb_logcat_process = None
 
+    def is_apk_installed(self, package_name):
+        """Check if the given apk is already installed.
+
+        Args:
+        package_name: Name of the package, e.g., com.android.phone.
+
+        Returns:
+        True if package is installed. False otherwise.
+        """
+        try:
+            out = self.adb.shell(
+                'pm list packages | grep -w "package:%s"' % package_name,
+                ignore_status=False)
+            if package_name in out:
+                self.log.debug("apk %s is installed", package_name)
+                return True
+            else:
+                self.log.debug("apk %s is not installed, query returns %s",
+                               package_name, out)
+                return False
+        except:
+            return False
+
+    def is_sl4a_installed(self):
+        return self.is_apk_installed(SL4A_APK_NAME)
+
+    def is_apk_running(self, package_name):
+        """Check if the given apk is running.
+
+        Args:
+        package_name: Name of the package, e.g., com.android.phone.
+
+        Returns:
+        True if package is installed. False otherwise.
+        """
+        for cmd in ("ps -A", "ps"):
+            try:
+                out = self.adb.shell(
+                    '%s | grep "S %s"' % (cmd, package_name),
+                    ignore_status=True)
+                if package_name in out:
+                    self.log.info("apk %s is running", package_name)
+                    return True
+            except Exception as e:
+                self.log.warn("Device fails to check is %s running by %s "
+                              "Exception %s", package_name, cmd, e)
+                continue
+        self.log.debug("apk %s is not running", package_name)
+        return False
+
+    def is_sl4a_running(self):
+        return self.is_apk_running(SL4A_APK_NAME)
+
+    def force_stop_apk(self, package_name):
+        """Force stop the given apk.
+
+        Args:
+        package_name: Name of the package, e.g., com.android.phone.
+
+        Returns:
+        True if package is installed. False otherwise.
+        """
+        try:
+            self.adb.shell(
+                'am force-stop %s' % package_name, ignore_status=True)
+        except Exception as e:
+            self.log.warn("Fail to stop package %s: %s", package_name, e)
+
+    def stop_sl4a(self):
+        return self.force_stop_apk(SL4A_APK_NAME)
+
+    def start_sl4a(self):
+        sl4a_client.start_sl4a(self.adb)
+
     def take_bug_report(self, test_name, begin_time):
         """Takes a bug report on the device and stores it in a file.
 
@@ -664,7 +849,11 @@
         """
         new_br = True
         try:
-            self.adb.shell("bugreportz -v")
+            stdout = self.adb.shell("bugreportz -v")
+            # This check is necessary for builds before N, where adb shell's ret
+            # code and stderr are not propagated properly.
+            if "not found" in stdout:
+                new_br = False
         except adb.AdbError:
             new_br = False
         br_path = os.path.join(self.log_path, "BugReports")
@@ -674,22 +863,55 @@
             base_name = base_name.replace(".txt", ".zip")
         test_name_len = utils.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(' ', '\ '))
+        full_out_path = os.path.join(br_path, out_name.replace(' ', '_'))
         # in case device restarted, wait for adb interface to return
         self.wait_for_boot_completion()
         self.log.info("Taking bugreport for %s.", test_name)
         if new_br:
-            out = self.adb.shell("bugreportz").decode("utf-8")
+            out = self.adb.shell("bugreportz", timeout=BUG_REPORT_TIMEOUT)
             if not out.startswith("OK"):
-                raise AndroidDeviceError("Failed to take bugreport on %s" %
-                                         self.serial)
+                raise AndroidDeviceError("Failed to take bugreport on %s: %s" %
+                                         (self.serial, out))
             br_out_path = out.split(':')[1].strip()
             self.adb.pull("%s %s" % (br_out_path, full_out_path))
         else:
-            self.adb.bugreport(" > {}".format(full_out_path))
+            self.adb.bugreport(
+                " > {}".format(full_out_path), timeout=BUG_REPORT_TIMEOUT)
         self.log.info("Bugreport for %s taken at %s.", test_name,
                       full_out_path)
 
+    def get_file_names(self, directory):
+        """Get files names with provided directory."""
+        file_names = []
+        out = self.adb.shell("ls %s" % directory, ignore_status=True)
+        if out and "No such" not in out:
+            return out.split('\n')
+        else:
+            return []
+
+    def pull_files(self, files, remote_path=None):
+        """Pull files from devies."""
+        if not remote_path:
+            remote_path = self.log_path
+        for file_name in files:
+            self.adb.pull(
+                "%s %s" % (file_name, remote_path), timeout=PULL_TIMEOUT)
+
+    def check_crash_report(self, log_crash_report=False):
+        """check crash report on the device."""
+        crash_reports = []
+        for crash_path in CRASH_REPORT_PATHS:
+            for report in self.get_file_names(crash_path):
+                if report in CRASH_REPORT_SKIPS:
+                    continue
+                crash_reports.append(os.path.join(crash_path, report))
+        if log_crash_report:
+            crash_log_path = os.path.join(self.log_path, "CrashReports",
+                                          time.strftime("%m-%d-%Y-%H-%M-%S"))
+            utils.create_dir(crash_log_path)
+            self.pull_files(crash_reports, crash_log_path)
+        return crash_reports
+
     def start_new_session(self):
         """Start a new session in sl4a.
 
@@ -700,13 +922,14 @@
                 device.
 
         Raises:
-            SL4AException: Something is wrong with sl4a and it returned an
+            Sl4aException: Something is wrong with sl4a and it returned an
             existing uid to a new session.
         """
-        droid = android.Android(port=self.h_port)
+        droid = sl4a_client.Sl4aClient(port=self.h_port)
+        droid.open()
         if droid.uid in self._droid_sessions:
-            raise android.SL4AException(("SL4A returned an existing uid for a "
-                "new session. Abort."))
+            raise sl4a_client.Sl4aException(
+                "SL4A returned an existing uid for a new session. Abort.")
         self._droid_sessions[droid.uid] = [droid]
         return droid
 
@@ -726,8 +949,8 @@
         """
         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)
+        droid = sl4a_client.Sl4aClient(port=self.h_port, uid=session_id)
+        droid.open(cmd=sl4a_client.Sl4aCommand.CONTINUE)
         return droid
 
     def terminate_session(self, session_id):
@@ -741,11 +964,11 @@
         """
         if self._droid_sessions and (session_id in self._droid_sessions):
             for droid in self._droid_sessions[session_id]:
-                droid.closeSl4aSession()
+                droid.closeSl4aSession(timeout=180)
                 droid.close()
             del self._droid_sessions[session_id]
         ed_key = self.serial + str(session_id)
-        if ed_key in self._event_dispatchers:
+        if self._event_dispatchers and ed_key in self._event_dispatchers:
             self._event_dispatchers[ed_key].clean_up()
             del self._event_dispatchers[ed_key]
 
@@ -760,14 +983,16 @@
                 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())
+                    self.log.exception("Failed to terminate session %d.",
+                                       session_id)
             if self.h_port:
-                self.adb.forward("--remove tcp:%d" % self.h_port)
+                self.adb.remove_tcp_forward(self.h_port)
                 self.h_port = None
 
-    def run_iperf_client(self, server_host, extra_args=""):
+    def run_iperf_client(self,
+                         server_host,
+                         extra_args="",
+                         timeout=IPERF_TIMEOUT):
         """Start iperf client on the device.
 
         Return status as true if iperf client start successfully.
@@ -782,24 +1007,43 @@
             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')
+        out = self.adb.shell(
+            "iperf3 -c {} {}".format(server_host, extra_args), timeout=timeout)
+        clean_out = out.split('\n')
         if "error" in clean_out[0].lower():
             return False, clean_out
         return True, clean_out
 
-    @utils.timeout(15 * 60)
+    def run_iperf_server(self, extra_args=""):
+        """Start iperf server on the device
+
+        Return status as true if iperf server started successfully.
+
+        Args:
+            extra_args: A string representing extra arguments for iperf server.
+
+        Returns:
+            status: true if iperf server started successfully.
+            results: results have output of command
+        """
+        out = self.adb.shell("iperf3 -s {}".format(extra_args))
+        clean_out = out.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 Android framework to boot back up and ready to launch
-        apps.
+        """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED.
 
         This function times out after 15 minutes.
         """
-        self.adb.wait_for_device()
-        while True:
+        timeout_start = time.time()
+        timeout = 15 * 60
+
+        self.adb.wait_for_device(timeout=180)
+        while time.time() < timeout_start + timeout:
             try:
-                out = self.adb.shell("getprop sys.boot_completed")
-                completed = out.decode('utf-8').strip()
+                completed = self.adb.getprop("sys.boot_completed")
                 if completed == '1':
                     return
             except adb.AdbError:
@@ -807,6 +1051,8 @@
                 # process, which is normal. Ignoring these errors.
                 pass
             time.sleep(5)
+        raise AndroidDeviceError(
+            "Device %s booting process timed out." % self.serial)
 
     def reboot(self):
         """Reboots the device.
@@ -844,3 +1090,85 @@
         if has_adb_log:
             self.start_adb_logcat()
         return droid, ed
+
+    def search_logcat(self, matching_string):
+        """Search logcat message with given string.
+
+        Args:
+            matching_string: matching_string to search.
+
+        Returns:
+            A list of dictionaries with full log message, time stamp string
+            and time object. For example:
+            [{"log_message": "05-03 17:39:29.898   968  1001 D"
+                              "ActivityManager: Sending BOOT_COMPLETE user #0",
+              "time_stamp": "2017-05-03 17:39:29.898",
+              "datetime_obj": datetime object}]
+        """
+        out = self.adb.logcat(
+            '-b all -d | grep "%s"' % matching_string, ignore_status=True)
+        if not out: return []
+        result = []
+        logs = re.findall(r'(\S+\s\S+)(.*%s.*)' % re.escape(matching_string),
+                          out)
+        for log in logs:
+            time_stamp = "%s-%s" % (datetime.now().year, log[0])
+            time_obj = datetime.strptime(time_stamp, "%Y-%m-%d %H:%M:%S.%f")
+            result.append({
+                "log_message": "".join(log),
+                "time_stamp": time_stamp,
+                "datetime_obj": time_obj
+            })
+        return result
+
+    def get_ipv4_address(self, interface='wlan0', timeout=5):
+        for timer in range(0, timeout):
+            try:
+                ip_string = self.adb.shell('ifconfig %s|grep inet' % interface)
+                break
+            except adb.AdbError as e:
+                if timer + 1 == timeout:
+                    self.log.warning(
+                        'Unable to find IP address for %s.' % interface)
+                    return None
+                else:
+                    time.sleep(1)
+        result = re.search('addr:(.*) Bcast', ip_string)
+        if result != None:
+            ip_address = result.group(1)
+            try:
+                socket.inet_aton(ip_address)
+                return ip_address
+            except socket.error:
+                return None
+        else:
+            return None
+
+    def get_ipv4_gateway(self, timeout=5):
+        for timer in range(0, timeout):
+            try:
+                gateway_string = self.adb.shell(
+                    'dumpsys wifi | grep mDhcpResults')
+                break
+            except adb.AdbError as e:
+                if timer + 1 == timeout:
+                    self.log.warning('Unable to find gateway')
+                    return None
+                else:
+                    time.sleep(1)
+        result = re.search('Gateway (.*) DNS servers', gateway_string)
+        if result != None:
+            ipv4_gateway = result.group(1)
+            try:
+                socket.inet_aton(ipv4_gateway)
+                return ipv4_gateway
+            except socket.error:
+                return None
+        else:
+            return None
+
+
+class AndroidDeviceLoggerAdapter(logging.LoggerAdapter):
+    def process(self, msg, kwargs):
+        msg = "[AndroidDevice|%s] %s" % (self.extra["serial"], msg)
+        return (msg, kwargs)
diff --git a/acts/framework/acts/controllers/anritsu_lib/__init__.py b/acts/framework/acts/controllers/anritsu_lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/anritsu_lib/__init__.py
diff --git a/acts/framework/acts/controllers/anritsu_lib/_anritsu_utils.py b/acts/framework/acts/controllers/anritsu_lib/_anritsu_utils.py
new file mode 100644
index 0000000..8e5445a
--- /dev/null
+++ b/acts/framework/acts/controllers/anritsu_lib/_anritsu_utils.py
@@ -0,0 +1,231 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""
+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(object):
+    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/anritsu_lib/cell_configurations.py b/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py
new file mode 100644
index 0000000..f82c6bd
--- /dev/null
+++ b/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py
@@ -0,0 +1,327 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Sanity tests for voice tests in telephony
+"""
+from acts.controllers.anritsu_lib.md8475a import BtsBandwidth
+from acts.test_utils.tel.anritsu_utils import GSM_BAND_PCS1900
+from acts.test_utils.tel.anritsu_utils import GSM_BAND_GSM850
+from acts.test_utils.tel.anritsu_utils import LTE_BAND_2
+from acts.test_utils.tel.anritsu_utils import LTE_BAND_4
+from acts.test_utils.tel.anritsu_utils import LTE_BAND_12
+from acts.test_utils.tel.anritsu_utils import WCDMA_BAND_1
+from acts.test_utils.tel.anritsu_utils import WCDMA_BAND_2
+
+# 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/framework/acts/controllers/anritsu_lib/md8475a.py b/acts/framework/acts/controllers/anritsu_lib/md8475a.py
new file mode 100644
index 0000000..584cf53
--- /dev/null
+++ b/acts/framework/acts/controllers/anritsu_lib/md8475a.py
@@ -0,0 +1,3651 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Controller interface for Anritsu Signalling Tester MD8475A.
+"""
+
+import time
+import socket
+from enum import Enum
+from enum import IntEnum
+
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuUtils
+from acts.controllers.anritsu_lib._anritsu_utils import NO_ERROR
+from acts.controllers.anritsu_lib._anritsu_utils import OPERATION_COMPLETE
+
+TERMINATOR = "\0"
+# The following wait times (except COMMUNICATION_STATE_WAIT_TIME) are actually
+# the times for socket to time out. Increasing them is to make sure there is
+# enough time for MD8475A operation to be completed in some cases.
+# It won't increase test execution time.
+SMARTSTUDIO_LAUNCH_WAIT_TIME = 300  # was 90
+SMARTSTUDIO_SIMULATION_START_WAIT_TIME = 300  # was 120
+REGISTRATION_STATE_WAIT_TIME = 240
+LOAD_SIMULATION_PARAM_FILE_WAIT_TIME = 30
+COMMUNICATION_STATE_WAIT_TIME = 240
+ANRITSU_SOCKET_BUFFER_SIZE = 8192
+COMMAND_COMPLETE_WAIT_TIME = 180  # was 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
+
+
+def create(configs, logger):
+    objs = []
+    for c in configs:
+        ip_address = c["ip_address"]
+        objs.append(MD8475A(ip_address, logger))
+    return objs
+
+
+def destroy(objs):
+    return
+
+
+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 ImsCscfStatus(Enum):
+    """ MD8475A ims cscf status for UE
+    """
+    OFF = "OFF"
+    SIPIDLE = "SIPIDLE"
+    CONNECTED = "CONNECTED"
+    CALLING = "CALLING"
+    RINGING = "RINGING"
+    UNKNOWN = "UNKNOWN"
+
+
+class ImsCscfCall(Enum):
+    """ MD8475A ims cscf call action
+    """
+    MAKE = "MAKE"
+    END = "END"
+    MAKEVIDEO = "MAKEVIDEO"
+    MAKE2ND = "MAKE2ND"
+    END2ND = "END2ND"
+    ANSWER = "ANSWER"
+    HOLD = "HOLD"
+    RESUME = "RESUME"
+
+
+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 Switch(Enum):
+    ''' Values for ENABLE or DISABLE '''
+    ENABLE = "ENABLE"
+    DISABLE = "DISABLE"
+
+
+class MD8475A(object):
+    """Class to communicate with Anritsu MD8475A Signalling Tester.
+       This uses GPIB command to interface with Anritsu MD8475A """
+
+    def __init__(self, ip_address, log_handle, wlan=False):
+        self._error_reporting = True
+        self._ipaddr = ip_address
+        self.log = log_handle
+        self._wlan = wlan
+
+        # 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=120)
+            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 get_IMS(self, vnid):
+        """ Returns the IMS Module Object with VNID
+
+        Args:
+            vnid: Virtual Network ID
+
+        Returns:
+            Anritsu IMS VNID Module Object
+        """
+        return _IMS_Services(self, vnid)
+
+    def get_ims_cscf_status(self, virtual_network_id):
+        """ Get the IMS CSCF Status of virtual network
+
+        Args:
+            virtual_network_id: virtual network id
+
+        Returns:
+            IMS CSCF status
+        """
+        cmd = "IMSCSCFSTAT? {}".format(virtual_network_id)
+        return self.send_query(cmd)
+
+    def ims_cscf_call_action(self, virtual_network_id, action):
+        """ IMS CSCF Call action
+
+        Args:
+            virtual_network_id: virtual network id
+            action: action to make
+
+        Returns:
+            None
+        """
+        cmd = "IMSCSCFCALL {},{}".format(virtual_network_id, action)
+        self.send_command(cmd)
+
+    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)
+            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", SMARTSTUDIO_LAUNCH_WAIT_TIME)
+            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", SMARTSTUDIO_SIMULATION_START_WAIT_TIME)
+
+        self.log.info("Waiting for CALLSTAT=POWEROFF")
+        callstat = self.send_query("CALLSTAT? BTS1").split(",")
+        while callstat[0] != "POWEROFF":
+            time.sleep(sleep_interval)
+            waiting_time += sleep_interval
+            if waiting_time <= time_to_wait:
+                callstat = self.send_query("CALLSTAT? BTS1").split(",")
+            else:
+                raise AnritsuError("Timeout: Starting simulation")
+
+    def stop_simulation(self):
+        """ Stop simulation operation
+
+        Args:
+          None
+
+        Returns:
+            None
+        """
+        # Stop virtual network (IMS) #1 if still running
+        # this is needed before Sync command is supported in 6.40a
+        if self.send_query("IMSVNSTAT? 1") == "RUNNING":
+            self.send_command("IMSSTOPVN 1")
+        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, LOAD_SIMULATION_PARAM_FILE_WAIT_TIME)
+
+    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, sim_model):
+        """ Set simulation model and valid the configuration
+
+        Args:
+            sim_model: simulation model
+
+        Returns:
+            True/False
+        """
+        error = int(
+            self.send_query("SIMMODEL %s;ERROR?" % sim_model,
+                            COMMAND_COMPLETE_WAIT_TIME))
+        if error:
+            return False
+        # Reset every time after SIMMODEL is set because SIMMODEL will load
+        # some of the contents from previous parameter files.
+        self.reset()
+        return True
+
+    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:
+            True or False
+        """
+        self.stop_simulation()
+        simmodel = bts1.value
+        if bts2 is not None:
+            simmodel = simmodel + "," + bts2.value
+        if self._wlan:
+            simmodel = simmodel + "," + "WLAN"
+        return self._set_simulation_model(simmodel)
+
+    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
+
+        self.log.info("Waiting for CALLSTAT=POWEROFF")
+        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
+
+        self.log.info("Waiting for CALLSTAT=IDLE")
+        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, bts=1):
+        """ Waits for UE registration state on Anritsu
+
+        Args:
+          bts: index of MD8475A BTS, eg 1, 2
+
+        Returns:
+            None
+        """
+        self.log.info("wait for IDLE/COMMUNICATION state on anritsu.")
+        time_to_wait = REGISTRATION_STATE_WAIT_TIME
+        sleep_interval = 1
+        sim_model = (self.get_simulation_model()).split(",")
+        #wait 1 more round for GSM because of PS attach
+        registration_check_iterations = 2 if sim_model[bts - 1] == "GSM" else 1
+        for _ in range(registration_check_iterations):
+            waiting_time = 0
+            while waiting_time <= time_to_wait:
+                callstat = self.send_query(
+                    "CALLSTAT? BTS{}".format(bts)).split(",")
+                if callstat[0] == "IDLE" or callstat[1] == "COMMUNICATION":
+                    break
+                time.sleep(sleep_interval)
+                waiting_time += sleep_interval
+            else:
+                raise AnritsuError(
+                    "UE failed to register in {} seconds".format(time_to_wait))
+            time.sleep(sleep_interval)
+
+    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
+
+        self.log.info("Waiting for CALLSTAT=COMMUNICATION")
+        callstat = self.send_query("CALLSTAT? BTS1").split(",")
+        while callstat[1] != "COMMUNICATION":
+            time.sleep(sleep_interval)
+            waiting_time += sleep_interval
+            if waiting_time <= time_to_wait:
+                callstat = self.send_query("CALLSTAT? BTS1").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)
+
+    @property
+    def usim_key(self):
+        """ Gets the USIM Security Key
+
+        Args:
+          None
+
+        Returns:
+            USIM Security Key
+        """
+        return self.send_query("USIMK?")
+
+    @usim_key.setter
+    def usim_key(self, usimk):
+        """ sets the USIM Security Key
+        Args:
+            usimk: USIM Security Key, eg "000102030405060708090A0B0C0D0E0F"
+
+        Returns:
+            None
+        """
+        cmd = "USIMK " + usimk
+        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
+        """
+        # no need to # 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={}&dcs=0x10&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
+        """
+        cmd = (
+            'PWSSENDWM 3GPP2,"BtsNo=1&ServiceCategory={}&MessageID={}&AlertText={}&'
+            'CharSet=ASCII&ResponseType={}&Severity={}&Urgency={}&Certainty={}"'
+        ).format(service_category, message_id, alert_ext, response_type,
+                 severity, urgency, certainty)
+        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])
+
+    def select_usim(self, usim):
+        """ Select pre-defined Anritsu USIM models
+
+        Args:
+            usim: any of P0035Bx, P0135Ax, P0250Ax, P0260Ax
+
+        Returns:
+            None
+        """
+        cmd = "SELECTUSIM {}".format(usim)
+        self.send_command(cmd)
+
+
+class _AnritsuTestCases(object):
+    '''Class to interact with the MD8475 supported test procedures '''
+
+    def __init__(self, anritsu):
+        self._anritsu = anritsu
+        self.log = anritsu.log
+
+    @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(object):
+    '''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
+        self.log = anritsu.log
+
+    @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 evdo_sid(self):
+        """ Gets the Sector ID of the EVDO cell
+
+        Args:
+            None
+
+        Returns:
+            Sector Id
+        """
+        cmd = "S1SECTORID? " + self._bts_number
+        return self._anritsu.send_query(cmd)
+
+    @evdo_sid.setter
+    def evdo_sid(self, sid):
+        """ Sets the Sector ID of the EVDO cell
+
+        Args:
+            sid: Sector ID of the EVDO cell
+
+        Returns:
+            None
+        """
+        cmd = "S1SECTORID {},{}".format(sid, 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(object):
+    '''Class to interact with virtual phone supported by MD8475 '''
+
+    def __init__(self, anritsu):
+        self._anritsu = anritsu
+        self.log = anritsu.log
+
+    @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(object):
+    '''Class to configure PDN parameters'''
+
+    def __init__(self, anritsu, pdnnumber):
+        self._pdn_number = pdnnumber
+        self._anritsu = anritsu
+        self.log = anritsu.log
+
+    @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)
+
+    @property
+    def pdn_ims(self):
+        """ Get PDN IMS VNID binding status
+
+        Args:
+            None
+
+        Returns:
+            PDN IMS VNID binding status
+        """
+        cmd = "PDNIMS? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @pdn_ims.setter
+    def pdn_ims(self, switch):
+        """ Set PDN IMS VNID binding Enable/Disable
+
+        Args:
+            switch: "ENABLE/DISABLE"
+
+        Returns:
+            None
+        """
+        if not isinstance(switch, Switch):
+            raise ValueError(' The parameter should be of type'
+                             ' "Switch", ie, ENABLE or DISABLE ')
+        cmd = "PDNIMS {},{}".format(self._pdn_number, switch.value)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def pdn_vnid(self):
+        """ Get PDN IMS VNID
+
+        Args:
+            None
+
+        Returns:
+            PDN IMS VNID
+        """
+        cmd = "PDNVNID? " + self._pdn_number
+        return self._anritsu.send_query(cmd)
+
+    @pdn_vnid.setter
+    def pdn_vnid(self, vnid):
+        """ Set PDN IMS VNID
+
+        Args:
+            vnid: 1~99
+
+        Returns:
+            None
+        """
+        cmd = "PDNVNID {},{}".format(self._pdn_number, vnid)
+        self._anritsu.send_command(cmd)
+
+
+class _TriggerMessage(object):
+    '''Class to interact with trigger message handling supported by MD8475 '''
+
+    def __init__(self, anritsu):
+        self._anritsu = anritsu
+        self.log = anritsu.log
+
+    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)
+
+
+class _IMS_Services(object):
+    '''Class to configure and operate IMS Services'''
+
+    def __init__(self, anritsu, vnid):
+        self._vnid = vnid
+        self._anritsu = anritsu
+        self.log = anritsu.log
+
+    @property
+    def sync(self):
+        """ Gets Sync Enable status
+
+        Args:
+            None
+
+        Returns:
+            VNID Sync Enable status
+        """
+        cmd = "IMSSYNCENABLE? " + self._vnid
+        return self._anritsu.send_query(cmd)
+
+    @sync.setter
+    def sync(self, switch):
+        """ Set Sync Enable or Disable
+
+        Args:
+            sync: ENABLE/DISABLE
+
+        Returns:
+            None
+        """
+        if not isinstance(switch, Switch):
+            raise ValueError(' The parameter should be of type "Switch"')
+        cmd = "IMSSYNCENABLE {},{}".format(self._vnid, switch.value)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def cscf_address_ipv4(self):
+        """ Gets CSCF IPv4 address
+
+        Args:
+            None
+
+        Returns:
+            CSCF IPv4 address
+        """
+        cmd = "IMSCSCFIPV4? " + self._vnid
+        return self._anritsu.send_query(cmd)
+
+    @cscf_address_ipv4.setter
+    def cscf_address_ipv4(self, ip_address):
+        """ Set CSCF IPv4 address
+
+        Args:
+            ip_address: CSCF IPv4 address
+
+        Returns:
+            None
+        """
+        cmd = "IMSCSCFIPV4 {},{}".format(self._vnid, ip_address)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def cscf_address_ipv6(self):
+        """ Gets CSCF IPv6 address
+
+        Args:
+            None
+
+        Returns:
+            CSCF IPv6 address
+        """
+        cmd = "IMSCSCFIPV6? " + self._vnid
+        return self._anritsu.send_query(cmd)
+
+    @cscf_address_ipv6.setter
+    def cscf_address_ipv6(self, ip_address):
+        """ Set CSCF IPv6 address
+
+        Args:
+            ip_address: CSCF IPv6 address
+
+        Returns:
+            None
+        """
+        cmd = "IMSCSCFIPV6 {},{}".format(self._vnid, ip_address)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def cscf_monitoring_ua(self):
+        """ Get CSCF Monitoring UA URI
+
+        Args:
+            None
+
+        Returns:
+            CSCF Monitoring UA URI
+        """
+        cmd = "IMSCSCFUAURI? " + self._vnid
+        return self._anritsu.send_query(cmd)
+
+    @cscf_monitoring_ua.setter
+    def cscf_monitoring_ua(self, ua_uri):
+        """ Set CSCF Monitoring UA URI
+
+        Args:
+            ua_uri: CSCF Monitoring UA URI
+
+        Returns:
+            None
+        """
+        cmd = "IMSCSCFUAURI {},{}".format(self._vnid, ua_uri)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def dns(self):
+        """ Gets DNS Enable status
+
+        Args:
+            None
+
+        Returns:
+            VNID DNS Enable status
+        """
+        cmd = "IMSDNS? " + self._vnid
+        return self._anritsu.send_query(cmd)
+
+    @dns.setter
+    def dns(self, switch):
+        """ Set DNS Enable or Disable
+
+        Args:
+            sync: ENABLE/DISABLE
+
+        Returns:
+            None
+        """
+        if not isinstance(switch, Switch):
+            raise ValueError(' The parameter should be of type "Switch"')
+        cmd = "IMSDNS {},{}".format(self._vnid, switch.value)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def ndp_nic(self):
+        """ Gets NDP Network Interface name
+
+        Args:
+            None
+
+        Returns:
+            NDP NIC name
+        """
+        cmd = "IMSNDPNIC? " + self._vnid
+        return self._anritsu.send_query(cmd)
+
+    @ndp_nic.setter
+    def ndp_nic(self, nic_name):
+        """ Set NDP Network Interface name
+
+        Args:
+            nic_name: NDP Network Interface name
+
+        Returns:
+            None
+        """
+        cmd = "IMSNDPNIC {},{}".format(self._vnid, nic_name)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def psap(self):
+        """ Gets PSAP Enable status
+
+        Args:
+            None
+
+        Returns:
+            VNID PSAP Enable status
+        """
+        cmd = "IMSPSAP? " + self._vnid
+        return self._anritsu.send_query(cmd)
+
+    @psap.setter
+    def psap(self, switch):
+        """ Set PSAP Enable or Disable
+
+        Args:
+            switch: ENABLE/DISABLE
+
+        Returns:
+            None
+        """
+        if not isinstance(switch, Switch):
+            raise ValueError(' The parameter should be of type "Switch"')
+        cmd = "IMSPSAP {},{}".format(self._vnid, switch.value)
+        self._anritsu.send_command(cmd)
+
+    @property
+    def psap_auto_answer(self):
+        """ Gets PSAP Auto Answer status
+
+        Args:
+            None
+
+        Returns:
+            VNID PSAP Auto Answer status
+        """
+        cmd = "IMSPSAPAUTOANSWER? " + self._vnid
+        return self._anritsu.send_query(cmd)
+
+    @psap_auto_answer.setter
+    def psap_auto_answer(self, switch):
+        """ Set PSAP Auto Answer Enable or Disable
+
+        Args:
+            switch: ENABLE/DISABLE
+
+        Returns:
+            None
+        """
+        if not isinstance(switch, Switch):
+            raise ValueError(' The parameter should be of type "Switch"')
+        cmd = "IMSPSAPAUTOANSWER {},{}".format(self._vnid, switch.value)
+        self._anritsu.send_command(cmd)
+
+    def start_virtual_network(self):
+        """ Start the specified Virtual Network (IMS service)
+
+        Args:
+            None
+
+        Returns:
+            None
+        """
+        cmd = "IMSSTARTVN " + self._vnid
+        return self._anritsu.send_command(cmd)
diff --git a/acts/framework/acts/controllers/anritsu_lib/mg3710a.py b/acts/framework/acts/controllers/anritsu_lib/mg3710a.py
new file mode 100644
index 0000000..baf350f
--- /dev/null
+++ b/acts/framework/acts/controllers/anritsu_lib/mg3710a.py
@@ -0,0 +1,710 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""
+Controller interface for Anritsu Signal Generator MG3710A.
+"""
+
+import time
+import socket
+from enum import Enum
+from enum import IntEnum
+
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib._anritsu_utils import NO_ERROR
+from acts.controllers.anritsu_lib._anritsu_utils import OPERATION_COMPLETE
+
+TERMINATOR = "\n"
+
+def create(configs, logger):
+    objs = []
+    for c in configs:
+        ip_address = c["ip_address"]
+        objs.append(MG3710A(ip_address, logger))
+    return objs
+
+def destroy(objs):
+    return
+
+class MG3710A(object):
+    """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/controllers/ap_lib/__init__.py b/acts/framework/acts/controllers/ap_lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/__init__.py
diff --git a/acts/framework/acts/controllers/ap_lib/dhcp_config.py b/acts/framework/acts/controllers/ap_lib/dhcp_config.py
new file mode 100644
index 0000000..ddf6ac1
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/dhcp_config.py
@@ -0,0 +1,133 @@
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import copy
+import ipaddress
+
+
+class Subnet(object):
+    """Configs for a subnet  on the dhcp server.
+
+    Attributes:
+        network: ipaddress.IPv4Network, the network that this subnet is in.
+        start: ipaddress.IPv4Address, the start ip address.
+        end: ipaddress.IPv4Address, the end ip address.
+        router: The router to give to all hosts in this subnet.
+        lease: The lease time of all hosts in this subnet.
+    """
+
+    def __init__(self,
+                 subnet,
+                 start=None,
+                 end=None,
+                 router=None,
+                 lease_time=None):
+        """
+        Args:
+            subnet_address: ipaddress.IPv4Network, The network that this
+                            subnet is.
+            start: ipaddress.IPv4Address, The start of the address range to
+                   give hosts in this subnet. If not given then the first ip in
+                   the network is used.
+            end: ipaddress.IPv4Address, The end of the address range to give
+                 hosts. If not given then the last ip in the network is used.
+            router: ipaddress.IPv4Address, The router hosts should use in this
+                    subnet. If not given the first ip in the network is used.
+            lease_time: int, The amount of lease time in seconds
+                        hosts in this subnet have.
+        """
+        self.network = subnet
+
+        if start:
+            self.start = start
+        else:
+            self.start = self.network[2]
+
+        if not self.start in self.network:
+            raise ValueError('The start range is not in the subnet.')
+        if self.start.is_reserved:
+            raise ValueError('The start of the range cannot be reserved.')
+
+        if end:
+            self.end = end
+        else:
+            self.end = self.network[-2]
+
+        if not self.end in self.network:
+            raise ValueError('The end range is not in the subnet.')
+        if self.end.is_reserved:
+            raise ValueError('The end of the range cannot be reserved.')
+        if self.end < self.start:
+            raise ValueError(
+                'The end must be an address larger than the start.')
+
+        if router:
+            if router >= self.start and router <= self.end:
+                raise ValueError('Router must not be in pool range.')
+            if not router in self.network:
+                raise ValueError('Router must be in the given subnet.')
+
+            self.router = router
+        else:
+            # TODO: Use some more clever logic so that we don't have to search
+            # every host potentially.
+            self.router = None
+            for host in self.network.hosts():
+                if host < self.start or host > self.end:
+                    self.router = host
+                    break
+
+            if not self.router:
+                raise ValueError('No useable host found.')
+
+        self.lease_time = lease_time
+
+
+class StaticMapping(object):
+    """Represents a static dhcp host.
+
+    Attributes:
+        identifier: How id of the host (usually the mac addres
+                    e.g. 00:11:22:33:44:55).
+        address: ipaddress.IPv4Address, The ipv4 address to give the host.
+        lease_time: How long to give a lease to this host.
+    """
+
+    def __init__(self, identifier, address, lease_time=None):
+        self.identifier = identifier
+        self.ipv4_address = address
+        self.lease_time = lease_time
+
+
+class DhcpConfig(object):
+    """The configs for a dhcp server.
+
+    Attributes:
+        subnets: A list of all subnets for the dhcp server to create.
+        static_mappings: A list of static host addresses.
+        default_lease_time: The default time for a lease.
+        max_lease_time: The max time to allow a lease.
+    """
+
+    def __init__(self,
+                 subnets=None,
+                 static_mappings=None,
+                 default_lease_time=600,
+                 max_lease_time=7200):
+        self.subnets = copy.deepcopy(subnets) if subnets else []
+        self.static_mappings = (copy.deepcopy(static_mappings)
+                                if static_mappings else [])
+        self.default_lease_time = default_lease_time
+        self.max_lease_time = max_lease_time
diff --git a/acts/framework/acts/controllers/ap_lib/dhcp_server.py b/acts/framework/acts/controllers/ap_lib/dhcp_server.py
new file mode 100644
index 0000000..59f1f2f
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/dhcp_server.py
@@ -0,0 +1,237 @@
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import itertools
+import os
+import time
+
+from acts.controllers.ap_lib import dhcp_config
+from acts.controllers.utils_lib.commands import shell
+
+# The router wan interface will be hard set since this the default for the
+# whirlwind.  In the future it maybe desireable to see which interface has a
+# public address and assign it dynamically.
+_ROUTER_WAN_INTERFACE = 'eth2'
+_ROUTER_DNS = '8.8.8.8, 4.4.4.4'
+
+
+class Error(Exception):
+    """An error caused by the dhcp server."""
+
+
+class NoInterfaceError(Exception):
+    """Error thrown when the dhcp server has no interfaces on any subnet."""
+
+
+class DhcpServer(object):
+    """Manages the dhcp server program.
+
+    Only one of these can run in an enviroment at a time.
+
+    Attributes:
+        config: The dhcp server configuration that is being used.
+    """
+
+    PROGRAM_FILE = 'dhcpd'
+
+    def __init__(self, runner, interface, working_dir='/tmp'):
+        """
+        Args:
+            runner: Object that has a run_async and run methods for running
+                    shell commands.
+            interface: string, The name of the interface to use.
+            working_dir: The directory to work out of.
+        """
+        self._runner = runner
+        self._working_dir = working_dir
+        self._shell = shell.ShellCommand(runner, working_dir)
+        self._log_file = 'dhcpd_%s.log' % interface
+        self._config_file = 'dhcpd_%s.conf' % interface
+        self._lease_file = 'dhcpd_%s.leases' % interface
+        self._identifier = '%s.*%s' % (self.PROGRAM_FILE, self._config_file)
+
+    def start(self, config, timeout=60):
+        """Starts the dhcp server.
+
+        Starts the dhcp server daemon and runs it in the background.
+
+        Args:
+            config: dhcp_config.DhcpConfig, Configs to start the dhcp server
+                    with.
+
+        Returns:
+            True if the daemon could be started. Note that the daemon can still
+            start and not work. Invalid configurations can take a long amount
+            of time to be produced, and because the daemon runs indefinitely
+            it's infeasible to wait on. If you need to check if configs are ok
+            then periodic checks to is_running and logs should be used.
+        """
+        if self.is_alive():
+            self.stop()
+
+        # The following three commands are needed to enable bridging between
+        # the WAN and LAN/WLAN ports.  This means anyone connecting to the
+        # WLAN/LAN ports will be able to access the internet if the WAN port
+        # is connected to the internet.
+        self._runner.run('iptables -t nat -F')
+        self._runner.run('iptables -t nat -A POSTROUTING -o %s -j MASQUERADE' %
+                         _ROUTER_WAN_INTERFACE)
+        self._runner.run('echo 1 > /proc/sys/net/ipv4/ip_forward')
+
+        self._write_configs(config)
+        self._shell.delete_file(self._log_file)
+        self._shell.touch_file(self._lease_file)
+
+        dhcpd_command = '%s -cf "%s" -lf %s -f""' % (
+            self.PROGRAM_FILE, self._config_file, self._lease_file)
+        base_command = 'cd "%s"; %s' % (self._working_dir, dhcpd_command)
+        job_str = '%s > "%s" 2>&1' % (base_command, self._log_file)
+        self._runner.run_async(job_str)
+
+        try:
+            self._wait_for_process(timeout=timeout)
+            self._wait_for_server(timeout=timeout)
+        except:
+            self.stop()
+            raise
+
+    def stop(self):
+        """Kills the daemon if it is running."""
+        self._shell.kill(self._identifier)
+
+    def is_alive(self):
+        """
+        Returns:
+            True if the deamon is running.
+        """
+        return self._shell.is_alive(self._identifier)
+
+    def get_logs(self):
+        """Pulls the log files from where dhcp server is running.
+
+        Returns:
+            A string of the dhcp server logs.
+        """
+        return self._shell.read_file(self._log_file)
+
+    def _wait_for_process(self, timeout=60):
+        """Waits for the process to come up.
+
+        Waits until the dhcp server process is found running, or there is
+        a timeout. If the program never comes up then the log file
+        will be scanned for errors.
+
+        Raises: See _scan_for_errors
+        """
+        start_time = time.time()
+        while time.time() - start_time < timeout and not self.is_alive():
+            self._scan_for_errors(False)
+            time.sleep(0.1)
+
+        self._scan_for_errors(True)
+
+    def _wait_for_server(self, timeout=60):
+        """Waits for dhcp server to report that the server is up.
+
+        Waits until dhcp server says the server has been brought up or an
+        error occurs.
+
+        Raises: see _scan_for_errors
+        """
+        start_time = time.time()
+        while time.time() - start_time < timeout:
+            success = self._shell.search_file(
+                'Wrote [0-9]* leases to leases file', self._log_file)
+            if success:
+                return
+
+            self._scan_for_errors(True)
+
+    def _scan_for_errors(self, should_be_up):
+        """Scans the dhcp server log for any errors.
+
+        Args:
+            should_be_up: If true then dhcp server is expected to be alive.
+                          If it is found not alive while this is true an error
+                          is thrown.
+
+        Raises:
+            Error: Raised when a dhcp server error is found.
+        """
+        # If this is checked last we can run into a race condition where while
+        # scanning the log the process has not died, but after scanning it
+        # has. If this were checked last in that condition then the wrong
+        # error will be thrown. To prevent this we gather the alive state first
+        # so that if it is dead it will definitely give the right error before
+        # just giving a generic one.
+        is_dead = not self.is_alive()
+
+        no_interface = self._shell.search_file(
+            'Not configured to listen on any interfaces', self._log_file)
+        if no_interface:
+            raise NoInterfaceError(
+                'Dhcp does not contain a subnet for any of the networks the'
+                ' current interfaces are on.')
+
+        if should_be_up and is_dead:
+            raise Error('Dhcp server failed to start.', self)
+
+    def _write_configs(self, config):
+        """Writes the configs to the dhcp server config file."""
+
+        self._shell.delete_file(self._config_file)
+
+        lines = []
+
+        if config.default_lease_time:
+            lines.append('default-lease-time %d;' % config.default_lease_time)
+        if config.max_lease_time:
+            lines.append('max-lease-time %s;' % config.max_lease_time)
+
+        for subnet in config.subnets:
+            address = subnet.network.network_address
+            mask = subnet.network.netmask
+            router = subnet.router
+            start = subnet.start
+            end = subnet.end
+            lease_time = subnet.lease_time
+
+            lines.append('subnet %s netmask %s {' % (address, mask))
+            lines.append('\toption subnet-mask %s;' % mask)
+            lines.append('\toption routers %s;' % router)
+            lines.append('\toption domain-name-servers %s;' % _ROUTER_DNS)
+            lines.append('\trange %s %s;' % (start, end))
+            if lease_time:
+                lines.append('\tdefault-lease-time %d;' % lease_time)
+                lines.append('\tmax-lease-time %d;' % lease_time)
+            lines.append('}')
+
+        for mapping in config.static_mappings:
+            identifier = mapping.identifier
+            fixed_address = mapping.ipv4_address
+            host_fake_name = 'host%s' % identifier.replace(':', '')
+            lease_time = mapping.lease_time
+
+            lines.append('host %s {' % host_fake_name)
+            lines.append('\thardware ethernet %s;' % identifier)
+            lines.append('\tfixed-address %s;' % fixed_address)
+            if lease_time:
+                lines.append('\tdefault-lease-time %d;' % lease_time)
+                lines.append('\tmax-lease-time %d;' % lease_time)
+            lines.append('}')
+
+        config_str = '\n'.join(lines)
+
+        self._shell.write_file(self._config_file, config_str)
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd.py b/acts/framework/acts/controllers/ap_lib/hostapd.py
new file mode 100644
index 0000000..9f78d79
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/hostapd.py
@@ -0,0 +1,201 @@
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import itertools
+import logging
+import os
+import time
+
+from acts.controllers.ap_lib import hostapd_config
+from acts.controllers.utils_lib.commands import shell
+
+
+class Error(Exception):
+    """An error caused by hostapd."""
+
+
+class Hostapd(object):
+    """Manages the hostapd program.
+
+    Attributes:
+        config: The hostapd configuration that is being used.
+    """
+
+    PROGRAM_FILE = '/usr/sbin/hostapd'
+
+    def __init__(self, runner, interface, working_dir='/tmp'):
+        """
+        Args:
+            runner: Object that has run_async and run methods for executing
+                    shell commands (e.g. connection.SshConnection)
+            interface: string, The name of the interface to use (eg. wlan0).
+            working_dir: The directory to work out of.
+        """
+        self._runner = runner
+        self._interface = interface
+        self._working_dir = working_dir
+        self.config = None
+        self._shell = shell.ShellCommand(runner, working_dir)
+        self._log_file = 'hostapd-%s.log' % self._interface
+        self._ctrl_file = 'hostapd-%s.ctrl' % self._interface
+        self._config_file = 'hostapd-%s.conf' % self._interface
+        self._identifier = '%s.*%s' % (self.PROGRAM_FILE, self._config_file)
+
+    def start(self, config, timeout=60, additional_parameters=None):
+        """Starts hostapd
+
+        Starts the hostapd daemon and runs it in the background.
+
+        Args:
+            config: Configs to start the hostapd with.
+            timeout: Time to wait for DHCP server to come up.
+            additional_parameters: A dicitonary of parameters that can sent
+                                   directly into the hostapd config file.  This
+                                   can be used for debugging and or adding one
+                                   off parameters into the config.
+
+        Returns:
+            True if the daemon could be started. Note that the daemon can still
+            start and not work. Invalid configurations can take a long amount
+            of time to be produced, and because the daemon runs indefinetly
+            it's impossible to wait on. If you need to check if configs are ok
+            then periodic checks to is_running and logs should be used.
+        """
+        if self.is_alive():
+            self.stop()
+
+        self.config = config
+
+        self._shell.delete_file(self._ctrl_file)
+        self._shell.delete_file(self._log_file)
+        self._shell.delete_file(self._config_file)
+        self._write_configs(additional_parameters=additional_parameters)
+
+        hostapd_command = '%s -dd -t "%s"' % (self.PROGRAM_FILE,
+                                              self._config_file)
+        base_command = 'cd "%s"; %s' % (self._working_dir, hostapd_command)
+        job_str = '%s > "%s" 2>&1' % (base_command, self._log_file)
+        self._runner.run_async(job_str)
+
+        try:
+            self._wait_for_process(timeout=timeout)
+            self._wait_for_interface(timeout=timeout)
+        except:
+            self.stop()
+            raise
+
+    def stop(self):
+        """Kills the daemon if it is running."""
+        self._shell.kill(self._identifier)
+
+    def is_alive(self):
+        """
+        Returns:
+            True if the deamon is running.
+        """
+        return self._shell.is_alive(self._identifier)
+
+    def pull_logs(self):
+        """Pulls the log files from where hostapd is running.
+
+        Returns:
+            A string of the hostapd logs.
+        """
+        # TODO: Auto pulling of logs when stop is called.
+        return self._shell.read_file(self._log_file)
+
+    def _wait_for_process(self, timeout=60):
+        """Waits for the process to come up.
+
+        Waits until the hostapd process is found running, or there is
+        a timeout. If the program never comes up then the log file
+        will be scanned for errors.
+
+        Raises: See _scan_for_errors
+        """
+        start_time = time.time()
+        while time.time() - start_time < timeout and not self.is_alive():
+            self._scan_for_errors(False)
+            time.sleep(0.1)
+
+    def _wait_for_interface(self, timeout=60):
+        """Waits for hostapd to report that the interface is up.
+
+        Waits until hostapd says the interface has been brought up or an
+        error occurs.
+
+        Raises: see _scan_for_errors
+        """
+        start_time = time.time()
+        while time.time() - start_time < timeout:
+            success = self._shell.search_file('Setup of interface done',
+                                              self._log_file)
+            if success:
+                return
+
+            self._scan_for_errors(True)
+
+    def _scan_for_errors(self, should_be_up):
+        """Scans the hostapd log for any errors.
+
+        Args:
+            should_be_up: If true then hostapd program is expected to be alive.
+                          If it is found not alive while this is true an error
+                          is thrown.
+
+        Raises:
+            Error: Raised when a hostapd error is found.
+        """
+        # Store this so that all other errors have priority.
+        is_dead = not self.is_alive()
+
+        bad_config = self._shell.search_file('Interface initialization failed',
+                                             self._log_file)
+        if bad_config:
+            raise Error('Interface failed to start', self)
+
+        bad_config = self._shell.search_file("Interface %s wasn't started" %
+                                             self._interface, self._log_file)
+        if bad_config:
+            raise Error('Interface failed to start', self)
+
+        if should_be_up and is_dead:
+            raise Error('Hostapd failed to start', self)
+
+    def _write_configs(self, additional_parameters=None):
+        """Writes the configs to the hostapd config file."""
+        self._shell.delete_file(self._config_file)
+
+        our_configs = collections.OrderedDict()
+        our_configs['interface'] = self._interface
+        our_configs['ctrl_interface'] = self._ctrl_file
+        packaged_configs = self.config.package_configs()
+        if additional_parameters:
+            packaged_configs.append(additional_parameters)
+
+        pairs = ('%s=%s' % (k, v) for k, v in our_configs.items())
+        for packaged_config in packaged_configs:
+            config_pairs = ('%s=%s' % (k, v)
+                            for k, v in packaged_config.items())
+            pairs = itertools.chain(pairs, config_pairs)
+
+        hostapd_conf = '\n'.join(pairs)
+
+        logging.info('Writing %s' % self._config_file)
+        logging.debug('******************Start*******************')
+        logging.debug('\n%s' % hostapd_conf)
+        logging.debug('*******************End********************')
+
+        self._shell.write_file(self._config_file, hostapd_conf)
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd_ap_preset.py b/acts/framework/acts/controllers/ap_lib/hostapd_ap_preset.py
new file mode 100644
index 0000000..a18f0c7
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/hostapd_ap_preset.py
@@ -0,0 +1,127 @@
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts.controllers.ap_lib import hostapd_config
+from acts.controllers.ap_lib import hostapd_constants
+
+
+def create_ap_preset(profile_name,
+                     channel=None,
+                     frequency=None,
+                     security=None,
+                     ssid=None,
+                     bss_settings=[]):
+    """AP preset config generator.  This a wrapper for hostapd_config but
+       but supplies the default settings for the preset that is selected.
+
+        You may specify channel or frequency, but not both.  Both options
+        are checked for validity (i.e. you can't specify an invalid channel
+        or a frequency that will not be accepted).
+
+    Args:
+        profile_name: The name of the device want the preset for.
+                      Options: whirlwind
+        channel: int, channel number.
+        frequency: int, frequency of channel.
+        security: Security, the secuirty settings to use.
+        ssid: string, The name of the ssid to brodcast.
+        bss_settings: The settings for all bss.
+
+    Returns: A hostapd_config object that can be used by the hostapd object.
+    """
+
+    force_wmm = None
+    force_wmm = None
+    beacon_interval = None
+    dtim_period = None
+    short_preamble = None
+    interface = None
+    mode = None
+    n_capabilities = None
+    ac_capabilities = None
+    if channel:
+        frequency = hostapd_config.get_frequency_for_channel(channel)
+    elif frequency:
+        channel = hostapd_config.get_channel_for_frequency(frequency)
+    else:
+        raise ValueError('Specify either frequency or channel.')
+
+    if (profile_name == 'whirlwind'):
+        force_wmm = True
+        beacon_interval = 100
+        dtim_period = 2
+        short_preamble = True
+        if frequency < 5000:
+            interface = hostapd_constants.WLAN0_STRING
+            mode = hostapd_constants.MODE_11N_MIXED
+            n_capabilities = [
+                hostapd_constants.N_CAPABILITY_LDPC,
+                hostapd_constants.N_CAPABILITY_SGI20,
+                hostapd_constants.N_CAPABILITY_SGI40,
+                hostapd_constants.N_CAPABILITY_TX_STBC,
+                hostapd_constants.N_CAPABILITY_RX_STBC1,
+                hostapd_constants.N_CAPABILITY_DSSS_CCK_40
+            ]
+            config = hostapd_config.HostapdConfig(
+                ssid=ssid,
+                security=security,
+                interface=interface,
+                mode=mode,
+                force_wmm=force_wmm,
+                beacon_interval=beacon_interval,
+                dtim_period=dtim_period,
+                short_preamble=short_preamble,
+                frequency=frequency,
+                n_capabilities=n_capabilities,
+                bss_settings=bss_settings)
+        else:
+            interface = hostapd_constants.WLAN1_STRING
+            mode = hostapd_constants.MODE_11AC_MIXED
+            if hostapd_config.ht40_plus_allowed(channel):
+                extended_channel = hostapd_constants.N_CAPABILITY_HT40_PLUS
+            elif hostapd_config.ht40_minus_allowed(channel):
+                extended_channel = hostapd_constants.N_CAPABILITY_HT40_MINUS
+            n_capabilities = [
+                hostapd_constants.N_CAPABILITY_LDPC, extended_channel,
+                hostapd_constants.N_CAPABILITY_SGI20,
+                hostapd_constants.N_CAPABILITY_SGI40,
+                hostapd_constants.N_CAPABILITY_TX_STBC,
+                hostapd_constants.N_CAPABILITY_RX_STBC1
+            ]
+            ac_capabilities = [
+                hostapd_constants.AC_CAPABILITY_MAX_MPDU_11454,
+                hostapd_constants.AC_CAPABILITY_RXLDPC,
+                hostapd_constants.AC_CAPABILITY_SHORT_GI_80,
+                hostapd_constants.AC_CAPABILITY_TX_STBC_2BY1,
+                hostapd_constants.AC_CAPABILITY_RX_STBC_1,
+                hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7,
+                hostapd_constants.AC_CAPABILITY_RX_ANTENNA_PATTERN,
+                hostapd_constants.AC_CAPABILITY_TX_ANTENNA_PATTERN
+            ]
+            config = hostapd_config.HostapdConfig(
+                ssid=ssid,
+                security=security,
+                interface=interface,
+                mode=mode,
+                force_wmm=force_wmm,
+                beacon_interval=beacon_interval,
+                dtim_period=dtim_period,
+                short_preamble=short_preamble,
+                frequency=frequency,
+                n_capabilities=n_capabilities,
+                ac_capabilities=ac_capabilities,
+                bss_settings=bss_settings)
+    else:
+        raise ValueError('Invalid ap model specified (%s)' % profile_name)
+    return config
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd_bss_settings.py b/acts/framework/acts/controllers/ap_lib/hostapd_bss_settings.py
new file mode 100644
index 0000000..396fbf6
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/hostapd_bss_settings.py
@@ -0,0 +1,52 @@
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+
+
+class BssSettings(object):
+    """Settings for a bss.
+
+    Settings for a bss to allow multiple network on a single device.
+
+    Attributes:
+        name: string, The name that this bss will go by.
+        ssid: string, The name of the ssid to brodcast.
+        hidden: bool, If true then the ssid will be hidden.
+        security: Security, The security settings to use.
+    """
+
+    def __init__(self, name, ssid, hidden=False, security=None, bssid=None):
+        self.name = name
+        self.ssid = ssid
+        self.hidden = hidden
+        self.security = security
+        self.bssid = bssid
+
+    def generate_dict(self):
+        """Returns: A dictionary of bss settings."""
+        settings = collections.OrderedDict()
+        settings['bss'] = self.name
+        if self.bssid:
+            settings['bssid'] = self.bssid
+        if self.ssid:
+            settings['ssid'] = self.ssid
+            settings['ignore_broadcast_ssid'] = 1 if self.hidden else 0
+
+        if self.security:
+            security_settings = self.security.generate_dict()
+            for k, v in security_settings.items():
+                settings[k] = v
+
+        return settings
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd_config.py b/acts/framework/acts/controllers/ap_lib/hostapd_config.py
new file mode 100644
index 0000000..4c70bd7
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/hostapd_config.py
@@ -0,0 +1,602 @@
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import enum
+import logging
+import os
+import collections
+import itertools
+
+from acts.controllers.ap_lib import hostapd_constants
+
+
+def ht40_plus_allowed(channel):
+    """Returns: True iff HT40+ is enabled for this configuration."""
+    channel_supported = (channel in hostapd_constants.HT40_ALLOW_MAP[
+        hostapd_constants.N_CAPABILITY_HT40_PLUS_CHANNELS])
+    return (channel_supported)
+
+
+def ht40_minus_allowed(channel):
+    """Returns: True iff HT40- is enabled for this configuration."""
+    channel_supported = (channel in hostapd_constants.HT40_ALLOW_MAP[
+        hostapd_constants.N_CAPABILITY_HT40_MINUS_CHANNELS])
+    return (channel_supported)
+
+
+def get_frequency_for_channel(channel):
+    """The frequency associated with a given channel number.
+
+    Args:
+        value: int channel number.
+
+    Returns:
+        int, frequency in MHz associated with the channel.
+
+    """
+    for frequency, channel_iter in \
+        hostapd_constants.CHANNEL_MAP.items():
+        if channel == channel_iter:
+            return frequency
+    else:
+        raise ValueError('Unknown channel value: %r.' % channel)
+
+
+def get_channel_for_frequency(frequency):
+    """The channel number associated with a given frequency.
+
+    Args:
+        value: int frequency in MHz.
+
+    Returns:
+        int, frequency associated with the channel.
+
+    """
+    return hostapd_constants.CHANNEL_MAP[frequency]
+
+
+class HostapdConfig(object):
+    """The root settings for the router.
+
+    All the settings for a router that are not part of an ssid.
+    """
+
+    def _get_11ac_center_channel_from_channel(self, channel):
+        """Returns the center channel of the selected channel band based
+           on the channel and channel bandwidth provided.
+        """
+        channel = int(channel)
+        center_channel_delta = hostapd_constants.CENTER_CHANNEL_MAP[
+            self._vht_oper_chwidth]['delta']
+
+        for channel_map in hostapd_constants.CENTER_CHANNEL_MAP[
+                self._vht_oper_chwidth]['channels']:
+            lower_channel_bound, upper_channel_bound = channel_map
+            if lower_channel_bound <= channel <= upper_channel_bound:
+                return lower_channel_bound + center_channel_delta
+        raise ValueError('Invalid channel for {channel_width}.'.format(
+            channel_width=self._vht_oper_chwidth))
+
+    @property
+    def _get_default_config(self):
+        """Returns: dict of default options for hostapd."""
+        return collections.OrderedDict([
+            ('logger_syslog', '-1'),
+            ('logger_syslog_level', '0'),
+            # default RTS and frag threshold to ``off''
+            ('rts_threshold', '2347'),
+            ('fragm_threshold', '2346'),
+            ('driver', hostapd_constants.DRIVER_NAME)
+        ])
+
+    @property
+    def _hostapd_ht_capabilities(self):
+        """Returns: string suitable for the ht_capab= line in a hostapd config.
+        """
+        ret = []
+        for cap in hostapd_constants.N_CAPABILITIES_MAPPING.keys():
+            if cap in self._n_capabilities:
+                ret.append(hostapd_constants.N_CAPABILITIES_MAPPING[cap])
+        return ''.join(ret)
+
+    @property
+    def _hostapd_vht_capabilities(self):
+        """Returns: string suitable for the vht_capab= line in a hostapd config.
+        """
+        ret = []
+        for cap in hostapd_constants.AC_CAPABILITIES_MAPPING.keys():
+            if cap in self._ac_capabilities:
+                ret.append(hostapd_constants.AC_CAPABILITIES_MAPPING[cap])
+        return ''.join(ret)
+
+    @property
+    def _require_ht(self):
+        """Returns: True iff clients should be required to support HT."""
+        # TODO(wiley) Why? (crbug.com/237370)
+        # DOES THIS APPLY TO US?
+        logging.warning('Not enforcing pure N mode because Snow does '
+                        'not seem to support it...')
+        return False
+
+    @property
+    def _require_vht(self):
+        """Returns: True if clients should be required to support VHT."""
+        return self._mode == hostapd_constants.MODE_11AC_PURE
+
+    @property
+    def hw_mode(self):
+        """Returns: string hardware mode understood by hostapd."""
+        if self._mode == hostapd_constants.MODE_11A:
+            return hostapd_constants.MODE_11A
+        if self._mode == hostapd_constants.MODE_11B:
+            return hostapd_constants.MODE_11B
+        if self._mode == hostapd_constants.MODE_11G:
+            return hostapd_constants.MODE_11G
+        if self.is_11n or self.is_11ac:
+            # For their own historical reasons, hostapd wants it this way.
+            if self._frequency > 5000:
+                return hostapd_constants.MODE_11A
+            return hostapd_constants.MODE_11G
+        raise ValueError('Invalid mode.')
+
+    @property
+    def is_11n(self):
+        """Returns: True if we're trying to host an 802.11n network."""
+        return self._mode in (hostapd_constants.MODE_11N_MIXED,
+                              hostapd_constants.MODE_11N_PURE)
+
+    @property
+    def is_11ac(self):
+        """Returns: True if we're trying to host an 802.11ac network."""
+        return self._mode in (hostapd_constants.MODE_11AC_MIXED,
+                              hostapd_constants.MODE_11AC_PURE)
+
+    @property
+    def channel(self):
+        """Returns: int channel number for self.frequency."""
+        return get_channel_for_frequency(self.frequency)
+
+    @channel.setter
+    def channel(self, value):
+        """Sets the channel number to configure hostapd to listen on.
+
+        Args:
+            value: int, channel number.
+
+        """
+        self.frequency = get_frequency_for_channel(value)
+
+    @property
+    def bssid(self):
+        return self._bssid
+
+    @bssid.setter
+    def bssid(self, value):
+        self._bssid = value
+
+    @property
+    def frequency(self):
+        """Returns: int, frequency for hostapd to listen on."""
+        return self._frequency
+
+    @frequency.setter
+    def frequency(self, value):
+        """Sets the frequency for hostapd to listen on.
+
+        Args:
+            value: int, frequency in MHz.
+
+        """
+        if value not in hostapd_constants.CHANNEL_MAP:
+            raise ValueError('Tried to set an invalid frequency: %r.' % value)
+
+        self._frequency = value
+
+    @property
+    def bss_lookup(self):
+        return self._bss_lookup
+
+    @property
+    def ssid(self):
+        """Returns: SsidSettings, The root Ssid settings being used."""
+        return self._ssid
+
+    @ssid.setter
+    def ssid(self, value):
+        """Sets the ssid for the hostapd.
+
+        Args:
+            value: SsidSettings, new ssid settings to use.
+
+        """
+        self._ssid = value
+
+    @property
+    def hidden(self):
+        """Returns: bool, True if the ssid is hidden, false otherwise."""
+        return self._hidden
+
+    @hidden.setter
+    def hidden(self, value):
+        """Sets if this ssid is hidden.
+
+        Args:
+            value: bool, If true the ssid will be hidden.
+        """
+        self.hidden = value
+
+    @property
+    def security(self):
+        """Returns: The security type being used."""
+        return self._security
+
+    @security.setter
+    def security(self, value):
+        """Sets the security options to use.
+
+        Args:
+            value: Security, The type of security to use.
+        """
+        self._security = value
+
+    @property
+    def ht_packet_capture_mode(self):
+        """Get an appropriate packet capture HT parameter.
+
+        When we go to configure a raw monitor we need to configure
+        the phy to listen on the correct channel.  Part of doing
+        so is to specify the channel width for HT channels.  In the
+        case that the AP is configured to be either HT40+ or HT40-,
+        we could return the wrong parameter because we don't know which
+        configuration will be chosen by hostap.
+
+        Returns:
+            string, HT parameter for frequency configuration.
+
+        """
+        if not self.is_11n:
+            return None
+
+        if ht40_plus_allowed(self.channel):
+            return 'HT40+'
+
+        if ht40_minus_allowed(self.channel):
+            return 'HT40-'
+
+        return 'HT20'
+
+    @property
+    def beacon_footer(self):
+        """Returns: bool _beacon_footer value."""
+        return self._beacon_footer
+
+    def beacon_footer(self, value):
+        """Changes the beacon footer.
+
+        Args:
+            value: bool, The beacon footer vlaue.
+        """
+        self._beacon_footer = value
+
+    @property
+    def scenario_name(self):
+        """Returns: string _scenario_name value, or None."""
+        return self._scenario_name
+
+    @property
+    def min_streams(self):
+        """Returns: int, _min_streams value, or None."""
+        return self._min_streams
+
+    def __init__(self,
+                 interface=None,
+                 mode=None,
+                 channel=None,
+                 frequency=None,
+                 n_capabilities=[],
+                 beacon_interval=None,
+                 dtim_period=None,
+                 frag_threshold=None,
+                 short_preamble=None,
+                 ssid=None,
+                 hidden=False,
+                 security=None,
+                 bssid=None,
+                 force_wmm=None,
+                 pmf_support=hostapd_constants.PMF_SUPPORT_DISABLED,
+                 obss_interval=None,
+                 vht_channel_width=None,
+                 vht_center_channel=None,
+                 ac_capabilities=[],
+                 beacon_footer='',
+                 spectrum_mgmt_required=None,
+                 scenario_name=None,
+                 min_streams=None,
+                 bss_settings=[],
+                 set_ap_defaults_model=None):
+        """Construct a HostapdConfig.
+
+        You may specify channel or frequency, but not both.  Both options
+        are checked for validity (i.e. you can't specify an invalid channel
+        or a frequency that will not be accepted).
+
+        Args:
+            interface: string, The name of the interface to use.
+            mode: string, MODE_11x defined above.
+            channel: int, channel number.
+            frequency: int, frequency of channel.
+            n_capabilities: list of N_CAPABILITY_x defined above.
+            beacon_interval: int, beacon interval of AP.
+            dtim_period: int, include a DTIM every |dtim_period| beacons.
+            frag_threshold: int, maximum outgoing data frame size.
+            short_preamble: Whether to use a short preamble.
+            ssid: string, The name of the ssid to brodcast.
+            hidden: bool, Should the ssid be hidden.
+            security: Security, the secuirty settings to use.
+            bssid: string, a MAC address like string for the BSSID.
+            force_wmm: True if we should force WMM on, False if we should
+                force it off, None if we shouldn't force anything.
+            pmf_support: one of PMF_SUPPORT_* above.  Controls whether the
+                client supports/must support 802.11w.
+            obss_interval: int, interval in seconds that client should be
+                required to do background scans for overlapping BSSes.
+            vht_channel_width: object channel width
+            vht_center_channel: int, center channel of segment 0.
+            ac_capabilities: list of AC_CAPABILITY_x defined above.
+            beacon_footer: string, containing (unvalidated) IE data to be
+                placed at the end of the beacon.
+            spectrum_mgmt_required: True if we require the DUT to support
+                spectrum management.
+            scenario_name: string to be included in file names, instead
+                of the interface name.
+            min_streams: int, number of spatial streams required.
+            control_interface: The file name to use as the control interface.
+            bss_settings: The settings for all bss.
+        """
+        self._interface = interface
+        if channel is not None and frequency is not None:
+            raise ValueError('Specify either frequency or channel '
+                             'but not both.')
+
+        self._wmm_enabled = False
+        unknown_caps = [
+            cap for cap in n_capabilities
+            if cap not in hostapd_constants.N_CAPABILITIES_MAPPING
+        ]
+        if unknown_caps:
+            raise ValueError('Unknown capabilities: %r' % unknown_caps)
+
+        self._frequency = None
+        if channel:
+            self.channel = channel
+        elif frequency:
+            self.frequency = frequency
+        else:
+            raise ValueError('Specify either frequency or channel.')
+        '''
+        if set_ap_defaults_model:
+            ap_default_config = hostapd_ap_default_configs.APDefaultConfig(
+                profile_name=set_ap_defaults_model, frequency=self.frequency)
+            force_wmm = ap_default_config.force_wmm
+            beacon_interval = ap_default_config.beacon_interval
+            dtim_period = ap_default_config.dtim_period
+            short_preamble = ap_default_config.short_preamble
+            self._interface = ap_default_config.interface
+            mode = ap_default_config.mode
+            if ap_default_config.n_capabilities:
+                n_capabilities = ap_default_config.n_capabilities
+            if ap_default_config.ac_capabilities:
+                ap_default_config = ap_default_config.ac_capabilities
+        '''
+
+        self._n_capabilities = set(n_capabilities)
+        if self._n_capabilities:
+            self._wmm_enabled = True
+        if self._n_capabilities and mode is None:
+            mode = hostapd_constants.MODE_11N_PURE
+        self._mode = mode
+
+        if not self.supports_frequency(self.frequency):
+            raise ValueError('Configured a mode %s that does not support '
+                             'frequency %d' % (self._mode, self.frequency))
+
+        self._beacon_interval = beacon_interval
+        self._dtim_period = dtim_period
+        self._frag_threshold = frag_threshold
+        self._short_preamble = short_preamble
+
+        self._ssid = ssid
+        self._hidden = hidden
+        self._security = security
+        self._bssid = bssid
+        if force_wmm is not None:
+            self._wmm_enabled = force_wmm
+        if pmf_support not in hostapd_constants.PMF_SUPPORT_VALUES:
+            raise ValueError('Invalid value for pmf_support: %r' % pmf_support)
+
+        self._pmf_support = pmf_support
+        self._obss_interval = obss_interval
+        if self.is_11ac:
+            if str(vht_channel_width) == '40':
+                self._vht_oper_chwidth = hostapd_constants.VHT_CHANNEL_WIDTH_40
+            elif str(vht_channel_width) == '80':
+                self._vht_oper_chwidth = hostapd_constants.VHT_CHANNEL_WIDTH_80
+            elif str(vht_channel_width) == '160':
+                self._vht_oper_chwidth = hostapd_constants.VHT_CHANNEL_WIDTH_160
+            elif str(vht_channel_width) == '80+80':
+                self._vht_oper_chwidth = hostapd_constants.VHT_CHANNEL_WIDTH_80_80
+            elif vht_channel_width is not None:
+                raise ValueError('Invalid channel width')
+            else:
+                logging.warning(
+                    'No channel bandwidth specified.  Using 80MHz for 11ac.')
+                self._vht_oper_chwidth = 1
+            if not vht_center_channel:
+                self._vht_oper_centr_freq_seg0_idx = self._get_11ac_center_channel_from_channel(
+                    self.channel)
+            else:
+                self._vht_oper_centr_freq_seg0_idx = vht_center_channel
+            self._ac_capabilities = set(ac_capabilities)
+        self._beacon_footer = beacon_footer
+        self._spectrum_mgmt_required = spectrum_mgmt_required
+        self._scenario_name = scenario_name
+        self._min_streams = min_streams
+
+        self._bss_lookup = {}
+        for bss in bss_settings:
+            if bss.name in self._bss_lookup:
+                raise ValueError('Cannot have multiple bss settings with the'
+                                 ' same name.')
+            self._bss_lookup[bss.name] = bss
+
+    def __repr__(self):
+        return (
+            '%s(mode=%r, channel=%r, frequency=%r, '
+            'n_capabilities=%r, beacon_interval=%r, '
+            'dtim_period=%r, frag_threshold=%r, ssid=%r, bssid=%r, '
+            'wmm_enabled=%r, security_config=%r, '
+            'spectrum_mgmt_required=%r)' %
+            (self.__class__.__name__, self._mode, self.channel, self.frequency,
+             self._n_capabilities, self._beacon_interval, self._dtim_period,
+             self._frag_threshold, self._ssid, self._bssid, self._wmm_enabled,
+             self._security, self._spectrum_mgmt_required))
+
+    def supports_channel(self, value):
+        """Check whether channel is supported by the current hardware mode.
+
+        @param value: int channel to check.
+        @return True iff the current mode supports the band of the channel.
+
+        """
+        for freq, channel in hostapd_constants.CHANNEL_MAP.iteritems():
+            if channel == value:
+                return self.supports_frequency(freq)
+
+        return False
+
+    def supports_frequency(self, frequency):
+        """Check whether frequency is supported by the current hardware mode.
+
+        @param frequency: int frequency to check.
+        @return True iff the current mode supports the band of the frequency.
+
+        """
+        if self._mode == hostapd_constants.MODE_11A and frequency < 5000:
+            return False
+
+        if self._mode in (hostapd_constants.MODE_11B,
+                          hostapd_constants.MODE_11G) and frequency > 5000:
+            return False
+
+        if frequency not in hostapd_constants.CHANNEL_MAP:
+            return False
+
+        channel = hostapd_constants.CHANNEL_MAP[frequency]
+        supports_plus = (channel in hostapd_constants.HT40_ALLOW_MAP[
+            hostapd_constants.N_CAPABILITY_HT40_PLUS_CHANNELS])
+        supports_minus = (channel in hostapd_constants.HT40_ALLOW_MAP[
+            hostapd_constants.N_CAPABILITY_HT40_MINUS_CHANNELS])
+        if (hostapd_constants.N_CAPABILITY_HT40_PLUS in self._n_capabilities
+                and not supports_plus):
+            return False
+
+        if (hostapd_constants.N_CAPABILITY_HT40_MINUS in self._n_capabilities
+                and not supports_minus):
+            return False
+
+        return True
+
+    def add_bss(self, bss):
+        """Adds a new bss setting.
+
+        Args:
+            bss: The bss settings to add.
+        """
+        if bss.name in self._bss_lookup:
+            raise ValueError('A bss with the same name already exists.')
+
+        self._bss_lookup[bss.name] = bss
+
+    def remove_bss(self, bss_name):
+        """Removes a bss setting from the config."""
+        del self._bss_lookup[bss_name]
+
+    def package_configs(self):
+        """Package the configs.
+
+        Returns:
+            A list of dictionaries, one dictionary for each section of the
+            config.
+        """
+        # Start with the default config parameters.
+        conf = self._get_default_config
+        if self._interface:
+            conf['interface'] = self._interface
+        if self._bssid:
+            conf['bssid'] = self._bssid
+        if self._ssid:
+            conf['ssid'] = self._ssid
+            conf['ignore_broadcast_ssid'] = 1 if self._hidden else 0
+        conf['channel'] = self.channel
+        conf['hw_mode'] = self.hw_mode
+        if self.is_11n or self.is_11ac:
+            conf['ieee80211n'] = 1
+            conf['ht_capab'] = self._hostapd_ht_capabilities
+        if self.is_11ac:
+            conf['ieee80211ac'] = 1
+            conf['vht_oper_chwidth'] = self._vht_oper_chwidth
+            conf['vht_oper_centr_freq_seg0_idx'] = \
+                    self._vht_oper_centr_freq_seg0_idx
+            conf['vht_capab'] = self._hostapd_vht_capabilities
+        if self._wmm_enabled:
+            conf['wmm_enabled'] = 1
+        if self._require_ht:
+            conf['require_ht'] = 1
+        if self._require_vht:
+            conf['require_vht'] = 1
+        if self._beacon_interval:
+            conf['beacon_int'] = self._beacon_interval
+        if self._dtim_period:
+            conf['dtim_period'] = self._dtim_period
+        if self._frag_threshold:
+            conf['fragm_threshold'] = self._frag_threshold
+        if self._pmf_support:
+            conf['ieee80211w'] = self._pmf_support
+        if self._obss_interval:
+            conf['obss_interval'] = self._obss_interval
+        if self._short_preamble:
+            conf['preamble'] = 1
+        if self._spectrum_mgmt_required:
+            # To set spectrum_mgmt_required, we must first set
+            # local_pwr_constraint. And to set local_pwr_constraint,
+            # we must first set ieee80211d. And to set ieee80211d, ...
+            # Point being: order matters here.
+            conf['country_code'] = 'US'  # Required for local_pwr_constraint
+            conf['ieee80211d'] = 1  # Required for local_pwr_constraint
+            conf['local_pwr_constraint'] = 0  # No local constraint
+            conf['spectrum_mgmt_required'] = 1  # Requires local_pwr_constraint
+
+        if self._security:
+            for k, v in self._security.generate_dict().items():
+                conf[k] = v
+
+        all_conf = [conf]
+
+        for bss in self._bss_lookup.values():
+            bss_conf = collections.OrderedDict()
+            for k, v in (bss.generate_dict()).items():
+                bss_conf[k] = v
+            all_conf.append(bss_conf)
+
+        return all_conf
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd_constants.py b/acts/framework/acts/controllers/ap_lib/hostapd_constants.py
new file mode 100755
index 0000000..d0dcce1
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/hostapd_constants.py
@@ -0,0 +1,245 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import itertools
+
+WPA1 = 1
+WPA2 = 2
+MIXED = 3
+MAX_WPA_PSK_LENGTH = 64
+MIN_WPA_PSK_LENGTH = 8
+WPA_STRICT_REKEY = 1
+WPA_DEFAULT_CIPHER = 'TKIP'
+WPA2_DEFAULT_CIPER = 'CCMP'
+WPA_GROUP_KEY_ROTATION_TIME = 600
+WPA_STRICT_REKEY_DEFAULT = True
+WPA_STRING = 'wpa'
+WPA2_STRING = 'wpa2'
+WPA_MIXED_STRING = 'wpa/wpa2'
+WLAN0_STRING = 'wlan0'
+WLAN1_STRING = 'wlan1'
+WLAN2_STRING = 'wlan2'
+WLAN3_STRING = 'wlan3'
+AP_DEFAULT_CHANNEL_2G = 6
+AP_DEFAULT_CHANNEL_5G = 36
+AP_DEFAULT_MAX_SSIDS_2G = 8
+AP_DEFAULT_MAX_SSIDS_5G = 8
+AP_SSID_LENGTH_2G = 8
+AP_PASSPHRASE_LENGTH_2G = 10
+AP_SSID_LENGTH_5G = 8
+AP_PASSPHRASE_LENGTH_5G = 10
+
+# A mapping of frequency to channel number.  This includes some
+# frequencies used outside the US.
+CHANNEL_MAP = {
+    2412: 1,
+    2417: 2,
+    2422: 3,
+    2427: 4,
+    2432: 5,
+    2437: 6,
+    2442: 7,
+    2447: 8,
+    2452: 9,
+    2457: 10,
+    2462: 11,
+    # 12, 13 are only legitimate outside the US.
+    2467: 12,
+    2472: 13,
+    # 14 is for Japan, DSSS and CCK only.
+    2484: 14,
+    # 34 valid in Japan.
+    5170: 34,
+    # 36-116 valid in the US, except 38, 42, and 46, which have
+    # mixed international support.
+    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,
+    # 120, 124, 128 valid in Europe/Japan.
+    5600: 120,
+    5620: 124,
+    5640: 128,
+    # 132+ valid in US.
+    5660: 132,
+    5680: 136,
+    5700: 140,
+    # 144 is supported by a subset of WiFi chips
+    # (e.g. bcm4354, but not ath9k).
+    5720: 144,
+    5745: 149,
+    5765: 153,
+    5785: 157,
+    5805: 161,
+    5825: 165
+}
+
+MODE_11A = 'a'
+MODE_11B = 'b'
+MODE_11G = 'g'
+MODE_11N_MIXED = 'n-mixed'
+MODE_11N_PURE = 'n-only'
+MODE_11AC_MIXED = 'ac-mixed'
+MODE_11AC_PURE = 'ac-only'
+
+N_CAPABILITY_LDPC = object()
+N_CAPABILITY_HT40_PLUS = object()
+N_CAPABILITY_HT40_MINUS = object()
+N_CAPABILITY_GREENFIELD = object()
+N_CAPABILITY_SGI20 = object()
+N_CAPABILITY_SGI40 = object()
+N_CAPABILITY_TX_STBC = object()
+N_CAPABILITY_RX_STBC1 = object()
+N_CAPABILITY_RX_STBC12 = object()
+N_CAPABILITY_RX_STBC123 = object()
+N_CAPABILITY_DSSS_CCK_40 = object()
+N_CAPABILITIES_MAPPING = {
+    N_CAPABILITY_LDPC: '[LDPC]',
+    N_CAPABILITY_HT40_PLUS: '[HT40+]',
+    N_CAPABILITY_HT40_MINUS: '[HT40-]',
+    N_CAPABILITY_GREENFIELD: '[GF]',
+    N_CAPABILITY_SGI20: '[SHORT-GI-20]',
+    N_CAPABILITY_SGI40: '[SHORT-GI-40]',
+    N_CAPABILITY_TX_STBC: '[TX-STBC]',
+    N_CAPABILITY_RX_STBC1: '[RX-STBC1]',
+    N_CAPABILITY_RX_STBC12: '[RX-STBC12]',
+    N_CAPABILITY_RX_STBC123: '[RX-STBC123]',
+    N_CAPABILITY_DSSS_CCK_40: '[DSSS_CCK-40]'
+}
+N_CAPABILITY_HT40_MINUS_CHANNELS = object()
+N_CAPABILITY_HT40_PLUS_CHANNELS = object()
+AC_CAPABILITY_VHT160 = object()
+AC_CAPABILITY_VHT160_80PLUS80 = object()
+AC_CAPABILITY_RXLDPC = object()
+AC_CAPABILITY_SHORT_GI_80 = object()
+AC_CAPABILITY_SHORT_GI_160 = object()
+AC_CAPABILITY_TX_STBC_2BY1 = object()
+AC_CAPABILITY_RX_STBC_1 = object()
+AC_CAPABILITY_RX_STBC_12 = object()
+AC_CAPABILITY_RX_STBC_123 = object()
+AC_CAPABILITY_RX_STBC_1234 = object()
+AC_CAPABILITY_SU_BEAMFORMER = object()
+AC_CAPABILITY_SU_BEAMFORMEE = object()
+AC_CAPABILITY_BF_ANTENNA_2 = object()
+AC_CAPABILITY_SOUNDING_DIMENSION_2 = object()
+AC_CAPABILITY_MU_BEAMFORMER = object()
+AC_CAPABILITY_MU_BEAMFORMEE = object()
+AC_CAPABILITY_VHT_TXOP_PS = object()
+AC_CAPABILITY_HTC_VHT = object()
+AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0 = object()
+AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1 = object()
+AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2 = object()
+AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3 = object()
+AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4 = object()
+AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5 = object()
+AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6 = object()
+AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7 = object()
+AC_CAPABILITY_VHT_LINK_ADAPT2 = object()
+AC_CAPABILITY_VHT_LINK_ADAPT3 = object()
+AC_CAPABILITY_RX_ANTENNA_PATTERN = object()
+AC_CAPABILITY_TX_ANTENNA_PATTERN = object()
+AC_CAPABILITY_MAX_MPDU_7991 = object()
+AC_CAPABILITY_MAX_MPDU_11454 = object()
+AC_CAPABILITIES_MAPPING = {
+    AC_CAPABILITY_VHT160: '[VHT160]',
+    AC_CAPABILITY_VHT160_80PLUS80: '[VHT160-80PLUS80]',
+    AC_CAPABILITY_RXLDPC: '[RXLDPC]',
+    AC_CAPABILITY_SHORT_GI_80: '[SHORT-GI-80]',
+    AC_CAPABILITY_SHORT_GI_160: '[SHORT-GI-160]',
+    AC_CAPABILITY_TX_STBC_2BY1: '[TX-STBC-2BY1]',
+    AC_CAPABILITY_RX_STBC_1: '[RX-STBC-1]',
+    AC_CAPABILITY_RX_STBC_12: '[RX-STBC-12]',
+    AC_CAPABILITY_RX_STBC_123: '[RX-STBC-123]',
+    AC_CAPABILITY_RX_STBC_1234: '[RX-STBC-1234]',
+    AC_CAPABILITY_SU_BEAMFORMER: '[SU-BEAMFORMER]',
+    AC_CAPABILITY_SU_BEAMFORMEE: '[SU-BEAMFORMEE]',
+    AC_CAPABILITY_BF_ANTENNA_2: '[BF-ANTENNA-2]',
+    AC_CAPABILITY_SOUNDING_DIMENSION_2: '[SOUNDING-DIMENSION-2]',
+    AC_CAPABILITY_MU_BEAMFORMER: '[MU-BEAMFORMER]',
+    AC_CAPABILITY_MU_BEAMFORMEE: '[MU-BEAMFORMEE]',
+    AC_CAPABILITY_VHT_TXOP_PS: '[VHT-TXOP-PS]',
+    AC_CAPABILITY_HTC_VHT: '[HTC-VHT]',
+    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0: '[MAX-A-MPDU-LEN-EXP0]',
+    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1: '[MAX-A-MPDU-LEN-EXP1]',
+    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2: '[MAX-A-MPDU-LEN-EXP2]',
+    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3: '[MAX-A-MPDU-LEN-EXP3]',
+    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4: '[MAX-A-MPDU-LEN-EXP4]',
+    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5: '[MAX-A-MPDU-LEN-EXP5]',
+    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6: '[MAX-A-MPDU-LEN-EXP6]',
+    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7: '[MAX-A-MPDU-LEN-EXP7]',
+    AC_CAPABILITY_VHT_LINK_ADAPT2: '[VHT-LINK-ADAPT2]',
+    AC_CAPABILITY_VHT_LINK_ADAPT3: '[VHT-LINK-ADAPT3]',
+    AC_CAPABILITY_RX_ANTENNA_PATTERN: '[RX-ANTENNA-PATTERN]',
+    AC_CAPABILITY_TX_ANTENNA_PATTERN: '[TX-ANTENNA-PATTERN]',
+    AC_CAPABILITY_MAX_MPDU_11454: '[MAX-MPDU-11454]',
+    AC_CAPABILITY_MAX_MPDU_7991: '[MAX-MPDU-7991]'
+}
+VHT_CHANNEL_WIDTH_40 = 0
+VHT_CHANNEL_WIDTH_80 = 1
+VHT_CHANNEL_WIDTH_160 = 2
+VHT_CHANNEL_WIDTH_80_80 = 3
+
+# This is a loose merging of the rules for US and EU regulatory
+# domains as taken from IEEE Std 802.11-2012 Appendix E.  For instance,
+# we tolerate HT40 in channels 149-161 (not allowed in EU), but also
+# tolerate HT40+ on channel 7 (not allowed in the US).  We take the loose
+# definition so that we don't prohibit testing in either domain.
+HT40_ALLOW_MAP = {
+    N_CAPABILITY_HT40_MINUS_CHANNELS: tuple(
+        itertools.chain(
+            range(6, 14), range(40, 65, 8), range(104, 137, 8), [153, 161])),
+    N_CAPABILITY_HT40_PLUS_CHANNELS: tuple(
+        itertools.chain(
+            range(1, 8), range(36, 61, 8), range(100, 133, 8), [149, 157]))
+}
+
+PMF_SUPPORT_DISABLED = 0
+PMF_SUPPORT_ENABLED = 1
+PMF_SUPPORT_REQUIRED = 2
+PMF_SUPPORT_VALUES = (PMF_SUPPORT_DISABLED, PMF_SUPPORT_ENABLED,
+                      PMF_SUPPORT_REQUIRED)
+
+DRIVER_NAME = 'nl80211'
+
+CENTER_CHANNEL_MAP = {
+    VHT_CHANNEL_WIDTH_40: {
+        'delta': 2,
+        'channels': ((36, 40), (44, 48), (52, 56), (60, 64), (100, 104),
+                     (108, 112), (116, 120), (124, 128), (132, 136),
+                     (140, 144), (149, 153), (147, 161))
+    },
+    VHT_CHANNEL_WIDTH_80: {
+        'delta': 6,
+        'channels': ((36, 48), (52, 64), (100, 112), (116, 128), (132, 144),
+                     (149, 161))
+    },
+    VHT_CHANNEL_WIDTH_160: {
+        'delta': 14,
+        'channels': ((36, 64), (100, 128))
+    }
+}
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd_security.py b/acts/framework/acts/controllers/ap_lib/hostapd_security.py
new file mode 100644
index 0000000..4e1ae3a
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/hostapd_security.py
@@ -0,0 +1,99 @@
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+
+from acts.controllers.ap_lib import hostapd_constants
+
+
+class Security(object):
+    """The Security class for hostapd representing some of the security
+       settings that are allowed in hostapd.  If needed more can be added.
+    """
+
+    def __init__(self,
+                 security_mode=None,
+                 password=None,
+                 wpa_cipher=hostapd_constants.WPA_DEFAULT_CIPHER,
+                 wpa2_cipher=hostapd_constants.WPA2_DEFAULT_CIPER,
+                 wpa_group_rekey=hostapd_constants.WPA_GROUP_KEY_ROTATION_TIME,
+                 wpa_strict_rekey=hostapd_constants.WPA_STRICT_REKEY_DEFAULT):
+        """Gather all of the security settings for WPA-PSK.  This could be
+           expanded later.
+
+        Args:
+            security_mode: Type of security modes.
+                           Options: wpa, wpa2, wpa/wpa2
+            password: The PSK or passphrase for the security mode.
+            wpa_cipher: The cipher to be used for wpa.
+                        Options: TKIP, CCMP, TKIP CCMP
+                        Default: TKIP
+            wpa2_cipher: The cipher to be used for wpa2.
+                         Options: TKIP, CCMP, TKIP CCMP
+                         Default: CCMP
+            wpa_group_rekey: How often to refresh the GTK regardless of network
+                             changes.
+                             Options: An integrer in seconds, None
+                             Default: 600 seconds
+            wpa_strict_rekey: Whether to do a group key update when client
+                              leaves the network or not.
+                              Options: True, False
+                              Default: True
+        """
+        self.wpa_cipher = wpa_cipher
+        self.wpa2_cipher = wpa2_cipher
+        self.wpa_group_rekey = wpa_group_rekey
+        self.wpa_strict_rekey = wpa_strict_rekey
+        if security_mode == hostapd_constants.WPA_STRING:
+            security_mode = hostapd_constants.WPA1
+        elif security_mode == hostapd_constants.WPA2_STRING:
+            security_mode = hostapd_constants.WPA2
+        elif security_mode == hostapd_constants.WPA_MIXED_STRING:
+            security_mode = hostapd_constants.MIXED
+        else:
+            security_mode = None
+        self.security_mode = security_mode
+        if len(password) < hostapd_constants.MIN_WPA_PSK_LENGTH or len(
+                password) > hostapd_constants.MAX_WPA_PSK_LENGTH:
+            raise ValueError(
+                'Password must be a minumum of %s characters and a maximum of %s'
+                % (hostapd_constants.MIN_WPA_PSK_LENGTH,
+                   hostapd_constants.MAX_WPA_PSK_LENGTH))
+        else:
+            self.password = password
+
+    def generate_dict(self):
+        """Returns: an ordered dictionary of settings"""
+        settings = collections.OrderedDict()
+        if self.security_mode:
+            settings['wpa'] = self.security_mode
+            if len(self.password) == hostapd_constants.MAX_WPA_PSK_LENGTH:
+                settings['wpa_psk'] = self.password
+            else:
+                settings['wpa_passphrase'] = self.password
+
+            if self.security_mode == hostapd_constants.MIXED:
+                settings['wpa_pairwise'] = self.wpa_cipher
+                settings['rsn_pairwise'] = self.wpa2_cipher
+            elif self.security_mode == hostapd_constants.WPA1:
+                settings['wpa_pairwise'] = self.wpa_cipher
+            elif self.security_mode == hostapd_constants.WPA2:
+                settings['rsn_pairwise'] = self.wpa2_cipher
+
+            if self.wpa_group_rekey:
+                settings['wpa_group_rekey'] = self.wpa_group_rekey
+            if self.wpa_strict_rekey:
+                settings[
+                    'wpa_strict_rekey'] = hostapd_constants.WPA_STRICT_REKEY
+        return settings
diff --git a/acts/framework/acts/controllers/attenuator.py b/acts/framework/acts/controllers/attenuator.py
index 29fcf74..38457a6 100644
--- a/acts/framework/acts/controllers/attenuator.py
+++ b/acts/framework/acts/controllers/attenuator.py
@@ -15,40 +15,44 @@
 #   limitations under the License.
 
 import importlib
+import logging
 
 from acts.keys import Config
 
 ACTS_CONTROLLER_CONFIG_NAME = "Attenuator"
 ACTS_CONTROLLER_REFERENCE_NAME = "attenuators"
 
-def create(configs, logger):
+
+def create(configs):
     objs = []
     for c in configs:
         attn_model = c["Model"]
         # Default to telnet.
         protocol = c.get("Protocol", "telnet")
         module_name = "acts.controllers.attenuator_lib.%s.%s" % (attn_model,
-            protocol)
+                                                                 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])
+                               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)
+                    logging.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.
 
@@ -115,7 +119,8 @@
         """
 
         if type(self) is AttenuatorInstrument:
-            raise NotImplementedError("Base class should not be instantiated directly!")
+            raise NotImplementedError(
+                "Base class should not be instantiated directly!")
 
         self.num_atten = num_atten
         self.max_atten = AttenuatorInstrument.INVALID_MAX_ATTEN
@@ -196,8 +201,9 @@
         self.idx = idx
         self.offset = offset
 
-        if(self.idx >= instrument.num_atten):
-            raise IndexError("Attenuator index out of range for attenuator instrument")
+        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.
@@ -212,10 +218,11 @@
             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!")
+        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)
+        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
@@ -238,7 +245,8 @@
         float
             Returns a the max attenuation value
         """
-        if (self.instrument.max_atten == AttenuatorInstrument.INVALID_MAX_ATTEN):
+        if (self.instrument.max_atten ==
+                AttenuatorInstrument.INVALID_MAX_ATTEN):
             raise ValueError("Invalid Max Attenuator Value")
 
         return self.instrument.max_atten - self.offset
diff --git a/acts/framework/acts/controllers/diag_logger.py b/acts/framework/acts/controllers/diag_logger.py
index 96f1f03..551a4ac 100644
--- a/acts/framework/acts/controllers/diag_logger.py
+++ b/acts/framework/acts/controllers/diag_logger.py
@@ -29,7 +29,7 @@
     pass
 
 
-def create(configs, logger):
+def create(configs):
     """Initializes the Diagnotic Logger instances based on the
     provided JSON configuration(s). The expected keys are:
 
@@ -56,11 +56,11 @@
         base_configs = c["Configs"]
         module_name = "{}.{}".format(diag_package_name, diag_logger_type)
         module = importlib.import_module(module_name)
-        logger = getattr(module, diag_logger_name)
+        logger_class = getattr(module, diag_logger_name)
 
-        objs.append(logger(host_log_path,
-                           logger,
-                           config_container=base_configs))
+        objs.append(logger_class(host_log_path,
+                                 None,
+                                 config_container=base_configs))
     return objs
 
 
diff --git a/acts/framework/acts/controllers/event_dispatcher.py b/acts/framework/acts/controllers/event_dispatcher.py
index 92b9c65..93ec2c5 100644
--- a/acts/framework/acts/controllers/event_dispatcher.py
+++ b/acts/framework/acts/controllers/event_dispatcher.py
@@ -22,17 +22,21 @@
 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.
     """
@@ -107,12 +111,12 @@
         """
         if self.started:
             raise IllegalStateError(("Can't register service after polling is"
-                " started"))
+                                     " started"))
         self.lock.acquire()
         try:
             if event_name in self.handlers:
-                raise DuplicateError(
-                    'A handler for {} already exists'.format(event_name))
+                raise DuplicateError('A handler for {} already exists'.format(
+                    event_name))
             self.handlers[event_name] = (handler, args)
         finally:
             self.lock.release()
@@ -179,8 +183,8 @@
         e_queue = self.get_event_q(event_name)
 
         if not e_queue:
-            raise TypeError(
-                "Failed to get an event queue for {}".format(event_name))
+            raise TypeError("Failed to get an event queue for {}".format(
+                event_name))
 
         try:
             # Block for timeout
@@ -190,15 +194,18 @@
             elif timeout == 0:
                 return e_queue.get(False)
             else:
-            # Block forever on event wait
+                # 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))
+            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):
+    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
@@ -238,7 +245,7 @@
                     'Timeout after {}s waiting for event: {}'.format(
                         timeout, event_name))
 
-    def pop_events(self, regex_pattern, timeout):
+    def pop_events(self, regex_pattern, timeout, freq=1):
         """Pop events whose names match a regex pattern.
 
         If such event(s) exist, pop one event from each event queue that
@@ -271,13 +278,12 @@
             results = self._match_and_pop(regex_pattern)
             if len(results) != 0 or time.time() > deadline:
                 break
-            time.sleep(1)
+            time.sleep(freq)
         if len(results) == 0:
-            raise queue.Empty(
-                'Timeout after {}s waiting for event: {}'.format(
-                    timeout, regex_pattern))
+            raise queue.Empty('Timeout after {}s waiting for event: {}'.format(
+                timeout, regex_pattern))
 
-        return sorted(results, key=lambda event : event['time'])
+        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
@@ -311,7 +317,8 @@
                 passed.
         """
         self.lock.acquire()
-        if not event_name in self.event_dict or self.event_dict[event_name] is None:
+        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()
 
@@ -331,9 +338,8 @@
         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):
+                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.
         """
@@ -342,8 +348,13 @@
         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):
+    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
@@ -368,7 +379,8 @@
                 needs to return something to unblock.
         """
         worker = self.executor.submit(self._handle, event_handler, event_name,
-            user_args, event_timeout, cond, cond_timeout)
+                                      user_args, event_timeout, cond,
+                                      cond_timeout)
         return worker
 
     def pop_all(self, event_name):
@@ -389,7 +401,7 @@
         """
         if not self.started:
             raise IllegalStateError(("Dispatcher needs to be started before "
-                "popping."))
+                                     "popping."))
         results = []
         try:
             self.lock.acquire()
diff --git a/acts/framework/acts/controllers/fastboot.py b/acts/framework/acts/controllers/fastboot.py
old mode 100644
new mode 100755
index 096dfae..84c984f
--- a/acts/framework/acts/controllers/fastboot.py
+++ b/acts/framework/acts/controllers/fastboot.py
@@ -14,33 +14,26 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from subprocess import Popen, PIPE
+from acts.libs.proc import job
 
-def exe_cmd(*cmds):
-    """Executes commands in a new shell. Directing stderr to PIPE.
+import logging
 
-    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."""
 
+    def __init__(self, cmd, stdout, stderr, ret_code):
+        self.cmd = cmd
+        self.stdout = stdout
+        self.stderr = stderr
+        self.ret_code = ret_code
+
+    def __str__(self):
+        return ("Error executing fastboot cmd '%s'. ret: %d, stdout: %s,"
+                " stderr: %s") % (self.cmd, self.ret_code, self.stdout,
+                                  self.stderr)
+
+
 class FastbootProxy():
     """Proxy class for fastboot.
 
@@ -49,22 +42,45 @@
     >> fb = FastbootProxy(<serial>)
     >> fb.devices() # will return the console output of "fastboot devices".
     """
-    def __init__(self, serial=""):
+
+    def __init__(self, serial="", ssh_connection=None):
         self.serial = serial
         if serial:
             self.fastboot_str = "fastboot -s {}".format(serial)
         else:
             self.fastboot_str = "fastboot"
+        self.ssh_connection = ssh_connection
 
-    def _exec_fastboot_cmd(self, name, arg_str):
-        return exe_cmd(' '.join((self.fastboot_str, name, arg_str)))
+    def _exec_fastboot_cmd(self,
+                           name,
+                           arg_str,
+                           ignore_status=False,
+                           timeout=60):
+        command = ' '.join((self.fastboot_str, name, arg_str))
+        if self.ssh_connection:
+            result = self.connection.run(command,
+                                         ignore_status=True,
+                                         timeout=timeout)
+        else:
+            result = job.run(command, ignore_status=True, timeout=timeout)
+        ret, out, err = result.exit_status, result.stdout, result.stderr
+        # TODO: This is only a temporary workaround for b/34815412.
+        # fastboot getvar outputs to stderr instead of stdout
+        if "getvar" in command:
+            out = err
+        if ret == 0 or ignore_status:
+            return out
+        else:
+            raise FastbootError(
+                cmd=command, stdout=out, stderr=err, ret_code=ret)
 
-    def args(self, *args):
-        return exe_cmd(' '.join((self.fastboot_str,) + args))
+    def args(self, *args, **kwargs):
+        return job.run(' '.join((self.fastboot_str, ) + args), **kwargs).stdout
 
     def __getattr__(self, name):
-        def fastboot_call(*args):
+        def fastboot_call(*args, **kwargs):
             clean_name = name.replace('_', '-')
             arg_str = ' '.join(str(elem) for elem in args)
-            return self._exec_fastboot_cmd(clean_name, arg_str)
+            return self._exec_fastboot_cmd(clean_name, arg_str, **kwargs)
+
         return fastboot_call
diff --git a/acts/framework/acts/controllers/iperf_server.py b/acts/framework/acts/controllers/iperf_server.py
old mode 100644
new mode 100755
index 3d17d5e..55f39b8
--- a/acts/framework/acts/controllers/iperf_server.py
+++ b/acts/framework/acts/controllers/iperf_server.py
@@ -14,27 +14,27 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import json
+import logging
 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
+from acts import utils
 
 ACTS_CONTROLLER_CONFIG_NAME = "IPerfServer"
 ACTS_CONTROLLER_REFERENCE_NAME = "iperf_servers"
 
-def create(configs, logger):
-    log_path = os.path.dirname(logger.handlers[1].baseFilename)
+
+def create(configs):
     results = []
     for c in configs:
         try:
-            results.append(IPerfServer(c, log_path))
+            results.append(IPerfServer(c, logging.log_path))
         except:
             pass
     return results
 
+
 def destroy(objs):
     for ipf in objs:
         try:
@@ -42,15 +42,86 @@
         except:
             pass
 
+
+class IPerfResult(object):
+    def __init__(self, result_path):
+        try:
+            with open(result_path, 'r') as f:
+                self.result = json.load(f)
+        except ValueError:
+            with open(result_path, 'r') as f:
+                # Possibly a result from interrupted iperf run, skip first line
+                # and try again.
+                lines = f.readlines()[1:]
+                self.result = json.loads(''.join(lines))
+
+    def _has_data(self):
+        """Checks if the iperf result has valid throughput data.
+
+        Returns:
+            True if the result contains throughput data. False otherwise.
+        """
+        return ('end' in self.result) and ('sum' in self.result["end"])
+
+    def get_json(self):
+        """
+        Returns:
+            The raw json output from iPerf.
+        """
+        return self.result
+
+    @property
+    def error(self):
+        if 'error' not in self.result:
+            return None
+        return self.result['error']
+
+    @property
+    def avg_rate(self):
+        """Average receiving rate in MB/s over the entire run.
+
+        If the result is not from a success run, this property is None.
+        """
+        if not self._has_data or 'sum' not in self.result['end']:
+            return None
+        bps = self.result['end']['sum']['bits_per_second']
+        return bps / 8 / 1024 / 1024
+
+    @property
+    def avg_receive_rate(self):
+        """Average receiving rate in MB/s over the entire run. This data may
+        not exist if iperf was interrupted.
+
+        If the result is not from a success run, this property is None.
+        """
+        if not self._has_data or 'sum_received' not in self.result['end']:
+            return None
+        bps = self.result['end']['sum_received']['bits_per_second']
+        return bps / 8 / 1024 / 1024
+
+    @property
+    def avg_send_rate(self):
+        """Average sending rate in MB/s over the entire run. This data may
+        not exist if iperf was interrupted.
+
+        If the result is not from a success run, this property is None.
+        """
+        if not self._has_data or 'sum_sent' not in self.result['end']:
+            return None
+        bps = self.result['end']['sum_sent']['bits_per_second']
+        return bps / 8 / 1024 / 1024
+
+
 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.log_path = os.path.join(log_path, "iPerf{}".format(self.port))
+        self.iperf_str = "iperf3 -s -J -p {}".format(port)
         self.iperf_process = None
-        self.exec_count = 0
+        self.log_files = []
         self.started = False
 
     def start(self, extra_args="", tag=""):
@@ -64,18 +135,18 @@
         """
         if self.started:
             return
-        create_dir(self.log_path)
-        self.exec_count += 1
+        utils.create_dir(self.log_path)
         if tag:
             tag = tag + ','
         out_file_name = "IPerfServer,{},{}{}.log".format(self.port, tag,
-            self.exec_count)
+                                                         len(self.log_files))
         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.iperf_process = utils.start_standing_subprocess(cmd)
+        self.log_files.append(full_out_path)
         self.started = True
 
     def stop(self):
         if self.started:
-            stop_standing_subprocess(self.iperf_process)
+            utils.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
index 080be1a..d573afc 100644
--- a/acts/framework/acts/controllers/monsoon.py
+++ b/acts/framework/acts/controllers/monsoon.py
@@ -13,25 +13,24 @@
 #   WITHOUT 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/).
+Based on the original py2 script of kens@google.com
 """
 
 import fcntl
+import logging
 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
 
-import acts.logger
 import acts.signals
 
 from acts import utils
@@ -40,19 +39,23 @@
 ACTS_CONTROLLER_CONFIG_NAME = "Monsoon"
 ACTS_CONTROLLER_REFERENCE_NAME = "monsoons"
 
-def create(configs, logger):
+
+def create(configs):
     objs = []
     for c in configs:
-        objs.append(Monsoon(serial=c, logger=logger))
+        objs.append(Monsoon(serial=c))
     return objs
 
+
 def destroy(objs):
     return
 
+
 class MonsoonError(acts.signals.ControllerError):
     """Raised for exceptions encountered in monsoon lib."""
 
-class MonsoonProxy:
+
+class MonsoonProxy(object):
     """Class that directly talks to monsoon over serial.
 
     Provides a simple class to use the power meter, e.g.
@@ -105,8 +108,7 @@
                 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)
+                    logging.error("device %s is in use", dev)
                     continue
 
                 try:  # try to open the device
@@ -115,23 +117,21 @@
                     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())
+                    logging.exception("Error opening device %s: %s", dev, e)
                     continue
 
                 if not status:
-                    print("no response from device %s" % dev, file=sys.stderr)
+                    logging.error("no response from device %s", dev)
                 elif serialno and status["serialNumber"] != serialno:
-                    print(("Note: another device serial #%d seen on %s" %
-                        (status["serialNumber"], dev)), file=sys.stderr)
+                    logging.error("Another device serial #%d seen on %s",
+                                  status["serialNumber"], dev)
                 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)
+            logging.info("Waiting for device...")
             time.sleep(1)
 
     def GetStatus(self):
@@ -143,21 +143,48 @@
         # 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", ]
+            "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
@@ -166,11 +193,11 @@
                 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)
+                logging.warning("Wanted status, dropped type=0x%02x, len=%d",
+                                read_bytes[0], len(read_bytes))
                 continue
             status = dict(zip(STATUS_FIELDS, struct.unpack(STATUS_FORMAT,
-                read_bytes)))
+                                                           read_bytes)))
             p_type = status["packetType"]
             if p_type != 0x10:
                 raise MonsoonError("Package type %s is not 0x10." % p_type)
@@ -178,9 +205,9 @@
                 if k.endswith("VoltageSetting"):
                     status[k] = 2.0 + status[k] * 0.01
                 elif k.endswith("FineCurrent"):
-                    pass # needs calibration data
+                    pass  # needs calibration data
                 elif k.endswith("CoarseCurrent"):
-                    pass # needs calibration data
+                    pass  # needs calibration data
                 elif k.startswith("voltage") or k.endswith("Voltage"):
                     status[k] = status[k] * 0.000125
                 elif k.endswith("Resistor"):
@@ -193,7 +220,7 @@
 
     def RampVoltage(self, start, end):
         v = start
-        if v < 3.0: v = 3.0 # protocol doesn't support lower than this
+        if v < 3.0: v = 3.0  # protocol doesn't support lower than this
         while (v < end):
             self.SetVoltage(v)
             v += .1
@@ -221,8 +248,8 @@
         """
         if i < 0 or i > 8:
             raise MonsoonError(("Target max current %sA, is out of acceptable "
-                "range [0, 8].") % i)
-        val = 1023 - int((i/8)*1023)
+                                "range [0, 8].") % i)
+        val = 1023 - int((i / 8) * 1023)
         self._SendStruct("BBB", 0x01, 0x0a, val & 0xff)
         self._SendStruct("BBB", 0x01, 0x0b, val >> 8)
 
@@ -231,8 +258,8 @@
         """
         if i < 0 or i > 8:
             raise MonsoonError(("Target max current %sA, is out of acceptable "
-                "range [0, 8].") % i)
-        val = 1023 - int((i/8)*1023)
+                                "range [0, 8].") % i)
+        val = 1023 - int((i / 8) * 1023)
         self._SendStruct("BBB", 0x01, 0x08, val & 0xff)
         self._SendStruct("BBB", 0x01, 0x09, val >> 8)
 
@@ -252,13 +279,13 @@
     def StartDataCollection(self):
         """Tell the device to start collecting and sending measurement data.
         """
-        self._SendStruct("BBB", 0x01, 0x1b, 0x01) # Mystery command
+        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
+        self._SendStruct("BB", 0x03, 0x00)  # stop
 
     def CollectData(self):
         """Return some current samples. Call StartDataCollection() first.
@@ -268,22 +295,22 @@
             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)
+                logging.warning("Wanted data, dropped type=0x%02x, len=%d",
+                                _bytes[0], len(_bytes))
                 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)]
+            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)
+                logging.warning("Data sequence skipped, lost packet?")
             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)
+                    logging.warning(
+                        "Waiting for calibration, dropped data packet.")
                     continue
                 out = []
                 for main, usb, aux, voltage in data:
@@ -300,13 +327,13 @@
                 self._fine_ref = data[0][0]
                 self._coarse_ref = data[1][0]
             else:
-                print("Discarding data packet type=0x%02x" % _type,
-                    file=sys.stderr)
+                logging.warning("Discarding data packet type=0x%02x", _type)
                 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)
+                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)
 
@@ -315,8 +342,8 @@
         """
         data = struct.pack(fmt, *args)
         data_len = len(data) + 1
-        checksum = (data_len + sum(data)) % 256
-        out = bytes([data_len]) + data + bytes([checksum])
+        checksum = (data_len + sum(bytearray(data))) % 256
+        out = struct.pack("B", data_len) + data + struct.pack("B", checksum)
         self.ser.write(out)
 
     def _ReadPacket(self):
@@ -324,22 +351,24 @@
         """
         len_char = self.ser.read(1)
         if not len_char:
-            print("Reading from serial port timed out.", file=sys.stderr)
+            logging.error("Reading from serial port timed out.")
             return None
 
         data_len = ord(len_char)
         if not data_len:
             return ""
         result = self.ser.read(int(data_len))
+        result = bytearray(result)
         if len(result) != data_len:
-            print("Length mismatch, expected %d bytes, got %d bytes." % data_len,
-                len(result))
+            logging.error("Length mismatch, expected %d bytes, got %d bytes.",
+                          data_len, len(result))
             return None
         body = result[:-1]
-        checksum = (sum(result[:-1]) + data_len) % 256
+        checksum = (sum(struct.unpack("B" * len(body), body)) + 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])))
+            logging.error(
+                "Invalid checksum from serial port! Expected %s, got %s",
+                hex(checksum), hex(result[-1]))
             return None
         return result[:-1]
 
@@ -348,10 +377,10 @@
         self.ser.flush()
         flushed = 0
         while True:
-            ready_r, ready_w, ready_x = select.select([self.ser], [],
-                [self.ser], 0)
+            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)
+                logging.error("Exception from serial port.")
                 return None
             elif len(ready_r) > 0:
                 flushed += 1
@@ -360,9 +389,10 @@
             else:
                 break
         # if flushed > 0:
-        #     print("dropped >%d bytes" % flushed, file=sys.stderr)
+        #     logging.info("dropped >%d bytes" % flushed)
 
-class MonsoonData:
+
+class MonsoonData(object):
     """A class for reporting power measurement data from monsoon.
 
     Data means the measured current value in Amps.
@@ -391,7 +421,8 @@
         num_of_data_pt = len(self._data_points)
         if self.offset >= num_of_data_pt:
             raise MonsoonError(("Offset number (%d) must be smaller than the "
-                "number of data points (%d).") % (offset, num_of_data_pt))
+                                "number of data points (%d).") %
+                               (offset, num_of_data_pt))
         self.data_points = self._data_points[self.offset:]
         self.timestamps = self._timestamps[self.offset:]
         self.hz = hz
@@ -437,8 +468,7 @@
         lines = data_str.strip().split('\n')
         err_msg = ("Invalid input string format. Is this string generated by "
                    "MonsoonData class?")
-        conditions = [len(lines) <= 4,
-                      "Average Current:" not in lines[1],
+        conditions = [len(lines) <= 4, "Average Current:" not in lines[1],
                       "Voltage: " not in lines[2],
                       "Total Power: " not in lines[3],
                       "samples taken at " not in lines[4],
@@ -561,9 +591,8 @@
         strs.append("Voltage: {}V.".format(self.voltage))
         strs.append("Total Power: {}mW.".format(self.total_power))
         strs.append(("{} samples taken at {}Hz, with an offset of {} samples."
-                    ).format(len(self._data_points),
-                             self.hz,
-                             self.offset))
+                     ).format(
+                         len(self._data_points), self.hz, self.offset))
         return "\n".join(strs)
 
     def __len__(self):
@@ -580,16 +609,15 @@
     def __repr__(self):
         return self._header()
 
-class Monsoon:
+
+class Monsoon(object):
     """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 = acts.logger.LoggerProxy(kwargs["logger"])
-        else:
-            self.log = acts.logger.LoggerProxy()
+        self.log = logging.getLogger()
         if "device" in kwargs:
             device = kwargs["device"]
         self.mon = MonsoonProxy(serialno=serial, device=device)
@@ -663,8 +691,8 @@
         """
         sys.stdout.flush()
         voltage = self.mon.GetVoltage()
-        self.log.info("Taking samples at %dhz for %ds, voltage %.2fv." % (
-            sample_hz, sample_num/sample_hz, voltage))
+        self.log.info("Taking samples at %dhz for %ds, voltage %.2fv.",
+                      sample_hz, (sample_num / sample_hz), voltage)
         sample_num += sample_offset
         # Make sure state is normal
         self.mon.StopDataCollection()
@@ -692,7 +720,7 @@
                 # 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
+                if need > len(collected):  # still need more input samples
                     samples = self.mon.CollectData()
                     if not samples:
                         break
@@ -708,22 +736,25 @@
                         this_time = int(time.time())
                         timestamps.append(this_time)
                         if live:
-                            self.log.info("%s %s" % (this_time, this_sample))
+                            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
+                        emitted += 1  # adjust for emitting 1 output sample
                     collected = collected[need:]
                     now = time.time()
-                    if now - last_flush >= 0.99: # flush every second
+                    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)
+            return MonsoonData(current_values,
+                               timestamps,
+                               sample_hz,
+                               voltage,
+                               offset=sample_offset)
         except:
             return None
 
@@ -744,15 +775,11 @@
         Returns:
             True if the state is legal and set. False otherwise.
         """
-        state_lookup = {
-            "off": 0,
-            "on": 1,
-            "auto": 2
-        }
+        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]):
+            while (current_state != state_lookup[state]):
                 self.mon.SetUsbPassthrough(state_lookup[state])
                 time.sleep(1)
                 current_state = self.mon.GetUsbPassthrough()
@@ -773,7 +800,13 @@
             pass
         ad.adb.wait_for_device()
 
-    def execute_sequence_and_measure(self, step_funcs, hz, duration, offset_sec=20, *args, **kwargs):
+    def execute_sequence_and_measure(self,
+                                     step_funcs,
+                                     hz,
+                                     duration,
+                                     offset_sec=20,
+                                     *args,
+                                     **kwargs):
         """@Deprecated.
         Executes a sequence of steps and take samples in-between.
 
@@ -813,7 +846,7 @@
         try:
             if len(duration) != len(step_funcs):
                 raise MonsoonError(("The number of durations need to be the "
-                    "same as the number of step functions."))
+                                    "same as the number of step functions."))
             for d in duration:
                 sample_nums.append(d * 60 * hz)
         except TypeError:
@@ -825,25 +858,24 @@
             try:
                 self.usb("auto")
                 step_name = func.__name__
-                self.log.info("Executing step function %s." % step_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)
+                    self.log.info("Skip taking samples for %s", step_name)
                     continue
                 time.sleep(1)
                 self.dut.stop_services()
                 time.sleep(1)
-                self.log.info("Taking samples for %s." % step_name)
+                self.log.info("Taking samples for %s.", step_name)
                 data = self.take_samples(hz, num, sample_offset=oset)
                 if not data:
                     raise MonsoonError("Sampling for %s failed." % step_name)
-                self.log.info("Sample summary: %s" % repr(data))
-                # self.log.debug(str(data))
+                self.log.info("Sample summary: %s", repr(data))
                 data.tag = step_name
                 results.append(data)
             except Exception:
-                msg = "Exception happened during step %s, abort!" % func.__name__
-                self.log.exception(msg)
+                self.log.exception("Exception happened during step %s, abort!"
+                                   % func.__name__)
                 return results
             finally:
                 self.mon.StopDataCollection()
@@ -862,7 +894,7 @@
 
         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.
+        is 30s. The total time taken to measure will be (duration + offset).
 
         Args:
             hz: Number of samples to take per second.
@@ -873,10 +905,6 @@
         Returns:
             A MonsoonData object with the measured power data.
         """
-        if offset >= duration:
-            raise MonsoonError(("Measurement duration (%ds) should be larger "
-                "than offset (%ds) for measurement %s."
-                ) % (duration, offset, tag))
         num = duration * hz
         oset = offset * hz
         data = None
@@ -887,10 +915,10 @@
             time.sleep(1)
             data = self.take_samples(hz, num, sample_offset=oset)
             if not data:
-                raise MonsoonError(("No data was collected in measurement %s."
-                    ) % tag)
+                raise MonsoonError((
+                    "No data was collected in measurement %s.") % tag)
             data.tag = tag
-            self.log.info("Measurement summary: %s" % repr(data))
+            self.log.info("Measurement summary: %s", repr(data))
         finally:
             self.mon.StopDataCollection()
             self.log.info("Finished taking samples, reconnecting to dut.")
diff --git a/acts/framework/acts/controllers/native.py b/acts/framework/acts/controllers/native.py
index c2375bd..7c9df2b 100644
--- a/acts/framework/acts/controllers/native.py
+++ b/acts/framework/acts/controllers/native.py
@@ -14,7 +14,6 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-
 from acts.controllers.android import Android
 import json
 import os
@@ -24,24 +23,29 @@
 HOST = os.environ.get('AP_HOST', None)
 PORT = os.environ.get('AP_PORT', 9999)
 
+
 class SL4NException(Exception):
     pass
 
+
 class SL4NAPIError(SL4NException):
     """Raised when remote API reports an error."""
 
+
 class SL4NProtocolError(SL4NException):
     """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 NativeAndroid(Android):
     COUNTER = IDCounter()
 
@@ -49,21 +53,19 @@
         self.lock.acquire()
         apiid = next(NativeAndroid.COUNTER)
         self.lock.release()
-        data = {'id': apiid,
-                'method': method,
-                'params': args}
+        data = {'id': apiid, 'method': method, 'params': args}
         request = json.dumps(data)
-        self.client.write(request.encode("utf8")+b'\n')
+        self.client.write(request.encode("utf8") + b'\n')
         self.client.flush()
         response = self.client.readline()
         if not response:
             raise SL4NProtocolError(SL4NProtocolError.NO_RESPONSE_FROM_SERVER)
         #TODO: (tturney) fix the C side from sending \x00 char over the socket.
-        result = json.loads(
-                str(response, encoding="utf8").rstrip().replace("\x00", ""))
+        result = json.loads(str(response,
+                                encoding="utf8").rstrip().replace("\x00", ""))
 
         if result['error']:
             raise SL4NAPIError(result['error'])
         if result['id'] != apiid:
             raise SL4NProtocolError(SL4NProtocolError.MISMATCHED_API_ID)
-        return result['result']
\ No newline at end of file
+        return result['result']
diff --git a/acts/framework/acts/controllers/native_android_device.py b/acts/framework/acts/controllers/native_android_device.py
index 4730f68..00662b3 100644
--- a/acts/framework/acts/controllers/native_android_device.py
+++ b/acts/framework/acts/controllers/native_android_device.py
@@ -15,11 +15,11 @@
 #   limitations under the License.
 
 from acts.controllers.android_device import AndroidDevice
-from acts.controllers.adb import is_port_available
-from acts.controllers.adb import get_available_host_port
+from acts.controllers.utils_lib  import host_utils
 import acts.controllers.native as native
 from subprocess import call
 
+import logging
 import time
 
 #TODO(tturney): Merge this into android device
@@ -27,7 +27,9 @@
 ACTS_CONTROLLER_CONFIG_NAME = "NativeAndroidDevice"
 ACTS_CONTROLLER_REFERENCE_NAME = "native_android_devices"
 
-def create(configs, logger):
+
+def create(configs):
+    logger = logging.getLogger()
     ads = get_instances(configs, logger)
     for ad in ads:
         try:
@@ -36,9 +38,11 @@
             logger.exception("Failed to start sl4n on %s" % ad.serial)
     return ads
 
+
 def destroy(ads):
     pass
 
+
 def get_instances(serials, logger=None):
     """Create AndroidDevice instances from a list of serials.
 
@@ -54,11 +58,12 @@
         results.append(NativeAndroidDevice(s, logger=logger))
     return results
 
+
 class NativeAndroidDeviceError(Exception):
     pass
 
-class NativeAndroidDevice(AndroidDevice):
 
+class NativeAndroidDevice(AndroidDevice):
     def __del__(self):
         if self.h_port:
             self.adb.forward("--remove tcp:%d" % self.h_port)
@@ -89,16 +94,17 @@
             >>> ad = NativeAndroidDevice()
             >>> droid, ed = ad.get_droid()
         """
-        if not self.h_port or not is_port_available(self.h_port):
-            self.h_port = get_available_host_port()
+        if not self.h_port or not host_utils.is_port_available(self.h_port):
+            self.h_port = host_utils.get_available_host_port()
         self.adb.tcp_forward(self.h_port, self.d_port)
-        pid = self.adb.shell(
-            "ps | grep sl4n | awk '{print $2}'").decode('ascii')
+        pid = self.adb.shell("ps | grep sl4n | awk '{print $2}'").decode(
+            'ascii')
         while (pid):
             self.adb.shell("kill {}".format(pid))
-            pid = self.adb.shell(
-                "ps | grep sl4n | awk '{print $2}'").decode('ascii')
-        call(["adb -s " + self.serial + " shell sh -c \"/system/bin/sl4n\" &"],
+            pid = self.adb.shell("ps | grep sl4n | awk '{print $2}'").decode(
+                'ascii')
+        call(
+            ["adb -s " + self.serial + " shell sh -c \"/system/bin/sl4n\" &"],
             shell=True)
         try:
             time.sleep(3)
@@ -123,7 +129,7 @@
         droid = native.NativeAndroid(port=self.h_port)
         if droid.uid in self._droid_sessions:
             raise bt.SL4NException(("SL4N returned an existing uid for a "
-                "new session. Abort."))
+                                    "new session. Abort."))
             return droid
         self._droid_sessions[droid.uid] = [droid]
         return droid
diff --git a/acts/framework/acts/controllers/sl4a_client.py b/acts/framework/acts/controllers/sl4a_client.py
new file mode 100644
index 0000000..c80cd09
--- /dev/null
+++ b/acts/framework/acts/controllers/sl4a_client.py
@@ -0,0 +1,360 @@
+#/usr/bin/env 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.
+"""
+
+from builtins import str
+
+import json
+import logging
+import os
+import socket
+import sys
+import threading
+import time
+
+from acts.controllers import adb
+
+HOST = os.environ.get('SL4A_HOST_ADDRESS', None)
+PORT = os.environ.get('SL4A_HOST_PORT', 9999)
+DEFAULT_DEVICE_SIDE_PORT = 8080
+
+UNKNOWN_UID = -1
+
+MAX_SL4A_START_RETRY = 3
+MAX_SL4A_WAIT_TIME = 30
+
+_SL4A_LAUNCH_CMD = (
+    "am start -a com.googlecode.android_scripting.action.LAUNCH_SERVER "
+    "--ei com.googlecode.android_scripting.extra.USE_SERVICE_PORT {} "
+    "com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher")
+
+
+class Sl4aException(Exception):
+    pass
+
+
+class Sl4aStartError(Sl4aException):
+    """Raised when sl4a is not able to be started."""
+
+
+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 start_sl4a(adb_proxy,
+               device_side_port=DEFAULT_DEVICE_SIDE_PORT,
+               wait_time=MAX_SL4A_WAIT_TIME,
+               retries=MAX_SL4A_START_RETRY):
+    """Starts sl4a server on the android device.
+
+    Args:
+        adb_proxy: adb.AdbProxy, The adb proxy to use to start sl4a
+        device_side_port: int, The port number to open on the device side.
+        wait_time: float, The time to wait for sl4a to come up before raising
+                   an error.
+        retries: number of sl4a start command retries.
+
+    Raises:
+        Sl4aException: Raised when SL4A was not able to be started.
+    """
+    if not is_sl4a_installed(adb_proxy):
+        raise Sl4aStartError("SL4A is not installed on %s" % adb_proxy.serial)
+    for _ in range(retries):
+        adb_proxy.shell(_SL4A_LAUNCH_CMD.format(device_side_port))
+        for _ in range(wait_time):
+            time.sleep(1)
+            if is_sl4a_running(adb_proxy):
+                logging.debug("SL4A is running")
+                return
+    raise Sl4aStartError("SL4A failed to start on %s." % adb_proxy.serial)
+
+
+def stop_sl4a(adb_proxy):
+    """Kills any running instance of sl4a.
+
+    Kills any running instance of sl4a. If no instance is running then nothing
+    is done.
+
+    Args:
+        adb_proxy: adb.AdbProxy, The adb proxy to use for checking.
+    """
+    adb_proxy.shell(
+        'am force-stop com.googlecode.android_scripting', ignore_status=True)
+
+
+def is_sl4a_installed(adb_proxy):
+    """Checks if sl4a is installed by querying the package path of sl4a.
+
+    Args:
+        adb: adb.AdbProxy, The adb proxy to use for checking install.
+
+    Returns:
+        True if sl4a is installed, False otherwise.
+    """
+    try:
+        if adb_proxy.shell("pm path com.googlecode.android_scripting"):
+            return True
+    except adb.AdbError as e:
+        if not e.stderr:
+            return False
+        raise
+    return False
+
+
+def is_sl4a_running(adb_proxy, use_new_ps=True):
+    """Checks if the sl4a app is running on an android device.
+
+    Args:
+        adb_proxy: adb.AdbProxy, The adb proxy to use for checking.
+        use_new_ps: Newer versions of ps allow for the flag -A to show all
+                    processes. But older versions will interpert this as a pid.
+                    When true this will use the newer version and fall back to
+                    the old on failure.
+
+    Returns:
+        True if the sl4a app is running, False otherwise.
+    """
+    # Grep for process with a preceding S which means it is truly started.
+    try:
+        if use_new_ps:
+            out = adb_proxy.shell(
+                'ps -A | grep "S com.googlecode.android_scripting"')
+        else:
+            out = adb_proxy.shell(
+                'ps | grep "S com.googlecode.android_scripting"')
+    except Exception as e:
+        logging.error("is_sl4a_running with exception %s", e)
+        out = None
+    if not out:
+        if use_new_ps:
+            out = adb_proxy.shell('ps -A | grep "bad pid"', ignore_status=True)
+            if 'bad pid' in str(out):
+                return is_sl4a_running(adb_proxy, use_new_ps=False)
+        return False
+    return True
+
+
+class Sl4aCommand(object):
+    """Commands that can be invoked on the sl4a client.
+
+    INIT: Initializes a new sessions in sl4a.
+    CONTINUE: Creates a connection.
+    """
+    INIT = 'initiate'
+    CONTINUE = 'continue'
+
+
+class Sl4aClient(object):
+    """A sl4a client that is connected to remotely.
+
+    Connects to a remove device running sl4a. Before opening a connection
+    a port forward must be setup to go over usb. This be done using
+    adb.tcp_forward(). This is calling the shell command
+    adb forward <local> remote>. Once the port has been forwarded it can be
+    used in this object as the port of communication.
+
+    Attributes:
+        port: int, The host port to communicate through.
+        addr: str, The host address who is communicating to the device (usually
+                   localhost).
+        client: file, The socket file used to communicate.
+        uid: int, The sl4a uid of this session.
+        conn: socket.Socket, The socket connection to the remote client.
+    """
+
+    _SOCKET_TIMEOUT = 60
+
+    def __init__(self, port=PORT, addr=HOST, uid=UNKNOWN_UID):
+        """
+        Args:
+            port: int, The port this client should connect to.
+            addr: str, The address this client should connect to.
+            uid: int, The uid of the session to join, or UNKNOWN_UID to start a
+                 new session.
+        """
+        self.port = port
+        self.addr = addr
+        self._lock = threading.Lock()
+        self._counter = self._id_counter()
+        self.client = None  # prevent close errors on connect failure
+        self.uid = uid
+        self.conn = None
+
+    def __del__(self):
+        self.close()
+
+    def _id_counter(self):
+        i = 0
+        while True:
+            yield i
+            i += 1
+
+    def open(self, connection_timeout=60, cmd=Sl4aCommand.INIT):
+        """Opens a connection to the remote client.
+
+        Opens a connection to a remote client with sl4a. The connection will
+        error out if it takes longer than the connection_timeout time. Once
+        connected if the socket takes longer than _SOCKET_TIMEOUT to respond
+        the connection will be closed.
+
+        Args:
+            connection_timeout: int, The time to wait for the connection to come
+                                up.
+            cmd: Sl4aCommand, The command to use for creating the connection.
+
+        Raises:
+            IOError: Raised when the socket times out from io error
+            socket.timeout: Raised when the socket waits to long for connection.
+            Sl4aProtocolError: Raised when there is an error in the protocol.
+        """
+        if connection_timeout:
+            time_left = connection_timeout
+        else:
+            time_left = None
+
+        start_time = time.time()
+
+        def get_time_left():
+            if connection_timeout:
+                return connection_timeout - (time.time() - start_time)
+            # Assume system default if none is given.
+            return socket.getdefaulttimeout()
+
+        while True:
+            try:
+                self.conn = socket.create_connection((self.addr, self.port),
+                                                     max(1, get_time_left()))
+                self.conn.settimeout(self._SOCKET_TIMEOUT)
+                self.client = self.conn.makefile(mode="brw")
+
+                # Some devices will allow a connection but then disconnect
+                # instead of failing on create connection. The first command
+                # Will be error handled to make sure this does not happen.
+                resp = self._cmd(cmd, self.uid)
+                break
+            except (socket.timeout):
+                logging.exception("Failed to create socket connection!")
+                raise
+            except (socket.error, IOError):
+                # TODO: optimize to only forgive some errors here
+                # error values are OS-specific so this will require
+                # additional tuning to fail faster
+                time_left = get_time_left()
+                if time_left <= 0:
+                    logging.exception("Failed to create socket connection!")
+                    raise
+                time.sleep(1)
+
+        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 = UNKNOWN_UID
+
+        return self  # Allow the idiom Sl4aClient(...).open()
+
+    def close(self):
+        """Close the connection to the remote client."""
+        if self.conn is not None:
+            self.conn.close()
+            self.conn = None
+
+    def _cmd(self, command, uid=None):
+        """Send a command to sl4a.
+
+        Given a command name, this will package the command and send it to
+        sl4a.
+
+        Args:
+            command: str, The name of the command to execute.
+            uid: int, the uid of the session to send the command to.
+
+        Returns:
+            The line that was written back.
+        """
+        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, **kwargs):
+        """Sends an rpc to sl4a.
+
+        Sends an rpc call to sl4a using this clients connection.
+
+        Args:
+            method: str, The name of the method to execute.
+            args: any, The args to send to sl4a.
+            kwargs: timeout: timeout for the RPC call.
+
+        Returns:
+            The result of the rpc.
+
+        Raises:
+            Sl4aProtocolError: Something went wrong with the sl4a protocol.
+            Sl4aApiError: The rpc went through, however executed with errors.
+        """
+        timeout = kwargs.get("timeout")
+        with self._lock:
+            apiid = next(self._counter)
+        if timeout:
+            self.conn.settimeout(timeout)
+        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:
+            logging.error("No response for RPC method %s", method)
+            raise Sl4aProtocolError(Sl4aProtocolError.NO_RESPONSE_FROM_SERVER)
+        result = json.loads(str(response, encoding="utf8"))
+        if timeout:
+            self.conn.settimeout(self._SOCKET_TIMEOUT)
+        if result['error']:
+            logging.error("RPC method %s with error %s", method,
+                          result['error'])
+            raise Sl4aApiError("RPC call %s failed with error %s" %
+                               (method, result['error']))
+        if result['id'] != apiid:
+            logging.error("RPC method %s with mismatched api id %s", method,
+                          result['id'])
+            raise Sl4aProtocolError(Sl4aProtocolError.MISMATCHED_API_ID)
+        return result['result']
+
+    def __getattr__(self, name):
+        """Wrapper for python magic to turn method calls into RPC calls."""
+
+        def rpc_call(*args, **kwargs):
+            return self._rpc(name, *args, **kwargs)
+
+        return rpc_call
diff --git a/acts/framework/acts/controllers/sniffer.py b/acts/framework/acts/controllers/sniffer.py
index 3ec335a..cc84ddb 100644
--- a/acts/framework/acts/controllers/sniffer.py
+++ b/acts/framework/acts/controllers/sniffer.py
@@ -15,11 +15,13 @@
 #   limitations under the License.
 
 import importlib
+import logging
 
 ACTS_CONTROLLER_CONFIG_NAME = "Sniffer"
 ACTS_CONTROLLER_REFERENCE_NAME = "sniffers"
 
-def create(configs, logger):
+
+def create(configs):
     """Initializes the sniffer structures based on the JSON configuration. The
     expected keys are:
 
@@ -38,10 +40,11 @@
         sniffer_subtype = c["SubType"]
         interface = c["Interface"]
         base_configs = c["BaseConfigs"]
-        module_name = "acts.controllers.sniffer_lib.{}.{}".format(sniffer_type,
-                                                            sniffer_subtype)
+        module_name = "acts.controllers.sniffer_lib.{}.{}".format(
+            sniffer_type, sniffer_subtype)
         module = importlib.import_module(module_name)
-        objs.append(module.Sniffer(interface, logger,
+        objs.append(module.Sniffer(interface,
+                                   logging.getLogger(),
                                    base_configs=base_configs))
     return objs
 
@@ -167,8 +170,11 @@
         """
         raise NotImplementedError("Base class should not be called directly!")
 
-    def start_capture(self, override_configs=None, additional_args=None,
-                      duration=None, packet_count=None):
+    def start_capture(self,
+                      override_configs=None,
+                      additional_args=None,
+                      duration=None,
+                      packet_count=None):
         """This function starts a capture which is saved to the specified file
         path.
 
diff --git a/acts/framework/acts/controllers/utils_lib/__init__.py b/acts/framework/acts/controllers/utils_lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/__init__.py
diff --git a/acts/framework/acts/controllers/utils_lib/commands/__init__.py b/acts/framework/acts/controllers/utils_lib/commands/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/commands/__init__.py
diff --git a/acts/framework/acts/controllers/utils_lib/commands/ip.py b/acts/framework/acts/controllers/utils_lib/commands/ip.py
new file mode 100644
index 0000000..0c1c025
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/commands/ip.py
@@ -0,0 +1,130 @@
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import ipaddress
+import re
+
+
+class LinuxIpCommand(object):
+    """Interface for doing standard IP commands on a linux system.
+
+    Wraps standard shell commands used for ip into a python object that can
+    be interacted with more easily.
+    """
+
+    def __init__(self, runner):
+        """
+        Args:
+            runner: Object that can take unix commands and run them in an
+                    enviroment (eg. connection.SshConnection).
+        """
+        self._runner = runner
+
+    def get_ipv4_addresses(self, net_interface):
+        """Gets all ipv4 addresses of a network interface.
+
+        Args:
+            net_interface: string, The network interface to get info on
+                           (eg. wlan0).
+
+        Returns: An iterator of tuples that contain (address, broadcast).
+                 where address is a ipaddress.IPv4Interface and broadcast
+                 is an ipaddress.IPv4Address.
+        """
+        results = self._runner.run('ip addr show dev %s' % net_interface)
+        lines = results.stdout.splitlines()
+
+        # Example stdout:
+        # 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
+        #   link/ether 48:0f:cf:3c:9d:89 brd ff:ff:ff:ff:ff:ff
+        #   inet 192.168.1.1/24 brd 192.168.1.255 scope global eth0
+        #       valid_lft forever preferred_lft forever
+        #   inet6 2620:0:1000:1500:a968:a776:2d80:a8b3/64 scope global temporary dynamic
+        #       valid_lft 599919sec preferred_lft 80919sec
+
+        for line in lines:
+            line = line.strip()
+            match = re.search('inet (?P<address>[^\s]*) brd (?P<bcast>[^\s]*)',
+                              line)
+            if match:
+                d = match.groupdict()
+                address = ipaddress.IPv4Interface(d['address'])
+                bcast = ipaddress.IPv4Address(d['bcast'])
+                yield (address, bcast)
+
+            match = re.search('inet (?P<address>[^\s]*)', line)
+            if match:
+                d = match.groupdict()
+                address = ipaddress.IPv4Interface(d['address'])
+                yield (address, None)
+
+    def add_ipv4_address(self, net_interface, address, broadcast=None):
+        """Adds an ipv4 address to a net_interface.
+
+        Args:
+            net_interface: string, The network interface
+                           to get the new ipv4 (eg. wlan0).
+            address: ipaddress.IPv4Interface, The new ipaddress and netmask
+                     to add to an interface.
+            broadcast: ipaddress.IPv4Address, The broadcast address to use for
+                       this net_interfaces subnet.
+        """
+        if broadcast:
+            self._runner.run('ip addr add %s broadcast %s dev %s' %
+                             (address, broadcast, net_interface))
+        else:
+            self._runner.run('ip addr add %s dev %s' %
+                             (address, net_interface))
+
+    def remove_ipv4_address(self, net_interface, address):
+        """Remove an ipv4 address.
+
+        Removes an ipv4 address from a network interface.
+
+        Args:
+            net_interface: string, The network interface to remove the
+                           ipv4 address from (eg. wlan0).
+            address: ipaddress.IPv4Interface or ipaddress.IPv4Address,
+                     The ip address to remove from the net_interface.
+        """
+        self._runner.run('ip addr del %s dev %s' % (address, net_interface))
+
+    def set_ipv4_address(self, net_interface, address, broadcast=None):
+        """Set the ipv4 address.
+
+        Sets the ipv4 address of a network interface. If the network interface
+        has any other ipv4 addresses these will be cleared.
+
+        Args:
+            net_interface: string, The network interface to set the ip address
+                           on (eg. wlan0).
+            address: ipaddress.IPv4Interface, The ip address and subnet to give
+                     the net_interface.
+            broadcast: ipaddress.IPv4Address, The broadcast address to use for
+                       the subnet.
+        """
+        self.clear_ipv4_addresses(net_interface)
+        self.add_ipv4_address(net_interface, address, broadcast)
+
+    def clear_ipv4_addresses(self, net_interface):
+        """Clears all ipv4 addresses registered to a net_interface.
+
+        Args:
+            net_interface: string, The network interface to clear addresses from
+                           (eg. wlan0).
+        """
+        ip_info = self.get_ipv4_addresses(net_interface)
+
+        for address, _ in ip_info:
+            self.remove_ipv4_address(net_interface, address)
diff --git a/acts/framework/acts/controllers/utils_lib/commands/route.py b/acts/framework/acts/controllers/utils_lib/commands/route.py
new file mode 100644
index 0000000..1597af9
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/commands/route.py
@@ -0,0 +1,191 @@
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import ipaddress
+import re
+
+from acts.controllers.utils_lib.ssh import connection
+
+
+class Error(Exception):
+    """Exception thrown when a valid ip command experiences errors."""
+
+
+class NetworkInterfaceDown(Error):
+    """Exception thrown when a network interface is down."""
+
+
+class LinuxRouteCommand(object):
+    """Interface for doing standard ip route commands on a linux system."""
+
+    DEFAULT_ROUTE = 'default'
+
+    def __init__(self, runner):
+        """
+        Args:
+            runner: Object that can take unix commands and run them in an
+                    enviroment.
+        """
+        self._runner = runner
+
+    def add_route(self, net_interface, address):
+        """Add an entry to the ip routing table.
+
+        Will add a route for either a specific ip address, or a network.
+
+        Args:
+            net_interface: string, Any packet that sends through this route
+                           will be sent using this network interface
+                           (eg. wlan0).
+            address: ipaddress.IPv4Address, ipaddress.IPv4Network,
+                     or DEFAULT_ROUTE. The address to use. If a network
+                     is given then the entire subnet will be routed.
+                     If DEFAULT_ROUTE is given then this will set the
+                     default route.
+
+        Raises:
+            NetworkInterfaceDown: Raised when the network interface is down.
+        """
+        try:
+            self._runner.run('ip route add %s dev %s' %
+                             (address, net_interface))
+        except connection.CommandError as e:
+            if 'File exists' in e.result.stderr:
+                raise Error('Route already exists.')
+            if 'Network is down' in e.result.stderr:
+                raise NetworkInterfaceDown(
+                    'Device must be up for adding a route.')
+            raise
+
+    def get_routes(self, net_interface=None):
+        """Get the routes in the ip routing table.
+
+        Args:
+            net_interface: string, If given, only retrive routes that have
+                           been registered to go through this network
+                           interface (eg. wlan0).
+
+        Returns: An iterator that returns a tuple of (address, net_interface).
+                 If it is the default route then address
+                 will be the DEFAULT_ROUTE. If the route is a subnet then
+                 it will be a ipaddress.IPv4Network otherwise it is a
+                 ipaddress.IPv4Address.
+        """
+        result = self._runner.run('ip route show')
+
+        lines = result.stdout.splitlines()
+
+        # Scan through each line for valid route entries
+        # Example output:
+        # default via 192.168.1.254 dev eth0  proto static
+        # 192.168.1.0/24 dev eth0  proto kernel  scope link  src 172.22.100.19  metric 1
+        # 192.168.2.1 dev eth2 proto kernel scope link metric 1
+        for line in lines:
+            if not 'dev' in line:
+                continue
+
+            if line.startswith(self.DEFAULT_ROUTE):
+                # The default route entry is formatted differently.
+                match = re.search('dev (?P<net_interface>.*)', line)
+                pair = None
+                if match:
+                    # When there is a match for the route entry pattern create
+                    # A pair to hold the info.
+                    pair = (self.DEFAULT_ROUTE,
+                            match.groupdict()['net_interface'])
+            else:
+                # Test the normal route entry pattern.
+                match = re.search(
+                    '(?P<address>[^\s]*) dev (?P<net_interface>[^\s]*)', line)
+                pair = None
+                if match:
+                    # When there is a match for the route entry pattern create
+                    # A pair to hold the info.
+                    d = match.groupdict()
+                    # Route can be either a network or specific address
+                    try:
+                        address = ipaddress.IPv4Address(d['address'])
+                    except ipaddress.AddressValueError:
+                        address = ipaddress.IPv4Network(d['address'])
+
+                    pair = (address, d['net_interface'])
+
+            # No pair means no pattern was found.
+            if not pair:
+                continue
+
+            if net_interface:
+                # If a net_interface was passed in then only give the pair when it is
+                # The correct net_interface.
+                if pair[1] == net_interface:
+                    yield pair
+            else:
+                # No net_interface given give all valid route entries.
+                yield pair
+
+    def is_route(self, address, net_interface=None):
+        """Checks to see if a route exists.
+
+        Args:
+            address: ipaddress.IPv4Address, ipaddress.IPv4Network,
+                     or DEFAULT_ROUTE, The address to use.
+            net_interface: string, If specified, the route must be
+                           registered to go through this network interface
+                           (eg. wlan0).
+
+        Returns: True if the route is found, False otherwise.
+        """
+        for route, _ in self.get_routes(net_interface):
+            if route == address:
+                return True
+
+        return False
+
+    def remove_route(self, address, net_interface=None):
+        """Removes a route from the ip routing table.
+
+        Removes a route from the ip routing table. If the route does not exist
+        nothing is done.
+
+        Args:
+            address: ipaddress.IPv4Address, ipaddress.IPv4Network,
+                     or DEFAULT_ROUTE, The address of the route to remove.
+            net_interface: string, If specified the route being removed is
+                           registered to go through this network interface
+                           (eg. wlan0)
+        """
+        try:
+            if net_interface:
+                self._runner.run('ip route del %s dev %s' %
+                                 (address, net_interface))
+            else:
+                self._runner.run('ip route del %s' % address)
+        except connection.CommandError as e:
+            if 'No such process' in e.result.stderr:
+                # The route didn't exist.
+                return
+            raise
+
+    def clear_routes(self, net_interface=None):
+        """Clears all routes.
+
+        Args:
+            net_interface: The network interface to clear routes on.
+            If not given then all routes will be removed on all network
+            interfaces (eg. wlan0).
+        """
+        routes = self.get_routes(net_interface)
+
+        for a, d in routes:
+            self.remove_route(a, d)
diff --git a/acts/framework/acts/controllers/utils_lib/commands/shell.py b/acts/framework/acts/controllers/utils_lib/commands/shell.py
new file mode 100644
index 0000000..eee65e1
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/commands/shell.py
@@ -0,0 +1,239 @@
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import shellescape
+import signal
+import time
+
+from acts.controllers.utils_lib.ssh import connection
+from acts.libs.proc import job
+
+
+class ShellCommand(object):
+    """Wraps basic commands that tend to be tied very closely to a shell.
+
+    This class is a wrapper for running basic shell commands through
+    any object that has a run command. Basic shell functionality for managing
+    the system, programs, and files in wrapped within this class.
+
+    Note: At the moment this only works with the ssh runner.
+    """
+
+    def __init__(self, runner, working_dir=None):
+        """Creates a new shell command invoker.
+
+        Args:
+            runner: The object that will run the shell commands.
+            working_dir: The directory that all commands should work in,
+                         if none then the runners enviroment default is used.
+        """
+        self._runner = runner
+        self._working_dir = working_dir
+
+    def run(self, command, timeout=3600):
+        """Runs a generic command through the runner.
+
+        Takes the command and prepares it to be run in the target shell using
+        this objects settings.
+
+        Args:
+            command: The command to run.
+            timeout: How long to wait for the command (in seconds).
+
+        Returns:
+            A CmdResult object containing the results of the shell command.
+
+        Raises:
+            job.Error: When the command executed but had an error.
+        """
+        if self._working_dir:
+            command_str = 'cd %s; %s' % (self._working_dir, command)
+        else:
+            command_str = command
+
+        return self._runner.run(command_str, timeout=timeout)
+
+    def is_alive(self, identifier):
+        """Checks to see if a program is alive.
+
+        Checks to see if a program is alive on the shells enviroment. This can
+        be used to check on generic programs, or a specific program using
+        a pid.
+
+        Args:
+            identifier: string or int, Used to identify the program to check.
+                        if given an int then it is assumed to be a pid. If
+                        given a string then it will be used as a search key
+                        to compare on the running processes.
+        Returns:
+            True if a process was found running, false otherwise.
+        """
+        try:
+            if isinstance(identifier, str):
+                self.run('ps aux | grep -v grep | grep %s' % identifier)
+            elif isinstance(identifier, int):
+                self.signal(identifier, 0)
+            else:
+                raise ValueError('Bad type was given for identifier')
+
+            return True
+        except job.Error:
+            return False
+
+    def get_pids(self, identifier):
+        """Gets the pids of a program.
+
+        Searches for a program with a specific name and grabs the pids for all
+        programs that match.
+
+        Args:
+            identifier: A search term that identifies the program.
+
+        Returns: An array of all pids that matched the identifier, or None
+                  if no pids were found.
+        """
+        try:
+            result = self.run('ps aux | grep -v grep | grep %s' % identifier)
+        except job.Error:
+            raise StopIteration
+
+        lines = result.stdout.splitlines()
+
+        # The expected output of the above command is like so:
+        # bob    14349  0.0  0.0  34788  5552 pts/2    Ss   Oct10   0:03 bash
+        # bob    52967  0.0  0.0  34972  5152 pts/4    Ss   Oct10   0:00 bash
+        # Where the format is:
+        # USER    PID  ...
+        for line in lines:
+            pieces = line.split()
+            yield int(pieces[1])
+
+    def search_file(self, search_string, file_name):
+        """Searches through a file for a string.
+
+        Args:
+            search_string: The string or pattern to look for.
+            file_name: The name of the file to search.
+
+        Returns:
+            True if the string or pattern was found, False otherwise.
+        """
+        try:
+            self.run('grep %s %s' %
+                     (shellescape.quote(search_string), file_name))
+            return True
+        except job.Error:
+            return False
+
+    def read_file(self, file_name):
+        """Reads a file through the shell.
+
+        Args:
+            file_name: The name of the file to read.
+
+        Returns:
+            A string of the files contents.
+        """
+        return self.run('cat %s' % file_name).stdout
+
+    def write_file(self, file_name, data):
+        """Writes a block of data to a file through the shell.
+
+        Args:
+            file_name: The name of the file to write to.
+            data: The string of data to write.
+        """
+        return self.run('echo %s > %s' % (shellescape.quote(data), file_name))
+
+    def append_file(self, file_name, data):
+        """Appends a block of data to a file through the shell.
+
+        Args:
+            file_name: The name of the file to write to.
+            data: The string of data to write.
+        """
+        return self.run('echo %s >> %s' % (shellescape.quote(data), file_name))
+
+    def touch_file(self, file_name):
+        """Creates a file through the shell.
+
+        Args:
+            file_name: The name of the file to create.
+        """
+        self.write_file(file_name, '')
+
+    def delete_file(self, file_name):
+        """Deletes a file through the shell.
+
+        Args:
+            file_name: The name of the file to delete.
+        """
+        try:
+            self.run('rm %s' % file_name)
+        except job.Error as e:
+            if 'No such file or directory' in e.result.stderr:
+                return
+
+            raise
+
+    def kill(self, identifier, timeout=10):
+        """Kills a program or group of programs through the shell.
+
+        Kills all programs that match an identifier through the shell. This
+        will send an increasing queue of kill signals to all programs
+        that match the identifier until either all are dead or the timeout
+        finishes.
+
+        Programs are guranteed to be killed after running this command.
+
+        Args:
+            identifier: A string used to identify the program.
+            timeout: The time to wait for all programs to die. Each signal will
+                     take an equal portion of this time.
+        """
+        if isinstance(identifier, int):
+            pids = [identifier]
+        else:
+            pids = list(self.get_pids(identifier))
+
+        signal_queue = [signal.SIGINT, signal.SIGTERM, signal.SIGKILL]
+
+        signal_duration = timeout / len(signal_queue)
+        for sig in signal_queue:
+            for pid in pids:
+                try:
+                    self.signal(pid, sig)
+                except job.Error:
+                    pass
+
+            start_time = time.time()
+            while pids and time.time() - start_time < signal_duration:
+                time.sleep(0.1)
+                pids = [pid for pid in pids if self.is_alive(pid)]
+
+            if not pids:
+                break
+
+    def signal(self, pid, sig):
+        """Sends a specific signal to a program.
+
+        Args:
+            pid: The process id of the program to kill.
+            sig: The singal to send.
+
+        Raises:
+            job.Error: Raised when the signal fail to reach
+                       the specified program.
+        """
+        self.run('kill -%d %d' % (sig, pid))
diff --git a/acts/framework/acts/controllers/utils_lib/host_utils.py b/acts/framework/acts/controllers/utils_lib/host_utils.py
new file mode 100644
index 0000000..5e28621
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/host_utils.py
@@ -0,0 +1,61 @@
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import socket
+
+
+def get_available_host_port():
+    """Finds a semi-random available port.
+
+    A race condition is still possible after the port number is returned, if
+    another process happens to bind it.
+
+    Returns:
+        A port number that is unused on both TCP and UDP.
+    """
+    # On the 2.6 kernel, calling _try_bind() on UDP socket returns the
+    # same port over and over. So always try TCP first.
+    while True:
+        # Ask the OS for an unused port.
+        port = _try_bind(0, socket.SOCK_STREAM, socket.IPPROTO_TCP)
+        # Check if this port is unused on the other protocol.
+        if port and _try_bind(port, socket.SOCK_DGRAM, socket.IPPROTO_UDP):
+            return port
+
+
+def is_port_available(port):
+    """Checks if a given port number is available on the system.
+
+    Args:
+        port: An integer which is the port number to check.
+
+    Returns:
+        True if the port is available; False otherwise.
+    """
+    return (_try_bind(port, socket.SOCK_STREAM, socket.IPPROTO_TCP) and
+            _try_bind(port, socket.SOCK_DGRAM, socket.IPPROTO_UDP))
+
+
+def _try_bind(port, socket_type, socket_proto):
+    s = socket.socket(socket.AF_INET, socket_type, socket_proto)
+    try:
+        try:
+            s.bind(('', port))
+            # The result of getsockname() is protocol dependent, but for both
+            # IPv4 and IPv6 the second field is a port number.
+            return s.getsockname()[1]
+        except socket.error:
+            return None
+    finally:
+        s.close()
diff --git a/acts/framework/acts/controllers/utils_lib/ssh/__init__.py b/acts/framework/acts/controllers/utils_lib/ssh/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/ssh/__init__.py
diff --git a/acts/framework/acts/controllers/utils_lib/ssh/connection.py b/acts/framework/acts/controllers/utils_lib/ssh/connection.py
new file mode 100644
index 0000000..9d238da
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/ssh/connection.py
@@ -0,0 +1,403 @@
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import collections
+import logging
+import os
+import re
+import shutil
+import tempfile
+import threading
+import time
+import uuid
+
+from acts.controllers.utils_lib import host_utils
+from acts.controllers.utils_lib.ssh import formatter
+from acts.libs.proc import job
+
+
+class Error(Exception):
+    """An error occured during an ssh operation."""
+
+
+class CommandError(Exception):
+    """An error occured with the command.
+
+    Attributes:
+        result: The results of the ssh command that had the error.
+    """
+
+    def __init__(self, result):
+        """
+        Args:
+            result: The result of the ssh command that created the problem.
+        """
+        self.result = result
+
+    def __str__(self):
+        return 'cmd: %s\nstdout: %s\nstderr: %s' % (
+            self.result.command, self.result.stdout, self.result.stderr)
+
+
+_Tunnel = collections.namedtuple('_Tunnel',
+                                 ['local_port', 'remote_port', 'proc'])
+
+
+class SshConnection(object):
+    """Provides a connection to a remote machine through ssh.
+
+    Provides the ability to connect to a remote machine and execute a command
+    on it. The connection will try to establish a persistent connection When
+    a command is run. If the persistent connection fails it will attempt
+    to connect normally.
+    """
+
+    @property
+    def socket_path(self):
+        """Returns: The os path to the master socket file."""
+        return os.path.join(self._master_ssh_tempdir, 'socket')
+
+    def __init__(self, settings):
+        """
+        Args:
+            settings: The ssh settings to use for this conneciton.
+            formatter: The object that will handle formatting ssh command
+                       for use with the background job.
+        """
+        self._settings = settings
+        self._formatter = formatter.SshFormatter()
+        self._lock = threading.Lock()
+        self._master_ssh_proc = None
+        self._master_ssh_tempdir = None
+        self._tunnels = list()
+
+    def __del__(self):
+        self.close()
+
+    def setup_master_ssh(self, timeout_seconds=5):
+        """Sets up the master ssh connection.
+
+        Sets up the inital master ssh connection if it has not already been
+        started.
+
+        Args:
+            timeout_seconds: The time to wait for the master ssh connection to be made.
+
+        Raises:
+            Error: When setting up the master ssh connection fails.
+        """
+        with self._lock:
+            if self._master_ssh_proc is not None:
+                socket_path = self.socket_path
+                if (not os.path.exists(socket_path) or
+                        self._master_ssh_proc.poll() is not None):
+                    logging.debug('Master ssh connection to %s is down.',
+                                  self._settings.hostname)
+                    self._cleanup_master_ssh()
+
+            if self._master_ssh_proc is None:
+                # Create a shared socket in a temp location.
+                self._master_ssh_tempdir = tempfile.mkdtemp(
+                    prefix='ssh-master')
+
+                # Setup flags and options for running the master ssh
+                # -N: Do not execute a remote command.
+                # ControlMaster: Spawn a master connection.
+                # ControlPath: The master connection socket path.
+                extra_flags = {'-N': None}
+                extra_options = {
+                    'ControlMaster': True,
+                    'ControlPath': self.socket_path,
+                    'BatchMode': True
+                }
+
+                # Construct the command and start it.
+                master_cmd = self._formatter.format_ssh_local_command(
+                    self._settings,
+                    extra_flags=extra_flags,
+                    extra_options=extra_options)
+                logging.info('Starting master ssh connection to %s',
+                             self._settings.hostname)
+                self._master_ssh_proc = job.run_async(master_cmd)
+
+                end_time = time.time() + timeout_seconds
+
+                while time.time() < end_time:
+                    if os.path.exists(self.socket_path):
+                        break
+                    time.sleep(.2)
+                else:
+                    self._cleanup_master_ssh()
+                    raise Error('Master ssh connection timed out.')
+
+    def run(self,
+            command,
+            timeout=3600,
+            ignore_status=False,
+            env=None,
+            io_encoding='utf-8'):
+        """Runs a remote command over ssh.
+
+        Will ssh to a remote host and run a command. This method will
+        block until the remote command is finished.
+
+        Args:
+            command: The command to execute over ssh. Can be either a string
+                     or a list.
+            timeout: number seconds to wait for command to finish.
+            ignore_status: bool True to ignore the exit code of the remote
+                           subprocess.  Note that if you do ignore status codes,
+                           you should handle non-zero exit codes explicitly.
+            env: dict enviroment variables to setup on the remote host.
+            io_encoding: str unicode encoding of command output.
+
+        Returns:
+            A job.Result containing the results of the ssh command.
+
+        Raises:
+            job.TimeoutError: When the remote command took to long to execute.
+            Error: When the ssh connection failed to be created.
+            CommandError: Ssh worked, but the command had an error executing.
+        """
+        if env is None:
+            env = {}
+
+        try:
+            self.setup_master_ssh(self._settings.connect_timeout)
+        except Error:
+            logging.warning('Failed to create master ssh connection, using '
+                            'normal ssh connection.')
+
+        extra_options = {'BatchMode': True}
+        if self._master_ssh_proc:
+            extra_options['ControlPath'] = self.socket_path
+
+        identifier = str(uuid.uuid4())
+        full_command = 'echo "CONNECTED: %s"; %s' % (identifier, command)
+
+        terminal_command = self._formatter.format_command(
+            full_command, env, self._settings, extra_options=extra_options)
+
+        dns_retry_count = 2
+        while True:
+            result = job.run(terminal_command,
+                             ignore_status=True,
+                             timeout=timeout)
+            output = result.stdout
+
+            # Check for a connected message to prevent false negatives.
+            valid_connection = re.search(
+                '^CONNECTED: %s' % identifier, output, flags=re.MULTILINE)
+            if valid_connection:
+                # Remove the first line that contains the connect message.
+                line_index = output.find('\n')
+                real_output = output[line_index + 1:].encode(result._encoding)
+
+                result = job.Result(
+                    command=result.command,
+                    stdout=real_output,
+                    stderr=result._raw_stderr,
+                    exit_status=result.exit_status,
+                    duration=result.duration,
+                    did_timeout=result.did_timeout,
+                    encoding=result._encoding)
+                if result.exit_status:
+                    raise job.Error(result)
+                return result
+
+            error_string = result.stderr
+
+            had_dns_failure = (result.exit_status == 255 and re.search(
+                r'^ssh: .*: Name or service not known',
+                error_string,
+                flags=re.MULTILINE))
+            if had_dns_failure:
+                dns_retry_count -= 1
+                if not dns_retry_count:
+                    raise Error('DNS failed to find host.', result)
+                logging.debug('Failed to connecto to host, retrying...')
+            else:
+                break
+
+        had_timeout = re.search(
+            r'^ssh: connect to host .* port .*: '
+            r'Connection timed out\r$',
+            error_string,
+            flags=re.MULTILINE)
+        if had_timeout:
+            raise Error('Ssh timed out.', result)
+
+        permission_denied = 'Permission denied' in error_string
+        if permission_denied:
+            raise Error('Permission denied.', result)
+
+        unknown_host = re.search(
+            r'ssh: Could not resolve hostname .*: '
+            r'Name or service not known',
+            error_string,
+            flags=re.MULTILINE)
+        if unknown_host:
+            raise Error('Unknown host.', result)
+
+        raise Error('The job failed for unkown reasons.', result)
+
+    def run_async(self, command, env=None):
+        """Starts up a background command over ssh.
+
+        Will ssh to a remote host and startup a command. This method will
+        block until there is confirmation that the remote command has started.
+
+        Args:
+            command: The command to execute over ssh. Can be either a string
+                     or a list.
+            env: A dictonary of enviroment variables to setup on the remote
+                 host.
+
+        Returns:
+            The result of the command to launch the background job.
+
+        Raises:
+            CmdTimeoutError: When the remote command took to long to execute.
+            SshTimeoutError: When the connection took to long to established.
+            SshPermissionDeniedError: When permission is not allowed on the
+                                      remote host.
+        """
+        command = '(%s) < /dev/null > /dev/null 2>&1 & echo -n $!' % command
+        result = self.run(command, env=env)
+        return result
+
+    def close(self):
+        """Clean up open connections to remote host."""
+        self._cleanup_master_ssh()
+        while self._tunnels:
+            self.close_ssh_tunnel(self._tunnels[0].local_port)
+
+    def _cleanup_master_ssh(self):
+        """
+        Release all resources (process, temporary directory) used by an active
+        master SSH connection.
+        """
+        # If a master SSH connection is running, kill it.
+        if self._master_ssh_proc is not None:
+            logging.debug('Nuking master_ssh_job.')
+            self._master_ssh_proc.kill()
+            self._master_ssh_proc.wait()
+            self._master_ssh_proc = None
+
+        # Remove the temporary directory for the master SSH socket.
+        if self._master_ssh_tempdir is not None:
+            logging.debug('Cleaning master_ssh_tempdir.')
+            shutil.rmtree(self._master_ssh_tempdir)
+            self._master_ssh_tempdir = None
+
+    def create_ssh_tunnel(self, port, local_port=None):
+        """Create an ssh tunnel from local_port to port.
+
+        This securely forwards traffic from local_port on this machine to the
+        remote SSH host at port.
+
+        Args:
+            port: remote port on the host.
+            local_port: local forwarding port, or None to pick an available
+                        port.
+
+        Returns:
+            the created tunnel process.
+        """
+        if local_port is None:
+            local_port = host_utils.get_available_host_port()
+        else:
+            for tunnel in self._tunnels:
+                if tunnel.remote_port == port:
+                    return tunnel.local_port
+
+        extra_flags = {
+            '-n': None,  # Read from /dev/null for stdin
+            '-N': None,  # Do not execute a remote command
+            '-q': None,  # Suppress warnings and diagnostic commands
+            '-L': '%d:localhost:%d' % (local_port, port),
+        }
+        extra_options = dict()
+        if self._master_ssh_proc:
+            extra_options['ControlPath'] = self.socket_path
+        tunnel_cmd = self._formatter.format_ssh_local_command(
+            self._settings,
+            extra_flags=extra_flags,
+            extra_options=extra_options)
+        logging.debug('Full tunnel command: %s', tunnel_cmd)
+        # Exec the ssh process directly so that when we deliver signals, we
+        # deliver them straight to the child process.
+        tunnel_proc = job.run_async(tunnel_cmd)
+        logging.debug('Started ssh tunnel, local = %d'
+                      ' remote = %d, pid = %d', local_port, port,
+                      tunnel_proc.pid)
+        self._tunnels.append(_Tunnel(local_port, port, tunnel_proc))
+        return local_port
+
+    def close_ssh_tunnel(self, local_port):
+        """Close a previously created ssh tunnel of a TCP port.
+
+        Args:
+            local_port: int port on localhost previously forwarded to the remote
+                        host.
+
+        Returns:
+            integer port number this port was forwarded to on the remote host or
+            None if no tunnel was found.
+        """
+        idx = None
+        for i, tunnel in enumerate(self._tunnels):
+            if tunnel.local_port == local_port:
+                idx = i
+                break
+        if idx is not None:
+            tunnel = self._tunnels.pop(idx)
+            tunnel.proc.kill()
+            tunnel.proc.wait()
+            return tunnel.remote_port
+        return None
+
+    def send_file(self, local_path, remote_path):
+        """Send a file from the local host to the remote host.
+
+        Args:
+            local_path: string path of file to send on local host.
+            remote_path: string path to copy file to on remote host.
+        """
+        # TODO: This may belong somewhere else: b/32572515
+        user_host = self._formatter.format_host_name(self._settings)
+        job.run('scp %s %s:%s' % (local_path, user_host, remote_path))
+
+    def find_free_port(self, interface_name='localhost'):
+        """Find a unused port on the remote host.
+
+        Note that this method is inherently racy, since it is impossible
+        to promise that the remote port will remain free.
+
+        Args:
+            interface_name: string name of interface to check whether a
+                            port is used against.
+
+        Returns:
+            integer port number on remote interface that was free.
+        """
+        # TODO: This may belong somewhere else: b/3257251
+        free_port_cmd = (
+            'python -c "import socket; s=socket.socket(); '
+            's.bind((\'%s\', 0)); print(s.getsockname()[1]); s.close()"'
+        ) % interface_name
+        port = int(self.run(free_port_cmd).stdout)
+        # Yield to the os to ensure the port gets cleaned up.
+        time.sleep(0.001)
+        return port
diff --git a/acts/framework/acts/controllers/utils_lib/ssh/formatter.py b/acts/framework/acts/controllers/utils_lib/ssh/formatter.py
new file mode 100644
index 0000000..8433f95
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/ssh/formatter.py
@@ -0,0 +1,210 @@
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class SshFormatter(object):
+    """Handles formatting ssh commands.
+
+    Handler for formatting chunks of the ssh command to run.
+    """
+
+    def format_ssh_executable(self, settings):
+        """Format the executable name.
+
+        Formats the executable name as a string.
+
+        Args:
+            settings: The ssh settings being used.
+
+        Returns:
+            A string for the ssh executable name.
+        """
+        return settings.executable
+
+    def format_host_name(self, settings):
+        """Format hostname.
+
+        Formats the hostname to connect to.
+
+        Args:
+            settings: The ssh settings being used.
+
+        Returns:
+            A string of the connection host name to connect to.
+        """
+        return '%s@%s' % (settings.username, settings.hostname)
+
+    def format_value(self, value):
+        """Formats a command line value.
+
+        Takes in a value and formats it so it can be safely used in the
+        command line.
+
+        Args:
+            value: The value to format.
+
+        Returns:
+            A string representation of the formatted value.
+        """
+        if isinstance(value, bool):
+            return 'yes' if value else 'no'
+
+        return str(value)
+
+    def format_options_list(self, options):
+        """Format the option list.
+
+        Formats a dictionary of options into a list of strings to be used
+        on the command line.
+
+        Args:
+            options: A dictionary of options.
+
+        Returns:
+            An iterator of strings that should go on the command line.
+        """
+        for option_name in options:
+            option = options[option_name]
+
+            yield '-o'
+            yield '%s=%s' % (option_name, self.format_value(option))
+
+    def format_flag_list(self, flags):
+        """Format the flags list.
+
+        Formats a dictionary of flags into a list of strings to be used
+        on the command line.
+
+        Args:
+            flags: A dictonary of options.
+
+        Returns:
+            An iterator of strings that should be used on the command line.
+        """
+        for flag_name in flags:
+            flag = flags[flag_name]
+
+            yield flag_name
+            if flag is not None:
+                yield self.format_value(flag)
+
+    def format_ssh_local_command(self,
+                                 settings,
+                                 extra_flags={},
+                                 extra_options={}):
+        """Formats the local part of the ssh command.
+
+        Formats the local section of the ssh command. This is the part of the
+        command that will actual launch ssh on our local machine with the
+        specified settings.
+
+        Args:
+            settings: The ssh settings.
+            extra_flags: Extra flags to inlcude.
+            extra_options: Extra options to include.
+
+        Returns:
+            An array of strings that make up the command and its local
+            arguments.
+        """
+        options = settings.construct_ssh_options()
+        for extra_option_name in extra_options:
+            options[extra_option_name] = extra_options[extra_option_name]
+        options_list = list(self.format_options_list(options))
+
+        flags = settings.construct_ssh_flags()
+        for extra_flag_name in extra_flags:
+            flags[extra_flag_name] = extra_flags[extra_flag_name]
+        flags_list = list(self.format_flag_list(flags))
+
+        all_options = options_list + flags_list
+        host_name = self.format_host_name(settings)
+        executable = self.format_ssh_executable(settings)
+
+        base_command = [executable] + all_options + [host_name]
+
+        return base_command
+
+    def format_ssh_command(self,
+                           remote_command,
+                           settings,
+                           extra_flags={},
+                           extra_options={}):
+        """Formats the full ssh command.
+
+        Creates the full format for an ssh command.
+
+        Args:
+            remote_command: A string that represents the remote command to
+                            execute.
+            settings: The ssh settings to use.
+            extra_flags: Extra flags to include in the settings.
+            extra_options: Extra options to include in the settings.
+
+        Returns:
+            A list of strings that make up the total ssh command.
+        """
+        local_command = self.format_ssh_local_command(settings, extra_flags,
+                                                      extra_options)
+
+        local_command.append(remote_command)
+        return local_command
+
+    def format_remote_command(self, command, env):
+        """Formats the remote part of the ssh command.
+
+        Formatts the command that will run on the remote machine.
+
+        Args:
+            command: string, The command to be executed.
+            env: Enviroment variables to add to the remote envirment.
+
+        Returns:
+            A string that represents the command line to execute on the remote
+            machine.
+        """
+        if not env:
+            env_str = ''
+        else:
+            env_str = 'export '
+            for name in env:
+                value = env[name]
+                env_str += '%s=%s ' % (name, str(value))
+            env_str += ';'
+
+        execution_line = '%s %s;' % (env_str, command)
+        return execution_line
+
+    def format_command(self,
+                       command,
+                       env,
+                       settings,
+                       extra_flags={},
+                       extra_options={}):
+        """Formats a full command.
+
+        Formats the full command to run in order to run a command on a remote
+        machine.
+
+        Args:
+            command: The command to run on the remote machine. Can either be
+                     a string or a list.
+            env: The enviroment variables to include on the remote machine.
+            settings: The ssh settings to use.
+            extra_flags: Extra flags to include with the settings.
+            extra_options: Extra options to include with the settings.
+        """
+        remote_command = self.format_remote_command(command, env)
+        return self.format_ssh_command(remote_command, settings, extra_flags,
+                                       extra_options)
diff --git a/acts/framework/acts/controllers/utils_lib/ssh/settings.py b/acts/framework/acts/controllers/utils_lib/ssh/settings.py
new file mode 100644
index 0000000..b8331bd
--- /dev/null
+++ b/acts/framework/acts/controllers/utils_lib/ssh/settings.py
@@ -0,0 +1,105 @@
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Create a SshSettings from a dictionary from an ACTS config
+
+Args:
+    config dict instance from an ACTS config
+
+Returns:
+    An instance of SshSettings or None
+"""
+
+
+def from_config(config):
+    if config is None:
+        return None  # Having no settings is not an error
+
+    user = config.get('user', None)
+    host = config.get('host', None)
+    port = config.get('port', 22)
+    identity_file = config.get('identity_file', None)
+    if user is None or host is None:
+        raise ValueError('Malformed SSH config did not include user and '
+                         'host keys: %s' % config)
+
+    return SshSettings(host, user, port=port, identity_file=identity_file)
+
+
+class SshSettings(object):
+    """Contains settings for ssh.
+
+    Container for ssh connection settings.
+
+    Attributes:
+        username: The name of the user to log in as.
+        hostname: The name of the host to connect to.
+        executable: The ssh executable to use.
+        port: The port to connect through (usually 22).
+        host_file: The known host file to use.
+        connect_timeout: How long to wait on a connection before giving a
+                         timeout.
+        alive_interval: How long between ssh heartbeat signals to keep the
+                        connection alive.
+    """
+
+    def __init__(self,
+                 hostname,
+                 username,
+                 port=22,
+                 host_file='/dev/null',
+                 connect_timeout=30,
+                 alive_interval=300,
+                 executable='/usr/bin/ssh',
+                 identity_file=None):
+        self.username = username
+        self.hostname = hostname
+        self.executable = executable
+        self.port = port
+        self.host_file = host_file
+        self.connect_timeout = connect_timeout
+        self.alive_interval = alive_interval
+        self.identity_file = identity_file
+
+    def construct_ssh_options(self):
+        """Construct the ssh options.
+
+        Constructs a dictionary of option that should be used with the ssh
+        command.
+
+        Returns:
+            A dictionary of option name to value.
+        """
+        current_options = {}
+        current_options['StrictHostKeyChecking'] = False
+        current_options['UserKnownHostsFile'] = self.host_file
+        current_options['ConnectTimeout'] = self.connect_timeout
+        current_options['ServerAliveInterval'] = self.alive_interval
+        return current_options
+
+    def construct_ssh_flags(self):
+        """Construct the ssh flags.
+
+        Constructs what flags should be used in the ssh connection.
+
+        Returns:
+            A dictonary of flag name to value. If value is none then it is
+            treated as a binary flag.
+        """
+        current_flags = {}
+        current_flags['-a'] = None
+        current_flags['-x'] = None
+        current_flags['-p'] = self.port
+        if self.identity_file:
+            current_flags['-i'] = self.identity_file
+        return current_flags
diff --git a/acts/framework/acts/jsonrpc.py b/acts/framework/acts/jsonrpc.py
index 6bb364a..944a96f 100644
--- a/acts/framework/acts/jsonrpc.py
+++ b/acts/framework/acts/jsonrpc.py
@@ -13,7 +13,6 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-
 """
 A simple JSON RPC client.
 """
@@ -21,12 +20,15 @@
 import time
 from urllib import request
 
+
 class HTTPError(Exception):
     pass
 
+
 class RemoteError(Exception):
     pass
 
+
 def JSONCounter():
     """A counter that generates JSON RPC call IDs.
 
@@ -38,9 +40,11 @@
         yield i
         i += 1
 
+
 class JSONRPCClient:
     COUNTER = JSONCounter()
     headers = {'content-type': 'application/json'}
+
     def __init__(self, baseurl):
         self._baseurl = baseurl
 
@@ -115,4 +119,5 @@
     def __getattr__(self, name):
         def rpc_call(*args):
             return self.call('uci', name, *args)
+
         return rpc_call
diff --git a/acts/framework/acts/keys.py b/acts/framework/acts/keys.py
index de2174f..75509fa 100644
--- a/acts/framework/acts/keys.py
+++ b/acts/framework/acts/keys.py
@@ -15,10 +15,11 @@
 #   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.
     """
@@ -32,10 +33,12 @@
     key_test_paths = "testpaths"
     key_port = "Port"
     key_address = "Address"
+    key_random = "random"
+    key_test_case_iterations = "test_case_iterations"
     # Config names for controllers packaged in ACTS.
     key_android_device = "AndroidDevice"
     key_native_android_device = "NativeAndroidDevice"
-    key_access_point = "AP"
+    key_access_point = "AccessPoint"
     key_attenuator = "Attenuator"
     key_iperf_server = "IPerfServer"
     key_monsoon = "Monsoon"
@@ -61,32 +64,31 @@
 
     # Controller names packaged with ACTS.
     builtin_controller_names = [
-        key_android_device,
-        key_native_android_device,
-        key_access_point,
-        key_attenuator,
-        key_iperf_server,
-        key_monsoon,
-        key_sniffer
+        key_android_device, key_native_android_device, key_access_point,
+        key_attenuator, key_iperf_server, key_monsoon, key_sniffer
     ]
 
+
 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.
diff --git a/acts/framework/acts/libs/__init__.py b/acts/framework/acts/libs/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/libs/__init__.py
diff --git a/acts/framework/acts/libs/config/__init__.py b/acts/framework/acts/libs/config/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/libs/config/__init__.py
diff --git a/acts/framework/acts/libs/config/base.py b/acts/framework/acts/libs/config/base.py
new file mode 100644
index 0000000..3a04733
--- /dev/null
+++ b/acts/framework/acts/libs/config/base.py
@@ -0,0 +1,136 @@
+#   Copyright 2016 - Google, Inc.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+
+class DataWrapper(object):
+    """A special type of packed data.
+
+    This is the base class for a type of data that wraps around another set
+    of data. This data will be unpacked on every read right before the real
+    value is needed. When unpacked the wrapper can perform special operations
+    to get the real data.
+    """
+
+    def get_real_value(self, unwrapper):
+        """Unpacks the data from the wrapper.
+
+        Args:
+            unwrapper: The object calling the unwrapping.
+
+        Returns:
+            The real value stored in the data wrapper.
+        """
+        value = self.unwrap(unwrapper)
+        while isinstance(value, DataWrapper):
+            value = value.unwrap(unwrapper)
+
+        return value
+
+    def unwrap(self, unwrapper):
+        """Unwraps the data from this wrapper and this wrapper only.
+
+        Unlike get_real_value, this method is abstract and is where user
+        code can be written to unpack the data value. If this returns
+        another data wrapper then get_real_value will continue to unwrap
+        until a true value is given.
+
+        Args:
+            unwrapper: The object calling the unwrapping.
+
+        Returns:
+            The unwrapped data.
+        """
+        pass
+
+
+class ConfigWrapper(object):
+    """Object used to asses config values.
+
+    Base class for all objects that wrap around the configs loaded into the
+    application.
+    """
+
+    def get_value(self, key):
+        """Reads the value of a config at a certain key.
+
+        Args:
+            key: The key of the value to read.
+
+        Returns:
+            The raw value stored at that key.
+        """
+        raise NotImplementedError()
+
+    def set_value(self, key, value):
+        """Writes a new value into the config at a certain key.
+
+        Args:
+            key: The key to write the value at.
+            value: The new value to store at that key.
+        """
+        raise NotImplementedError()
+
+    def has_value(self, key):
+        """Checks if this config has a certain key.
+
+        Args:
+            key: The key to check for.
+
+        Returns:
+            True if the key exists.
+        """
+        raise NotImplementedError()
+
+    def keys(self):
+        """Gets all keys in this config.
+
+        Returns:
+            An iterator (or some iterable object) with all keys in this config.
+        """
+        raise NotImplementedError()
+
+    def __getitem__(self, key):
+        """Overrides the bracket accessor to read a value.
+
+        Unlike get_value, this will return the true value of a config. This
+        will unpack any data wrappers.
+
+        Args:
+            key: The key to read.
+
+        Returns:
+            The true value stored at the key.
+        """
+        value = self.get_value(key)
+        if isinstance(value, DataWrapper):
+            return value.get_real_value(self)
+        else:
+            return value
+
+    def __setitem__(self, key, value):
+        """Overrides the bracket accessor to write a value.
+
+        Args:
+            key: The key to write to.
+        """
+        return self.set_value(key, value)
+
+    def __iter__(self):
+        """Iterates over all values in the config.
+
+        Returns:
+            An iterator of key, value pairs.
+        """
+        for key in self.keys():
+            yield (key, self[key])
diff --git a/acts/framework/acts/libs/config/data_source.py b/acts/framework/acts/libs/config/data_source.py
new file mode 100644
index 0000000..6a41c60
--- /dev/null
+++ b/acts/framework/acts/libs/config/data_source.py
@@ -0,0 +1,200 @@
+#   Copyright 2016 - Google, Inc.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import json
+
+from acts.libs.config import base
+
+
+class ConfigDataSource(base.ConfigWrapper):
+    """Base class for a config from a data source.
+
+    A config wrapper that derives from this type reads directly from a
+    data source such as a file on disk.
+    """
+    pass
+
+
+class DictConfigDataSource(ConfigDataSource):
+    """A config data source that reads from a dictionary."""
+
+    def __init__(self, d={}):
+        """Creates a new dictionary data source.
+
+        Args:
+            d: The dictionary to use.
+        """
+        self.dict = d
+
+    def get_value(self, key):
+        """Reads a value from the dictionary.
+
+        Reads a value from the dictionary. Will throw a key error if the key
+        does not exist.
+
+        Args:
+            key: The key in the dictionary to read from.
+
+        Returns:
+            The value stored at that key.
+        """
+        return self.dict[key]
+
+    def set_value(self, key, value):
+        """Writes a value to the dictionary.
+
+        Args:
+            key: The key to write to.
+            value: The new value to store.
+        """
+
+        self.dict[key] = value
+
+    def has_value(self, key):
+        """Returns true if key is in dictionary."""
+        return key in self.dict
+
+    def keys(self):
+        """Gives an iterator of all keys in the dictionary."""
+        return iter(self.dict)
+
+
+class JsonConfigDataSource(DictConfigDataSource):
+    """A data source from a json file.
+
+    Attributes:
+        file_name: The name of the file this was loaded from.
+    """
+
+    def __init__(self, json_file):
+        """
+        Args:
+            json_file: The path to the json file on disk.
+        """
+        self.file_name = json_file
+        with open(json_file) as fd:
+            self.dict = json.load(fd)
+
+
+class MergedData(base.DataWrapper):
+    """A data wrapper that wraps data from multiple data sources.
+
+    Attributes:
+        values: A list of values from different data sources sorted from
+                lowest to highest priority.
+        """
+
+    def __init__(self, values):
+        """
+        Args:
+            values: The list of values to set self.values to.
+        """
+        self.values = values
+
+    def __str__(self):
+        return 'MergedData: %s' % self.values
+
+    def unwrap(self, unwrapper):
+        """Unwraps the merged data.
+
+        Will attempt to have the unwrapper handle unpacking the values. If
+        no unpack func is found then the highest priority values is returned.
+        """
+        unpack_func = getattr(unwrapper, 'unpack', None)
+        if callable(unpack_func):
+            return unpack_func(self.values)
+        else:
+            return self.values[-1]
+
+
+class MergeConfigDataSource(ConfigDataSource):
+    """A data source created from multiple config wrappers."""
+
+    def __init__(self, *configs):
+        """
+        Args:
+            configs: The configs to create this merged source from.
+        """
+        self.configs = list(configs)
+        # Have a shadow copy for changes made at runtime.
+        self.configs.append(DictConfigDataSource())
+
+    def get_value(self, key):
+        """Gets the value from all configs and packs them into a MergedData.
+
+        Returns:
+            A MergedData of all values.
+        """
+        values = [c.get_value(key) for c in self.configs if c.has_value(key)]
+        if values:
+            return MergedData(values)
+
+    def set_value(self, key, value):
+        """Writes a new value to override the old values of this key.
+
+        Writes the new value to a runtime shadow data store that has higher
+        priority than the other data sources.
+        """
+        for c in reversed(self.configs):
+            if c.has_value(key):
+                c.set_value(key, value)
+                return
+
+        self.configs[-1].set_value(key, value)
+
+    def has_value(self, key):
+        """Checks if any of the configs have a value at the key."""
+        return any(c.has_value(key) for c in self.configs)
+
+    def keys(self):
+        """Gives an iterator of the set of all keys in all configs.
+
+        Gives an iterator over the set of all keys in all configs. Keys are
+        given in no particular order.
+        """
+        given_keys = set()
+        for config in self.configs:
+            for key in config.keys():
+                if not key in given_keys:
+                    given_keys.add(key)
+                    yield key
+
+    def __getattr__(self, key):
+        """Override of undefined attributes to foward messages to configs.
+
+        If a message is found in any of the inner configs then a special
+        function will be returned to allow forwarding the message to
+        the inner configs.
+
+        Args:
+            key: The key of the undefined attribute.
+
+        Returns:
+            A function that can be called to send the message to all inner
+            configs. If no inner config has the message then None is given
+            instead.
+        """
+
+        def inner_call(*args, **kwargs):
+            for config in self.configs:
+                func = getattr(config, key, None)
+                if callable(func):
+                    func(*args, **kwargs)
+
+        for config in self.configs:
+            func = getattr(config, key, None)
+            if callable(func):
+                return inner_call
+
+        return None
diff --git a/acts/framework/acts/libs/config/schema.py b/acts/framework/acts/libs/config/schema.py
new file mode 100644
index 0000000..53abbd3
--- /dev/null
+++ b/acts/framework/acts/libs/config/schema.py
@@ -0,0 +1,163 @@
+#   Copyright 2016 - Google, Inc.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts.libs.config import base
+
+
+class ConfigDataHandler(object):
+    """An object that handles formatting config data.
+
+    An object that handles formatting a specific slot of data stored in
+    the configs.
+
+    Attributes:
+        owner: The config object that owns this data handler.
+        key: The key of the config value this hander manages. This can change
+             between calls.
+    """
+
+    def __init__(self):
+        self.owner = None
+        self.key = None
+
+    def get_value(self):
+        """Formats the value stored at key from the inner config.
+
+        Returns:
+            The formated value.
+        """
+        return self.get_inner()
+
+    def set_value(self, value):
+        """Unformatts the passed in data and stores it in the inner config.
+
+        Args:
+            values: The value to unformat.
+        """
+        return self.set_inner(value)
+
+    def get_inner(self):
+        """Gets the unpacked value stored in the inner config.
+
+        Returns:
+            The unpacked value stored in the inner config.
+        """
+        value = self.owner.inner.get_value(self.key)
+        if isinstance(value, base.DataWrapper):
+            return value.get_real_value(self)
+        else:
+            return value
+
+    def set_inner(self, key, value):
+        """Stores a value in the inner config."""
+        return self.owner.inner.set_value(self.key, value)
+
+
+class ConfigSchema(base.ConfigWrapper):
+    """A config data wrapper that handles formatting config data.
+
+    A config data wrapper that can take another config object and manage
+    formatting it into the real type of data that is stored at that value.
+    All class attributes of type ConfigDataHandler will become data handlers
+    for configs of that field name. The only exception to this is
+    DEFAULT_HANDLER, which is used as the data hander for all keys that
+    do not have a data handler.
+
+    Attributes:
+        DEFAULT_HANDLER: The data handler to use when no hander is found.
+        inner: The config wrapper this schema is wrapping around.
+    """
+    DEFAULT_HANDLER = ConfigDataHandler()
+
+    def __init__(self, inner):
+        """
+        Args:
+            inner: The config wrapper to format.
+        """
+        self.inner = inner
+        self._data_handlers = {}
+
+        d = type(self).__dict__
+        for k, v in d.items():
+            if isinstance(v, ConfigDataHandler):
+                self._data_handlers[k] = v
+                v.owner = self
+                v.key = k
+
+    def get_value(self, key):
+        """Gets the value after being formatted by a data handler.
+
+        Args:
+            key: The key of the data to get.
+
+        Returns:
+            The formatted data.
+        """
+        handler = self._data_handlers.get(key)
+        if handler:
+            return handler.get_value()
+        else:
+            self.DEFAULT_HANDLER.key = key
+            return self.DEFAULT_HANDLER.get_value()
+
+    def set_value(self, key, value):
+        """Unformats a value and sets it in the inner config."""
+        handler = self._data_handlers.get(key)
+        if handler:
+            return handler.set_value(value)
+        else:
+            self.DEFAULT_HANDLER.key = key
+            return self.DEFAULT_HANDLER.set_value(value)
+
+    def has_value(self, key):
+        """Checks if the inner config contains a key."""
+        return self.inner.has_value(key)
+
+    def keys(self):
+        """Returns the keys of the inner config."""
+        return self.inner.keys()
+
+    def __getattr__(self, key):
+        """Overrides not found attributes to allow method calling to inner.
+
+        Will allow calling a method on all data handlers and the inner config.
+        """
+
+        def inner_call(*args, **kwargs):
+            for key in self.keys():
+                handler = self._data_handlers.get(key)
+                if not handler:
+                    handler = self.DEFAULT_HANDLER
+                    handler.key = key
+                inner_func = getattr(handler, key, None)
+                if callable(inner_func):
+                    inner_func(*args, **kwargs)
+
+            if self.inner:
+                inner_func = getattr(self.inner, key, None)
+                if callable(inner_func):
+                    inner_func(*args, **kwargs)
+
+        # Only return a callable func if there is an inner callable func
+        for k, v in self._data_handlers.items():
+            inner_func = getattr(v, key, None)
+            if callable(inner_func):
+                return inner_call
+
+        if self.inner:
+            inner_func = getattr(self.inner, key, None)
+            if callable(inner_func):
+                return inner_call
+
+        return None
diff --git a/acts/framework/acts/libs/proc/__init__.py b/acts/framework/acts/libs/proc/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/libs/proc/__init__.py
diff --git a/acts/framework/acts/libs/proc/job.py b/acts/framework/acts/libs/proc/job.py
new file mode 100644
index 0000000..84c4993
--- /dev/null
+++ b/acts/framework/acts/libs/proc/job.py
@@ -0,0 +1,201 @@
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import os
+import shlex
+import sys
+import time
+
+if os.name == 'posix' and sys.version_info[0] < 3:
+    import subprocess32 as subprocess
+    DEVNULL = open(os.devnull, 'wb')
+else:
+    import subprocess
+    # Only exists in python3.3
+    from subprocess import DEVNULL
+
+
+class Error(Exception):
+    """Indicates that a command failed, is fatal to the test unless caught."""
+
+    def __init__(self, result):
+        super(Error, self).__init__(result)
+        self.result = result
+
+
+class TimeoutError(Error):
+    """Thrown when a BackgroundJob times out on wait."""
+
+
+class Result(object):
+    """Command execution result.
+
+    Contains information on subprocess execution after it has exited.
+
+    Attributes:
+        command: An array containing the command and all arguments that
+                 was executed.
+        exit_status: Integer exit code of the process.
+        stdout_raw: The raw bytes output from standard out.
+        stderr_raw: The raw bytes output from standard error
+        duration: How long the process ran for.
+        did_timeout: True if the program timed out and was killed.
+    """
+
+    @property
+    def stdout(self):
+        """String representation of standard output."""
+        if not self._stdout_str:
+            self._stdout_str = self._raw_stdout.decode(encoding=self._encoding)
+            self._stdout_str = self._stdout_str.strip()
+        return self._stdout_str
+
+    @property
+    def stderr(self):
+        """String representation of standard error."""
+        if not self._stderr_str:
+            self._stderr_str = self._raw_stderr.decode(encoding=self._encoding)
+            self._stderr_str = self._stderr_str.strip()
+        return self._stderr_str
+
+    def __init__(self,
+                 command=[],
+                 stdout=bytes(),
+                 stderr=bytes(),
+                 exit_status=None,
+                 duration=0,
+                 did_timeout=False,
+                 encoding='utf-8'):
+        """
+        Args:
+            command: The command that was run. This will be a list containing
+                     the executed command and all args.
+            stdout: The raw bytes that standard output gave.
+            stderr: The raw bytes that standard error gave.
+            exit_status: The exit status of the command.
+            duration: How long the command ran.
+            did_timeout: True if the command timed out.
+            encoding: The encoding standard that the program uses.
+        """
+        self.command = command
+        self.exit_status = exit_status
+        self._raw_stdout = stdout
+        self._raw_stderr = stderr
+        self._stdout_str = None
+        self._stderr_str = None
+        self._encoding = encoding
+        self.duration = duration
+        self.did_timeout = did_timeout
+
+    def __repr__(self):
+        return ('job.Result(command=%r, stdout=%r, stderr=%r, exit_status=%r, '
+                'duration=%r, did_timeout=%r, encoding=%r)') % (
+                    self.command, self._raw_stdout, self._raw_stderr,
+                    self.exit_status, self.duration, self.did_timeout,
+                    self._encoding)
+
+
+def run(command,
+        timeout=60,
+        ignore_status=False,
+        env=None,
+        io_encoding='utf-8'):
+    """Execute a command in a subproccess and return its output.
+
+    Commands can be either shell commands (given as strings) or the
+    path and arguments to an executable (given as a list).  This function
+    will block until the subprocess finishes or times out.
+
+    Args:
+        command: The command to execute. Can be either a string or a list.
+        timeout: number seconds to wait for command to finish.
+        ignore_status: bool True to ignore the exit code of the remote
+                       subprocess.  Note that if you do ignore status codes,
+                       you should handle non-zero exit codes explicitly.
+        env: dict enviroment variables to setup on the remote host.
+        io_encoding: str unicode encoding of command output.
+
+    Returns:
+        A job.Result containing the results of the ssh command.
+
+    Raises:
+        job.TimeoutError: When the remote command took to long to execute.
+        Error: When the ssh connection failed to be created.
+        CommandError: Ssh worked, but the command had an error executing.
+    """
+    start_time = time.time()
+    proc = subprocess.Popen(
+        command,
+        env=env,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        shell=not isinstance(command, list))
+    # Wait on the process terminating
+    timed_out = False
+    out = bytes()
+    err = bytes()
+    try:
+        (out, err) = proc.communicate(timeout=timeout)
+    except subprocess.TimeoutExpired:
+        timed_out = True
+        proc.kill()
+        proc.wait()
+
+    result = Result(
+        command=command,
+        stdout=out,
+        stderr=err,
+        exit_status=proc.returncode,
+        duration=time.time() - start_time,
+        encoding=io_encoding,
+        did_timeout=timed_out)
+    logging.debug(result)
+
+    if timed_out:
+        logging.error("Command %s with %s timeout setting timed out", command,
+                      timeout)
+        raise TimeoutError(result)
+
+    if not ignore_status and proc.returncode != 0:
+        raise Error(result)
+
+    return result
+
+
+def run_async(command, env=None):
+    """Execute a command in a subproccess asynchronously.
+
+    It is the callers responsibility to kill/wait on the resulting
+    subprocess.Popen object.
+
+    Commands can be either shell commands (given as strings) or the
+    path and arguments to an executable (given as a list).  This function
+    will not block.
+
+    Args:
+        command: The command to execute. Can be either a string or a list.
+        env: dict enviroment variables to setup on the remote host.
+
+    Returns:
+        A subprocess.Popen object representing the created subprocess.
+
+    """
+    return subprocess.Popen(
+        command,
+        env=env,
+        close_fds=True,
+        shell=not isinstance(command, list),
+        stdout=DEVNULL,
+        stderr=subprocess.STDOUT)
diff --git a/acts/framework/acts/libs/proto/__init__.py b/acts/framework/acts/libs/proto/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/libs/proto/__init__.py
diff --git a/acts/framework/acts/libs/proto/proto_utils.py b/acts/framework/acts/libs/proto/proto_utils.py
new file mode 100644
index 0000000..da38082
--- /dev/null
+++ b/acts/framework/acts/libs/proto/proto_utils.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+import logging
+import os
+import subprocess
+import sys
+from distutils.spawn import find_executable
+from google import protobuf
+from importlib import import_module
+
+
+def compile_proto(proto_path, output_dir):
+    """Invoke Protocol Compiler to generate python from given source .proto."""
+    # Find compiler path
+    protoc = None
+    if 'PROTOC' in os.environ and os.path.exists(os.environ['PROTOC']):
+        protoc = os.environ['PROTOC']
+    if not protoc:
+        protoc = find_executable('protoc')
+    if not protoc:
+        logging.error(
+            "Cannot find protobuf compiler (>=3.0.0), please install"
+            "protobuf-compiler package. Prefer copying from <top>/prebuilts/tools"
+        )
+        logging.error("    prebuilts/tools/linux-x86_64/protoc/bin/protoc")
+        logging.error("If prebuilts are not available, use apt-get:")
+        logging.error("    sudo apt-get install protobuf-compiler")
+        return None
+    # Validate input proto path
+    if not os.path.exists(proto_path):
+        logging.error('Can\'t find required file: %s\n' % proto_path)
+        return None
+    # Validate output py-proto path
+    if not os.path.exists(output_dir):
+        os.mkdirs(output_dir)
+    elif not os.path.isdir(output_dir):
+        logging.error("Output path is not a valid directory: %s" %
+                      (output_dir))
+        return None
+    input_dir = os.path.dirname(proto_path)
+    output_filename = os.path.basename(proto_path).replace('.proto', '_pb2.py')
+    output_path = os.path.join(output_dir, output_filename)
+    # Compiling proto
+    logging.info('Generating %s' % output_path)
+    protoc_command = [
+        protoc, '-I=%s' % (input_dir), '--python_out=%s' % (output_dir),
+        proto_path
+    ]
+    if subprocess.call(protoc_command, stderr=subprocess.STDOUT) != 0:
+        logging.error("Fail to compile proto")
+        return None
+    output_module_name = os.path.splitext(output_filename)[0]
+    return output_module_name
+
+
+def compile_import_proto(output_dir, proto_path):
+    """
+    Compile protobuf from PROTO_PATH and put the result in OUTPUT_DIR.
+    Return the imported module to caller.
+    :param output_dir: To store generated python proto library
+    :param proto_path: Path to the .proto file that needs to be compiled
+    :return: python proto module
+    """
+    output_module_name = compile_proto(proto_path, output_dir)
+    if not output_module_name:
+        return None
+    sys.path.append(output_dir)
+    output_module = None
+    try:
+        output_module = import_module(output_module_name)
+    except ImportError:
+        logging.error("Cannot import generated py-proto %s" %
+                      (output_module_name))
+    return output_module
+
+
+def parse_proto_to_ascii(binary_proto_msg):
+    """
+    Parse binary protobuf message to human readable ascii string
+    :param binary_proto_msg:
+    :return: ascii string of the message
+    """
+    return protobuf.text_format.MessageToString(binary_proto_msg)
diff --git a/acts/framework/acts/logger.py b/acts/framework/acts/logger.py
index c89c4f2..5d7fed9 100755
--- a/acts/framework/acts/logger.py
+++ b/acts/framework/acts/logger.py
@@ -49,12 +49,14 @@
     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.
 
@@ -74,15 +76,29 @@
             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]
+    """Converts an epoch timestamp in ms to log line timestamp format, which
+    is readible for humans.
+
+    Args:
+        epoch_time: integer, an epoch timestamp in ms.
+
+    Returns:
+        A string that is the corresponding timestamp in log line timestamp
+        format.
+    """
+    s, ms = divmod(epoch_time, 1000)
+    d = datetime.datetime.fromtimestamp(s)
+    return d.strftime("%m-%d %H:%M:%S.") + str(ms)
+
 
 def get_log_line_timestamp(delta=None):
     """Returns a timestamp in the format used by log lines.
@@ -98,6 +114,7 @@
     """
     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.
 
@@ -112,8 +129,9 @@
     """
     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.
+
+def _setup_test_logger(log_path, prefix=None, filename=None):
+    """Customizes the root logger for a test run.
 
     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
@@ -121,18 +139,12 @@
 
     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
+                  is requested.
     """
-    log = logging.getLogger(TAG)
-    if log.handlers:
-        # This logger has been requested before.
-        return log
+    log = logging.getLogger()
+    kill_test_logger(log)
     log.propagate = False
     log.setLevel(logging.DEBUG)
     # Log info to stream
@@ -152,13 +164,22 @@
     fh = logging.FileHandler(os.path.join(log_path, 'test_run_details.txt'))
     fh.setFormatter(f_formatter)
     fh.setLevel(logging.DEBUG)
+    fh_info = logging.FileHandler(os.path.join(log_path, 'test_run_info.txt'))
+    fh_info.setFormatter(f_formatter)
+    fh_info.setLevel(logging.INFO)
+    fh_error = logging.FileHandler(os.path.join(log_path, 'test_run_error.txt'))
+    fh_error.setFormatter(f_formatter)
+    fh_error.setLevel(logging.WARNING)
     log.addHandler(ch)
     log.addHandler(fh)
+    log.addHandler(fh_info)
+    log.addHandler(fh_error)
     log.log_path = log_path
-    return log
+    logging.log_path = log_path
+
 
 def kill_test_logger(logger):
-    """Cleans up a test logger object created by get_test_logger.
+    """Cleans up a test logger object by removing all of its handlers.
 
     Args:
         logger: The logging object to clean up.
@@ -168,6 +189,7 @@
         if isinstance(h, logging.FileHandler):
             h.close()
 
+
 def create_latest_log_alias(actual_path):
     """Creates a symlink to the latest test run logs.
 
@@ -179,25 +201,22 @@
         os.remove(link_path)
     os.symlink(actual_path, link_path)
 
-def get_test_logger(log_path, TAG, prefix=None, filename=None):
-    """Returns a logger customized for a test run.
+
+def setup_test_logger(log_path, prefix=None, filename=None):
+    """Customizes the root logger for a test run.
 
     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 logger object.
     """
     if filename is None:
         filename = get_log_file_timestamp()
     create_dir(log_path)
-    logger = _get_test_logger(log_path, TAG, prefix, filename)
+    logger = _setup_test_logger(log_path, prefix, filename)
     create_latest_log_alias(log_path)
-    return logger
+
 
 def normalize_log_line_timestamp(log_line_timestamp):
     """Replace special characters in log line timestamp with normal characters.
@@ -214,27 +233,3 @@
     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
-
-    @property
-    def log_path(self):
-        if self.log:
-            return self.log.log_path
-        return "/tmp/logs"
-
-    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/records.py b/acts/framework/acts/records.py
index 77f3936..9b9a1c6 100644
--- a/acts/framework/acts/records.py
+++ b/acts/framework/acts/records.py
@@ -13,17 +13,16 @@
 # WITHOUT 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 logging
 import pprint
 
-from acts.signals import TestSignal
-from acts.utils import epoch_to_human_time
-from acts.utils import get_current_epoch_time
+from acts import signals
+from acts import utils
+
 
 class TestResultEnums(object):
     """Enums used for TestResultRecord class.
@@ -44,8 +43,10 @@
     TEST_RESULT_PASS = "PASS"
     TEST_RESULT_FAIL = "FAIL"
     TEST_RESULT_SKIP = "SKIP"
+    TEST_RESULT_BLOCKED = "BLOCKED"
     TEST_RESULT_UNKNOWN = "UNKNOWN"
 
+
 class TestResultRecord(object):
     """A record that holds the information of a test case execution.
 
@@ -75,7 +76,7 @@
 
         Sets the begin_time of this record.
         """
-        self.begin_time = get_current_epoch_time()
+        self.begin_time = utils.get_current_epoch_time()
 
     def _test_end(self, result, e):
         """Class internal function to signal the end of a test case execution.
@@ -84,11 +85,13 @@
             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.
+                acts.signals.TestSignal.
         """
-        self.end_time = get_current_epoch_time()
+        self.end_time = utils.get_current_epoch_time()
         self.result = result
-        if isinstance(e, TestSignal):
+        if self.extra_errors:
+            self.result = TestResultEnums.TEST_RESULT_UNKNOWN
+        if isinstance(e, signals.TestSignal):
             self.details = e.details
             self.extras = e.extras
         elif e:
@@ -122,6 +125,14 @@
         """
         self._test_end(TestResultEnums.TEST_RESULT_SKIP, e)
 
+    def test_blocked(self, e=None):
+        """To mark the test as blocked in this record.
+
+        Args:
+            e: An instance of acts.signals.Test
+        """
+        self._test_end(TestResultEnums.TEST_RESULT_BLOCKED, e)
+
     def test_unknown(self, e=None):
         """To mark the test as unknown in this record.
 
@@ -152,7 +163,7 @@
 
     def __repr__(self):
         """This returns a short string representation of the test record."""
-        t = epoch_to_human_time(self.begin_time)
+        t = utils.epoch_to_human_time(self.begin_time)
         return "%s %s %s" % (t, self.test_name, self.result)
 
     def to_dict(self):
@@ -189,6 +200,7 @@
         """
         return json.dumps(self.to_dict())
 
+
 class TestResult(object):
     """A class that contains metrics of a test run.
 
@@ -210,6 +222,7 @@
         self.executed = []
         self.passed = []
         self.skipped = []
+        self.blocked = []
         self.unknown = []
         self.controller_info = {}
 
@@ -260,35 +273,38 @@
         Args:
             record: A test record object to add.
         """
-        self.executed.append(record)
         if record.result == TestResultEnums.TEST_RESULT_FAIL:
+            self.executed.append(record)
             self.failed.append(record)
         elif record.result == TestResultEnums.TEST_RESULT_SKIP:
             self.skipped.append(record)
         elif record.result == TestResultEnums.TEST_RESULT_PASS:
+            self.executed.append(record)
             self.passed.append(record)
+        elif record.result == TestResultEnums.TEST_RESULT_BLOCKED:
+            self.blocked.append(record)
         else:
+            self.executed.append(record)
             self.unknown.append(record)
 
-    def fail_class(self, class_name, e):
-        """Add a record to indicate a test class setup has failed and no test
-        in the class was executed.
+    def add_controller_info(self, name, info):
+        try:
+            json.dumps(info)
+        except TypeError:
+            logging.warning(("Controller info for %s is not JSON serializable!"
+                             " Coercing it to string.") % name)
+            self.controller_info[name] = str(info)
+            return
+        self.controller_info[name] = info
 
-        Args:
-            class_name: A string that is the name of the failed test class.
-            e: An exception object.
-        """
-        record = TestResultRecord("", class_name)
-        record.test_begin()
-        if isinstance(e, TestSignal):
-            new_e = type(e)("setup_class failed for %s: %s" % (
-                            class_name, e.details), e.extras)
-        else:
-            new_e = type(e)("setup_class failed for %s: %s" % (
-                            class_name, str(e)))
-        record.test_fail(new_e)
-        self.executed.append(record)
-        self.failed.append(record)
+    @property
+    def is_all_pass(self):
+        """True if no tests failed or threw errors, False otherwise."""
+        num_of_failures = (
+            len(self.failed) + len(self.unknown) + len(self.blocked))
+        if num_of_failures == 0:
+            return True
+        return False
 
     def json_str(self):
         """Converts this test result to a string in json format.
@@ -307,8 +323,8 @@
             A json-format string representing the test results.
         """
         d = {}
-        executed = [record.to_dict() for record in self.executed]
-        d["Results"] = executed
+        d["ControllerInfo"] = self.controller_info
+        d["Results"] = [record.to_dict() for record in self.executed]
         d["Summary"] = self.summary_dict()
         json_str = json.dumps(d, indent=4, sort_keys=True)
         return json_str
@@ -346,5 +362,6 @@
         d["Passed"] = len(self.passed)
         d["Failed"] = len(self.failed)
         d["Skipped"] = len(self.skipped)
+        d["Blocked"] = len(self.blocked)
         d["Unknown"] = len(self.unknown)
         return d
diff --git a/acts/framework/acts/signals.py b/acts/framework/acts/signals.py
index 5fab3c8..49be989 100644
--- a/acts/framework/acts/signals.py
+++ b/acts/framework/acts/signals.py
@@ -13,13 +13,13 @@
 # WITHOUT 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.
@@ -27,53 +27,79 @@
     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__)
+        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."""
+    """Base class for all test result control signals. This is used to signal
+    the result of a test.
+
+    Attribute:
+        details: A string that describes the reason for raising this signal.
+        extras: A json-serializable data type to convey extra information about
+                a test result.
+    """
+
     def __init__(self, details, extras=None):
-        if not isinstance(details, str):
-            raise TestSignalError("Message has to be a string.")
         super(TestSignal, self).__init__(details)
-        self.details = details
+        self.details = str(details)
         try:
             json.dumps(extras)
             self.extras = extras
         except TypeError:
             raise TestSignalError(("Extras must be json serializable. %s "
                                    "is not.") % extras)
+
     def __str__(self):
         return "Details=%s, Extras=%s" % (self.details, self.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 TestBlocked(TestSignal):
+    """Raised when a test has been blocked from running."""
+
+
+class TestSkipClass(TestSignal):
+    """Raised in setup_class when a whole test class should be 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
+    """Raised when an error occured in controller classes."""
diff --git a/acts/framework/acts/test_decorators.py b/acts/framework/acts/test_decorators.py
index 55838c9..ca2a360 100644
--- a/acts/framework/acts/test_decorators.py
+++ b/acts/framework/acts/test_decorators.py
@@ -17,11 +17,13 @@
 from acts import signals
 
 
-def test_info(**keyed_info):
+def test_info(predicate=None, **keyed_info):
     """Adds info about test.
 
     Extra Info to include about the test. This info will be available in the
-    test output.
+    test output. Note that if a key is given multiple times it will be added
+    as a list of all values. If multiples of these are stacked there results
+    will be merged.
 
     Example:
         # This test will have a variable my_var
@@ -30,40 +32,19 @@
             return False
 
     Args:
-        func: The test case to wrap around.
+        predicate: A func to call that if false will skip adding this test
+                   info. Function signature is bool(test_obj, args, kwargs)
         **keyed_info: The key, value info to include in the extras for this
                       test.
     """
 
     def test_info_decoractor(func):
-        def func_wrapper(*args, **kwargs):
-            try:
-                result = func(*args, **kwargs)
-                if result or result is None:
-                    new_signal = signals.TestPass('')
-                else:
-                    new_signal = signals.TestFailure('')
-            except signals.TestSignal as signal:
-                new_signal = signal
-
-            if not isinstance(new_signal.extras, dict) and new_signal.extras:
-                raise ValueError('test_info can only append to signal data '
-                                 'that has a dict as the extra value.')
-            elif not new_signal.extras:
-                new_signal.extras = {}
-
-            for k, v in keyed_info.items():
-                if k not in new_signal.extras:
-                    new_signal.extras[k] = v
-
-            raise new_signal
-
-        return func_wrapper
+        return _TestInfoDecoratorFunc(func, predicate, keyed_info)
 
     return test_info_decoractor
 
 
-def test_tracker_info(uuid):
+def test_tracker_info(uuid, extra_environment_info=None, predicate=None):
     """Decorator for adding test tracker info to tests results.
 
     Will add test tracker info inside of Extras/test_tracker_info.
@@ -76,6 +57,135 @@
 
     Args:
         uuid: The uuid of the test case in test tracker.
+        extra_environment_info: Extra info about the test tracker environment.
+        predicate: A func that if false when called will ignore this info.
     """
-    tt_info = {'test_tracker_uuid': uuid}
-    return test_info(test_tracker_info=tt_info)
+    return test_info(
+        test_tracker_uuid=uuid,
+        test_tracker_enviroment_info=extra_environment_info,
+        predicate=predicate)
+
+
+class _TestInfoDecoratorFunc(object):
+    """Object that acts as a function decorator test info."""
+
+    def __init__(self, func, predicate, keyed_info):
+        self.func = func
+        self.predicate = predicate
+        self.keyed_info = keyed_info
+        self.__name__ = func.__name__
+
+    def __get__(self, instance, owner):
+        """Called by Python to create a binding for an instance closure.
+
+        When called by Python this object will create a special binding for
+        that instance. That binding will know how to interact with this
+        specific decorator.
+        """
+        return _TestInfoBinding(self, instance)
+
+    def __call__(self, *args, **kwargs):
+        """
+        When called runs the underlying func and then attaches test info
+        to a signal.
+        """
+        try:
+            result = self.func(*args, **kwargs)
+
+            if result or result is None:
+                new_signal = signals.TestPass('')
+            else:
+                new_signal = signals.TestFailure('')
+        except signals.TestSignal as signal:
+            new_signal = signal
+
+        if not isinstance(new_signal.extras, dict) and new_signal.extras:
+            raise ValueError('test_info can only append to signal data '
+                             'that has a dict as the extra value.')
+        elif not new_signal.extras:
+            new_signal.extras = {}
+
+        gathered_extras = self._gather_local_info(None, *args, **kwargs)
+        for k, v in gathered_extras.items():
+            if k not in new_signal.extras:
+                new_signal.extras[k] = v
+            else:
+                if not isinstance(new_signal.extras[k], list):
+                    new_signal.extras[k] = [new_signal.extras[k]]
+
+                new_signal.extras[k].insert(0, v)
+
+        raise new_signal
+
+    def gather(self, *args, **kwargs):
+        """
+        Gathers the info from this decorator without invoking the underlying
+        function. This will also gather all child info if the underlying func
+        has that ability.
+
+        Returns: A dictionary of info.
+        """
+        if hasattr(self.func, 'gather'):
+            extras = self.func.gather(*args, **kwargs)
+        else:
+            extras = {}
+
+        self._gather_local_info(extras, *args, **kwargs)
+
+        return extras
+
+    def _gather_local_info(self, gather_into, *args, **kwargs):
+        """Gathers info from this decorator and ignores children.
+
+        Args:
+            gather_into: Gathers into a dictionary that already exists.
+
+        Returns: The dictionary with gathered info in it.
+        """
+        if gather_into is None:
+            extras = {}
+        else:
+            extras = gather_into
+        if not self.predicate or self.predicate(args, kwargs):
+            for k, v in self.keyed_info.items():
+                if v and k not in extras:
+                    extras[k] = v
+                elif v and k in extras:
+                    if not isinstance(extras[k], list):
+                        extras[k] = [extras[k]]
+                    extras[k].insert(0, v)
+
+        return extras
+
+
+class _TestInfoBinding(object):
+    """
+    When Python creates an instance of an object it creates a binding object
+    for each closure that contains what the instance variable should be when
+    called. This object is a similar binding for _TestInfoDecoratorFunc.
+    When Python tries to create a binding of a _TestInfoDecoratorFunc it
+    will return one of these objects to hold the instance for that closure.
+    """
+
+    def __init__(self, target, instance):
+        """
+        Args:
+            target: The target for creating a binding to.
+            instance: The instance to bind the target with.
+        """
+        self.target = target
+        self.instance = instance
+        self.__name__ = target.__name__
+
+    def __call__(self, *args, **kwargs):
+        """
+        When this object is called it will call the target with the bound
+        instance.
+        """
+        return self.target(self.instance, *args, **kwargs)
+
+    def gather(self, *args, **kwargs):
+        """
+        Will gather the target with the bound instance.
+        """
+        return self.target.gather(self.instance, *args, **kwargs)
diff --git a/acts/framework/acts/test_runner.py b/acts/framework/acts/test_runner.py
index de5b310..69e8dd2 100644
--- a/acts/framework/acts/test_runner.py
+++ b/acts/framework/acts/test_runner.py
@@ -17,13 +17,17 @@
 from future import standard_library
 standard_library.install_aliases()
 
+import argparse
 import copy
 import importlib
 import inspect
+import logging
 import os
 import pkgutil
 import sys
 
+from acts import base_test
+from acts import config_parser
 from acts import keys
 from acts import logger
 from acts import records
@@ -31,10 +35,126 @@
 from acts import utils
 
 
-class USERError(Exception):
-    """Raised when a problem is caused by user mistake, e.g. wrong command,
-    misformatted config, test info, wrong test paths etc.
+def main():
+    """Execute the test class in a test module.
+
+    This is the default entry point for running a test script file directly.
+    In this case, only one test class in a test script is allowed.
+
+    To make your test script executable, add the following to your file:
+
+        from acts import test_runner
+        ...
+        if __name__ == "__main__":
+            test_runner.main()
+
+    If you want to implement your own cli entry point, you could use function
+    execute_one_test_class(test_class, test_config, test_identifier)
     """
+    # Parse cli args.
+    parser = argparse.ArgumentParser(description="ACTS Test Executable.")
+    parser.add_argument(
+        '-c',
+        '--config',
+        nargs=1,
+        type=str,
+        required=True,
+        metavar="<PATH>",
+        help="Path to the test configuration file.")
+    parser.add_argument(
+        '--test_case',
+        nargs='+',
+        type=str,
+        metavar="[test_a test_b...]",
+        help="A list of test case names in the test script.")
+    parser.add_argument(
+        '-tb',
+        '--test_bed',
+        nargs='+',
+        type=str,
+        metavar="[<TEST BED NAME1> <TEST BED NAME2> ...]",
+        help="Specify which test beds to run tests on.")
+    args = parser.parse_args(sys.argv[1:])
+    # Load test config file.
+    test_configs = config_parser.load_test_config_file(args.config[0],
+                                                       args.test_bed)
+    # Find the test class in the test script.
+    test_class = _find_test_class()
+    test_class_name = test_class.__name__
+    # Parse test case specifiers if exist.
+    test_case_names = None
+    if args.test_case:
+        test_case_names = args.test_case
+    test_identifier = [(test_class_name, test_case_names)]
+    # Execute the test class with configs.
+    ok = True
+    for config in test_configs:
+        try:
+            result = execute_one_test_class(test_class, config,
+                                            test_identifier)
+            ok = result and ok
+        except signals.TestAbortAll:
+            pass
+        except:
+            logging.exception("Error occurred when executing test bed %s",
+                              config[keys.Config.key_testbed.value])
+            ok = False
+    if not ok:
+        sys.exit(1)
+
+
+def _find_test_class():
+    """Finds the test class in a test script.
+
+    Walk through module memebers and find the subclass of BaseTestClass. Only
+    one subclass is allowed in a test script.
+
+    Returns:
+        The test class in the test module.
+    """
+    test_classes = []
+    main_module_members = sys.modules["__main__"]
+    for _, module_member in main_module_members.__dict__.items():
+        if inspect.isclass(module_member):
+            if issubclass(module_member, base_test.BaseTestClass):
+                test_classes.append(module_member)
+    if len(test_classes) != 1:
+        logging.error("Expected 1 test class per file, found %s.",
+                      [t.__name__ for t in test_classes])
+        sys.exit(1)
+    return test_classes[0]
+
+
+def execute_one_test_class(test_class, test_config, test_identifier):
+    """Executes one specific test class.
+
+    You could call this function in your own cli test entry point if you choose
+    not to use act.py or test_runner.main.
+
+    Args:
+        test_class: A subclass of acts.base_test.BaseTestClass that has the test
+                    logic to be executed.
+        test_config: A dict representing one set of configs for a test run.
+        test_identifier: A list of tuples specifying which test cases to run in
+                         the test class.
+
+    Returns:
+        True if all tests passed without any error, False otherwise.
+
+    Raises:
+        If signals.TestAbortAll is raised by a test run, pipe it through.
+    """
+    tr = TestRunner(test_config, test_identifier)
+    try:
+        tr.run(test_class)
+        return tr.results.is_all_pass
+    except signals.TestAbortAll:
+        raise
+    except:
+        logging.exception("Exception when executing %s.", tr.testbed_name)
+    finally:
+        tr.stop()
+
 
 class TestRunner(object):
     """The class that instantiates test classes, executes test cases, and
@@ -63,24 +183,35 @@
         self.running: A boolean signifies whether this test run is ongoing or
                       not.
     """
+
     def __init__(self, test_configs, run_list):
         self.test_run_info = {}
         self.test_configs = test_configs
         self.testbed_configs = self.test_configs[keys.Config.key_testbed.value]
-        self.testbed_name = self.testbed_configs[keys.Config.key_testbed_name.value]
+        self.testbed_name = self.testbed_configs[
+            keys.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(self.test_configs[keys.Config.key_log_path.value],
-                              self.testbed_name,
-                              start_time)
+        l_path = os.path.join(
+            self.test_configs[keys.Config.key_log_path.value],
+            self.testbed_name, start_time)
         self.log_path = os.path.abspath(l_path)
-        self.log = logger.get_test_logger(self.log_path,
-                                          self.id,
-                                          self.testbed_name)
+        logger.setup_test_logger(self.log_path, self.testbed_name)
+        self.log = logging.getLogger()
         self.controller_registry = {}
         self.controller_destructors = {}
-        self.run_list = run_list
+        if self.test_configs.get(keys.Config.key_random.value):
+            test_case_iterations = self.test_configs.get(
+                keys.Config.key_test_case_iterations.value, 10)
+            self.log.info(
+                "Campaign randomizer is enabled with test_case_iterations %s",
+                test_case_iterations)
+            self.run_list = config_parser.test_randomizer(
+                run_list, test_case_iterations=test_case_iterations)
+            self.write_test_campaign()
+        else:
+            self.run_list = run_list
         self.results = records.TestResult()
         self.running = False
 
@@ -99,11 +230,13 @@
             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 = utils.find_files(test_paths, is_testfile_name)
         test_classes = {}
         for path, name, _ in file_list:
@@ -124,7 +257,7 @@
                         # under py2, because "raise X from Y" syntax is only
                         # supported under py3.
                         self.log.exception(msg)
-                        raise USERError(msg)
+                        raise ValueError(msg)
                 continue
             for member_name in dir(module):
                 if not member_name.startswith("__"):
@@ -169,26 +302,61 @@
             ControllerError is raised if the module does not match the ACTS
             controller interface, or one of the required members is null.
         """
-        required_attributes = ("create",
-                               "destroy",
+        required_attributes = ("create", "destroy",
                                "ACTS_CONTROLLER_CONFIG_NAME")
         for attr in required_attributes:
             if not hasattr(module, attr):
-                raise signals.ControllerError(("Module %s missing required "
-                    "controller module attribute %s.") % (module.__name__,
-                                                          attr))
+                raise signals.ControllerError(
+                    ("Module %s missing required "
+                     "controller module attribute %s.") % (module.__name__,
+                                                           attr))
             if not getattr(module, attr):
                 raise signals.ControllerError(
                     "Controller interface %s in %s cannot be null." % (
                      attr, module.__name__))
 
     def register_controller(self, module, required=True):
-        """Registers a controller module for a test run.
+        """Registers an ACTS controller module for a test run.
 
-        This declares a controller dependency of this test class. If the target
-        module exists and matches the controller interface, the controller
-        module will be instantiated with corresponding configs in the test
-        config file. The module should be imported first.
+        An ACTS controller module is a Python lib that can be used to control
+        a device, service, or equipment. To be ACTS compatible, a controller
+        module needs to have the following members:
+
+            def create(configs):
+                [Required] Creates controller objects from configurations.
+                Args:
+                    configs: A list of serialized data like string/dict. Each
+                             element of the list is a configuration for a
+                             controller object.
+                Returns:
+                    A list of objects.
+
+            def destroy(objects):
+                [Required] Destroys controller objects created by the create
+                function. Each controller object shall be properly cleaned up
+                and all the resources held should be released, e.g. memory
+                allocation, sockets, file handlers etc.
+                Args:
+                    A list of controller objects created by the create function.
+
+            def get_info(objects):
+                [Optional] Gets info from the controller objects used in a test
+                run. The info will be included in test_result_summary.json under
+                the key "ControllerInfo". Such information could include unique
+                ID, version, or anything that could be useful for describing the
+                test bed and debugging.
+                Args:
+                    objects: A list of controller objects created by the create
+                             function.
+                Returns:
+                    A list of json serializable objects, each represents the
+                    info of a controller object. The order of the info object
+                    should follow that of the input objects.
+
+        Registering a controller module declares a test class's dependency the
+        controller. If the module config exists and the module matches the
+        controller interface, controller objects will be instantiated with
+        corresponding configs. The module should be imported first.
 
         Args:
             module: A module that follows the controller module interface.
@@ -217,10 +385,9 @@
             builtin = False
             module_ref_name = module.__name__.split('.')[-1]
         if module_ref_name in self.controller_registry:
-            raise signals.ControllerError(("Controller module %s has already "
-                                           "been registered. It can not be "
-                                           "registered again."
-                                           ) % module_ref_name)
+            raise signals.ControllerError(
+                ("Controller module %s has already been registered. It can not"
+                 " be registered again.") % module_ref_name)
         # Create controller objects.
         create = module.create
         module_config_name = module.ACTS_CONTROLLER_CONFIG_NAME
@@ -238,14 +405,16 @@
             # in case the controller module modifies the config internally.
             original_config = self.testbed_configs[module_config_name]
             controller_config = copy.deepcopy(original_config)
-            objects = create(controller_config, self.log)
+            objects = create(controller_config)
         except:
-            self.log.exception(("Failed to initialize objects for controller "
-                                "%s, abort!"), module_config_name)
+            self.log.exception(
+                "Failed to initialize objects for controller %s, abort!",
+                module_config_name)
             raise
         if not isinstance(objects, list):
-            raise ControllerError(("Controller module %s did not return a list"
-                                   " of objects, abort.") % module_ref_name)
+            raise signals.ControllerError(
+                "Controller module %s did not return a list of objects, abort."
+                % module_ref_name)
         self.controller_registry[module_ref_name] = objects
         # Collect controller information and write to test result.
         # Implementation of "get_info" is optional for a controller module.
@@ -263,7 +432,7 @@
         if builtin:
             self.test_run_info[module_ref_name] = objects
         self.log.debug("Found %d objects for controller %s", len(objects),
-                       module_config_name)
+                      module_config_name)
         destroy_func = module.destroy
         self.controller_destructors[module_ref_name] = destroy_func
         return objects
@@ -295,7 +464,7 @@
         self.test_run_info["register_controller"] = self.register_controller
         self.test_run_info[keys.Config.ikey_logpath.value] = self.log_path
         self.test_run_info[keys.Config.ikey_logger.value] = self.log
-        cli_args = test_configs[keys.Config.ikey_cli_args.value]
+        cli_args = test_configs.get(keys.Config.ikey_cli_args.value)
         self.test_run_info[keys.Config.ikey_cli_args.value] = cli_args
         user_param_pairs = []
         for item in test_configs.items():
@@ -339,9 +508,9 @@
             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.
+        Raises:
+            ValueError is raised if the requested test class could not be found
+            in the test_paths directories.
         """
         try:
             test_cls = self.test_classes[test_cls_name]
@@ -352,15 +521,23 @@
             record.test_skip(signals.TestSkip("Test class does not exist."))
             self.results.add_record(record)
             return
+        if self.test_configs.get(keys.Config.key_random.value) or (
+                "Preflight" in test_cls_name) or "Postflight" in test_cls_name:
+            test_case_iterations = 1
+        else:
+            test_case_iterations = self.test_configs.get(
+                keys.Config.key_test_case_iterations.value, 1)
+
         with test_cls(self.test_run_info) as test_cls_instance:
             try:
-                cls_result = test_cls_instance.run(test_cases)
+                cls_result = test_cls_instance.run(test_cases,
+                                                   test_case_iterations)
                 self.results += cls_result
             except signals.TestAbortAll as e:
                 self.results += e.results
                 raise e
 
-    def run(self):
+    def run(self, test_class=None):
         """Executes test cases.
 
         This will instantiate controller and test classes, and execute test
@@ -369,14 +546,22 @@
 
         A call to TestRunner.stop should eventually happen to conclude the life
         cycle of a TestRunner.
+
+        Args:
+            test_class: The python module of a test class. If provided, run this
+                        class; otherwise, import modules in under test_paths
+                        based on run_list.
         """
         if not self.running:
             self.running = True
         # Initialize controller objects and pack appropriate objects/params
         # to be passed to test class.
         self.parse_config(self.test_configs)
-        t_configs = self.test_configs[keys.Config.key_test_paths.value]
-        self.test_classes = self.import_test_modules(t_configs)
+        if test_class:
+            self.test_classes = {test_class.__name__: test_class}
+        else:
+            t_paths = self.test_configs[keys.Config.key_test_paths.value]
+            self.test_classes = self.import_test_modules(t_paths)
         self.log.debug("Executing run list %s.", self.run_list)
         for test_cls_name, test_case_names in self.run_list:
             if not self.running:
@@ -406,8 +591,8 @@
         This function concludes a test run and writes out a test report.
         """
         if self.running:
-            msg = "\nSummary for test run %s: %s\n" % (self.id,
-                self.results.summary_str())
+            msg = "\nSummary for test run %s: %s\n" % (
+                self.id, self.results.summary_str())
             self._write_results_json_str()
             self.log.info(msg.strip())
             logger.kill_test_logger(self.log)
@@ -422,5 +607,10 @@
         with open(path, 'w') as f:
             f.write(self.results.json_str())
 
-if __name__ == "__main__":
-    pass
+    def write_test_campaign(self):
+        """Log test campaign file."""
+        path = os.path.join(self.log_path, "test_campaign.log")
+        with open(path, 'w') as f:
+            for test_class, test_cases in self.run_list:
+                f.write("%s:\n%s" % (test_class, ",\n".join(test_cases)))
+                f.write("\n\n")
diff --git a/acts/framework/acts/test_utils/bt/BleEnum.py b/acts/framework/acts/test_utils/bt/BleEnum.py
index 3982d09..5b446f9 100644
--- a/acts/framework/acts/test_utils/bt/BleEnum.py
+++ b/acts/framework/acts/test_utils/bt/BleEnum.py
@@ -46,11 +46,14 @@
     SCAN_MODE_BALANCED = 1
     SCAN_MODE_LOW_LATENCY = 2
 
-
 class ScanSettingsReportDelaySeconds(Enum):
     MIN = 0
     MAX = 9223372036854775807
 
+class ScanSettingsPhy(Enum):
+    PHY_LE_1M = 1
+    PHY_LE_CODED = 3
+    PHY_LE_ALL_SUPPORTED = 255
 
 class AdvertiseSettingsAdvertiseType(Enum):
     ADVERTISE_TYPE_NON_CONNECTABLE = 0
diff --git a/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py b/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py
index f9b654e..5cb3549 100644
--- a/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py
+++ b/acts/framework/acts/test_utils/bt/BluetoothBaseTest.py
@@ -17,18 +17,18 @@
     Base Class for Defining Common Bluetooth Test Functionality
 """
 
-import os
+import threading
 import time
 import traceback
+import os
 from acts import utils
 from acts.base_test import BaseTestClass
 from acts.signals import TestSignal
-from acts.utils import set_location_service
+
 from acts.controllers import android_device
-from acts.test_utils.bt.bt_test_utils import (
-    reset_bluetooth, setup_multiple_devices_for_bt_test, take_btsnoop_logs)
-from acts.utils import sync_device_time
-import threading
+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 BluetoothBaseTest(BaseTestClass):
@@ -85,10 +85,6 @@
 
         return _safe_wrap_test_case
 
-    def _reboot_device(self, ad):
-        self.log.info("Rebooting device {}.".format(ad.serial))
-        ad = ad.reboot()
-
     def setup_class(self):
         if "reboot_between_test_class" in self.user_params:
             threads = []
@@ -99,9 +95,6 @@
                 thread.start()
             for t in threads:
                 t.join()
-        for a in self.android_devices:
-            set_location_service(a, False)
-            sync_device_time(a)
         return setup_multiple_devices_for_bt_test(self.android_devices)
 
     def setup_test(self):
@@ -114,9 +107,8 @@
         return True
 
     def on_fail(self, test_name, begin_time):
-        self.log.debug(
-            "Test {} failed. Gathering bugreport and btsnoop logs".format(
-                test_name))
+        self.log.debug("Test {} failed. Gathering bugreport and btsnoop logs".
+                       format(test_name))
         take_btsnoop_logs(self.android_devices, self, test_name)
         self._take_bug_report(test_name, begin_time)
         for _ in range(5):
@@ -126,25 +118,6 @@
                 self.log.error("Failed to reset Bluetooth... retrying.")
         return
 
-    def _take_bug_report(self, test_name, begin_time):
-        if "no_bug_report_on_fail" in self.user_params:
-            return
-
-        # 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_report(test_name, begin_time)
-                tombstone_path = os.path.join(
-                    ad.log_path, "BugReports",
-                    "{},{}".format(begin_time, ad.serial).replace(' ', '_'))
-                utils.create_dir(tombstone_path)
-                ad.adb.pull('/data/tombstones/', tombstone_path)
-            except:
-                self.log.error("Failed to take a bug report for {}, {}"
-                               .format(ad.serial, test_name))
-
     def _get_time_in_milliseconds(self):
         return int(round(time.time() * 1000))
 
@@ -164,6 +137,6 @@
                 sum(self.timer_list) / float(len(self.timer_list))))
             self.log.info("Maximum of list {}".format(max(self.timer_list)))
             self.log.info("Minimum of list {}".format(min(self.timer_list)))
-            self.log.info("Total items in list {}".format(len(
-                self.timer_list)))
+            self.log.info("Total items in list {}".format(
+                len(self.timer_list)))
         self.timer_list = []
diff --git a/acts/framework/acts/test_utils/bt/BluetoothCarHfpBaseTest.py b/acts/framework/acts/test_utils/bt/BluetoothCarHfpBaseTest.py
new file mode 100644
index 0000000..82c11b2
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/BluetoothCarHfpBaseTest.py
@@ -0,0 +1,107 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+This is base class for tests that exercises different GATT procedures between two connected devices.
+Setup/Teardown methods take care of establishing connection, and doing GATT DB initialization/discovery.
+"""
+
+import os
+import time
+from queue import Empty
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts.test_utils.tel.tel_test_utils import get_phone_number
+from acts.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+
+
+class BluetoothCarHfpBaseTest(BluetoothBaseTest):
+    DEFAULT_TIMEOUT = 15
+    ag_phone_number = ""
+    re_phone_number = ""
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        # HF : HandsFree (CarKit role)
+        self.hf = self.android_devices[0]
+        self.hf.log.info("Role set to HF (HandsFree Carkit role).")
+        # AG : Audio Gateway (Phone role)
+        self.ag = self.android_devices[1]
+        self.ag.log.info("Role set to AG (Audio Gateway Phone role).")
+        # RE : Remote Device (Phone being talked to role)
+        if len(self.android_devices) > 2:
+            self.re = self.android_devices[2]
+            self.re.log.info("Role set to RE (Remote device).")
+        else:
+            self.re = None
+        if len(self.android_devices) > 3:
+            self.re2 = self.android_devices[3]
+            self.re2.log.info("Role set to RE2 (Remote device 2).")
+        else:
+            self.re2 = None
+
+    def setup_class(self):
+        super(BluetoothCarHfpBaseTest, self).setup_class()
+        if not "sim_conf_file" in self.user_params.keys():
+            self.log.error("Missing mandatory user config \"sim_conf_file\"!")
+            return False
+        sim_conf_file = self.user_params["sim_conf_file"]
+        if not os.path.isfile(sim_conf_file):
+            sim_conf_file = os.path.join(
+                self.user_params[Config.key_config_path], sim_conf_file)
+            if not os.path.isfile(sim_conf_file):
+                self.log.error("Unable to load user config " + sim_conf_file +
+                               " from test config file.")
+                return False
+        setup_droid_properties(self.log, self.ag, sim_conf_file)
+        self.ag_phone_number = get_phone_number(self.log, self.ag)
+        self.ag.log.info("ag tel: {}".format(self.ag_phone_number))
+        if self.re:
+            setup_droid_properties(self.log, self.re, sim_conf_file)
+            self.re_phone_number = get_phone_number(self.log, self.re)
+            self.re.log.info("re tel: {}".format(self.re_phone_number))
+        if self.re2:
+            setup_droid_properties(self.log, self.re2, sim_conf_file)
+            self.re2_phone_number = get_phone_number(self.log, self.re2)
+            self.re2.log.info("re2 tel: {}".format(self.re2_phone_number))
+        # Pair and connect the devices.
+        # Grace time inbetween stack state changes
+        time.sleep(5)
+        if not pair_pri_to_sec(
+                self.hf, self.ag, attempts=4, auto_confirm=False):
+            self.log.error("Failed to pair")
+            return False
+        return True
+
+    def setup_test(self):
+        if not super(BluetoothCarHfpBaseTest, self).setup_test():
+            return False
+        return ensure_phones_default_state(self.log, self.android_devices[1:])
+
+    def teardown_test(self):
+        if not super(BluetoothCarHfpBaseTest, self).teardown_test():
+            return False
+        return ensure_phones_default_state(self.log, self.android_devices[1:])
+
+    def on_fail(self, test_name, begin_time):
+        result = True
+        if not super(BluetoothCarHfpBaseTest, self).on_fail(test_name,
+                                                            begin_time):
+            result = False
+        ensure_phones_default_state(self.log, self.android_devices[1:])
+        return result
diff --git a/acts/framework/acts/test_utils/bt/BtEnum.py b/acts/framework/acts/test_utils/bt/BtEnum.py
index de6daf2..4fa41f8 100644
--- a/acts/framework/acts/test_utils/bt/BtEnum.py
+++ b/acts/framework/acts/test_utils/bt/BtEnum.py
@@ -34,9 +34,11 @@
     STATE_BLE_ON = 15
     STATE_BLE_TURNING_OFF = 16
 
+
 class RfcommUuid(Enum):
     DEFAULT_UUID = "457807c0-4897-11df-9879-0800200c9a66"
 
+
 class BluetoothProfile(IntEnum):
     # Should be kept in sync with BluetoothProfile.java
     HEADSET = 1
@@ -44,7 +46,7 @@
     HEALTH = 3
     INPUT_DEVICE = 4
     PAN = 5
-    PBAP = 6
+    PBAP_SERVER = 6
     GATT = 7
     GATT_SERVER = 8
     MAP = 9
@@ -53,11 +55,31 @@
     AVRCP_CONTROLLER = 12
     HEADSET_CLIENT = 16
     PBAP_CLIENT = 17
+    MAP_MCE = 18
 
-class BluetoothProfileState(IntEnum):
+
+class RfcommUuid(Enum):
+    DEFAULT_UUID = "457807c0-4897-11df-9879-0800200c9a66"
+
+
+class BluetoothProfileState(Enum):
     # Should be kept in sync with BluetoothProfile#STATE_* constants.
     STATE_DISCONNECTED = 0
     STATE_CONNECTING = 1
     STATE_CONNECTED = 2
     STATE_DISCONNECTING = 3
 
+
+class BluetoothAccessLevel(Enum):
+    # Access Levels from BluetoothDevice.
+    ACCESS_ALLOWED = 1
+    ACCESS_DENIED = 2
+
+
+class BluetoothPriorityLevel(Enum):
+    # Priority levels as defined in BluetoothProfile.java.
+    PRIORITY_AUTO_CONNECT = 1000
+    PRIORITY_ON = 100
+    PRIORITY_OFF = 0
+    PRIORITY_UNDEFINED = -1
+
diff --git a/acts/framework/acts/test_utils/bt/BtFunhausBaseTest.py b/acts/framework/acts/test_utils/bt/BtFunhausBaseTest.py
new file mode 100644
index 0000000..2efeee2
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/BtFunhausBaseTest.py
@@ -0,0 +1,316 @@
+# /usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Test script to automate the Bluetooth Audio Funhaus.
+"""
+from acts.keys import Config
+from acts.test_utils.bt.BtMetricsBaseTest import BtMetricsBaseTest
+from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts.utils import bypass_setup_wizard
+from acts.utils import create_dir
+from acts.utils import exe_cmd
+from acts.utils import sync_device_time
+import json
+import time
+import os
+
+BT_CONF_PATH = "/data/misc/bluedroid/bt_config.conf"
+
+
+class BtFunhausBaseTest(BtMetricsBaseTest):
+    """
+    Base class for Bluetooth A2DP audio tests, this class is in charge of
+    pushing link key to Android device so that it could be paired with remote
+    A2DP device, pushing music to Android device, playing audio, monitoring
+    audio play, and stop playing audio
+    """
+    music_file_to_play = ""
+    device_fails_to_connect_list = []
+
+    def __init__(self, controllers):
+        BtMetricsBaseTest.__init__(self, controllers)
+
+    def on_fail(self, test_name, begin_time):
+        self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
+        super(BtFunhausBaseTest, self).on_fail(test_name, begin_time)
+
+    def setup_class(self):
+        if not super(BtFunhausBaseTest, self).setup_class():
+            return False
+        for ad in self.android_devices:
+            sync_device_time(ad)
+            # Disable Bluetooth HCI Snoop Logs for audio tests
+            ad.adb.shell("setprop persist.bluetooth.btsnoopenable false")
+            if not bypass_setup_wizard(ad):
+                self.log.debug(
+                    "Failed to bypass setup wizard, continuing test.")
+        if not "bt_config" in self.user_params.keys():
+            self.log.error("Missing mandatory user config \"bt_config\"!")
+            return False
+        bt_config_map_file = self.user_params["bt_config"]
+        return self._setup_bt_config(bt_config_map_file)
+
+    def _setup_bt_config(self, bt_config_map_file):
+        bt_config_map = {}
+        if not os.path.isfile(bt_config_map_file):
+            bt_config_map_file = os.path.join(
+                self.user_params[Config.key_config_path], bt_config_map_file)
+            if not os.path.isfile(bt_config_map_file):
+                self.log.error("Unable to load bt_config file {}.".format(
+                    bt_config_map_file))
+                return False
+        try:
+            f = open(bt_config_map_file, 'r')
+            bt_config_map = json.load(f)
+            f.close()
+        except FileNotFoundError:
+            self.log.error("File not found: {}.".format(bt_config_map_file))
+            return False
+        # Connected devices return all upper case mac addresses.
+        # Make the peripheral_info address attribute upper case
+        # in order to ensure the BT mac addresses match.
+        for serial in bt_config_map.keys():
+            mac_address = bt_config_map[serial]["peripheral_info"][
+                "address"].upper()
+            bt_config_map[serial]["peripheral_info"]["address"] = mac_address
+        for ad in self.android_devices:
+            serial = ad.serial
+
+            # Verify serial number in bt_config_map
+            self.log.info("Verify serial number of Android device in config.")
+            if serial not in bt_config_map.keys():
+                self.log.error(
+                    "Missing android device serial {} in bt_config.".format(
+                        serial))
+                return False
+
+            # Push the bt_config.conf file to Android device
+            if (not self._push_config_to_android_device(ad, bt_config_map,
+                                                        serial)):
+                return False
+
+            # Add music to the Android device
+            if not self._add_music_to_android_device(ad):
+                return False
+
+            # Verify Bluetooth is enabled
+            self.log.info("Verifying Bluetooth is enabled on Android Device.")
+            if not bluetooth_enabled_check(ad):
+                self.log.error("Failed to toggle on Bluetooth on device {}".
+                               format(serial))
+                return False
+
+            # Verify Bluetooth device is connected
+            self.log.info(
+                "Waiting up to 10 seconds for device to reconnect...")
+            if not self._verify_bluetooth_device_is_connected(
+                    ad, bt_config_map, serial):
+                self.device_fails_to_connect_list.append(ad)
+        if len(self.device_fails_to_connect_list) == len(self.android_devices):
+            self.log.error("All devices failed to reconnect.")
+            return False
+        return True
+
+    def _push_config_to_android_device(self, ad, bt_config_map, serial):
+        """
+        Push Bluetooth config file to android device so that it will have the
+        paired link key to the remote device
+        :param ad: Android device
+        :param bt_config_map: Map to each device's config
+        :param serial: Serial number of device
+        :return: True on success, False on failure
+        """
+        self.log.info("Pushing bt_config.conf file to Android device.")
+        config_path = bt_config_map[serial]["config_path"]
+        if not os.path.isfile(config_path):
+            config_path = os.path.join(
+                self.user_params[Config.key_config_path], config_path)
+            if not os.path.isfile(config_path):
+                self.log.error(
+                    "Unable to load bt_config file {}.".format(config_path))
+                return False
+        ad.adb.push("{} {}".format(config_path, BT_CONF_PATH))
+        return True
+
+    def _add_music_to_android_device(self, ad):
+        """
+        Add music to Android device as specified by the test config
+        :param ad: Android device
+        :return: True on success, False on failure
+        """
+        self.log.info("Pushing music to the Android device.")
+        music_path_str = "music_path"
+        android_music_path = "/sdcard/Music/"
+        if music_path_str not in self.user_params:
+            self.log.error("Need music for audio testcases...")
+            return False
+        music_path = self.user_params[music_path_str]
+        if not os.path.isdir(music_path):
+            music_path = os.path.join(self.user_params[Config.key_config_path],
+                                      music_path)
+            if not os.path.isdir(music_path):
+                self.log.error(
+                    "Unable to find music directory {}.".format(music_path))
+                return False
+        for dirname, dirnames, filenames in os.walk(music_path):
+            for filename in filenames:
+                self.music_file_to_play = filename
+                file = os.path.join(dirname, filename)
+                # TODO: Handle file paths with spaces
+                ad.adb.push("{} {}".format(file, android_music_path))
+        ad.reboot()
+        return True
+
+    def _verify_bluetooth_device_is_connected(self, ad, bt_config_map, serial):
+        """
+        Verify that remote Bluetooth device is connected
+        :param ad: Android device
+        :param bt_config_map: Config map
+        :param serial: Serial number of Android device
+        :return: True on success, False on failure
+        """
+        connected_devices = ad.droid.bluetoothGetConnectedDevices()
+        start_time = time.time()
+        wait_time = 10
+        result = False
+        while time.time() < start_time + wait_time:
+            connected_devices = ad.droid.bluetoothGetConnectedDevices()
+            if len(connected_devices) > 0:
+                if bt_config_map[serial]["peripheral_info"]["address"] in {
+                        d['address']
+                        for d in connected_devices
+                }:
+                    result = True
+                    break
+            else:
+                try:
+                    ad.droid.bluetoothConnectBonded(
+                        bt_config_map[serial]["peripheral_info"]["address"])
+                except Exception as err:
+                    self.log.error(
+                        "Failed to connect bonded. Err: {}".format(err))
+        if not result:
+            self.log.info("Connected Devices: {}".format(connected_devices))
+            self.log.info("Bonded Devices: {}".format(
+                ad.droid.bluetoothGetBondedDevices()))
+            self.log.error(
+                "Failed to connect to peripheral name: {}, address: {}".format(
+                    bt_config_map[serial]["peripheral_info"]["name"],
+                    bt_config_map[serial]["peripheral_info"]["address"]))
+            self.device_fails_to_connect_list.append("{}:{}".format(
+                serial, bt_config_map[serial]["peripheral_info"]["name"]))
+
+    def _collect_bluetooth_manager_dumpsys_logs(self, ads):
+        """
+        Collect "adb shell dumpsys bluetooth_manager" logs
+        :param ads: list of active Android devices
+        :return: None
+        """
+        for ad in ads:
+            serial = ad.serial
+            out_name = "{}_{}".format(serial, "bluetooth_dumpsys.txt")
+            dumpsys_path = ''.join((ad.log_path, "/BluetoothDumpsys"))
+            create_dir(dumpsys_path)
+            cmd = ''.join(
+                ("adb -s ", serial, " shell dumpsys bluetooth_manager > ",
+                 dumpsys_path, "/", out_name))
+            exe_cmd(cmd)
+
+    def start_playing_music_on_all_devices(self):
+        """
+        Start playing music all devices
+        :return: None
+        """
+        for ad in self.android_devices:
+            ad.droid.mediaPlayOpen(
+                "file:///sdcard/Music/{}".format(self.music_file_to_play))
+            ad.droid.mediaPlaySetLooping(True)
+            self.log.info(
+                "Music is now playing on device {}".format(ad.serial))
+
+    def stop_playing_music_on_all_devices(self):
+        """
+        Stop playing music on all devices
+        :return: None
+        """
+        for ad in self.android_devices:
+            ad.droid.mediaPlayStopAll()
+
+    def monitor_music_play_util_deadline(self, end_time, sleep_interval=1):
+        """
+        Monitor music play on all devices, if a device's Bluetooth adapter is
+        OFF or if a device is not connected to any remote Bluetooth devices,
+        we add them to failure lists bluetooth_off_list and
+        device_not_connected_list respectively
+        :param end_time: The deadline in epoch floating point seconds that we
+            must stop playing
+        :param sleep_interval: How often to monitor, too small we may drain
+            too much resources on Android, too big the deadline might be passed
+            by a maximum of this amount
+        :return:
+            status: False iff all devices are off or disconnected otherwise True
+            bluetooth_off_list: List of ADs that have Bluetooth at OFF state
+            device_not_connected_list: List of ADs with no remote device
+                                        connected
+        """
+        bluetooth_off_list = []
+        device_not_connected_list = []
+        while time.time() < end_time:
+            for ad in self.android_devices:
+                serial = ad.serial
+                if (not ad.droid.bluetoothCheckState() and
+                        serial not in bluetooth_off_list):
+                    self.log.error(
+                        "Device {}'s Bluetooth state is off.".format(serial))
+                    bluetooth_off_list.append(serial)
+                if (ad.droid.bluetoothGetConnectedDevices() == 0 and
+                        serial not in device_not_connected_list):
+                    self.log.error(
+                        "Device {} not connected to any Bluetooth devices.".
+                        format(serial))
+                    device_not_connected_list.append(serial)
+                if len(bluetooth_off_list) == len(self.android_devices):
+                    self.log.error(
+                        "Bluetooth off on all Android devices. Ending Test")
+                    return False, bluetooth_off_list, device_not_connected_list
+                if len(device_not_connected_list) == len(self.android_devices):
+                    self.log.error(
+                        "Every Android device has no device connected.")
+                    return False, bluetooth_off_list, device_not_connected_list
+            time.sleep(sleep_interval)
+        return True, bluetooth_off_list, device_not_connected_list
+
+    def play_music_for_duration(self, duration, sleep_interval=1):
+        """
+        A convenience method for above methods. It starts run music on all
+        devices, monitors the health of music play and stops playing them when
+        time passes the duration
+        :param duration: Duration in floating point seconds
+        :param sleep_interval: How often to check the health of music play
+        :return:
+            status: False iff all devices are off or disconnected otherwise True
+            bluetooth_off_list: List of ADs that have Bluetooth at OFF state
+            device_not_connected_list: List of ADs with no remote device
+                                        connected
+        """
+        start_time = time.time()
+        end_time = start_time + duration
+        self.start_playing_music_on_all_devices()
+        status, bluetooth_off_list, device_not_connected_list = \
+            self.monitor_music_play_util_deadline(end_time, sleep_interval)
+        if status:
+            self.stop_playing_music_on_all_devices()
+        return status, bluetooth_off_list, device_not_connected_list
diff --git a/acts/framework/acts/test_utils/bt/BtMetricsBaseTest.py b/acts/framework/acts/test_utils/bt/BtMetricsBaseTest.py
new file mode 100644
index 0000000..1371396
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/BtMetricsBaseTest.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+import os
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.libs.proto.proto_utils import compile_import_proto, parse_proto_to_ascii
+from acts.test_utils.bt.bt_metrics_utils import get_bluetooth_metrics
+from acts.utils import create_dir, dump_string_to_file
+
+
+class BtMetricsBaseTest(BluetoothBaseTest):
+    """
+    Base class for tests that requires dumping and parsing Bluetooth Metrics
+    """
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.bluetooth_proto_module = None
+        self.metrics_path = None
+        self.bluetooth_proto_path = None
+
+    def setup_class(self):
+        """
+        This method finds bluetooth protobuf definitions from config file,
+        compile the protobuf and create a log directory for metrics dumping
+        :return: True on success, False on failure
+        """
+        super(BtMetricsBaseTest, self).setup_class()
+        self.metrics_path = os.path.join(self.android_devices[0].log_path,
+                                         "BluetoothMetrics")
+        self.bluetooth_proto_path = self.user_params["bluetooth_proto_path"]
+        if not os.path.isfile(self.bluetooth_proto_path):
+            try:
+                self.bluetooth_proto_path = "{}/bluetooth.proto".format(
+                    os.path.dirname(os.path.realpath(__file__)))
+            except Exception:
+                self.log.error("File not found.")
+            if not os.path.isfile(self.bluetooth_proto_path):
+                self.log.error("Unable to find Bluetooth proto {}."
+                               .format(self.bluetooth_proto_path))
+                return False
+        create_dir(self.metrics_path)
+        self.bluetooth_proto_module = \
+            compile_import_proto(self.metrics_path, self.bluetooth_proto_path)
+        if not self.bluetooth_proto_module:
+            self.log.error("Unable to compile bluetooth proto at " +
+                           self.bluetooth_proto_path)
+            return False
+        return True
+
+    def setup_test(self):
+        """
+        This method clears the current metrics, should be called after child
+        class setup_test()
+        :return: True
+        """
+        super(BtMetricsBaseTest, self).setup_test()
+        # Clear all metrics
+        get_bluetooth_metrics(self.android_devices[0],
+                              self.bluetooth_proto_module)
+        return True
+
+    def collect_bluetooth_manager_metrics_logs(self, ads):
+        """
+        Collect Bluetooth metrics logs, save an ascii log to disk and return
+        both binary and ascii logs to caller
+        :param ads: list of active Android devices
+        :return: List of binary metrics logs,
+                List of ascii metrics logs
+        """
+        bluetooth_logs = []
+        bluetooth_logs_ascii = []
+        for ad in ads:
+            serial = ad.serial
+            out_name = "{}_{}".format(serial, "bluetooth_metrics.txt")
+            bluetooth_log = get_bluetooth_metrics(ad,
+                                                  self.bluetooth_proto_module)
+            bluetooth_log_ascii = parse_proto_to_ascii(bluetooth_log)
+            dump_string_to_file(bluetooth_log_ascii,
+                                os.path.join(self.metrics_path, out_name))
+            bluetooth_logs.append(bluetooth_log)
+            bluetooth_logs_ascii.append(bluetooth_log_ascii)
+        return bluetooth_logs, bluetooth_logs_ascii
diff --git a/acts/framework/acts/test_utils/bt/GattEnum.py b/acts/framework/acts/test_utils/bt/GattEnum.py
index 7b84be7..ec35327 100644
--- a/acts/framework/acts/test_utils/bt/GattEnum.py
+++ b/acts/framework/acts/test_utils/bt/GattEnum.py
@@ -34,6 +34,8 @@
     MTU_SERV_CHANGED_ERR = "MTU Server Changed event not found. Expected {}"
     GATT_CONN_CHANGE_ERR = "GATT Connection Changed event not found. Expected {}"
     CHAR_CHANGE_ERR = "GATT Characteristic Changed event not fond. Expected {}"
+    PHY_READ_ERR = "Phy Read event not fond. Expected {}"
+    PHY_UPDATE_ERR = "Phy Update event not fond. Expected {}"
 
 class GattCbStrings(Enum):
     CHAR_WRITE_REQ = "GattServer{}onCharacteristicWriteRequest"
@@ -52,6 +54,10 @@
     MTU_SERV_CHANGED = "GattServer{}onMtuChanged"
     GATT_CONN_CHANGE = "GattConnect{}onConnectionStateChange"
     CHAR_CHANGE = "GattConnect{}onCharacteristicChanged"
+    PHY_READ = "GattConnect{}onPhyRead"
+    PHY_UPDATE = "GattConnect{}onPhyUpdate"
+    SERV_PHY_READ = "GattServer{}onPhyRead"
+    SERV_PHY_UPDATE = "GattServer{}onPhyUpdate"
 
 
 class GattEvent(Enum):
@@ -85,6 +91,14 @@
                         "err": GattCbErr.GATT_CONN_CHANGE_ERR.value}
     CHAR_CHANGE = {"evt": GattCbStrings.CHAR_CHANGE.value,
                         "err": GattCbErr.CHAR_CHANGE_ERR.value}
+    PHY_READ = {"evt": GattCbStrings.PHY_READ.value,
+                        "err": GattCbErr.PHY_READ_ERR.value}
+    PHY_UPDATE = {"evt": GattCbStrings.PHY_UPDATE.value,
+                        "err": GattCbErr.PHY_UPDATE_ERR.value}
+    SERV_PHY_READ = {"evt": GattCbStrings.SERV_PHY_READ.value,
+                        "err": GattCbErr.PHY_READ_ERR.value}
+    SERV_PHY_UPDATE = {"evt": GattCbStrings.SERV_PHY_UPDATE.value,
+                        "err": GattCbErr.PHY_UPDATE_ERR.value}
 
 
 class GattConnectionState(IntEnum):
@@ -168,4 +182,16 @@
 class GattTransport(IntEnum):
     TRANSPORT_AUTO = 0x00
     TRANSPORT_BREDR = 0x01
-    TRANSPORT_LE = 0x02
\ No newline at end of file
+    TRANSPORT_LE = 0x02
+
+
+class GattPhy(IntEnum):
+    PHY_LE_1M = 1
+    PHY_LE_2M = 2
+    PHY_LE_CODED = 3
+
+
+class GattPhyMask(IntEnum):
+    PHY_LE_1M_MASK = 1
+    PHY_LE_2M_MASK = 2
+    PHY_LE_CODED_MASK = 4
\ No newline at end of file
diff --git a/acts/framework/acts/test_utils/bt/PowerBaseTest.py b/acts/framework/acts/test_utils/bt/PowerBaseTest.py
new file mode 100644
index 0000000..907efbc
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/PowerBaseTest.py
@@ -0,0 +1,301 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+This test script is the base class for Bluetooth power testing
+"""
+
+import json
+import os
+import statistics
+import time
+
+from acts import asserts
+from acts import utils
+from acts.controllers import monsoon
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts.test_utils.tel.tel_test_utils import set_phone_screen_on
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.utils import create_dir
+from acts.utils import force_airplane_mode
+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
+from acts.utils import sync_device_time
+
+
+class PowerBaseTest(BluetoothBaseTest):
+    # Monsoon output Voltage in V
+    MONSOON_OUTPUT_VOLTAGE = 4.2
+    # Monsoon output max current in A
+    MONSOON_MAX_CURRENT = 7.8
+    # Power mesaurement sampling rate in Hz
+    POWER_SAMPLING_RATE = 20
+    SCREEN_TIME_OFF = 10
+    # Wait time for PMC to start in seconds
+    WAIT_TIME = 10
+    # Accuracy for current and power data
+    ACCURACY = 4
+    THOUSAND = 1000
+
+    START_PMC_CMD = ("am start -n com.android.pmc/com.android.pmc."
+                     "PMCMainActivity")
+    PMC_VERBOSE_CMD = "setprop log.tag.PMC VERBOSE"
+
+    def setup_class(self):
+        # Not to call Base class setup_class()
+        # since it removes the bonded devices
+        for ad in self.android_devices:
+            sync_device_time(ad)
+        self.ad = self.android_devices[0]
+        self.mon = self.monsoons[0]
+        self.mon.set_voltage(self.MONSOON_OUTPUT_VOLTAGE)
+        self.mon.set_max_current(self.MONSOON_MAX_CURRENT)
+        # Monsoon phone
+        self.mon.attach_device(self.ad)
+        self.monsoon_log_path = os.path.join(self.log_path, "MonsoonLog")
+        create_dir(self.monsoon_log_path)
+
+        asserts.assert_true(
+            self.mon.usb("auto"),
+            "Failed to turn USB mode to auto on monsoon.")
+
+        asserts.assert_true(
+            force_airplane_mode(self.ad, True),
+            "Can not turn on airplane mode on: %s" % self.ad.serial)
+        asserts.assert_true(
+            bluetooth_enabled_check(self.ad),
+            "Failed to set Bluetooth state to enabled")
+        set_location_service(self.ad, False)
+        set_adaptive_brightness(self.ad, False)
+        set_ambient_display(self.ad, False)
+        self.ad.adb.shell("settings put system screen_brightness 0")
+        set_auto_rotate(self.ad, False)
+        set_phone_screen_on(self.log, self.ad, self.SCREEN_TIME_OFF)
+
+        wutils.wifi_toggle_state(self.ad, False)
+
+        # Start PMC app.
+        self.log.info("Start PMC app...")
+        self.ad.adb.shell(self.START_PMC_CMD)
+        self.ad.adb.shell(self.PMC_VERBOSE_CMD)
+
+        self.log.info("Check to see if PMC app started")
+        for _ in range(self.WAIT_TIME):
+            time.sleep(1)
+            try:
+                self.ad.adb.shell('ps -A | grep "S com.android.pmc"')
+                break
+            except adb.AdbError as e:
+                self.log.info("PMC app is NOT started yet")
+
+    def save_logs_for_power_test(self, monsoon_result, measure_time,
+                                 idle_time):
+        """Utility function to save power data into log file.
+
+        Steps:
+        1. Save power data into a file if being configed.
+        2. Create a bug report if being configured
+
+        Args:
+            monsoon_result: power data object
+            measure_time: time duration (sec) for measure power
+            idle_time: time duration (sec) which is not counted toward
+                       power measurement
+
+        Returns:
+            None
+        """
+        current_time = get_current_human_time()
+        file_name = "{}_{}".format(self.current_test_name, current_time)
+        self.save_to_text_file(monsoon_result,
+                               os.path.join(self.monsoon_log_path, file_name),
+                               measure_time, idle_time)
+
+        self.ad.take_bug_report(self.current_test_name, current_time)
+
+    def _calculate_average_current_n_std_dev(self, monsoon_data, measure_time,
+                                             idle_time):
+        """Utility function to calculate average current and standard deviation
+           in the unit of mA.
+
+        Args:
+            monsoon_result: power data object
+            measure_time: time duration (sec) when power data is counted toward
+                          calculation of average and std deviation
+            idle_time: time duration (sec) when power data is not counted
+                       toward calculation of average and std deviation
+
+        Returns:
+            A tuple of average current and std dev as float
+        """
+        if idle_time == 0:
+            # if idle time is 0 use Monsoon calculation
+            # in this case standard deviation is 0
+            return round(monsoon_data.average_current, self.ACCURACY), 0
+
+        self.log.info(
+            "Measure time: {} Idle time: {} Total Data Points: {}".format(
+                measure_time, idle_time, len(monsoon_data.data_points)))
+
+        # The base time to be used to calculate the relative time
+        base_time = monsoon_data.timestamps[0]
+
+        # Index for measure and idle cycle index
+        measure_cycle_index = 0
+        # Measure end time of measure cycle
+        measure_end_time = measure_time
+        # Idle end time of measure cycle
+        idle_end_time = measure_time + idle_time
+        # Sum of current data points for a measure cycle
+        current_sum = 0
+        # Number of current data points for a measure cycle
+        data_point_count = 0
+        average_60_sec = []
+        # Total number of measure data point
+        total_measured_data_point_count = 0
+
+        # Flag to indicate whether the average is calculated for this cycle
+        # For 1 second there are multiple data points
+        # so time comparison will yield to multiple cases
+        done_average = False
+
+        for t, d in zip(monsoon_data.timestamps, monsoon_data.data_points):
+            relative_timepoint = t - base_time
+            # When time exceeds 1 cycle of measurement update 2 end times
+            if relative_timepoint > idle_end_time:
+                measure_cycle_index += 1
+                measure_end_time = measure_cycle_index * (
+                    measure_time + idle_time) + measure_time
+                idle_end_time = measure_end_time + idle_time
+                done_average = False
+
+            # Within measure time sum the current
+            if relative_timepoint <= measure_end_time:
+                current_sum += d
+                data_point_count += 1
+            elif not done_average:
+                # Calculate the average current for this cycle
+                average_60_sec.append(current_sum / data_point_count)
+                total_measured_data_point_count += data_point_count
+                current_sum = 0
+                data_point_count = 0
+                done_average = True
+
+        # Calculate the average current and convert it into mA
+        current_avg = round(
+            statistics.mean(average_60_sec) * self.THOUSAND, self.ACCURACY)
+        # Calculate the min and max current and convert it into mA
+        current_min = round(min(average_60_sec) * self.THOUSAND, self.ACCURACY)
+        current_max = round(max(average_60_sec) * self.THOUSAND, self.ACCURACY)
+
+        # Calculate the standard deviation and convert it into mA
+        stdev = round(
+            statistics.stdev(average_60_sec) * self.THOUSAND, self.ACCURACY)
+        self.log.info("Total Counted Data Points: {}".format(
+            total_measured_data_point_count))
+        self.log.info("Average Current: {} mA ".format(current_avg))
+        self.log.info("Standard Deviation: {} mA".format(stdev))
+        self.log.info("Min Current: {} mA ".format(current_min))
+        self.log.info("Max Current: {} mA".format(current_max))
+
+        return current_avg, stdev
+
+    def _format_header(self, monsoon_data, measure_time, idle_time):
+        """Utility function to write the header info to the file.
+           The data is formated as tab delimited for spreadsheets.
+
+        Args:
+            monsoon_result: power data object
+            measure_time: time duration (sec) when power data is counted toward
+                          calculation of average and std deviation
+            idle_time: time duration (sec) when power data is not counted
+                       toward calculation of average and std deviation
+
+        Returns:
+            None
+        """
+        strs = [""]
+        if monsoon_data.tag:
+            strs.append("\t\t" + monsoon_data.tag)
+        else:
+            strs.append("\t\tMonsoon Measurement Data")
+        average_cur, stdev = self._calculate_average_current_n_std_dev(
+            monsoon_data, measure_time, idle_time)
+        total_power = round(average_cur * monsoon_data.voltage, self.ACCURACY)
+
+        strs.append("\t\tAverage Current: {} mA.".format(average_cur))
+        strs.append("\t\tSTD DEV Current: {} mA.".format(stdev))
+        strs.append("\t\tVoltage: {} V.".format(monsoon_data.voltage))
+        strs.append("\t\tTotal Power: {} mW.".format(total_power))
+        strs.append((
+            "\t\t{} samples taken at {}Hz, with an offset of {} samples."
+        ).format(
+            len(monsoon_data._data_points), monsoon_data.hz,
+            monsoon_data.offset))
+        return "\n".join(strs)
+
+    def _format_data_point(self, monsoon_data, measure_time, idle_time):
+        """Utility function to format the data into a string.
+           The data is formated as tab delimited for spreadsheets.
+
+        Args:
+            monsoon_result: power data object
+            measure_time: time duration (sec) when power data is counted toward
+                          calculation of average and std deviation
+            idle_time: time duration (sec) when power data is not counted
+                       toward calculation of average and std deviation
+
+        Returns:
+            Average current as float
+        """
+        strs = []
+        strs.append(self._format_header(monsoon_data, measure_time, idle_time))
+        strs.append("\t\tTime\tAmp")
+        # Get the relative time
+        start_time = monsoon_data.timestamps[0]
+        for t, d in zip(monsoon_data.timestamps, monsoon_data.data_points):
+            strs.append("{}\t{}".format(
+                round((t - start_time), 0), round(d, self.ACCURACY)))
+
+        return "\n".join(strs)
+
+    def save_to_text_file(self, monsoon_data, file_path, measure_time,
+                          idle_time):
+        """Save multiple MonsoonData objects to a text file.
+           The data is formated as tab delimited for spreadsheets.
+
+        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.
+        """
+        if not monsoon_data:
+            self.log.error("Attempting to write empty Monsoon data to "
+                           "file, abort")
+            return
+
+        utils.create_dir(os.path.dirname(file_path))
+        try:
+            with open(file_path, 'w') as f:
+                f.write(self._format_data_point(monsoon_data, measure_time,
+                                                idle_time))
+                f.write("\t\t" + monsoon_data.delimiter)
+        except IOError:
+            self.log.error("Fail to write power data into file")
diff --git a/acts/framework/acts/test_utils/bt/bluetooth.proto b/acts/framework/acts/test_utils/bt/bluetooth.proto
new file mode 100644
index 0000000..a43ff47
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/bluetooth.proto
@@ -0,0 +1,223 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+// Author: pkanwar@google.com (Pankaj Kanwar)
+// Protos for uploading bluetooth metrics.
+
+syntax = "proto2";
+
+package clearcut.connectivity;
+
+option java_package = "com.google.wireless.android.play.playlog.connectivity";
+// option (datapol.file_vetting_status) = "latest";
+
+// import "storage/datapol/annotations/proto/semantic_annotations.proto";
+
+message BluetoothLog {
+  // Session information that gets logged for every BT connection.
+  repeated BluetoothSession session = 1;
+
+  // Session information that gets logged for every Pair event.
+  repeated PairEvent pair_event = 2;
+
+  // Information for Wake locks.
+  repeated WakeEvent wake_event = 3;
+
+  // Scan event information.
+  repeated ScanEvent scan_event = 4;
+
+  // Number of bonded devices.
+  optional int32 num_bonded_devices = 5;
+
+  // Number of BluetoothSession including discarded ones beyond capacity
+  optional int64 num_bluetooth_session = 6;
+
+  // Number of PairEvent including discarded ones beyond capacity
+  optional int64 num_pair_event = 7;
+
+  // Number of WakeEvent including discarded ones beyond capacity
+  optional int64 num_wake_event = 8;
+
+  // Number of ScanEvent including discarded ones beyond capacity
+  optional int64 num_scan_event = 9;
+}
+
+// The information about the device.
+message DeviceInfo {
+  // Device type.
+  enum DeviceType {
+    // Type is unknown.
+    DEVICE_TYPE_UNKNOWN = 0;
+
+    DEVICE_TYPE_BREDR = 1;
+
+    DEVICE_TYPE_LE = 2;
+
+    DEVICE_TYPE_DUMO = 3;
+  }
+
+  // Device class
+  // https://cs.corp.google.com/#android/system/bt/stack/include/btm_api.h&q=major_computer.
+  optional int32 device_class = 1;
+
+  // Device type.
+  optional DeviceType device_type = 2;
+}
+
+// Information that gets logged for every Bluetooth connection.
+message BluetoothSession {
+  // Type of technology used in the connection.
+  enum ConnectionTechnologyType {
+    CONNECTION_TECHNOLOGY_TYPE_UNKNOWN = 0;
+
+    CONNECTION_TECHNOLOGY_TYPE_LE = 1;
+
+    CONNECTION_TECHNOLOGY_TYPE_BREDR = 2;
+  }
+
+  enum DisconnectReasonType {
+    UNKNOWN = 0;
+
+    // A metrics dump takes a snapshot of current Bluetooth session and thus
+    // is not a real disconnect, but a discontinuation in metrics logging.
+    // This enum indicates this situation.
+    METRICS_DUMP = 1;
+
+    NEXT_START_WITHOUT_END_PREVIOUS = 2;
+  }
+
+  // Duration of the session.
+  optional int64 session_duration_sec = 2;
+
+  // Technology type.
+  optional ConnectionTechnologyType connection_technology_type = 3;
+
+  // Reason for disconnecting.
+  optional string disconnect_reason = 4 [deprecated = true];
+
+  // The information about the device which it is connected to.
+  optional DeviceInfo device_connected_to = 5;
+
+  // The information about the RFComm session.
+  optional RFCommSession rfcomm_session = 6;
+
+  // The information about the A2DP audio session.
+  optional A2DPSession a2dp_session = 7;
+
+  // Numeric reason for disconnecting as defined in metrics.h
+  optional DisconnectReasonType disconnect_reason_type = 8;
+}
+
+message RFCommSession {
+  // bytes transmitted.
+  optional int32 rx_bytes = 1;
+
+  // bytes transmitted.
+  optional int32 tx_bytes = 2;
+}
+
+// Session information that gets logged for A2DP session.
+message A2DPSession {
+  // Media timer in milliseconds.
+  optional int32 media_timer_min_millis = 1;
+
+  // Media timer in milliseconds.
+  optional int32 media_timer_max_millis = 2;
+
+  // Media timer in milliseconds.
+  optional int32 media_timer_avg_millis = 3;
+
+  // Buffer overruns count.
+  optional int32 buffer_overruns_max_count = 4;
+
+  // Buffer overruns total.
+  optional int32 buffer_overruns_total = 5;
+
+  // Buffer underruns average.
+  optional float buffer_underruns_average = 6;
+
+  // Buffer underruns count.
+  optional int32 buffer_underruns_count = 7;
+
+  // Total audio time in this A2DP session
+  optional int64 audio_duration_millis = 8;
+}
+
+message PairEvent {
+  // The reason for disconnecting
+  // https://cs.corp.google.com/#android/system/bt/stack/include/hcidefs.h&q=failed_establish.
+  optional int32 disconnect_reason = 1;
+
+  // Pair event time
+  optional int64 event_time_millis =
+      2;  // [(datapol.semantic_type) = ST_TIMESTAMP];
+
+  // The information about the device which it is paired to.
+  optional DeviceInfo device_paired_with = 3;
+}
+
+message WakeEvent {
+  // Information about the wake event type.
+  enum WakeEventType {
+    // Type is unknown.
+    UNKNOWN = 0;
+
+    // WakeLock was acquired.
+    ACQUIRED = 1;
+
+    // WakeLock was released.
+    RELEASED = 2;
+  }
+
+  // Information about the wake event type.
+  optional WakeEventType wake_event_type = 1;
+
+  // Initiator of the scan. Only the first three names will be stored.
+  // e.g. com.google.gms.
+  optional string requestor = 2;
+
+  // Name of the wakelock (e.g. bluedroid_timer).
+  optional string name = 3;
+
+  // Time of the event.
+  optional int64 event_time_millis =
+      4;  // [(datapol.semantic_type) = ST_TIMESTAMP];
+}
+
+message ScanEvent {
+  // Scan type.
+  enum ScanTechnologyType {
+    // Scan Type is unknown.
+    SCAN_TYPE_UNKNOWN = 0;
+
+    SCAN_TECH_TYPE_LE = 1;
+
+    SCAN_TECH_TYPE_BREDR = 2;
+
+    SCAN_TECH_TYPE_BOTH = 3;
+  }
+
+  // Scan event type.
+  enum ScanEventType {
+    // Scan started.
+    SCAN_EVENT_START = 0;
+
+    // Scan stopped.
+    SCAN_EVENT_STOP = 1;
+  }
+
+  // Scan event type.
+  optional ScanEventType scan_event_type = 1;
+
+  // Initiator of the scan. Only the first three names will be stored.
+  // e.g. com.google.gms.
+  optional string initiator = 2;
+
+  // Technology used for scanning.
+  optional ScanTechnologyType scan_technology_type = 3;
+
+  // Number of results returned.
+  optional int32 number_results = 4;
+
+  // Time of the event.
+  optional int64 event_time_millis =
+      5;  // [(datapol.semantic_type) = ST_TIMESTAMP];
+}
diff --git a/acts/framework/acts/test_utils/bt/bt_contacts_utils.py b/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
new file mode 100644
index 0000000..ccebdbf
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
@@ -0,0 +1,409 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Compare_contacts accepts 2 vcf files, extracts full name, email, and
+telephone numbers from each and reports how many unique cards it finds across
+the two files.
+"""
+
+from mmap import ACCESS_READ
+from mmap import mmap
+import logging
+import re
+import random
+import string
+
+from acts.utils import exe_cmd
+import queue
+
+# CallLog types
+INCOMMING_CALL_TYPE = "1"
+OUTGOING_CALL_TYPE = "2"
+MISSED_CALL_TYPE = "3"
+
+# Callback strings.
+CONTACTS_CHANGED_CALLBACK = "ContactsChanged"
+CALL_LOG_CHANGED = "CallLogChanged"
+CONTACTS_ERASED_CALLBACK = "ContactsErased"
+
+# URI for contacts database on Nexus.
+CONTACTS_URI = "content://com.android.contacts/data/phones"
+
+# Path for temporary file storage on device.
+STORAGE_PATH = "/storage/emulated/0/Download/"
+
+PBAP_SYNC_TIME = 30
+
+log = logging
+
+
+def parse_contacts(file_name):
+    """Read vcf file and generate a list of contacts.
+
+    Contacts full name, prefered email, and all phone numbers are extracted.
+    """
+
+    vcard_regex = re.compile(b"^BEGIN:VCARD((\n*?.*?)*?)END:VCARD",
+                             re.MULTILINE)
+    fullname_regex = re.compile(b"^FN:(.*)", re.MULTILINE)
+    email_regex = re.compile(b"^EMAIL;PREF:(.*)", re.MULTILINE)
+    tel_regex = re.compile(b"^TEL;(.*):(.*)", re.MULTILINE)
+
+    with open(file_name, "r") as contacts_file:
+        contacts = []
+        contacts_map = mmap(
+            contacts_file.fileno(), length=0, access=ACCESS_READ)
+        new_contact = None
+
+        # Find all VCARDs in the input file, then extract the first full name,
+        # first email address, and all phone numbers from it.  If there is at
+        # least a full name add it to the contact list.
+        for current_vcard in vcard_regex.findall(contacts_map):
+            new_contact = VCard()
+
+            fullname = fullname_regex.search(current_vcard[0])
+            if fullname is not None:
+                new_contact.name = fullname.group(1)
+
+            email = email_regex.search(current_vcard[0])
+            if email is not None:
+                new_contact.email = email.group(1)
+
+            for phone_number in tel_regex.findall(current_vcard[0]):
+                new_contact.add_phone_number(
+                    PhoneNumber(phone_number[0], phone_number[1]))
+
+            contacts.append(new_contact)
+
+        return contacts
+
+
+def phone_number_count(destination_path, file_name):
+    """Counts number of phone numbers in a VCF.
+    """
+    tel_regex = re.compile(b"^TEL;(.*):(.*)", re.MULTILINE)
+    with open("{}{}".format(destination_path, file_name),
+              "r") as contacts_file:
+        contacts_map = mmap(
+            contacts_file.fileno(), length=0, access=ACCESS_READ)
+        numbers = tel_regex.findall(contacts_map)
+        return len(numbers)
+
+
+def count_contacts_with_differences(destination_path,
+                                    pce_contacts_vcf_file_name,
+                                    pse_contacts_vcf_file_name):
+    """Compare two contact files and report the number of differences.
+
+    Difference count is returned, and the differences are logged, this is order
+    independent.
+    """
+
+    pce_contacts = parse_contacts("{}{}".format(destination_path,
+                                                pce_contacts_vcf_file_name))
+    pse_contacts = parse_contacts("{}{}".format(destination_path,
+                                                pse_contacts_vcf_file_name))
+
+    differences = set(pce_contacts).symmetric_difference(set(pse_contacts))
+    if not differences:
+        log.info("All {} contacts in the phonebooks match".format(
+            str(len(pce_contacts))))
+    else:
+        log.info("{} contacts match, but ".format(
+            str(len(set(pce_contacts).intersection(set(pse_contacts))))))
+        log.info("the following {} entries don't match:".format(
+            str(len(differences))))
+        for current_vcard in differences:
+            log.info(current_vcard)
+    return len(differences)
+
+
+class PhoneNumber(object):
+    """Simple class for maintaining a phone number entry and type with only the
+    digits.
+    """
+
+    def __init__(self, phone_type, phone_number):
+        self.phone_type = phone_type
+        # remove non digits from phone_number
+        self.phone_number = re.sub(r"\D", "", str(phone_number))
+
+    def __eq__(self, other):
+        return (self.phone_type == other.phone_type and
+                self.phone_number == other.phone_number)
+
+    def __hash__(self):
+        return hash(self.phone_type) ^ hash(self.phone_number)
+
+
+class VCard(object):
+    """Contains name, email, and phone numbers.
+    """
+
+    def __init__(self):
+        self.name = None
+        self.first_name = None
+        self.last_name = None
+        self.email = None
+        self.phone_numbers = []
+        self.photo = None
+
+    def __lt__(self, other):
+        return self.name < other.name
+
+    def __hash__(self):
+        result = hash(self.name) ^ hash(self.email) ^ hash(self.photo == None)
+        for number in self.phone_numbers:
+            result ^= hash(number)
+        return result
+
+    def __eq__(self, other):
+        return hash(self) == hash(other)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __str__(self):
+        vcard_strings = ["BEGIN:VCARD\n", "VERSION:2.1\n"]
+
+        if self.first_name or self.last_name:
+            vcard_strings.append("N:{};{};;;\nFN:{} {}\n".format(
+                self.last_name, self.first_name, self.first_name,
+                self.last_name))
+        elif self.name:
+            vcard_strings.append("FN:{}\n".format(self.name))
+
+        if self.phone_numbers:
+            for phone in self.phone_numbers:
+                vcard_strings.append("TEL;{}:{}\n".format(
+                    str(phone.phone_type), phone.phone_number))
+
+        if self.email:
+            vcard_strings.append("EMAIL;PREF:{}\n".format(self.email))
+
+        vcard_strings.append("END:VCARD\n")
+        return "".join(vcard_strings)
+
+    def add_phone_number(self, phone_number):
+        if phone_number not in self.phone_numbers:
+            self.phone_numbers.append(phone_number)
+
+
+def generate_random_phone_number():
+    """Generate a random phone number/type
+    """
+    return PhoneNumber("CELL",
+                       "+{0:010d}".format(random.randint(0, 9999999999)))
+
+
+def generate_random_string(length=8,
+                           charset="{}{}{}".format(string.digits,
+                                                   string.ascii_letters,
+                                                   string.punctuation)):
+    """Generate a random string of specified length from the characterset
+    """
+    # Remove ; since that would make 2 words.
+    charset = charset.replace(";", "")
+    name = []
+    for i in range(length):
+        name.append(random.choice(charset))
+    return "".join(name)
+
+
+def generate_contact_list(destination_path,
+                          file_name,
+                          contact_count,
+                          phone_number_count=1):
+    """Generate a simple VCF file for count contacts with basic content.
+
+    An example with count = 1 and local_number = 2]
+
+    BEGIN:VCARD
+    VERSION:2.1
+    N:Person;1;;;
+    FN:1 Person
+    TEL;CELL:+1-555-555-1234
+    TEL;CELL:+1-555-555-4321
+    EMAIL;PREF:person1@gmail.com
+    END:VCARD
+    """
+    vcards = []
+    for i in range(contact_count):
+        current_contact = VCard()
+        current_contact.first_name = generate_random_string(
+            random.randint(1, 19))
+        current_contact.last_name = generate_random_string(
+            random.randint(1, 19))
+        current_contact.email = "{}{}@{}.{}".format(
+            current_contact.last_name, current_contact.first_name,
+            generate_random_string(random.randint(1, 19)),
+            generate_random_string(random.randint(1, 4)))
+        for number in range(phone_number_count):
+            current_contact.add_phone_number(generate_random_phone_number())
+        vcards.append(current_contact)
+    create_new_contacts_vcf_from_vcards(destination_path, file_name, vcards)
+
+
+def create_new_contacts_vcf_from_vcards(destination_path, vcf_file_name,
+                                        vcards):
+    """Create a new file with filename
+    """
+    contact_file = open("{}{}".format(destination_path, vcf_file_name), "w+")
+    for card in vcards:
+        contact_file.write(str(card))
+    contact_file.close()
+
+
+def get_contact_count(device):
+    """Returns the number of name:phone number pairs.
+    """
+    contact_list = device.droid.contactsQueryContent(
+        CONTACTS_URI, ["display_name", "data1"], "", [], "display_name")
+    return len(contact_list)
+
+
+def import_device_contacts_from_vcf(device, destination_path, vcf_file):
+    """Uploads and import vcf file to device.
+    """
+    number_count = phone_number_count(destination_path, vcf_file)
+    device.log.info("Trying to add {} phone numbers.".format(number_count))
+    local_phonebook_path = "{}{}".format(destination_path, vcf_file)
+    phone_phonebook_path = "{}{}".format(STORAGE_PATH, vcf_file)
+    device.adb.push("{} {}".format(local_phonebook_path, phone_phonebook_path))
+    device.droid.importVcf("file://{}{}".format(STORAGE_PATH, vcf_file))
+    if wait_for_phone_number_update_complete(device, number_count):
+        return number_count
+    else:
+        return 0
+
+
+def export_device_contacts_to_vcf(device, destination_path, vcf_file):
+    """Export and download vcf file from device.
+    """
+    path_on_phone = "{}{}".format(STORAGE_PATH, vcf_file)
+    device.droid.exportVcf("{}".format(path_on_phone))
+    # Download and then remove file from device
+    device.adb.pull("{} {}".format(path_on_phone, destination_path))
+    return True
+
+
+def erase_contacts(device):
+    """Erase all contacts out of devices contact database.
+    """
+    device.log.info("Erasing contacts.")
+    if get_contact_count(device) > 0:
+        device.droid.contactsEraseAll()
+        try:
+            device.ed.pop_event(CONTACTS_ERASED_CALLBACK, PBAP_SYNC_TIME)
+        except queue.Empty:
+            log.error("Phone book not empty.")
+            return False
+    return True
+
+
+def wait_for_phone_number_update_complete(device, expected_count):
+    """Check phone_number count on device and wait for updates until it has the
+    expected number of phone numbers in its contact database.
+    """
+    update_completed = True
+    try:
+        while (expected_count != get_contact_count(device) and
+               device.ed.pop_event(CONTACTS_CHANGED_CALLBACK, PBAP_SYNC_TIME)):
+            pass
+    except queue.Empty:
+        log.error("Contacts failed to update.")
+        update_completed = False
+    device.log.info("Found {} out of the expected {} contacts.".format(
+        get_contact_count(device), expected_count))
+    return update_completed
+
+
+def wait_for_call_log_update_complete(device, expected_count):
+    """Check call log count on device and wait for updates until it has the
+    expected number of calls in its call log database.
+    """
+    update_completed = True
+    try:
+        while (expected_count != device.droid.callLogGetCount() and
+               device.ed.pop_event(CALL_LOG_CHANGED, PBAP_SYNC_TIME)):
+            pass
+    except queue.Empty:
+        log.error("Call Log failed to update.")
+        update_completed = False
+    device.log.info("Found {} out of the expected {} call logs.".format(
+        device.droid.callLogGetCount(), expected_count))
+    return
+
+
+def add_call_log(device, call_log_type, phone_number, call_time):
+    """Add call number and time to specified log.
+    """
+    new_call_log = {}
+    new_call_log["type"] = str(call_log_type)
+    new_call_log["number"] = phone_number
+    new_call_log["time"] = str(call_time)
+    device.droid.callLogsPut(new_call_log)
+
+
+def get_and_compare_call_logs(pse, pce, call_log_type):
+    """Gather and compare call logs from PSE and PCE for the specified type.
+    """
+    pse_call_log = pse.droid.callLogsGet(call_log_type)
+    pce_call_log = pce.droid.callLogsGet(call_log_type)
+    return compare_call_logs(pse_call_log, pce_call_log)
+
+
+def normalize_phonenumber(phone_number):
+    """Remove all non-digits from phone_number
+    """
+    return re.sub(r"\D", "", phone_number)
+
+
+def compare_call_logs(pse_call_log, pce_call_log):
+    """Gather and compare call logs from PSE and PCE for the specified type.
+    """
+    call_logs_match = True
+    if len(pse_call_log) == len(pce_call_log):
+        for i in range(len(pse_call_log)):
+            # Compare the phone number
+            if normalize_phonenumber(pse_call_log[i][
+                    "number"]) != normalize_phonenumber(pce_call_log[i][
+                        "number"]):
+                log.warning("Call Log numbers differ")
+                call_logs_match = False
+
+            # Compare which log it was taken from (Incomming, Outgoing, Missed
+            if pse_call_log[i]["type"] != pce_call_log[i]["type"]:
+                log.warning("Call Log types differ")
+                call_logs_match = False
+
+            # Compare time to truncated second.
+            if int(pse_call_log[i]["date"]) // 1000 != int(pce_call_log[i][
+                    "date"]) // 1000:
+                log.warning("Call log times don't match, check timezone.")
+                call_logs_match = False
+
+    else:
+        log.warning("Call Log lengths differ {}:{}".format(
+            len(pse_call_log), len(pce_call_log)))
+        call_logs_match = False
+
+    if not call_logs_match:
+        log.info("PSE Call Log:")
+        log.info(pse_call_log)
+        log.info("PCE Call Log:")
+        log.info(pce_call_log)
+
+    return call_logs_match
diff --git a/acts/framework/acts/test_utils/bt/bt_gatt_utils.py b/acts/framework/acts/test_utils/bt/bt_gatt_utils.py
index b2d8be6..79bf2cb 100644
--- a/acts/framework/acts/test_utils/bt/bt_gatt_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_gatt_utils.py
@@ -14,7 +14,7 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-from acts.logger import LoggerProxy
+import logging
 
 from acts.test_utils.bt.bt_test_utils import BtTestUtilsError
 from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
@@ -23,13 +23,14 @@
 from acts.test_utils.bt.GattEnum import GattConnectionState
 from acts.test_utils.bt.GattEnum import GattCharacteristic
 from acts.test_utils.bt.GattEnum import GattDescriptor
+from acts.test_utils.bt.GattEnum import GattPhyMask
 from acts.test_utils.bt.GattEnum import GattService
 from acts.test_utils.bt.GattEnum import GattTransport
 import pprint
 from queue import Empty
 
 default_timeout = 10
-log = LoggerProxy()
+log = logging
 
 
 class GattTestUtilsError(Exception):
@@ -43,7 +44,8 @@
     gatt_callback = cen_ad.droid.gattCreateGattCallback()
     log.info("Gatt Connect to mac address {}.".format(mac_address))
     bluetooth_gatt = cen_ad.droid.gattClientConnectGatt(
-        gatt_callback, mac_address, autoconnect, transport)
+        gatt_callback, mac_address, autoconnect, transport,
+        GattPhyMask.PHY_LE_1M_MASK)
     expected_event = GattCbStrings.GATT_CONN_CHANGE.value.format(gatt_callback)
     try:
         event = cen_ad.ed.pop_event(expected_event, default_timeout)
diff --git a/acts/framework/acts/test_utils/bt/bt_metrics_utils.py b/acts/framework/acts/test_utils/bt/bt_metrics_utils.py
new file mode 100644
index 0000000..ba9a1b6
--- /dev/null
+++ b/acts/framework/acts/test_utils/bt/bt_metrics_utils.py
@@ -0,0 +1,39 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+import base64
+
+
+def get_bluetooth_metrics(ad, bluetooth_proto_module):
+    """
+    Get metric proto from the Bluetooth stack
+
+    Parameter:
+      ad - Android device
+      bluetooth_proto_module - the Bluetooth protomodule returned by
+                                compile_import_proto()
+
+    Return:
+      a protobuf object representing Bluetooth metric
+
+    """
+    bluetooth_log = bluetooth_proto_module.BluetoothLog()
+    proto_native_str_64 = \
+        ad.adb.shell("/system/bin/dumpsys bluetooth_manager --proto-bin")
+    proto_native_str = base64.b64decode(proto_native_str_64)
+    proto_java_str_64 = \
+        ad.adb.shell("/system/bin/dumpsys bluetooth_manager --proto-java-bin")
+    proto_java_str = base64.b64decode(proto_java_str_64)
+    bluetooth_log.MergeFromString(proto_native_str)
+    bluetooth_log.MergeFromString(proto_java_str)
+    return bluetooth_log
diff --git a/acts/framework/acts/test_utils/bt/bt_test_utils.py b/acts/framework/acts/test_utils/bt/bt_test_utils.py
index 5cc6634..ea861d6 100644
--- a/acts/framework/acts/test_utils/bt/bt_test_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_test_utils.py
@@ -14,17 +14,18 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+import logging
 import random
 import pprint
 import string
 from queue import Empty
+import queue
 import threading
 import time
 from acts import utils
 
 from subprocess import call
 
-from acts.logger import LoggerProxy
 from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
 from acts.test_utils.bt.BleEnum import ScanSettingsCallbackType
 from acts.test_utils.bt.BleEnum import ScanSettingsMatchMode
@@ -37,18 +38,26 @@
 from acts.test_utils.bt.BleEnum import ScanSettingsMatchNum
 from acts.test_utils.bt.BleEnum import ScanSettingsScanResultType
 from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.BtEnum import BluetoothProfile
+from acts.test_utils.bt.BtEnum import BluetoothProfileState
 from acts.test_utils.bt.BtEnum import BluetoothScanModeType
 from acts.test_utils.bt.BtEnum import RfcommUuid
 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.utils import exe_cmd
+from acts.utils import create_dir
 
 DEFAULT_TIMEOUT = 15
 DEFAULT_RFCOMM_TIMEOUT = 10000
 MAGIC_PAN_CONNECT_TIMEOUT = 5
 DEFAULT_DISCOVERY_TIMEOUT = 3
+TIMEOUT_SMALL = 0.0001
 
-log = LoggerProxy()
+PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2
+
+BTSNOOP_LOG_PATH_ON_DEVICE = "/data/misc/bluetooth/logs/btsnoop_hci.log"
+
+log = logging
 
 # Callback strings
 scan_result = "BleScan{}onScanResults"
@@ -58,6 +67,22 @@
 adv_succ = "BleAdvertise{}onSuccess"
 bluetooth_off = "BluetoothStateChangedOff"
 bluetooth_on = "BluetoothStateChangedOn"
+mtu_changed = "GattConnect{}onMtuChanged"
+bluetooth_profile_connection_state_changed = \
+    "BluetoothProfileConnectionStateChanged"
+advertising_set_started = "AdvertisingSet{}onAdvertisingSetStarted"
+advertising_set_on_own_address_read = "AdvertisingSet{}onOwnAddressRead"
+advertising_set_stopped = "AdvertisingSet{}onAdvertisingSetStopped"
+advertising_set_enabled = "AdvertisingSet{}onAdvertisingEnabled"
+advertising_set_data_set = "AdvertisingSet{}onAdvertisingDataSet"
+advertising_set_scan_response_set = "AdvertisingSet{}onScanResponseDataSet"
+advertising_set_parameters_update = \
+    "AdvertisingSet{}onAdvertisingParametersUpdated"
+advertising_set_periodic_parameters_updated = \
+    "AdvertisingSet{}onPeriodicAdvertisingParametersUpdated"
+advertising_set_periodic_data_set = \
+    "AdvertisingSet{}onPeriodicAdvertisingDataSet"
+advertising_set_periodic_enable = "AdvertisingSet{}onPeriodicAdvertisingEnable"
 
 # rfcomm test uuids
 rfcomm_secure_uuid = "fa87c0d0-afac-11de-8a39-0800200c9a66"
@@ -65,9 +90,11 @@
 
 advertisements_to_devices = {}
 
-batch_scan_not_supported_list = ["Nexus 4",
-                                 "Nexus 5",
-                                 "Nexus 7", ]
+batch_scan_not_supported_list = [
+    "Nexus 4",
+    "Nexus 5",
+    "Nexus 7",
+]
 
 
 class BtTestUtilsError(Exception):
@@ -135,9 +162,9 @@
         try:
             adv_ad.ed.pop_event(
                 adv_succ.format(advertise_callback), DEFAULT_TIMEOUT)
-            log.info("Advertisement {} started.".format(i + 1))
+            adv_ad.log.info("Advertisement {} started.".format(i + 1))
         except Empty as error:
-            log.error("Advertisement {} failed to start.".format(i + 1))
+            adv_ad.log.error("Advertisement {} failed to start.".format(i + 1))
             raise BtTestUtilsError("Test failed with Empty error: {}".format(
                 error))
     return advertise_callback_list
@@ -226,17 +253,16 @@
             d = a.droid
             setup_result = d.bluetoothSetLocalName(generate_id_by_size(4))
             if not setup_result:
-                log.error("Failed to set device name.")
+                a.log.error("Failed to set device name.")
                 return setup_result
             d.bluetoothDisableBLE()
             bonded_devices = d.bluetoothGetBondedDevices()
             for b in bonded_devices:
+                a.log.info("Removing bond for device {}".format(b['address']))
                 d.bluetoothUnbond(b['address'])
         for a in android_devices:
-            setup_result = a.droid.bluetoothConfigHciSnoopLog(True)
-            if not setup_result:
-                log.error("Failed to enable Bluetooth Hci Snoop Logging.")
-                return setup_result
+            if not a.adb.shell("setprop persist.bluetooth.btsnoopenable true"):
+                a.log.warning("Failed to enable Bluetooth Hci Snoop Logging.")
     except Exception as err:
         log.error("Something went wrong in multi device setup: {}".format(err))
         return False
@@ -259,12 +285,12 @@
         try:
             ad.ed.pop_event(expected_bluetooth_on_event_name, DEFAULT_TIMEOUT)
         except Empty:
-            log.info("Failed to toggle Bluetooth on (no broadcast received).")
+            ad.log.info("Failed to toggle Bluetooth on(no broadcast received).")
             # Try one more time to poke at the actual state.
             if ad.droid.bluetoothCheckState():
-                log.info(".. actual state is ON")
+                ad.log.info(".. actual state is ON")
                 return True
-            log.error(".. actual state is OFF")
+            ad.log.error(".. actual state is OFF")
             return False
     return True
 
@@ -280,16 +306,14 @@
     """
     for a in android_devices:
         droid, ed = a.droid, a.ed
-        log.info("Reset state of bluetooth on device: {}".format(
-            droid.getBuildSerial()))
+        a.log.info("Reset state of bluetooth on device.")
         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)
+                ed.pop_event(expected_bluetooth_off_event_name, DEFAULT_TIMEOUT)
             except Exception:
-                log.error("Failed to toggle Bluetooth off.")
+                a.log.error("Failed to toggle Bluetooth off.")
                 return False
         # temp sleep for b/17723234
         time.sleep(3)
@@ -308,7 +332,8 @@
     Returns:
         The maximum advertisement count.
     """
-    log.info("Determining number of maximum concurrent advertisements...")
+    android_device.log.info(
+        "Determining number of maximum concurrent advertisements...")
     advertisement_count = 0
     bt_enabled = False
     if not android_device.droid.bluetoothCheckState():
@@ -317,13 +342,15 @@
         android_device.ed.pop_event(expected_bluetooth_on_event_name,
                                     DEFAULT_TIMEOUT)
     except Exception:
-        log.info("Failed to toggle Bluetooth on (no broadcast received).")
+        android_device.log.info(
+            "Failed to toggle Bluetooth on(no broadcast received).")
         # Try one more time to poke at the actual state.
         if android_device.droid.bluetoothCheckState() is True:
-            log.info(".. actual state is ON")
+            android_device.log.info(".. actual state is ON")
         else:
-            log.error(
-                "Failed to turn Bluetooth on. Setting default advertisements to 1")
+            android_device.log.error(
+                "Failed to turn Bluetooth on. Setting default advertisements to 1"
+            )
             advertisement_count = -1
             return advertisement_count
     advertise_callback_list = []
@@ -335,22 +362,34 @@
 
         android_device.droid.bleStartBleAdvertising(
             advertise_callback, advertise_data, advertise_settings)
-        try:
-            android_device.ed.pop_event(
-                adv_succ.format(advertise_callback), DEFAULT_TIMEOUT)
-            log.info("Advertisement {} started.".format(advertisement_count +
-                                                        1))
+
+        regex = "(" + adv_succ.format(
+            advertise_callback) + "|" + adv_fail.format(
+                advertise_callback) + ")"
+        # wait for either success or failure event
+        evt = android_device.ed.pop_events(regex, DEFAULT_TIMEOUT,
+                                           TIMEOUT_SMALL)
+        if evt[0]["name"] == adv_succ.format(advertise_callback):
             advertisement_count += 1
-        except Exception as err:
-            log.info(
-                "Advertisement failed to start. Reached max advertisements at {}".
-                format(advertisement_count))
-            break
+            android_device.log.info("Advertisement {} started.".format(
+                advertisement_count))
+        else:
+            error = evt[0]["data"]["Error"]
+            if error == "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS":
+                android_device.log.info(
+                    "Advertisement failed to start. Reached max " +
+                    "advertisements at {}".format(advertisement_count))
+                break
+            else:
+                raise BtTestUtilsError(
+                    "Expected ADVERTISE_FAILED_TOO_MANY_ADVERTISERS," +
+                    " but received bad error code {}".format(error))
     try:
         for adv in advertise_callback_list:
             android_device.droid.bleStopBleAdvertising(adv)
     except Exception:
-        log.error("Failed to stop advertisingment, resetting Bluetooth.")
+        android_device.log.error(
+            "Failed to stop advertisingment, resetting Bluetooth.")
         reset_bluetooth([android_device])
     return advertisement_count
 
@@ -381,8 +420,8 @@
             max_tries = 3
             #Retry to calculate max advertisements
             while max_advertisements == -1 and max_tries > 0:
-                log.info("Attempts left to determine max advertisements: {}".
-                         format(max_tries))
+                a.log.info("Attempts left to determine max advertisements: {}".
+                           format(max_tries))
                 max_advertisements = determine_max_advertisements(a)
                 max_tries -= 1
             advertisements_to_devices[model] = max_advertisements
@@ -431,14 +470,15 @@
         for scan_callback in scn_callback_list:
             scan_droid.bleStopBleScan(scan_callback)
     except Exception as err:
-        log.debug("Failed to stop LE scan... reseting Bluetooth. Error {}".
-                  format(err))
+        scn_android_device.log.debug(
+            "Failed to stop LE scan... reseting Bluetooth. Error {}".format(
+                err))
         reset_bluetooth([scn_android_device])
     try:
         for adv_callback in adv_callback_list:
             adv_droid.bleStopBleAdvertising(adv_callback)
     except Exception as err:
-        log.debug(
+        adv_android_device.log.debug(
             "Failed to stop LE advertisement... reseting Bluetooth. Error {}".
             format(err))
         reset_bluetooth([adv_android_device])
@@ -468,16 +508,15 @@
                                         advertise_settings)
     try:
         adv_ad.ed.pop_event(
-            adv_succ.format(advertise_callback),
-            DEFAULT_TIMEOUT)
+            adv_succ.format(advertise_callback), DEFAULT_TIMEOUT)
     except Empty as err:
         raise BtTestUtilsError(
             "Advertiser did not start successfully {}".format(err))
     filter_list = scan_ad.droid.bleGenFilterList()
     scan_settings = scan_ad.droid.bleBuildScanSetting()
     scan_callback = scan_ad.droid.bleGenScanCallback()
-    scan_ad.droid.bleSetScanFilterDeviceName(
-        adv_ad.droid.bluetoothGetLocalName())
+    scan_ad.droid.bleSetScanFilterDeviceName(adv_ad.droid.bluetoothGetLocalName(
+    ))
     scan_ad.droid.bleBuildScanFilter(filter_list)
     scan_ad.droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
     try:
@@ -491,6 +530,25 @@
     return mac_address, advertise_callback
 
 
+def enable_bluetooth(droid, ed):
+    if droid.bluetoothCheckState() is True:
+        return True
+
+    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 (no broadcast received)")
+        if droid.bluetoothCheckState() is True:
+            log.info(".. actual state is ON")
+            return True
+        log.info(".. actual state is OFF")
+        return False
+
+    return True
+
+
 def disable_bluetooth(droid):
     """Disable Bluetooth on input Droid object.
 
@@ -579,53 +637,197 @@
     profile_dict['hsp'] = droid.bluetoothHspIsReady()
     profile_dict['a2dp'] = droid.bluetoothA2dpIsReady()
     profile_dict['avrcp'] = droid.bluetoothAvrcpIsReady()
+    profile_dict['a2dp_sink'] = droid.bluetoothA2dpSinkIsReady()
+    profile_dict['hfp_client'] = droid.bluetoothHfpClientIsReady()
+    profile_dict['pbap_client'] = droid.bluetoothPbapClientIsReady()
     return profile_dict
 
 
-def pair_pri_to_sec(pri_droid, sec_droid):
-    """Pairs pri droid to sec droid.
+def log_energy_info(android_devices, state):
+    """Logs energy info of input Android devices.
 
     Args:
-        pri_droid: Droid initiating pairing.
-        sec_droid: Droid accepting pairing.
+        android_devices: input Android device list to log energy info from.
+        state: the input state to log. Usually 'Start' or 'Stop' for logging.
 
     Returns:
-        True if pairing is successful, false if uncsuccsessful.
+        A logging string of the Bluetooth energy info reported.
     """
-    # Enable discovery on sec_droid so that pri_droid can find it.
+    return_string = "{} Energy info collection:\n".format(state)
+    # Bug: b/31966929
+    return return_string
+
+
+def set_profile_priority(host_ad, client_ad, profiles, priority):
+    """Sets the priority of said profile(s) on host_ad for client_ad"""
+    for profile in profiles:
+        host_ad.log.info("Profile {} on {} for {} set to priority {}".format(
+            profile,
+            host_ad.droid.bluetoothGetLocalName(),
+            client_ad.droid.bluetoothGetLocalAddress(), priority.value))
+        if BluetoothProfile.A2DP_SINK.value == profile:
+            host_ad.droid.bluetoothA2dpSinkSetPriority(
+                client_ad.droid.bluetoothGetLocalAddress(), priority.value)
+        elif BluetoothProfile.HEADSET_CLIENT.value == profile:
+            host_ad.droid.bluetoothHfpClientSetPriority(
+                client_ad.droid.bluetoothGetLocalAddress(), priority.value)
+        elif BluetoothProfile.PBAP_CLIENT.value == profile:
+            host_ad.droid.bluetoothPbapClientSetPriority(
+                client_ad.droid.bluetoothGetLocalAddress(), priority.value)
+        else:
+            host_ad.log.error(
+                "Profile {} not yet supported for priority settings".format(
+                    profile))
+
+
+def pair_pri_to_sec(pri_ad, sec_ad, attempts=2, auto_confirm=True):
+    """Pairs pri droid to secondary droid.
+
+    Args:
+        pri_ad: Android device initiating connection
+        sec_ad: Android device accepting connection
+        attempts: Number of attempts to try until failure.
+        auto_confirm: Auto confirm passkey match for both devices
+
+    Returns:
+        Pass if True
+        Fail if False
+    """
+    pri_ad.droid.bluetoothStartConnectionStateChangeMonitor(
+        sec_ad.droid.bluetoothGetLocalAddress())
+    curr_attempts = 0
+    while curr_attempts < attempts:
+        if _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm):
+            return True
+        # Wait 2 seconds before unbound
+        time.sleep(2)
+        if not clear_bonded_devices(pri_ad):
+            log.error("Failed to clear bond for primary device at attempt {}"
+                      .format(str(curr_attempts)))
+            return False
+        if not clear_bonded_devices(sec_ad):
+            log.error("Failed to clear bond for secondary device at attempt {}"
+                      .format(str(curr_attempts)))
+            return False
+        # Wait 2 seconds after unbound
+        time.sleep(2)
+        curr_attempts += 1
+    log.error("pair_pri_to_sec failed to connect after {} attempts".format(
+        str(attempts)))
+    return False
+
+
+def _wait_for_passkey_match(pri_ad, sec_ad):
+    pri_pin, sec_pin = -1, 1
+    pri_variant, sec_variant = -1, 1
+    pri_pairing_req, sec_pairing_req = None, None
+    try:
+        pri_pairing_req = pri_ad.ed.pop_event(
+            event_name="BluetoothActionPairingRequest", timeout=DEFAULT_TIMEOUT)
+        pri_variant = pri_pairing_req["data"]["PairingVariant"]
+        pri_pin = pri_pairing_req["data"]["Pin"]
+        pri_ad.log.info("Primary device received Pin: {}, Variant: {}"
+                        .format(pri_pin, pri_variant))
+        sec_pairing_req = sec_ad.ed.pop_event(
+            event_name="BluetoothActionPairingRequest", timeout=DEFAULT_TIMEOUT)
+        sec_variant = sec_pairing_req["data"]["PairingVariant"]
+        sec_pin = sec_pairing_req["data"]["Pin"]
+        sec_ad.log.info("Secondary device received Pin: {}, Variant: {}"
+                        .format(sec_pin, sec_variant))
+    except Empty as err:
+        log.error("Wait for pin error: {}".format(err))
+        log.error("Pairing request state, Primary: {}, Secondary: {}"
+                  .format(pri_pairing_req, sec_pairing_req))
+        return False
+    if pri_variant == sec_variant == PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+        confirmation = pri_pin == sec_pin
+        if confirmation:
+            log.info("Pairing code matched, accepting connection")
+        else:
+            log.info("Pairing code mismatched, rejecting connection")
+        pri_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
+                               str(confirmation))
+        sec_ad.droid.eventPost("BluetoothActionPairingRequestUserConfirm",
+                               str(confirmation))
+        if not confirmation:
+            return False
+    elif pri_variant != sec_variant:
+        log.error("Pairing variant mismatched, abort connection")
+        return False
+    return True
+
+
+def _pair_pri_to_sec(pri_ad, sec_ad, auto_confirm):
+    # Enable discovery on sec_ad so that pri_ad can find it.
     # The timeout here is based on how much time it would take for two devices
-    # to pair with each other once pri_droid starts seeing devices.
-    log.info(
-        "Bonding device {} to {}".format(pri_droid.bluetoothGetLocalAddress(),
-                                         sec_droid.bluetoothGetLocalAddress()))
+    # to pair with each other once pri_ad starts seeing devices.
+    pri_droid = pri_ad.droid
+    sec_droid = sec_ad.droid
+    pri_ad.ed.clear_all_events()
+    sec_ad.ed.clear_all_events()
+    log.info("Bonding device {} to {}".format(
+        pri_droid.bluetoothGetLocalAddress(),
+        sec_droid.bluetoothGetLocalAddress()))
     sec_droid.bluetoothMakeDiscoverable(DEFAULT_TIMEOUT)
     target_address = sec_droid.bluetoothGetLocalAddress()
     log.debug("Starting paring helper on each device")
-    pri_droid.bluetoothStartPairingHelper()
-    sec_droid.bluetoothStartPairingHelper()
-    log.info("Primary device starting discovery and executing bond")
+    pri_droid.bluetoothStartPairingHelper(auto_confirm)
+    sec_droid.bluetoothStartPairingHelper(auto_confirm)
+    pri_ad.log.info("Primary device starting discovery and executing bond")
     result = pri_droid.bluetoothDiscoverAndBond(target_address)
+    if not auto_confirm:
+        if not _wait_for_passkey_match(pri_ad, sec_ad):
+            return False
     # Loop until we have bonded successfully or timeout.
     end_time = time.time() + DEFAULT_TIMEOUT
-    log.info("Verifying devices are bonded")
+    pri_ad.log.info("Verifying devices are bonded")
     while time.time() < end_time:
         bonded_devices = pri_droid.bluetoothGetBondedDevices()
         bonded = False
         for d in bonded_devices:
             if d['address'] == target_address:
-                log.info("Successfully bonded to device")
+                pri_ad.log.info("Successfully bonded to device")
                 return True
+        time.sleep(0.1)
     # Timed out trying to bond.
-    log.info("Failed to bond devices.")
+    pri_ad.log.info("Failed to bond devices.")
     return False
 
 
-def connect_pri_to_sec(log, pri_droid, sec_droid, profiles_set):
+def connect_pri_to_sec(pri_ad, sec_ad, profiles_set, attempts=2):
     """Connects pri droid to secondary droid.
 
     Args:
-        pri_droid: Droid initiating connection.
-        sec_droid: Droid accepting connection.
+        pri_ad: AndroidDroid initiating connection
+        sec_ad: AndroidDroid accepting connection
+        profiles_set: Set of profiles to be connected
+        attempts: Number of attempts to try until failure.
+
+    Returns:
+        Pass if True
+        Fail if False
+    """
+    device_addr = sec_ad.droid.bluetoothGetLocalAddress()
+    # Allows extra time for the SDP records to be updated.
+    time.sleep(2)
+    curr_attempts = 0
+    while curr_attempts < attempts:
+        log.info("connect_pri_to_sec curr attempt {} total {}".format(
+            curr_attempts, attempts))
+        if _connect_pri_to_sec(pri_ad, sec_ad, profiles_set):
+            return True
+        curr_attempts += 1
+    log.error("connect_pri_to_sec failed to connect after {} attempts".format(
+        attempts))
+    return False
+
+
+def _connect_pri_to_sec(pri_ad, sec_ad, profiles_set):
+    """Connects pri droid to secondary droid.
+
+    Args:
+        pri_ad: AndroidDroid initiating connection.
+        sec_ad: AndroidDroid accepting connection.
         profiles_set: Set of profiles to be connected.
 
     Returns:
@@ -635,37 +837,63 @@
     supported_profiles = [i.value for i in BluetoothProfile]
     for profile in profiles_set:
         if profile not in supported_profiles:
-            log.info("Profile {} is not supported list {}".format(
+            pri_ad.log.info("Profile {} is not supported list {}".format(
                 profile, supported_profiles))
             return False
 
     # First check that devices are bonded.
     paired = False
-    for paired_device in pri_droid.droid.bluetoothGetBondedDevices():
+    for paired_device in pri_ad.droid.bluetoothGetBondedDevices():
         if paired_device['address'] == \
-            sec_droid.bluetoothGetLocalAddress():
+            sec_ad.droid.bluetoothGetLocalAddress():
             paired = True
             break
 
     if not paired:
-        log.info("{} not paired to {}".format(pri_droid.droid.getBuildSerial(),
-                                              sec_droid.getBuildSerial()))
+        pri_ad.log.error("Not paired to {}".format(sec_ad.serial))
         return False
 
     # Now try to connect them, the following call will try to initiate all
     # connections.
-    pri_droid.droid.bluetoothConnectBonded(sec_droid.bluetoothGetLocalAddress(
-    ))
+    pri_ad.droid.bluetoothConnectBonded(sec_ad.droid.bluetoothGetLocalAddress())
 
+    end_time = time.time() + 10
     profile_connected = set()
-    log.info("Profiles to be connected {}".format(profiles_set))
+    sec_addr = sec_ad.droid.bluetoothGetLocalAddress()
+    pri_ad.log.info("Profiles to be connected {}".format(profiles_set))
+    # First use APIs to check profile connection state
+    while (time.time() < end_time and
+           not profile_connected.issuperset(profiles_set)):
+        if (BluetoothProfile.HEADSET_CLIENT.value not in profile_connected and
+                BluetoothProfile.HEADSET_CLIENT.value in profiles_set):
+            if is_hfp_client_device_connected(pri_ad, sec_addr):
+                profile_connected.add(BluetoothProfile.HEADSET_CLIENT.value)
+        if (BluetoothProfile.A2DP.value not in profile_connected and
+                BluetoothProfile.A2DP.value in profiles_set):
+            if is_a2dp_src_device_connected(pri_ad, sec_addr):
+                profile_connected.add(BluetoothProfile.A2DP.value)
+        if (BluetoothProfile.A2DP_SINK.value not in profile_connected and
+                BluetoothProfile.A2DP_SINK.value in profiles_set):
+            if is_a2dp_snk_device_connected(pri_ad, sec_addr):
+                profile_connected.add(BluetoothProfile.A2DP_SINK.value)
+        if (BluetoothProfile.MAP_MCE.value not in profile_connected and
+                BluetoothProfile.MAP_MCE.value in profiles_set):
+            if is_map_mce_device_connected(pri_ad, sec_addr):
+                profile_connected.add(BluetoothProfile.MAP_MCE.value)
+        if (BluetoothProfile.MAP.value not in profile_connected and
+                BluetoothProfile.MAP.value in profiles_set):
+            if is_map_mse_device_connected(pri_ad, sec_addr):
+                profile_connected.add(BluetoothProfile.MAP.value)
+        time.sleep(0.1)
+    # If APIs fail, try to find the connection broadcast receiver.
     while not profile_connected.issuperset(profiles_set):
         try:
-            profile_event = pri_droid.ed.pop_event(
-                bluetooth_profile_connection_state_changed, DEFAULT_TIMEOUT)
-            log.info("Got event {}".format(profile_event))
+            profile_event = pri_ad.ed.pop_event(
+                bluetooth_profile_connection_state_changed,
+                DEFAULT_TIMEOUT + 10)
+            pri_ad.log.info("Got event {}".format(profile_event))
         except Exception:
-            log.error("Did not get {} profiles left {}".format(
+            pri_ad.log.error("Did not get {} profiles left {}".format(
                 bluetooth_profile_connection_state_changed, profile_connected))
             return False
 
@@ -674,14 +902,73 @@
         device_addr = profile_event['data']['addr']
 
         if state == BluetoothProfileState.STATE_CONNECTED.value and \
-            device_addr == sec_droid.bluetoothGetLocalAddress():
+            device_addr == sec_ad.droid.bluetoothGetLocalAddress():
             profile_connected.add(profile)
-        log.info("Profiles connected until now {}".format(profile_connected))
+        pri_ad.log.info("Profiles connected until now {}".format(
+            profile_connected))
     # Failure happens inside the while loop. If we came here then we already
     # connected.
     return True
 
 
+def disconnect_pri_from_sec(pri_ad, sec_ad, profiles_list):
+    """
+    Disconnect primary from secondary on a specific set of profiles
+    Args:
+        pri_ad - Primary android_device initiating disconnection
+        sec_ad - Secondary android droid (sl4a interface to keep the
+          method signature the same connect_pri_to_sec above)
+        profiles_list - List of profiles we want to disconnect from
+
+    Returns:
+        True on Success
+        False on Failure
+    """
+    # Sanity check to see if all the profiles in the given set is supported
+    supported_profiles = [i.value for i in BluetoothProfile]
+    for profile in profiles_list:
+        if profile not in supported_profiles:
+            pri_ad.log.info("Profile {} is not in supported list {}".format(
+                profile, supported_profiles))
+            return False
+
+    pri_ad.log.info(pri_ad.droid.bluetoothGetBondedDevices())
+    # Disconnecting on a already disconnected profile is a nop,
+    # so not checking for the connection state
+    try:
+        pri_ad.droid.bluetoothDisconnectConnectedProfile(
+            sec_ad.droid.bluetoothGetLocalAddress(), profiles_list)
+    except Exception as err:
+        pri_ad.log.error(
+            "Exception while trying to disconnect profile(s) {}: {}".format(
+                profiles_list, err))
+        return False
+
+    profile_disconnected = set()
+    pri_ad.log.info("Disconnecting from profiles: {}".format(profiles_list))
+
+    while not profile_disconnected.issuperset(profiles_list):
+        try:
+            profile_event = pri_ad.ed.pop_event(
+                bluetooth_profile_connection_state_changed, default_timeout)
+            pri_ad.log.info("Got event {}".format(profile_event))
+        except Exception:
+            pri_ad.log.error("Did not disconnect from Profiles")
+            return False
+
+        profile = profile_event['data']['profile']
+        state = profile_event['data']['state']
+        device_addr = profile_event['data']['addr']
+
+        if state == BluetoothProfileState.STATE_DISCONNECTED.value and \
+            device_addr == sec_ad.droid.bluetoothGetLocalAddress():
+            profile_disconnected.add(profile)
+        pri_ad.log.info("Profiles disconnected so far {}".format(
+            profile_disconnected))
+
+    return True
+
+
 def take_btsnoop_logs(android_devices, testcase, testname):
     """Pull btsnoop logs from an input list of android devices.
 
@@ -708,13 +995,13 @@
         testname: Name of the test case that triggered this bug report.
     """
     testname = "".join(x for x in testname if x.isalnum())
-    serial = ad.droid.getBuildSerial()
+    serial = ad.serial
     device_model = ad.droid.getBuildModel()
     device_model = device_model.replace(" ", "")
     out_name = ','.join((testname, device_model, serial))
     snoop_path = ad.log_path + "/BluetoothSnoopLogs"
     utils.create_dir(snoop_path)
-    cmd = ''.join(("adb -s ", serial, " pull /sdcard/btsnoop_hci.log ",
+    cmd = ''.join(("adb -s ", serial, " pull ", BTSNOOP_LOG_PATH_ON_DEVICE, " ",
                    snoop_path + '/' + out_name, ".btsnoop_hci.log"))
     testcase.log.info("Test failed, grabbing the bt_snoop logs on {} {}."
                       .format(device_model, serial))
@@ -727,7 +1014,7 @@
     Args:
         ad: Android device to kill BT process on.
     """
-    log.info("Killing Bluetooth process.")
+    ad.log.info("Killing Bluetooth process.")
     pid = ad.adb.shell(
         "ps | grep com.android.bluetooth | awk '{print $2}'").decode('ascii')
     call(["adb -s " + ad.serial + " shell kill " + pid], shell=True)
@@ -756,13 +1043,13 @@
     while time.time() < end_time:
         if len(client_ad.droid.bluetoothRfcommActiveConnections()) > 0:
             test_result = True
-            log.info("RFCOMM Client Connection Active")
+            client_ad.log.info("RFCOMM Client Connection Active")
             break
         else:
             test_result = False
         time.sleep(1)
     if not test_result:
-        log.error("Failed to establish an RFCOMM connection")
+        client_ad.log.error("Failed to establish an RFCOMM connection")
         return False
     return True
 
@@ -779,16 +1066,16 @@
     Returns:
         True if the data written matches the data read, false if not.
     """
-    log.info("Write message.")
+    client_ad.log.info("Write message.")
     try:
         if binary:
             client_ad.droid.bluetoothRfcommWriteBinary(msg)
         else:
             client_ad.droid.bluetoothRfcommWrite(msg)
     except Exception as err:
-        log.error("Failed to write data: {}".format(err))
+        client_ad.log.error("Failed to write data: {}".format(err))
         return False
-    log.info("Read message.")
+    server_ad.log.info("Read message.")
     try:
         if binary:
             read_msg = server_ad.droid.bluetoothRfcommReadBinary().rstrip(
@@ -796,7 +1083,7 @@
         else:
             read_msg = server_ad.droid.bluetoothRfcommRead()
     except Exception as err:
-        log.error("Failed to read data: {}".format(err))
+        server_ad.log.error("Failed to read data: {}".format(err))
         return False
     log.info("Verify message.")
     if msg != read_msg:
@@ -839,10 +1126,10 @@
     """
     test_result = True
     if len(server_ad.droid.bluetoothRfcommActiveConnections()) == 0:
-        log.error("No rfcomm connections found on server.")
+        server_ad.log.error("No rfcomm connections found on server.")
         test_result = False
     if len(client_ad.droid.bluetoothRfcommActiveConnections()) == 0:
-        log.error("No rfcomm connections found on client.")
+        client_ad.log.error("No rfcomm connections found on client.")
         test_result = False
     return test_result
 
@@ -859,15 +1146,15 @@
         false if unsuccessful.
     """
     if not toggle_airplane_mode(log, panu_dut, True):
-        log.error("Failed to toggle airplane mode on")
+        panu_dut.log.error("Failed to toggle airplane mode on")
         return False
     if not bluetooth_enabled_check(panu_dut):
         return False
     pan_dut.droid.bluetoothPanSetBluetoothTethering(True)
-    if not (pair_pri_to_sec(pan_dut.droid, panu_dut.droid)):
+    if not (pair_pri_to_sec(pan_dut, panu_dut)):
         return False
     if not pan_dut.droid.bluetoothPanIsTetheringOn():
-        log.error("Failed to enable Bluetooth tethering.")
+        pan_dut.log.error("Failed to enable Bluetooth tethering.")
         return False
     # Magic sleep needed to give the stack time in between bonding and
     # connecting the PAN profile.
@@ -875,8 +1162,89 @@
     panu_dut.droid.bluetoothConnectBonded(
         pan_dut.droid.bluetoothGetLocalAddress())
     if not verify_http_connection(log, panu_dut):
-        log.error("Can't verify http connection on PANU device.")
+        panu_dut.log.error("Can't verify http connection on PANU device.")
         if not verify_http_connection(log, pan_dut):
-            log.info("Can't verify http connection on PAN service device")
+            pan_dut.log.info(
+                "Can't verify http connection on PAN service device")
         return False
     return True
+
+
+def is_hfp_client_device_connected(ad, addr):
+    """Determines if an AndroidDevice has HFP connectivity to input address
+
+    Args:
+        ad: the Android device
+        addr: the address that's expected
+    Returns:
+        True if connection was successful, false if unsuccessful.
+    """
+    devices = ad.droid.bluetoothHfpClientGetConnectedDevices()
+    ad.log.info("Connected HFP Client devices: {}".format(devices))
+    if addr in {d['address'] for d in devices}:
+        return True
+    return False
+
+
+def is_a2dp_src_device_connected(ad, addr):
+    """Determines if an AndroidDevice has A2DP connectivity to input address
+
+    Args:
+        ad: the Android device
+        addr: the address that's expected
+    Returns:
+        True if connection was successful, false if unsuccessful.
+    """
+    devices = ad.droid.bluetoothA2dpGetConnectedDevices()
+    ad.log.info("Connected A2DP Source devices: {}".format(devices))
+    if addr in {d['address'] for d in devices}:
+        return True
+    return False
+
+
+def is_a2dp_snk_device_connected(ad, addr):
+    """Determines if an AndroidDevice has A2DP snk connectivity to input address
+
+    Args:
+        ad: the Android device
+        addr: the address that's expected
+    Returns:
+        True if connection was successful, false if unsuccessful.
+    """
+    devices = ad.droid.bluetoothA2dpSinkGetConnectedDevices()
+    ad.log.info("Connected A2DP Sink devices: {}".format(devices))
+    if addr in {d['address'] for d in devices}:
+        return True
+    return False
+
+
+def is_map_mce_device_connected(ad, addr):
+    """Determines if an AndroidDevice has MAP MCE connectivity to input address
+
+    Args:
+        ad: the Android device
+        addr: the address that's expected
+    Returns:
+        True if connection was successful, false if unsuccessful.
+    """
+    devices = ad.droid.bluetoothMapClientGetConnectedDevices()
+    ad.log.info("Connected MAP MCE devices: {}".format(devices))
+    if addr in {d['address'] for d in devices}:
+        return True
+    return False
+
+
+def is_map_mse_device_connected(ad, addr):
+    """Determines if an AndroidDevice has MAP MSE connectivity to input address
+
+    Args:
+        ad: the Android device
+        addr: the address that's expected
+    Returns:
+        True if connection was successful, false if unsuccessful.
+    """
+    devices = ad.droid.bluetoothMapGetConnectedDevices()
+    ad.log.info("Connected MAP MSE devices: {}".format(devices))
+    if addr in {d['address'] for d in devices}:
+        return True
+    return False
diff --git a/acts/framework/acts/test_utils/bt/native_bt_test_utils.py b/acts/framework/acts/test_utils/bt/native_bt_test_utils.py
index 341862f..d070f13 100644
--- a/acts/framework/acts/test_utils/bt/native_bt_test_utils.py
+++ b/acts/framework/acts/test_utils/bt/native_bt_test_utils.py
@@ -14,11 +14,13 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-from acts.logger import LoggerProxy
+import logging
+
 from subprocess import call
 import time
 
-log = LoggerProxy()
+log = logging
+
 
 def setup_native_bluetooth(native_devices):
     for n in native_devices:
@@ -35,4 +37,3 @@
         time.sleep(5)  #temporary sleep statement
         droid.BluetoothBinderRegisterBLE()
         time.sleep(5)  #temporary sleep statement
-
diff --git a/acts/framework/acts/test_utils/car/__init__.py b/acts/framework/acts/test_utils/car/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/test_utils/car/__init__.py
diff --git a/acts/framework/acts/test_utils/car/car_bt_utils.py b/acts/framework/acts/test_utils/car/car_bt_utils.py
new file mode 100644
index 0000000..d4125eb
--- /dev/null
+++ b/acts/framework/acts/test_utils/car/car_bt_utils.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - 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.
+
+# Defines utilities that can be used for making calls indenpendent of
+# subscription IDs. This can be useful when making calls over mediums not SIM
+# based.
+
+# Make a phone call to the specified URI. It is assumed that we are making the
+# call to the user selected default account.
+#
+# We usually want to make sure that the call has ended up in a good state.
+#
+# NOTE: This util is applicable to only non-conference type calls. It is best
+# suited to test cases where only one call is in action at any point of time.
+
+import queue
+import time
+
+from acts import logger
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.bt.BtEnum import *
+
+
+def set_car_profile_priorities_off(car_droid, ph_droid):
+    """Sets priority of car related profiles to OFF. This avoids
+    autoconnect being triggered randomly. The use of this function
+    is encouraged when you're testing individual profiles in isolation
+
+    Args:
+        log: log object
+        car_droid: Car droid
+        ph_droid: Phone droid
+
+    Returns:
+        True if success, False if fail.
+    """
+    # TODO investigate MCE
+    car_profiles = [BluetoothProfile.A2DP_SINK,
+                    BluetoothProfile.HEADSET_CLIENT,
+                    BluetoothProfile.PBAP_CLIENT, BluetoothProfile.MAP_MCE]
+    bt_test_utils.set_profile_priority(car_droid, ph_droid, car_profiles,
+                                       BluetoothPriorityLevel.PRIORITY_OFF)
diff --git a/acts/framework/acts/test_utils/car/car_media_utils.py b/acts/framework/acts/test_utils/car/car_media_utils.py
new file mode 100644
index 0000000..aa1efd7
--- /dev/null
+++ b/acts/framework/acts/test_utils/car/car_media_utils.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - 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.
+
+#   Utilities that can be used for testing media related usecases.
+
+# Events dispatched from the RPC Server
+EVENT_PLAY_RECEIVED = "playReceived"
+EVENT_PAUSE_RECEIVED = "pauseReceived"
+EVENT_SKIP_NEXT_RECEIVED = "skipNextReceived"
+EVENT_SKIP_PREV_RECEIVED = "skipPrevReceived"
+
+# Passthrough Commands sent to the RPC Server
+CMD_MEDIA_PLAY = "play"
+CMD_MEDIA_PAUSE = "pause"
+CMD_MEDIA_SKIP_NEXT = "skipNext"
+CMD_MEDIA_SKIP_PREV = "skipPrev"
+
+# MediaMetaData keys. Keep them the same as in BluetoothMediaFacade.
+MEDIA_KEY_TITLE = "keyTitle"
+MEDIA_KEY_ALBUM = "keyAlbum"
+MEDIA_KEY_ARTIST = "keyArtist"
+MEDIA_KEY_DURATION = "keyDuration"
+MEDIA_KEY_NUM_TRACKS = "keyNumTracks"
+
+
+def verifyEventReceived(log, device, event, timeout):
+    """
+    Verify if the event was received from the given device.
+    When a fromDevice talks to a toDevice and expects an event back,
+    this util function can be used to see if the toDevice posted it.
+    Args:
+        log:        The logging object
+        device:     The device to pop the event from
+        event:      The event we are interested in.
+        timeout:    The time in seconds before we timeout
+    Returns:
+        True        if the event was received
+        False       if we timed out waiting for the event
+    """
+    try:
+        device.ed.pop_event(event, timeout)
+    except Exception:
+        log.info(" {} Event Not received".format(event))
+        return False
+    log.info("Event Received : {}".format(event))
+    return True
+
+
+def send_media_passthrough_cmd(log,
+                               fromDevice,
+                               toDevice,
+                               cmd,
+                               expctEvent,
+                               timeout=1.0):
+    """
+    Send a media passthrough command from one device to another
+    via bluetooth.
+    Args:
+        log:        The logging object
+        fromDevice: The device to send the command from
+        toDevice:   The device the command is sent to
+        cmd:        The passthrough command to send
+        expctEvent: The expected event
+        timeout:    The time in seconds before we timeout, deafult = 1sec
+    Returns:
+        True        if the event was received
+        False       if we timed out waiting for the event
+    """
+    log.info("Sending passthru : {}".format(cmd))
+    fromDevice.droid.bluetoothMediaPassthrough(cmd)
+    return verifyEventReceived(log, toDevice, expctEvent, timeout)
+
+
+def is_a2dp_connected(log, sink, source):
+    """
+    Convenience Function to see if the 2 devices are connected on
+    A2dp.
+    ToDo: Move to bt_test_utils if used in more places.
+    Args:
+        sink:       Audio Sink
+        source:     Audio Source
+    Returns:
+        True if Connected
+        False if Not connected
+    """
+    devices = sink.droid.bluetoothA2dpSinkGetConnectedDevices()
+    for device in devices:
+        log.info("A2dp Connected device {}".format(device["name"]))
+        if (device["address"] == source.droid.bluetoothGetLocalAddress()):
+            return True
+    return False
+
+
+def log_metadata(log, metadata):
+    """
+    Log the Metadata to the console.
+    Args:
+        log:        The logging object
+        metadata:   Dictionary of the song's metadata
+    """
+    title = metadata[MEDIA_KEY_TITLE]
+    album = metadata[MEDIA_KEY_ALBUM]
+    artist = metadata[MEDIA_KEY_ARTIST]
+    duration = metadata[MEDIA_KEY_DURATION]
+    numTracks = metadata[MEDIA_KEY_NUM_TRACKS]
+    log.info("Playing Artist: {}, Album: {}, Title: {}".format(artist, album,
+                                                               title))
+    log.info("Duration: {}, NumTracks: {}".format(duration, numTracks))
+
+
+def compare_metadata(log, metadata1, metadata2):
+    """
+    Compares the Metadata between the two devices
+    Args:
+        log:        The logging object
+        metadata1    Media Metadata of device1
+        metadata2    Media Metadata of device2
+    Returns:
+        True        if the Metadata matches
+        False       if the Metadata do not match
+    """
+    log.info("Device1 metadata:")
+    log_metadata(log, metadata1)
+    log.info("Device2 metadata:")
+    log_metadata(log, metadata2)
+
+    if not (metadata1[MEDIA_KEY_TITLE] == metadata2[MEDIA_KEY_TITLE]):
+        log.info("Song Titles do not match")
+        return False
+
+    if not (metadata1[MEDIA_KEY_ALBUM] == metadata2[MEDIA_KEY_ALBUM]):
+        log.info("Song Albums do not match")
+        return False
+
+    if not (metadata1[MEDIA_KEY_ARTIST] == metadata2[MEDIA_KEY_ARTIST]):
+        log.info("Song Artists do not match")
+        return False
+
+    if not (metadata1[MEDIA_KEY_DURATION] == metadata2[MEDIA_KEY_DURATION]):
+        log.info("Song Duration do not match")
+        return False
+
+    if not (metadata1[MEDIA_KEY_NUM_TRACKS] == metadata2[MEDIA_KEY_NUM_TRACKS]
+            ):
+        log.info("Song Num Tracks do not match")
+        return False
+
+    return True
+
+
+def check_metadata(log, device1, device2):
+    """
+    Gets the now playing metadata from 2 devices and checks if they are the same
+    Args:
+        log:        The logging object
+        device1     Device 1
+        device2     Device 2
+    Returns:
+        True        if the Metadata matches
+        False       if the Metadata do not match
+    """
+    metadata1 = device1.droid.bluetoothMediaGetCurrentMediaMetaData()
+    if metadata1 is None:
+        return False
+
+    metadata2 = device2.droid.bluetoothMediaGetCurrentMediaMetaData()
+    if metadata2 is None:
+        return False
+    return compare_metadata(log, metadata1, metadata2)
+
+
+def isMediaSessionActive(log, device, mediaSession):
+    """
+    Checks if the passed mediaSession is active.
+    Used to see if the device is playing music.
+    Args:
+        log:            The logging object
+        device          Device to check
+        mediaSession    MediaSession to check if it is active
+    Returns:
+        True            if the given mediaSession is active
+        False           if the given mediaSession is not active
+    """
+    # Get a list of MediaSession tags (String) that is currently active
+    activeSessions = device.droid.bluetoothMediaGetActiveMediaSessions()
+    if len(activeSessions) > 0:
+        for session in activeSessions:
+            log.info(session)
+            if (session == mediaSession):
+                return True
+    log.info("No Media Sessions")
+    return False
diff --git a/acts/framework/acts/test_utils/car/car_telecom_utils.py b/acts/framework/acts/test_utils/car/car_telecom_utils.py
new file mode 100644
index 0000000..af7840d
--- /dev/null
+++ b/acts/framework/acts/test_utils/car/car_telecom_utils.py
@@ -0,0 +1,547 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - 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.
+
+# Defines utilities that can be used for making calls indenpendent of
+# subscription IDs. This can be useful when making calls over mediums not SIM
+# based.
+
+# Make a phone call to the specified URI. It is assumed that we are making the
+# call to the user selected default account.
+#
+# We usually want to make sure that the call has ended up in a good state.
+#
+# NOTE: This util is applicable to only non-conference type calls. It is best
+# suited to test cases where only one call is in action at any point of time.
+
+import queue
+import time
+
+from acts import logger
+from acts.test_utils.tel import tel_defines
+
+def dial_number(log, ad, uri):
+    """Dial a number
+
+    Args:
+        log: log object
+        ad: android device object
+        uri: Tel number to dial
+
+    Returns:
+        True if success, False if fail.
+    """
+    log.info("Dialing up droid {} call uri {}".format(
+        ad.serial, uri))
+
+    # First check that we are not in call.
+    if ad.droid.telecomIsInCall():
+        log.info("We're still in call {}".format(ad.serial))
+        return False
+
+    # Start tracking updates.
+    ad.droid.telecomStartListeningForCallAdded()
+    #If a phone number is passed in
+    if "tel:" not in uri:
+        uri = "tel:" + uri
+    ad.droid.telecomCallTelUri(uri)
+
+    event = None
+    try:
+        event = ad.ed.pop_event(
+            tel_defines.EventTelecomCallAdded,
+            tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+    except queue.Empty:
+        log.info(
+            "Did not get {} event!".format(tel_defines.EventTelecomCallAdded))
+        # Return failure.
+        return False
+    finally:
+        ad.droid.telecomStopListeningForCallAdded()
+
+    call_id = event['data']['CallId']
+    log.info("Call ID: {} dev {}".format(call_id, ad.serial))
+
+    if not call_id:
+        log.info("CallId is empty!")
+        return False
+    if not wait_for_dialing(log, ad):
+        return False
+
+    return call_id
+
+def wait_for_call_state(log, ad, call_id, state):
+    """Wait for the given call id to transition to the given call state.
+
+    Args:
+        log: log object
+        ad: android device object
+        call_id: ID of the call that we're waiting for the call state to
+        transition into.
+        state: desired final state.
+
+    Returns:
+        True if success, False if fail.
+    """
+    # Lets track the call now.
+    # NOTE: Disable this everywhere we return.
+    ad.droid.telecomCallStartListeningForEvent(
+        call_id, tel_defines.EVENT_CALL_STATE_CHANGED)
+
+    # We may have missed the update so do a quick check.
+    if ad.droid.telecomCallGetCallState(call_id) == state:
+        log.info("Call ID {} already in {} dev {}!".format(
+            call_id, state, ad.serial))
+        ad.droid.telecomCallStopListeningForEvent(call_id,
+            tel_defines.EventTelecomCallStateChanged)
+        return True
+
+    # If not then we need to poll for the event.
+    # We return if we have found a match or we timeout for further updates of
+    # the call
+    end_time = time.time() + 10
+    while True and time.time() < end_time:
+        try:
+            event = ad.ed.pop_event(
+                tel_defines.EventTelecomCallStateChanged,
+                tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+            call_state = event['data']['Event']
+            if call_state == state:
+                ad.droid.telecomCallStopListeningForEvent(call_id,
+                    tel_defines.EventTelecomCallStateChanged)
+                return True
+            else:
+                log.info("Droid {} in call {} state {}".format(
+                    ad.serial, call_id, call_state))
+                continue
+        except queue.Empty:
+            log.info("Did not get into state {} dev {}".format(
+                state, ad.serial))
+            ad.droid.telecomCallStopListeningForEvent(call_id,
+                tel_defines.EventTelecomCallStateChanged)
+            return False
+    return False
+
+def hangup_call(log, ad, call_id):
+    """Hangup a number
+
+    Args:
+        log: log object
+        ad: android device object
+        call_id: Call to hangup.
+
+    Returns:
+        True if success, False if fail.
+    """
+    log.info("Hanging up droid {} call {}".format(
+        ad.serial, call_id))
+    # First check that we are in call, otherwise fail.
+    if not ad.droid.telecomIsInCall():
+        log.info("We are not in-call {}".format(ad.serial))
+        return False
+
+    # Make sure we are registered with the events.
+    ad.droid.telecomStartListeningForCallRemoved()
+
+    # Disconnect call.
+    ad.droid.telecomCallDisconnect(call_id)
+
+    # Wait for removed event.
+    event = None
+    try:
+        event = ad.ed.pop_event(
+            tel_defines.EventTelecomCallRemoved,
+            tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+    except queue.Empty:
+        log.info("Did not get TelecomCallRemoved event")
+        return False
+    finally:
+        ad.droid.telecomStopListeningForCallRemoved()
+
+    log.info("Removed call {}".format(event))
+    if event['data']['CallId'] != call_id:
+        return False
+
+    return True
+
+def hangup_conf(log, ad, conf_id):
+    """Hangup a conference call
+
+    Args:
+        log: log object
+        ad: android device object
+        conf_id: Conf call to hangup.
+
+    Returns:
+        True if success, False if fail.
+    """
+    log.info("hangup_conf: Hanging up droid {} call {}".format(
+        ad.serial, conf_id))
+
+    # First check that we are in call, otherwise fail.
+    if not ad.droid.telecomIsInCall():
+        log.info("We are not in-call {}".format(ad.serial))
+        return False
+
+    # Get the list of children for this conference.
+    all_calls = get_call_id_children(log, ad, conf_id)
+
+    # All calls that needs disconnecting (Parent + Children)
+    all_calls.add(conf_id)
+
+    # Make sure we are registered with the events.
+    ad.droid.telecomStartListeningForCallRemoved()
+
+    # Disconnect call.
+    ad.droid.telecomCallDisconnect(conf_id)
+
+    # Wait for removed event.
+    while len(all_calls) > 0:
+        event = None
+        try:
+            event = ad.ed.pop_event(
+                tel_defines.EventTelecomCallRemoved,
+                tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+        except queue.Empty:
+            log.info("Did not get TelecomCallRemoved event")
+            ad.droid.telecomStopListeningForCallRemoved()
+            return False
+
+        removed_call_id = event['data']['CallId']
+        all_calls.remove(removed_call_id)
+        log.info("Removed call {} left calls {}".format(removed_call_id, all_calls))
+
+    ad.droid.telecomStopListeningForCallRemoved()
+    return True
+
+def accept_call(log, ad, call_id):
+    """Accept a number
+
+    Args:
+        log: log object
+        ad: android device object
+        call_id: Call to accept.
+
+    Returns:
+        True if success, False if fail.
+    """
+    log.info("Accepting call at droid {} call {}".format(
+        ad.serial, call_id))
+    # First check we are in call, otherwise fail.
+    if not ad.droid.telecomIsInCall():
+        log.info("We are not in-call {}".format(ad.serial))
+        return False
+
+    # Accept the call and wait for the call to be accepted on AG.
+    ad.droid.telecomCallAnswer(call_id, tel_defines.VT_STATE_AUDIO_ONLY)
+    if not wait_for_call_state(log, ad, call_id, tel_defines.CALL_STATE_ACTIVE):
+        log.error("Call {} on droid {} not active".format(
+            call_id, ad.serial))
+        return False
+
+    return True
+
+def wait_for_not_in_call(log, ad):
+    """Wait for the droid to be OUT OF CALLING state.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    ad.droid.telecomStartListeningForCallRemoved()
+
+    calls = ad.droid.telecomCallGetCallIds()
+    if len(calls) > 1:
+        log.info("More than one call {} {}".format(calls, ad.serial))
+        return False
+
+    if len(calls) > 0:
+        log.info("Got calls {} for {}".format(
+            calls, ad.serial))
+        try:
+            event = ad.ed.pop_event(
+                tel_defines.EventTelecomCallRemoved,
+                tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+        except queue.Empty:
+            log.info("wait_for_not_in_call Did not get {} droid {}".format(
+                tel_defines.EventTelecomCallRemoved,
+                ad.serial))
+            return False
+        finally:
+            ad.droid.telecomStopListeningForCallRemoved()
+
+    # Either we removed the only call or we never had a call previously, either
+    # ways simply check if we are in in call now.
+    return (not ad.droid.telecomIsInCall())
+
+def wait_for_dialing(log, ad):
+    """Wait for the droid to be in dialing state.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    # Start listening for events before anything else happens.
+    ad.droid.telecomStartListeningForCallAdded()
+
+    # First check if we re in call, then simply return.
+    if ad.droid.telecomIsInCall():
+        ad.droid.telecomStopListeningForCallAdded()
+        return True
+
+    call_id = None
+    # Now check if we already have calls matching the state.
+    calls_in_state = get_calls_in_states(log, ad,
+                                         [tel_defines.CALL_STATE_CONNECTING,
+                                         tel_defines.CALL_STATE_DIALING])
+
+    # If not then we need to poll for the calls themselves.
+    if len(calls_in_state) == 0:
+        event = None
+        try:
+            event = ad.ed.pop_event(
+                tel_defines.EventTelecomCallAdded,
+                tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+        except queue.Empty:
+            log.info("Did not get {}".format(
+                tel_defines.EventTelecomCallAdded))
+            return False
+        finally:
+            ad.droid.telecomStopListeningForCallAdded()
+        call_id = event['data']['CallId']
+    else:
+        call_id = calls_in_state[0]
+
+    # We may still not be in-call if the call setup is going on.
+    # We wait for the call state to move to dialing.
+    log.info("call id {} droid {}".format(call_id, ad.serial))
+    if not wait_for_call_state(
+        log, ad, call_id, tel_defines.CALL_STATE_DIALING):
+        return False
+
+    # Finally check the call state.
+    return ad.droid.telecomIsInCall()
+
+def wait_for_ringing(log, ad):
+    """Wait for the droid to be in ringing state.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    log.info("waiting for ringing {}".format(ad.serial))
+    # Start listening for events before anything else happens.
+    ad.droid.telecomStartListeningForCallAdded()
+
+    # First check if we re in call, then simply return.
+    if ad.droid.telecomIsInCall():
+        log.info("Device already in call {}".format(ad.serial))
+        ad.droid.telecomStopListeningForCallAdded()
+        return True
+
+    call_id = None
+    # Now check if we already have calls matching the state.
+    calls_in_state = ad.droid.telecomCallGetCallIds()
+
+    for c_id in calls_in_state:
+        if ad.droid.telecomCallGetCallState(c_id) == tel_defines.CALL_STATE_RINGING:
+            return True
+
+    event = None
+    call_id = None
+    try:
+        event = ad.ed.pop_event(
+            tel_defines.EventTelecomCallAdded,
+            tel_defines.MAX_WAIT_TIME_CALLEE_RINGING)
+    except queue.Empty:
+        log.info("Did not get {} droid {}".format(
+            tel_defines.EventTelecomCallAdded,
+            ad.serial))
+        return False
+    finally:
+        ad.droid.telecomStopListeningForCallAdded()
+    call_id = event['data']['CallId']
+    log.info("wait_for_ringing call found {} dev {}".format(
+        call_id, ad.serial))
+
+    # If the call already existed then we would have returned above otherwise
+    # we will verify that the newly added call is indeed ringing.
+    if not wait_for_call_state(
+        log, ad, call_id, tel_defines.CALL_STATE_RINGING):
+        log.info("No ringing call id {} droid {}".format(
+            call_id, ad.serial))
+        return False
+    return True
+
+def wait_for_active(log, ad):
+    """Wait for the droid to be in active call state.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    log.info("waiting for active {}".format(ad.serial))
+    # Start listening for events before anything else happens.
+    ad.droid.telecomStartListeningForCallAdded()
+
+    call_id = None
+    # Now check if we already have calls matching the state.
+    calls_in_state = ad.droid.telecomCallGetCallIds()
+
+    if len(calls_in_state) == 0:
+        event = None
+        try:
+            event = ad.ed.pop_event(
+                tel_defines.EventTelecomCallAdded,
+                tel_defines.MAX_WAIT_TIME_CALLEE_RINGING)
+        except queue.Empty:
+            log.info("Did not get {} droid {}".format(
+                tel_defines.EventTelecomCallAdded,
+                ad.serial))
+            return False
+        finally:
+            ad.droid.telecomStopListeningForCallAdded()
+        call_id = event['data']['CallId']
+        log.info("wait_for_ringing call found {} dev {}".format(
+            call_id, ad.serial))
+    else:
+        call_id = calls_in_state[0]
+
+    # We have found a new call to be added now wait it to transition into
+    # active state.
+    if not wait_for_call_state(
+        log, ad, call_id, tel_defines.CALL_STATE_ACTIVE):
+        log.info("No active call id {} droid {}".format(
+            call_id, ad.serial))
+        return False
+    return True
+
+def wait_for_conference(log, ad, conf_calls):
+    """Wait for the droid to be in a conference with calls specified
+    in conf_calls.
+
+    Args:
+        log: log object
+        ad: android device object
+        conf_calls: List of calls that should transition to conference
+
+    Returns:
+        call_id if success, None if fail.
+    """
+    conf_calls = set(conf_calls)
+
+    log.info("waiting for conference {}".format(ad.serial))
+    ad.droid.telecomStartListeningForCallAdded()
+
+    call_ids = ad.droid.telecomCallGetCallIds()
+
+    # Check if we have a conference call and if the children match
+    for call_id in call_ids:
+        call_chld = get_call_id_children(log, ad, call_id)
+        if call_chld == conf_calls:
+            ad.droid.telecomStopListeningForCallAdded()
+            return call_id
+
+    # If not poll for new calls.
+    event = None
+    call_id = None
+    try:
+        event = ad.ed.pop_event(
+            tel_defines.EventTelecomCallAdded,
+            tel_defines.MAX_WAIT_TIME_CALLEE_RINGING)
+        log.info("wait_for_conference event {} droid {}".format(
+            event, ad.serial))
+    except queue.Empty:
+        log.error("Did not get {} droid {}".format(
+            tel_defines.EventTelecomCallAdded,
+            ad.serial))
+        return None
+    finally:
+        ad.droid.telecomStopListeningForCallAdded()
+    call_id = event['data']['CallId']
+
+    # Now poll until the children change.
+    ad.droid.telecomCallStartListeningForEvent(
+        call_id, tel_defines.EVENT_CALL_CHILDREN_CHANGED)
+
+    event = None
+    while True:
+        try:
+            event = ad.ed.pop_event(
+                tel_defines.EventTelecomCallChildrenChanged,
+                tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+            call_chld = set(event['data']['Event'])
+            log.info("wait_for_conference children chld event {}".format(call_chld))
+            if call_chld == conf_calls:
+                ad.droid.telecomCallStopListeningForEvent(
+                    call_id, tel_defines.EVENT_CALL_CHILDREN_CHANGED)
+                return call_id
+        except queue.Empty:
+            log.error("Did not get {} droid {}".format(
+                tel_defines.EventTelecomCallChildrenChanged, ad.serial))
+            ad.droid.telecomCallStopListeningForEvent(
+                call_id, tel_defines.EVENT_CALL_CHILDREN_CHANGED)
+            return None
+
+def get_call_id_children(log, ad, call_id):
+    """Return the list of calls that are children to call_id
+
+    Args:
+        log: log object
+        ad: android device object
+        call_id: Conference call id
+
+    Returns:
+        List containing call_ids.
+    """
+    call = ad.droid.telecomCallGetCallById(call_id)
+    call_chld = set(call['Children'])
+    log.info("get_call_id_children droid {} call {} children {}".format(
+        ad.serial, call, call_chld))
+    return call_chld
+
+def get_calls_in_states(log, ad, call_states):
+    """Return the list of calls that are any of the states passed in call_states
+
+    Args:
+        log: log object
+        ad: android device object
+        call_states: List of desired call states
+
+    Returns:
+        List containing call_ids.
+    """
+    # Get the list of calls.
+    call_ids = ad.droid.telecomCallGetCallIds()
+    call_in_state = []
+    for call_id in call_ids:
+        call = ad.droid.telecomCallGetCallById(call_id)
+        log.info("Call id: {} desc: {}".format(call_id, call))
+        if call['State'] in call_states:
+            log.info("Adding call id {} to result set.".format(call_id))
+            call_in_state.append(call_id)
+    return call_in_state
diff --git a/acts/framework/acts/test_utils/car/tel_telecom_utils.py b/acts/framework/acts/test_utils/car/tel_telecom_utils.py
new file mode 100644
index 0000000..632e17b
--- /dev/null
+++ b/acts/framework/acts/test_utils/car/tel_telecom_utils.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - 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.
+
+# Defines utilities that can be used for making calls independent of
+# subscription IDs. This can be useful when making calls over mediums not SIM
+# based.
+
+import queue
+import time
+
+from acts import logger
+from acts.test_utils.tel import tel_defines
+
+def dial_number(log, ad, uri):
+    """Dial a Tel: URI
+
+    Args:
+        log: log object
+        ad: android device object
+        uri: Tel uri dial
+
+    Returns:
+        True if success, False if fail.
+    """
+    if "tel:" not in uri:
+        uri = "tel:" + uri
+    log.info("Dialing up droid {} call uri {}".format(
+        ad.serial, uri))
+
+    # Start tracking updates.
+    ad.droid.telecomStartListeningForCallAdded()
+
+    ad.droid.telecomCallTelUri(uri)
+
+    event = None
+    try:
+        event = ad.ed.pop_event(
+            tel_defines.EventTelecomCallAdded,
+            tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+    except queue.Empty:
+        log.info(
+            "Did not get {} event!".format(tel_defines.EventTelecomCallAdded))
+        # Return failure.
+        return False
+    finally:
+        ad.droid.telecomStopListeningForCallAdded()
+
+    call_id = event['data']['CallId']
+    log.info("Call ID: {} dev {}".format(call_id, ad.serial))
+
+    if not call_id:
+        log.info("CallId is empty!")
+        return False
+    if not wait_for_dialing(log, ad):
+        return False
+
+    return call_id
+
+def wait_for_call_state(log, ad, call_id, state):
+    """Wait for the given call id to transition to the given call state.
+
+    Args:
+        log: log object
+        ad: android device object
+        call_id: ID of the call that we're waiting for the call state to
+        transition into.
+        state: desired final state.
+
+    Returns:
+        True if success, False if fail.
+    """
+    # Lets track the call now.
+    ad.droid.telecomCallStartListeningForEvent(
+        call_id, tel_defines.EVENT_CALL_STATE_CHANGED)
+
+    # We may have missed the update so do a quick check.
+    if ad.droid.telecomCallGetCallState(call_id) == state:
+        log.info("Call ID {} already in {} dev {}!".format(
+            call_id, state, ad.serial))
+        return state
+
+    # If not then we need to poll for the event.
+    try:
+        event = ad.ed.pop_event(
+            tel_defines.EventTelecomCallStateChanged,
+            tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+        call_state = event['data']['Event']
+    except queue.Empty:
+        log.info("Did not get into state {} dev {}".format(
+            state, ad.serial))
+        return None
+    finally:
+        ad.droid.telecomCallStopListeningForEvent(call_id,
+            tel_defines.EventTelecomCallStateChanged)
+    return call_state
+
+def hangup_call(log, ad, call_id):
+    """Hangup a number
+
+    Args:
+        log: log object
+        ad: android device object
+        call_id: Call to hangup.
+
+    Returns:
+        True if success, False if fail.
+    """
+    log.info("Hanging up droid {} call {}".format(
+        ad.serial, call_id))
+    # First check that we are in call, otherwise fail.
+    if not ad.droid.telecomIsInCall():
+        log.info("We are not in-call {}".format(ad.serial))
+        return False
+
+    # Make sure we are registered with the events.
+    ad.droid.telecomStartListeningForCallRemoved()
+
+    # Disconnect call.
+    ad.droid.telecomCallDisconnect(call_id)
+
+    # Wait for removed event.
+    event = None
+    try:
+        event = ad.ed.pop_event(
+            tel_defines.EventTelecomCallRemoved,
+            tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+    except queue.Empty:
+        log.info("Did not get TelecomCallRemoved event")
+        return False
+    finally:
+        ad.droid.telecomStopListeningForCallRemoved()
+
+    log.info("Removed call {}".format(event))
+    if event['data']['CallId'] != call_id:
+        return False
+
+    return True
+
+def wait_for_not_in_call(log, ad):
+    """Wait for the droid to be OUT OF CALLING state.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    ad.droid.telecomStartListeningForCallRemoved()
+
+    calls = ad.droid.telecomCallGetCallIds()
+
+    timeout = time.time() + tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+    remaining_time = tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+    if len(calls) == 0:
+        log.info("Got calls {} for {}".format(
+            calls, ad.serial))
+        try:
+            while len(calls) > 0:
+                event = ad.ed.pop_event(
+                    tel_defines.EventTelecomCallRemoved,
+                    remaining_time)
+                remaining_time = timeout - time.time()
+                if (remaining_time <= 0
+                        or len(ad.droid.telecomCallGetCallIds() == 0)):
+                    break
+
+        except queue.Empty:
+            log.info("wait_for_not_in_call Did not get {} droid {}".format(
+                tel_defines.EventTelecomCallRemoved,
+                ad.serial))
+            return False
+        finally:
+            ad.droid.telecomStopListeningForCallRemoved()
+
+    # Either we removed the only call or we never had a call previously, either
+    # ways simply check if we are in in call now.
+    return (not ad.droid.telecomIsInCall())
+
+def wait_for_dialing(log, ad):
+    """Wait for the droid to be in dialing state.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    # Start listening for events before anything else happens.
+    ad.droid.telecomStartListeningForCallAdded()
+
+    call_id = None
+    # Now check if we already have calls matching the state.
+    calls_in_state = get_calls_in_states(log, ad,
+                                         [tel_defines.CALL_STATE_CONNECTING,
+                                         tel_defines.CALL_STATE_DIALING])
+
+    # If not then we need to poll for the calls themselves.
+    if len(calls_in_state) == 0:
+        event = None
+        try:
+            event = ad.ed.pop_event(
+                tel_defines.EventTelecomCallAdded,
+                tel_defines.MAX_WAIT_TIME_CONNECTION_STATE_UPDATE)
+        except queue.Empty:
+            log.info("Did not get {}".format(
+                tel_defines.EventTelecomCallAdded))
+            return False
+        finally:
+            ad.droid.telecomStopListeningForCallAdded()
+        call_id = event['data']['CallId']
+    else:
+        call_id = calls_in_state[0]
+
+    # We may still not be in-call if the call setup is going on.
+    # We wait for the call state to move to dialing.
+    log.info("call id {} droid {}".format(call_id, ad.serial))
+    curr_state = wait_for_call_state(
+        log, ad, call_id, tel_defines.CALL_STATE_DIALING)
+    if curr_state == tel_defines.CALL_STATE_CONNECTING:
+        log.info("Got connecting state now waiting for dialing state.")
+        if wait_for_call_state(
+            log, ad, call_id, tel_defines.CALL_STATE_DIALING) != \
+            tel_defines.CALL_STATE_DIALING:
+            return False
+    else:
+        if curr_state != tel_defines.CALL_STATE_DIALING:
+            log.info("First state is not dialing")
+            return False
+
+    # Finally check the call state.
+    log.info("wait_for_dialing returns " + str(ad.droid.telecomIsInCall()) +
+             " " + str(ad.serial))
+    return ad.droid.telecomIsInCall()
+
+def wait_for_ringing(log, ad):
+    """Wait for the droid to be in ringing state.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    log.info("waiting for ringing {}".format(ad.serial))
+    # Start listening for events before anything else happens.
+    ad.droid.telecomStartListeningForCallAdded()
+
+    # First check if we re in call, then simply return.
+    if ad.droid.telecomIsInCall():
+        log.info("Device already in call {}".format(ad.serial))
+        ad.droid.telecomStopListeningForCallAdded()
+        return True
+
+    call_id = None
+    # Now check if we already have calls matching the state.
+    calls_in_state = ad.droid.telecomCallGetCallIds()
+
+    if len(calls_in_state) == 0:
+        event = None
+        try:
+            event = ad.ed.pop_event(
+                tel_defines.EventTelecomCallAdded,
+                tel_defines.MAX_WAIT_TIME_CALLEE_RINGING)
+        except queue.Empty:
+            log.info("Did not get {} droid {}".format(
+                tel_defines.EventTelecomCallAdded,
+                ad.serial))
+            return False
+        finally:
+            ad.droid.telecomStopListeningForCallAdded()
+        call_id = event['data']['CallId']
+        log.info("wait_for_ringing call found {} dev {}".format(
+            call_id, ad.serial))
+    else:
+        call_id = calls_in_state[0]
+
+    # A rining call is ringing when it is added so simply wait for ringing
+    # state.
+    if wait_for_call_state(
+        log, ad, call_id, tel_defines.CALL_STATE_RINGING) != \
+        tel_defines.CALL_STATE_RINGING:
+        log.info("No ringing call id {} droid {}".format(
+            call_id, ad.serial))
+        return False
+    return True
+
+def get_calls_in_states(log, ad, call_states):
+    """Return the list of calls that are any of the states passed in call_states
+
+    Args:
+        log: log object
+        ad: android device object
+        call_states: List of desired call states
+
+    Returns:
+        List containing call_ids.
+    """
+    # Get the list of calls.
+    call_ids = ad.droid.telecomCallGetCallIds()
+    call_in_state = []
+    for call_id in call_ids:
+        call = ad.droid.telecomCallGetCallById(call_id)
+        log.info("Call id: {} desc: {}".format(call_id, call))
+        if call['State'] in call_states:
+            log.info("Adding call id {} to result set.".format(call_id))
+            call_in_state.append(call_id)
+    return call_in_state
diff --git a/acts/framework/acts/test_utils/net/nsd_const.py b/acts/framework/acts/test_utils/net/nsd_const.py
new file mode 100644
index 0000000..759ec3d
--- /dev/null
+++ b/acts/framework/acts/test_utils/net/nsd_const.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - 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.
+
+######################################################
+# NsdManager.RegistrationListener events
+######################################################
+REG_LISTENER_EVENT = "NsdRegistrationListener"
+
+# event type - using REG_LISTENER_CALLBACK
+REG_LISTENER_EVENT_ON_REG_FAILED = "OnRegistrationFailed"
+REG_LISTENER_EVENT_ON_SERVICE_REGISTERED = "OnServiceRegistered"
+REG_LISTENER_EVENT_ON_SERVICE_UNREG = "OnServiceUnregistered"
+REG_LISTENER_EVENT_ON_UNREG_FAILED = "OnUnregistrationFailed"
+
+# event data keys
+REG_LISTENER_DATA_ID = "id"
+REG_LISTENER_CALLBACK = "callback"
+REG_LISTENER_ERROR_CODE = "error_code"
+
+######################################################
+# NsdManager.DiscoveryListener events
+######################################################
+DISCOVERY_LISTENER_EVENT = "NsdDiscoveryListener"
+
+# event type - using DISCOVERY_LISTENER_DATA_CALLBACK
+DISCOVERY_LISTENER_EVENT_ON_DISCOVERY_STARTED = "OnDiscoveryStarted"
+DISCOVERY_LISTENER_EVENT_ON_DISCOVERY_STOPPED = "OnDiscoveryStopped"
+DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND = "OnServiceFound"
+DISCOVERY_LISTENER_EVENT_ON_SERVICE_LOST = "OnServiceLost"
+DISCOVERY_LISTENER_EVENT_ON_START_DISCOVERY_FAILED = "OnStartDiscoveryFailed"
+DISCOVERY_LISTENER_EVENT_ON_STOP_DISCOVERY_FAILED = "OnStopDiscoveryFailed"
+
+# event data keys
+DISCOVERY_LISTENER_DATA_ID = "id"
+DISCOVERY_LISTENER_DATA_CALLBACK = "callback"
+DISCOVERY_LISTENER_DATA_SERVICE_TYPE = "service_type"
+DISCOVERY_LISTENER_DATA_ERROR_CODE = "error_code"
+
+######################################################
+# NsdManager.ResolveListener events
+######################################################
+RESOLVE_LISTENER_EVENT = "NsdResolveListener"
+
+# event type using RESOLVE_LISTENER_DATA_CALLBACK
+RESOLVE_LISTENER_EVENT_ON_RESOLVE_FAIL = "OnResolveFail"
+RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED = "OnServiceResolved"
+
+# event data keys
+RESOLVE_LISTENER_DATA_ID = "id"
+RESOLVE_LISTENER_DATA_CALLBACK = "callback"
+RESOLVE_LISTENER_DATA_ERROR_CODE = "error_code"
+
+######################################################
+# NsdServiceInfo elements
+######################################################
+NSD_SERVICE_INFO_HOST = "serviceInfoHost"
+NSD_SERVICE_INFO_PORT = "serviceInfoPort"
+NSD_SERVICE_INFO_SERVICE_NAME = "serviceInfoServiceName"
+NSD_SERVICE_INFO_SERVICE_TYPE = "serviceInfoServiceType"
\ No newline at end of file
diff --git a/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
index 812ef4e..d90f7ec 100644
--- a/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
+++ b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
@@ -27,6 +27,8 @@
 from acts.base_test import BaseTestClass
 from acts.keys import Config
 from acts.signals import TestSignal
+from acts.signals import TestAbortClass
+from acts.signals import TestAbortAll
 from acts import utils
 
 from acts.test_utils.tel.tel_subscription_utils import \
@@ -38,6 +40,7 @@
     set_subid_for_message
 from acts.test_utils.tel.tel_subscription_utils import \
     set_subid_for_outgoing_call
+from acts.test_utils.tel.tel_test_utils import abort_all_tests
 from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
 from acts.test_utils.tel.tel_test_utils import \
@@ -52,32 +55,6 @@
 from acts.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_ENABLED
 from acts.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_DISABLED
 from acts.utils import force_airplane_mode
-from acts.utils import get_current_human_time
-
-
-class _TelephonyTraceLogger():
-    def __init__(self, logger):
-        self._logger = logger
-
-    @staticmethod
-    def _get_trace_info():
-        # we want the stack frame above this and above the error/warning/info
-        stack_frames = inspect.stack()[2]
-        info = inspect.getframeinfo(stack_frames[0])
-
-        return "{}:{}:{}".format(
-            os.path.basename(info.filename), info.function, info.lineno)
-
-    def error(self, msg, *args, **kwargs):
-        trace_info = _TelephonyTraceLogger._get_trace_info()
-        self._logger.error("{} - {}".format(trace_info, msg), *args, **kwargs)
-
-    def warning(self, msg, *args, **kwargs):
-        trace_info = _TelephonyTraceLogger._get_trace_info()
-        self._logger.warning("{} - {}".format(trace_info, msg), *args, **kwargs)
-
-    def __getattr__(self, name):
-        return getattr(self._logger, name)
 
 
 class TelephonyBaseTest(BaseTestClass):
@@ -86,38 +63,66 @@
         BaseTestClass.__init__(self, controllers)
         self.logger_sessions = []
 
-        self.log = _TelephonyTraceLogger(self.log)
+        for ad in self.android_devices:
+            #The puk and pin should be provided in testbed config file.
+            #"AndroidDevice": [{"serial": "84B5T15A29018214",
+            #                   "adb_logcat_param": "-b all",
+            #                   "puk": "12345678",
+            #                   "puk_pin": "1234"}]
+            if hasattr(ad, 'puk'):
+                if not hasattr(ad, 'puk_pin'):
+                    abort_all_tests(ad.log, "puk_pin is not provided")
+                ad.log.info("Enter PUK code and pin")
+                if not ad.droid.telephonySupplyPuk(ad.puk, ad.puk_pin):
+                    abort_all_tests(
+                        ad.log,
+                        "Puk and puk_pin provided in testbed config do NOT work"
+                    )
+
+        self.skip_reset_between_cases = self.user_params.get(
+            "skip_reset_between_cases", False)
 
     # Use for logging in the test cases to facilitate
     # faster log lookup and reduce ambiguity in logging.
     @staticmethod
     def tel_test_wrap(fn):
         def _safe_wrap_test_case(self, *args, **kwargs):
-            current_time = get_current_human_time()
+            current_time = time.strftime("%m-%d-%Y-%H-%M-%S")
             func_name = fn.__name__
-            test_id = "{}:{}:{}".format(self.__class__.__name__, func_name,
-                                        current_time.replace(" ", "-"))
+            test_id = "%s:%s:%s" % (self.__class__.__name__, func_name,
+                                    current_time)
             self.test_id = test_id
             self.begin_time = current_time
             self.test_name = func_name
-            log_string = "[Test ID] {}".format(test_id)
+            log_string = "[Test ID] %s" % test_id
             self.log.info(log_string)
             try:
                 for ad in self.android_devices:
-                    ad.droid.logI("Started " + log_string)
+                    ad.droid.logI("Started %s" % log_string)
+                    ad.crash_report = ad.check_crash_report(
+                        log_crash_report=False)
+                    if ad.crash_report:
+                        ad.log.info("Found crash reports %s before test start",
+                                    ad.crash_report)
+
                 # TODO: b/19002120 start QXDM Logging
                 result = fn(self, *args, **kwargs)
                 for ad in self.android_devices:
-                    ad.droid.logI("Finished " + log_string)
-                if result is not True and "telephony_auto_rerun" in self.user_params:
+                    ad.droid.logI("Finished %s" % log_string)
+                    new_crash = ad.check_crash_report()
+                    crash_diff = set(new_crash).difference(
+                        set(ad.crash_report))
+                    if crash_diff:
+                        ad.log.error("Find new crash reports %s",
+                                     list(crash_diff))
+                if not result and self.user_params.get("telephony_auto_rerun"):
                     self.teardown_test()
                     # re-run only once, if re-run pass, mark as pass
-                    log_string = "[Rerun Test ID] {}. 1st run failed.".format(
-                        test_id)
+                    log_string = "[Rerun Test ID] %s. 1st run failed." % test_id
                     self.log.info(log_string)
                     self.setup_test()
                     for ad in self.android_devices:
-                        ad.droid.logI("Rerun Started " + log_string)
+                        ad.droid.logI("Rerun Started %s" % log_string)
                     result = fn(self, *args, **kwargs)
                     if result is True:
                         self.log.info("Rerun passed.")
@@ -131,7 +136,7 @@
                         self.log.info("Rerun indeterminate.")
                         result = False
                 return result
-            except TestSignal:
+            except (TestSignal, TestAbortClass, TestAbortAll):
                 raise
             except Exception as e:
                 self.log.error(traceback.format_exc())
@@ -148,67 +153,69 @@
         return _safe_wrap_test_case
 
     def setup_class(self):
-
-        if not "sim_conf_file" in self.user_params.keys():
-            self.log.error("Missing mandatory user config \"sim_conf_file\"!")
-            return False
-
-        sim_conf_file = self.user_params["sim_conf_file"]
-        # If the sim_conf_file is not a full path, attempt to find it
-        # relative to the config file.
-
-        if not os.path.isfile(sim_conf_file):
-            sim_conf_file = os.path.join(
-                self.user_params[Config.key_config_path], sim_conf_file)
+        sim_conf_file = self.user_params.get("sim_conf_file")
+        if not sim_conf_file:
+            self.log.info("\"sim_conf_file\" is not provided test bed config!")
+        else:
+            # If the sim_conf_file is not a full path, attempt to find it
+            # relative to the config file.
             if not os.path.isfile(sim_conf_file):
-                self.log.error("Unable to load user config " + sim_conf_file +
-                               " from test config file.")
-                return False
+                sim_conf_file = os.path.join(
+                    self.user_params[Config.key_config_path], sim_conf_file)
+                if not os.path.isfile(sim_conf_file):
+                    self.log.error("Unable to load user config %s ",
+                                   sim_conf_file)
+                    return False
 
-        setattr(self,
-                "diag_logger",
-                self.register_controller(acts.controllers.diag_logger,
-                                         required=False))
+        setattr(self, "diag_logger",
+                self.register_controller(
+                    acts.controllers.diag_logger, required=False))
+        is_mobility_setup = self.user_params.get("Attenuator")
+        if not is_mobility_setup:
+            ensure_phones_default_state(self.log, self.android_devices)
         for ad in self.android_devices:
-            # Ensure the phone is not in airplane mode before setup_droid_properties
-            toggle_airplane_mode(self.log, ad, False, strict_checking=False)
             setup_droid_properties(self.log, ad, sim_conf_file)
 
             # Setup VoWiFi MDN for Verizon. b/33187374
             build_id = ad.build_info["build_id"]
-            if "vzw" in [sub["operator"] for sub in ad.cfg[
-                    "subscription"].values()] and ad.model in (
-                            "marlin", "sailfish") and (build_id.startswith(
-                                    "N") or build_id.startswith("O")):
-                ad.log.info("setup VoWiFi MDN for MR2 or OC branch per b/33187374")
+            if "vzw" in [
+                    sub["operator"] for sub in ad.cfg["subscription"].values()
+            ] and ad.is_apk_installed("com.google.android.wfcactivation"):
+                ad.log.info("setup VoWiFi MDN per b/33187374")
                 ad.adb.shell("setprop dbg.vzw.force_wfc_nv_enabled true")
                 ad.adb.shell("am start --ei EXTRA_LAUNCH_CARRIER_APP 0 -n "
                              "\"com.google.android.wfcactivation/"
                              ".VzwEmergencyAddressActivity\"")
+            if not ad.is_apk_running("com.google.telephonymonitor"):
+                ad.log.info("TelephonyMonitor is not running, start it now")
+                ad.adb.shell(
+                    'am broadcast -a '
+                    'com.google.gservices.intent.action.GSERVICES_OVERRIDE -e '
+                    '"ce.telephony_monitor_enable" "true"')
 
             # Ensure that a test class starts from a consistent state that
             # improves chances of valid network selection and facilitates
             # logging.
-            if not set_phone_screen_on(self.log, ad):
-                self.log.error("Failed to set phone screen-on time.")
-                return False
-            if not set_phone_silent_mode(self.log, ad):
-                self.log.error("Failed to set phone silent mode.")
-                return False
+            try:
+                if not set_phone_screen_on(self.log, ad):
+                    self.log.error("Failed to set phone screen-on time.")
+                    return False
+                if not set_phone_silent_mode(self.log, ad):
+                    self.log.error("Failed to set phone silent mode.")
+                    return False
 
-            ad.droid.telephonyAdjustPreciseCallStateListenLevel(
-                PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND, True)
-            ad.droid.telephonyAdjustPreciseCallStateListenLevel(
-                PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING, True)
-            ad.droid.telephonyAdjustPreciseCallStateListenLevel(
-                PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND, True)
+                ad.droid.telephonyAdjustPreciseCallStateListenLevel(
+                    PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND, True)
+                ad.droid.telephonyAdjustPreciseCallStateListenLevel(
+                    PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING, True)
+                ad.droid.telephonyAdjustPreciseCallStateListenLevel(
+                    PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND, True)
 
-            if "enable_wifi_verbose_logging" in self.user_params:
-                ad.droid.wifiEnableVerboseLogging(WIFI_VERBOSE_LOGGING_ENABLED)
-
-            # Reset preferred network type.
-            reset_preferred_network_type_to_allowable_range(self.log, ad)
-
+                if "enable_wifi_verbose_logging" in self.user_params:
+                    ad.droid.wifiEnableVerboseLogging(
+                        WIFI_VERBOSE_LOGGING_ENABLED)
+            except Exception as e:
+                self.log.error("Failure with %s", e)
         # Sub ID setup
         for ad in self.android_devices:
             initial_set_up_for_subid_infomation(self.log, ad)
@@ -228,41 +235,23 @@
                 if "enable_wifi_verbose_logging" in self.user_params:
                     ad.droid.wifiEnableVerboseLogging(
                         WIFI_VERBOSE_LOGGING_DISABLED)
-        finally:
-            for ad in self.android_devices:
-                try:
-                    toggle_airplane_mode(self.log, ad, True, strict_checking=False)
-                except BrokenPipeError:
-                    # Broken Pipe, can not call SL4A API to turn on Airplane Mode.
-                    # Use adb command to turn on Airplane Mode.
-                    if not force_airplane_mode(ad, True):
-                        self.log.error(
-                            "Can not turn on airplane mode on:{}".format(
-                                ad.serial))
-        return True
+            return True
+        except Exception as e:
+            self.log.error("Failure with %s", e)
 
     def setup_test(self):
-        for ad in self.android_devices:
-            refresh_droid_config(self.log, ad)
-
         if getattr(self, "diag_logger", None):
             for logger in self.diag_logger:
-                self.log.info("Starting a diagnostic session {}".format(
-                    logger))
+                self.log.info("Starting a diagnostic session %s", logger)
                 self.logger_sessions.append((logger, logger.start()))
 
+        if self.skip_reset_between_cases:
+            return True
         return ensure_phones_default_state(self.log, self.android_devices)
 
     def teardown_test(self):
         return True
 
-    def _cleanup_logger_sessions(self):
-        for (logger, session) in self.logger_sessions:
-            self.log.info("Resetting a diagnostic session {},{}".format(
-                logger, session))
-            logger.reset()
-        self.logger_sessions = []
-
     def on_exception(self, test_name, begin_time):
         self._pull_diag_logs(test_name, begin_time)
         self._take_bug_report(test_name, begin_time)
@@ -276,42 +265,9 @@
     def on_pass(self, test_name, begin_time):
         self._cleanup_logger_sessions()
 
-    def _pull_diag_logs(self, test_name, begin_time):
-        for (logger, session) in self.logger_sessions:
-            self.log.info("Pulling diagnostic session {}".format(logger))
-            logger.stop(session)
-            diag_path = os.path.join(self.log_path, begin_time)
-            utils.create_dir(diag_path)
-            logger.pull(session, diag_path)
-
-    def _take_bug_report(self, test_name, begin_time):
-        if "no_bug_report_on_fail" in self.user_params:
-            return
-
-        # 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_report(test_name, begin_time)
-                tombstone_path = os.path.join(
-                    ad.log_path, "BugReports",
-                    "{},{}".format(begin_time, ad.serial).replace(' ', '_'))
-                utils.create_dir(tombstone_path)
-                ad.adb.pull('/data/tombstones/', tombstone_path)
-            except:
-                ad.log.error("Failed to take a bug report for {}, {}"
-                             .format(ad.serial, test_name))
-
     def get_stress_test_number(self):
         """Gets the stress_test_number param from user params.
 
-        Gets the stress_test_number param. If absent, returns None
-        and logs a warning.
+        Gets the stress_test_number param. If absent, returns default 100.
         """
-
-        number = self.user_params.get("stress_test_number")
-        if number is None:
-            self.log.warning("stress_test_number is not set.")
-            return None
-        return int(number)
+        return int(self.user_params.get("stress_test_number", 100))
diff --git a/acts/framework/acts/test_utils/tel/anritsu_utils.py b/acts/framework/acts/test_utils/tel/anritsu_utils.py
new file mode 100644
index 0000000..173c839
--- /dev/null
+++ b/acts/framework/acts/test_utils/tel/anritsu_utils.py
@@ -0,0 +1,1819 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import time
+from queue import Empty
+from datetime import datetime
+
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuUtils
+from acts.controllers.anritsu_lib.md8475a import BtsNumber
+from acts.controllers.anritsu_lib.md8475a import BtsNwNameEnable
+from acts.controllers.anritsu_lib.md8475a import BtsServiceState
+from acts.controllers.anritsu_lib.md8475a import BtsTechnology
+from acts.controllers.anritsu_lib.md8475a import CsfbType
+from acts.controllers.anritsu_lib.md8475a import ImsCscfCall
+from acts.controllers.anritsu_lib.md8475a import ImsCscfStatus
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import ReturnToEUTRAN
+from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
+from acts.controllers.anritsu_lib.md8475a import TestProcedure
+from acts.controllers.anritsu_lib.md8475a import TestPowerControl
+from acts.controllers.anritsu_lib.md8475a import TestMeasurement
+from acts.controllers.anritsu_lib.md8475a import Switch
+from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
+from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_REMOTE
+from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
+from acts.test_utils.tel.tel_defines import RAT_1XRTT
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts.test_utils.tel.tel_defines import EventCmasReceived
+from acts.test_utils.tel.tel_defines import EventEtwsReceived
+from acts.test_utils.tel.tel_defines import EventSmsDeliverSuccess
+from acts.test_utils.tel.tel_defines import EventSmsSentSuccess
+from acts.test_utils.tel.tel_defines import EventSmsReceived
+from acts.test_utils.tel.tel_test_utils import ensure_phone_idle
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts.test_utils.tel.tel_test_utils import wait_for_droid_not_in_call
+
+# Timers
+# 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
+# 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
+# Max time to wait for Anritsu's virtual phone state change
+MAX_WAIT_TIME_VIRTUAL_PHONE_STATE = 45
+# Time to wait for Anritsu's IMS CSCF state change
+MAX_WAIT_TIME_IMS_CSCF_STATE = 30
+# Time to wait for before aSRVCC
+WAIT_TIME_IN_ALERT = 5
+
+# SIM card names
+P0250Ax = "P0250Ax"
+VzW12349 = "VzW12349"
+
+# 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
+VzW_MCC = "311"
+VzW_MNC = "480"
+
+# IP address information for internet sharing
+#GATEWAY_IPV4_ADDR = "192.168.137.1"
+#UE_IPV4_ADDR_1 = "192.168.137.2"
+#UE_IPV4_ADDR_2 = "192.168.137.3"
+#UE_IPV4_ADDR_3 = "192.168.137.4"
+#DNS_IPV4_ADDR = "192.168.137.1"
+#CSCF_IPV4_ADDR = "192.168.137.1"
+
+# Default IP address in Smart Studio, work for Internet Sharing with and
+# without WLAN ePDG server. Remember to add 192.168.1.2 to Ethernet 0
+# on MD8475A after turn on Windows' Internet Coonection Sharing
+GATEWAY_IPV4_ADDR = "192.168.1.2"
+UE_IPV4_ADDR_1 = "192.168.1.1"
+UE_IPV4_ADDR_2 = "192.168.1.11"
+UE_IPV4_ADDR_3 = "192.168.1.21"
+UE_IPV6_ADDR_1 = "2001:0:0:1::1"
+UE_IPV6_ADDR_2 = "2001:0:0:1::11"
+UE_IPV6_ADDR_3 = "2001:0:0:1::21"
+DNS_IPV4_ADDR = "192.168.1.2"
+CSCF_IPV4_ADDR = "192.168.1.2"
+CSCF_IPV6_ADDR = "2001:0:0:1::2"
+
+# 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"
+
+LTE_BAND_2 = 2
+LTE_BAND_4 = 4
+LTE_BAND_12 = 12
+WCDMA_BAND_1 = 1
+WCDMA_BAND_2 = 2
+
+# Default Cell Parameters
+DEFAULT_OUTPUT_LEVEL = -30
+DEFAULT_INPUT_LEVEL = -10  # apply to LTE & WCDMA only
+DEFAULT_LTE_BAND = 2
+DEFAULT_WCDMA_BAND = 1
+DEFAULT_GSM_BAND = GSM_BAND_GSM850
+DEFAULT_CDMA1X_BAND = 1
+DEFAULT_CDMA1X_CH = 0
+DEFAULT_CDMA1X_SID = 0
+DEFAULT_CDMA1X_NID = 65535
+DEFAULT_EVDO_BAND = 0
+DEFAULT_EVDO_CH = 356
+DEFAULT_EVDO_SECTOR_ID = "00000000,00000000,00000000,00000000"
+VzW_CDMA1x_BAND = 1
+VzW_CDMA1x_CH = 150
+VzW_CDMA1X_SID = 26
+VzW_CDMA1X_NID = 65535
+VzW_EVDO_BAND = 0
+VzW_EVDO_CH = 384
+VzW_EVDO_SECTOR_ID = "12345678,00000000,00000000,00000000"
+
+# 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
+PDN_NO_2 = 2
+PDN_NO_3 = 3
+
+# IMS Services parameters
+DEFAULT_VNID = 1
+NDP_NIC_NAME = '"Intel(R) 82577LM Gigabit Network Connection"'
+CSCF_Monitoring_UA_URI = '"sip:+11234567890@test.3gpp.com"'
+
+#Cell Numbers
+CELL_1 = 1
+CELL_2 = 2
+
+# default ims virtual network id for Anritsu ims call test.
+DEFAULT_IMS_VIRTUAL_NETWORK_ID = 1
+
+
+def cb_serial_number():
+    """ CMAS/ETWS serial number generator """
+    i = 0x3000
+    while True:
+        yield i
+        i += 1
+
+
+def set_usim_parameters(anritsu_handle, sim_card):
+    """ set USIM parameters in MD8475A simulationn parameter
+
+    Args:
+        anritsu_handle: anritusu device object.
+        sim_card : "P0250Ax" or "12349"
+
+    Returns:
+        None
+    """
+    if sim_card == P0250Ax:
+        anritsu_handle.usim_key = "000102030405060708090A0B0C0D0E0F"
+    elif sim_card == VzW12349:
+        anritsu_handle.usim_key = "465B5CE8B199B49FAA5F0A2EE238A6BC"
+        anritsu_handle.send_command("IMSI 311480012345678")
+        anritsu_handle.send_command("SECURITY3G MILENAGE")
+        anritsu_handle.send_command(
+            "MILENAGEOP 5F1D289C5D354D0A140C2548F5F3E3BA")
+
+
+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, sim_card):
+    """ 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, sim_card)
+    bts.mnc = get_lte_mnc(user_params, cell_no, sim_card)
+    bts.band = get_lte_band(user_params, cell_no)
+    bts.output_level = DEFAULT_OUTPUT_LEVEL
+    bts.input_level = DEFAULT_INPUT_LEVEL
+
+
+def _init_wcdma_bts(bts, user_params, cell_no, sim_card):
+    """ 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_wcdma_mcc(user_params, cell_no, sim_card)
+    bts.mnc = get_wcdma_mnc(user_params, cell_no, sim_card)
+    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)
+    bts.output_level = DEFAULT_OUTPUT_LEVEL
+    bts.input_level = DEFAULT_INPUT_LEVEL
+
+
+def _init_gsm_bts(bts, user_params, cell_no, sim_card):
+    """ 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_gsm_mcc(user_params, cell_no, sim_card)
+    bts.mnc = get_gsm_mnc(user_params, cell_no, sim_card)
+    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)
+    bts.output_level = DEFAULT_OUTPUT_LEVEL
+
+
+def _init_1x_bts(bts, user_params, cell_no, sim_card):
+    """ 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, sim_card)
+    bts.band = get_1x_band(user_params, cell_no, sim_card)
+    bts.dl_channel = get_1x_channel(user_params, cell_no, sim_card)
+    bts.sector1_sid = get_1x_sid(user_params, cell_no, sim_card)
+    bts.sector1_nid = get_1x_nid(user_params, cell_no, sim_card)
+    bts.output_level = DEFAULT_OUTPUT_LEVEL
+
+
+def _init_evdo_bts(bts, user_params, cell_no, sim_card):
+    """ 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
+    """
+    bts.band = get_evdo_band(user_params, cell_no, sim_card)
+    bts.dl_channel = get_evdo_channel(user_params, cell_no, sim_card)
+    bts.evdo_sid = get_evdo_sid(user_params, cell_no, sim_card)
+    bts.output_level = DEFAULT_OUTPUT_LEVEL
+
+
+def _init_PDN(anritsu_handle, pdn, ipv4, ipv6, ims_binding):
+    """ 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
+        ims_binding: to bind with IMS VNID(1) or not
+
+    Returns:
+        None
+    """
+    # Setting IP address for internet connection sharing
+    anritsu_handle.gateway_ipv4addr = GATEWAY_IPV4_ADDR
+    pdn.ue_address_ipv4 = ipv4
+    pdn.ue_address_ipv6 = ipv6
+    if ims_binding:
+        pdn.pdn_ims = Switch.ENABLE
+        pdn.pdn_vnid = DEFAULT_VNID
+    else:
+        pdn.primary_dns_address_ipv4 = DNS_IPV4_ADDR
+        pdn.secondary_dns_address_ipv4 = DNS_IPV4_ADDR
+        pdn.cscf_address_ipv4 = CSCF_IPV4_ADDR
+
+
+def _init_IMS(anritsu_handle, vnid):
+    """ initializes the IMS VNID parameters
+        All IMS parameters should be set here
+
+    Args:
+        anritsu_handle: anritusu device object.
+        vnid: IMS Services object
+
+    Returns:
+        None
+    """
+    # vnid.sync = Switch.ENABLE # supported in 6.40a release
+    vnid.cscf_address_ipv4 = CSCF_IPV4_ADDR
+    vnid.cscf_address_ipv6 = CSCF_IPV6_ADDR
+    vnid.dns = Switch.DISABLE
+    vnid.ndp_nic = NDP_NIC_NAME
+    vnid.cscf_monitoring_ua = CSCF_Monitoring_UA_URI
+    vnid.psap = Switch.ENABLE
+    vnid.psap_auto_answer = Switch.ENABLE
+
+
+def set_system_model_lte_lte(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    _init_lte_bts(lte2_bts, user_params, CELL_2, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+    pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+    _init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+    _init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+    vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+    _init_IMS(anritsu_handle, vnid1)
+    return [lte1_bts, lte2_bts]
+
+
+def set_system_model_wcdma_wcdma(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    _init_wcdma_bts(wcdma2_bts, user_params, CELL_2, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+    return [wcdma1_bts, wcdma2_bts]
+
+
+def set_system_model_lte_wcdma(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    _init_wcdma_bts(wcdma_bts, user_params, CELL_2, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+    pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+    _init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+    _init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+    vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+    _init_IMS(anritsu_handle, vnid1)
+    return [lte_bts, wcdma_bts]
+
+
+def set_system_model_lte_gsm(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    _init_gsm_bts(gsm_bts, user_params, CELL_2, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+    pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+    _init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+    _init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+    vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+    _init_IMS(anritsu_handle, vnid1)
+    return [lte_bts, gsm_bts]
+
+
+def set_system_model_lte_1x(anritsu_handle, user_params, sim_card):
+    """ Configures Anritsu system for LTE and 1x simulation
+
+    Args:
+        anritsu_handle: anritusu device object.
+        user_params: pointer to user supplied parameters
+
+    Returns:
+        Lte and 1x BTS objects
+    """
+    anritsu_handle.set_simulation_model(BtsTechnology.LTE,
+                                        BtsTechnology.CDMA1X)
+    # setting BTS parameters
+    lte_bts = anritsu_handle.get_BTS(BtsNumber.BTS1)
+    cdma1x_bts = anritsu_handle.get_BTS(BtsNumber.BTS2)
+    _init_lte_bts(lte_bts, user_params, CELL_1, sim_card)
+    _init_1x_bts(cdma1x_bts, user_params, CELL_2, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+    pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+    _init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+    _init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+    vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+    _init_IMS(anritsu_handle, vnid1)
+    return [lte_bts, cdma1x_bts]
+
+
+def set_system_model_wcdma_gsm(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    _init_gsm_bts(gsm_bts, user_params, CELL_2, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+    return [wcdma_bts, gsm_bts]
+
+
+def set_system_model_gsm_gsm(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    _init_gsm_bts(gsm2_bts, user_params, CELL_2, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+    return [gsm1_bts, gsm2_bts]
+
+
+def set_system_model_lte(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    pdn2 = anritsu_handle.get_PDN(PDN_NO_2)
+    pdn3 = anritsu_handle.get_PDN(PDN_NO_3)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, True)
+    _init_PDN(anritsu_handle, pdn2, UE_IPV4_ADDR_2, UE_IPV6_ADDR_2, False)
+    _init_PDN(anritsu_handle, pdn3, UE_IPV4_ADDR_3, UE_IPV6_ADDR_3, True)
+    vnid1 = anritsu_handle.get_IMS(DEFAULT_VNID)
+    _init_IMS(anritsu_handle, vnid1)
+    return [lte_bts]
+
+
+def set_system_model_wcdma(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+    return [wcdma_bts]
+
+
+def set_system_model_gsm(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_NO_1)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+    return [gsm_bts]
+
+
+def set_system_model_1x(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_ONE)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+    return [cdma1x_bts]
+
+
+def set_system_model_1x_evdo(anritsu_handle, user_params, sim_card):
+    """ 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, sim_card)
+    _init_evdo_bts(evdo_bts, user_params, CELL_2, sim_card)
+    pdn1 = anritsu_handle.get_PDN(PDN_ONE)
+    # Initialize PDN IP address for internet connection sharing
+    _init_PDN(anritsu_handle, pdn1, UE_IPV4_ADDR_1, UE_IPV6_ADDR_1, False)
+    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
+
+
+class _CallSequenceException(Exception):
+    pass
+
+
+def call_mo_setup_teardown(
+        log,
+        ad,
+        anritsu_handle,
+        callee_number,
+        teardown_side=CALL_TEARDOWN_PHONE,
+        is_emergency=False,
+        wait_time_in_call=WAIT_TIME_IN_CALL,
+        is_ims_call=False,
+        ims_virtual_network_id=DEFAULT_IMS_VIRTUAL_NETWORK_ID):
+    """ Makes a MO call and tear down the call
+
+    Args:
+        ad: Android device object.
+        anritsu_handle: Anritsu object.
+        callee_number: Number to be called.
+        teardown_side: the side to end the call (Phone or remote).
+        is_emergency: is the call an emergency call.
+        wait_time_in_call: Time to wait when phone in call.
+        is_ims_call: is the call expected to be ims call.
+        ims_virtual_network_id: ims virtual network id.
+
+    Returns:
+        True for success False for failure
+    """
+
+    log.info("Making Call to " + callee_number)
+    virtual_phone_handle = anritsu_handle.get_VirtualPhone()
+
+    try:
+        # for an IMS call we either check CSCF or *nothing* (no virtual phone).
+        if is_ims_call:
+            # we only need pre-call registration in a non-emergency case
+            if not is_emergency:
+                if not wait_for_ims_cscf_status(log, anritsu_handle,
+                                                ims_virtual_network_id,
+                                                ImsCscfStatus.SIPIDLE.value):
+                    raise _CallSequenceException(
+                        "Phone IMS status is not idle.")
+        else:
+            if not wait_for_virtualphone_state(log, virtual_phone_handle,
+                                               VirtualPhoneStatus.STATUS_IDLE):
+                raise _CallSequenceException("Virtual Phone not idle.")
+
+        if not initiate_call(log, ad, callee_number, is_emergency):
+            raise _CallSequenceException("Initiate call failed.")
+
+        if is_ims_call:
+            if not wait_for_ims_cscf_status(log, anritsu_handle,
+                                            ims_virtual_network_id,
+                                            ImsCscfStatus.CALLING.value):
+                raise _CallSequenceException(
+                    "Phone IMS status is not calling.")
+            if not wait_for_ims_cscf_status(log, anritsu_handle,
+                                            ims_virtual_network_id,
+                                            ImsCscfStatus.CONNECTED.value):
+                raise _CallSequenceException(
+                    "Phone IMS status is not connected.")
+        else:
+            # check Virtual phone answered the call
+            if not wait_for_virtualphone_state(
+                    log, virtual_phone_handle,
+                    VirtualPhoneStatus.STATUS_VOICECALL_INPROGRESS):
+                raise _CallSequenceException("Virtual Phone not in call.")
+
+        time.sleep(wait_time_in_call)
+
+        if not ad.droid.telecomIsInCall():
+            raise _CallSequenceException("Call ended before delay_in_call.")
+
+        if teardown_side is CALL_TEARDOWN_REMOTE:
+            log.info("Disconnecting the call from Remote")
+            if is_ims_call:
+                anritsu_handle.ims_cscf_call_action(ims_virtual_network_id,
+                                                    ImsCscfCall.END.value)
+            else:
+                virtual_phone_handle.set_voice_on_hook()
+            if not wait_for_droid_not_in_call(log, ad,
+                                              MAX_WAIT_TIME_CALL_DROP):
+                raise _CallSequenceException("DUT call not drop.")
+        else:
+            log.info("Disconnecting the call from DUT")
+            if not hangup_call(log, ad):
+                raise _CallSequenceException(
+                    "Error in Hanging-Up Call on DUT.")
+
+        if is_ims_call:
+            if not wait_for_ims_cscf_status(log, anritsu_handle,
+                                            ims_virtual_network_id,
+                                            ImsCscfStatus.SIPIDLE.value):
+                raise _CallSequenceException("Phone IMS status is not idle.")
+        else:
+            if not wait_for_virtualphone_state(log, virtual_phone_handle,
+                                               VirtualPhoneStatus.STATUS_IDLE):
+                raise _CallSequenceException(
+                    "Virtual Phone not idle after hangup.")
+        return True
+
+    except _CallSequenceException as e:
+        log.error(e)
+        return False
+    finally:
+        try:
+            if ad.droid.telecomIsInCall():
+                ad.droid.telecomEndCall()
+        except Exception as e:
+            log.error(str(e))
+
+
+# This procedure is for SRLTE CSFB and SRVCC test cases
+def ims_call_cs_teardown(
+        log,
+        ad,
+        anritsu_handle,
+        callee_number,
+        teardown_side=CALL_TEARDOWN_PHONE,
+        is_emergency=False,
+        check_ims_reg=True,
+        check_ims_calling=True,
+        srvcc=None,
+        mo=True,
+        wait_time_in_volte=WAIT_TIME_IN_CALL_FOR_IMS,
+        wait_time_in_cs=WAIT_TIME_IN_CALL,
+        wait_time_in_alert=WAIT_TIME_IN_ALERT,
+        ims_virtual_network_id=DEFAULT_IMS_VIRTUAL_NETWORK_ID):
+    """ Makes a MO call after IMS registred, transit to CS, tear down the call
+
+    Args:
+        ad: Android device object.
+        anritsu_handle: Anritsu object.
+        callee_number: Number to be called.
+        teardown_side: the side to end the call (Phone or remote).
+        is_emergency: to make emergency call on the phone.
+        check_ims_reg: check if Anritsu cscf server state is "SIPIDLE".
+        check_ims_calling: check if Anritsu cscf server state is "CALLING".
+        srvcc: is the test case a SRVCC call.
+        mo: Mobile originated call
+        wait_time_in_volte: Time for phone in VoLTE call, not used for SRLTE
+        wait_time_in_cs: Time for phone in CS call.
+        ims_virtual_network_id: ims virtual network id.
+
+    Returns:
+        True for success False for failure
+    """
+
+    virtual_phone_handle = anritsu_handle.get_VirtualPhone()
+
+    try:
+        # confirm ims registration
+        if check_ims_reg:
+            if not wait_for_ims_cscf_status(log, anritsu_handle,
+                                            ims_virtual_network_id,
+                                            ImsCscfStatus.SIPIDLE.value):
+                raise _CallSequenceException("IMS/CSCF status is not idle.")
+        # confirm virtual phone in idle
+        if not wait_for_virtualphone_state(log, virtual_phone_handle,
+                                           VirtualPhoneStatus.STATUS_IDLE):
+            raise _CallSequenceException("Virtual Phone not idle.")
+        if mo:  # make MO call
+            log.info("Making Call to " + callee_number)
+            if not initiate_call(log, ad, callee_number, is_emergency):
+                raise _CallSequenceException("Initiate call failed.")
+        else:  # make MT call
+            log.info("Making IMS Call to UE from MD8475A...")
+            anritsu_handle.ims_cscf_call_action(ims_virtual_network_id,
+                                                ImsCscfCall.MAKE.value)
+        # if check ims calling is required
+        if check_ims_calling:
+            if mo:
+                if not wait_for_ims_cscf_status(log, anritsu_handle,
+                                                ims_virtual_network_id,
+                                                ImsCscfStatus.CALLING.value):
+                    raise _CallSequenceException(
+                        "Phone IMS status is not calling.")
+            else:
+                if not wait_for_ims_cscf_status(log, anritsu_handle,
+                                                ims_virtual_network_id,
+                                                ImsCscfStatus.RINGING.value):
+                    raise _CallSequenceException(
+                        "Phone IMS status is not ringing.")
+
+            # if SRVCC, check if VoLTE call is connected, then Handover
+            if srvcc != None:
+                if srvcc == "InCall":
+                    if not wait_for_ims_cscf_status(
+                            log, anritsu_handle, ims_virtual_network_id,
+                            ImsCscfStatus.CONNECTED.value):
+                        raise _CallSequenceException(
+                            "Phone IMS status is not connected.")
+                    # stay in call for "wait_time_in_volte" seconds
+                    time.sleep(wait_time_in_volte)
+                elif srvcc == "Alert":
+                    # ring for WAIT_TIME_IN_ALERT seconds
+                    time.sleep(WAIT_TIME_IN_ALERT)
+                # SRVCC by handover test case procedure
+                srvcc_tc = anritsu_handle.get_AnritsuTestCases()
+                srvcc_tc.procedure = TestProcedure.PROCEDURE_HO
+                srvcc_tc.bts_direction = (BtsNumber.BTS1, BtsNumber.BTS2)
+                srvcc_tc.power_control = TestPowerControl.POWER_CONTROL_DISABLE
+                srvcc_tc.measurement_LTE = TestMeasurement.MEASUREMENT_DISABLE
+                anritsu_handle.start_testcase()
+                time.sleep(5)
+        if not mo:
+            # answer the call on the UE
+            if not wait_and_answer_call(log, ad):
+                raise _CallSequenceException("UE Answer call Fail")
+        # check if Virtual phone in the call
+        if not wait_for_virtualphone_state(
+                log, virtual_phone_handle,
+                VirtualPhoneStatus.STATUS_VOICECALL_INPROGRESS):
+            raise _CallSequenceException("Virtual Phone not in call.")
+        # stay in call for "wait_time_in_cs" seconds
+        time.sleep(wait_time_in_cs)
+        # check if the phone stay in call
+        if not ad.droid.telecomIsInCall():
+            raise _CallSequenceException("Call ended before delay_in_call.")
+        # end the call
+        if teardown_side is CALL_TEARDOWN_REMOTE:
+            log.info("Disconnecting the call from Remote")
+            virtual_phone_handle.set_voice_on_hook()
+            if not wait_for_droid_not_in_call(log, ad,
+                                              MAX_WAIT_TIME_CALL_DROP):
+                raise _CallSequenceException("DUT call not drop.")
+        else:
+            log.info("Disconnecting the call from DUT")
+            if not hangup_call(log, ad):
+                raise _CallSequenceException(
+                    "Error in Hanging-Up Call on DUT.")
+        # confirm if virtual phone status is back to idle
+        if not wait_for_virtualphone_state(log, virtual_phone_handle,
+                                           VirtualPhoneStatus.STATUS_IDLE):
+            raise _CallSequenceException(
+                "Virtual Phone not idle after hangup.")
+        return True
+
+    except _CallSequenceException as e:
+        log.error(e)
+        return False
+    finally:
+        try:
+            if ad.droid.telecomIsInCall():
+                ad.droid.telecomEndCall()
+        except Exception as e:
+            log.error(str(e))
+
+
+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: b/26296388 There is some problem when retrieving message with é
+        # from Anritsu.
+        return True
+    for i in range(len(split)):
+        if split[i].startswith('Text='):
+            text = split[i][5:]
+            text = AnritsuUtils.gsm_decode(text)
+            break
+    # TODO: b/26296388 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_ims_cscf_status(log,
+                             anritsu_handle,
+                             virtual_network_id,
+                             status,
+                             timeout=MAX_WAIT_TIME_IMS_CSCF_STATE):
+    """ Wait for IMS CSCF to be in expected state.
+
+    Args:
+        log: log object
+        anritsu_handle: anritsu object
+        virtual_network_id: virtual network id to be monitored
+        status: expected status
+        timeout: wait time
+    """
+    sleep_interval = 1
+    wait_time = timeout
+    while wait_time > 0:
+        if status == anritsu_handle.get_ims_cscf_status(virtual_network_id):
+            return True
+        time.sleep(sleep_interval)
+        wait_time = wait_time - sleep_interval
+    return False
+
+
+def wait_for_virtualphone_state(log,
+                                vp_handle,
+                                state,
+                                timeout=MAX_WAIT_TIME_VIRTUAL_PHONE_STATE):
+    """ 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: b/26296388 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: b/26296388 need to add logic to check etws.
+    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
+    """
+    key = "cell{}_lte_band".format(cell_no)
+    try:
+        lte_band = user_params[key]
+    except KeyError:
+        lte_band = DEFAULT_LTE_BAND
+    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
+    """
+    key = "cell{}_wcdma_band".format(cell_no)
+    try:
+        wcdma_band = user_params[key]
+    except KeyError:
+        wcdma_band = DEFAULT_WCDMA_BAND
+    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
+    """
+    key = "cell{}_gsm_band".format(cell_no)
+    try:
+        gsm_band = user_params[key]
+    except KeyError:
+        gsm_band = DEFAULT_GSM_BAND
+    return gsm_band
+
+
+def get_1x_band(user_params, cell_no, sim_card):
+    """ 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
+    """
+    key = "cell{}_1x_band".format(cell_no)
+    band = VzW_CDMA1x_BAND if sim_card == VzW12349 else DEFAULT_CDMA1X_BAND
+    return user_params.get(key, band)
+
+
+def get_evdo_band(user_params, cell_no, sim_card):
+    """ Returns the EVDO 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:
+        EVDO BAND to be used
+    """
+    key = "cell{}_evdo_band".format(cell_no)
+    band = VzW_EVDO_BAND if sim_card == VzW12349 else DEFAULT_EVDO_BAND
+    return user_params.get(key, 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
+    """
+    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
+    """
+    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
+    """
+    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
+    """
+    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, sim_card):
+    """ 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
+    """
+
+    key = "cell{}_lte_mcc".format(cell_no)
+    mcc = VzW_MCC if sim_card == VzW12349 else DEFAULT_MCC
+    return user_params.get(key, mcc)
+
+
+def get_lte_mnc(user_params, cell_no, sim_card):
+    """ 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
+    """
+    key = "cell{}_lte_mnc".format(cell_no)
+    mnc = VzW_MNC if sim_card == VzW12349 else DEFAULT_MNC
+    return user_params.get(key, mnc)
+
+
+def get_wcdma_mcc(user_params, cell_no, sim_card):
+    """ 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
+    """
+    key = "cell{}_wcdma_mcc".format(cell_no)
+    mcc = VzW_MCC if sim_card == VzW12349 else DEFAULT_MCC
+    return user_params.get(key, mcc)
+
+
+def get_wcdma_mnc(user_params, cell_no, sim_card):
+    """ 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
+    """
+    key = "cell{}_wcdma_mnc".format(cell_no)
+    mnc = VzW_MNC if sim_card == VzW12349 else DEFAULT_MNC
+    return user_params.get(key, mnc)
+
+
+def get_gsm_mcc(user_params, cell_no, sim_card):
+    """ 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
+    """
+    key = "cell{}_gsm_mcc".format(cell_no)
+    mcc = VzW_MCC if sim_card == VzW12349 else DEFAULT_MCC
+    return user_params.get(key, mcc)
+
+
+def get_gsm_mnc(user_params, cell_no, sim_card):
+    """ 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
+    """
+    key = "cell{}_gsm_mnc".format(cell_no)
+    mnc = VzW_MNC if sim_card == VzW12349 else DEFAULT_MNC
+    return user_params.get(key, mnc)
+
+
+def get_1x_mcc(user_params, cell_no, sim_card):
+    """ 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
+    """
+    key = "cell{}_1x_mcc".format(cell_no)
+    mcc = VzW_MCC if sim_card == VzW12349 else DEFAULT_MCC
+    return user_params.get(key, mcc)
+
+
+def get_1x_channel(user_params, cell_no, sim_card):
+    """ 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)
+    ch = VzW_CDMA1x_CH if sim_card == VzW12349 else DEFAULT_CDMA1X_CH
+    return user_params.get(key, ch)
+
+
+def get_1x_sid(user_params, cell_no, sim_card):
+    """ 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
+    """
+    key = "cell{}_1x_sid".format(cell_no)
+    sid = VzW_CDMA1X_SID if sim_card == VzW12349 else DEFAULT_CDMA1X_SID
+    return user_params.get(key, sid)
+
+
+def get_1x_nid(user_params, cell_no, sim_card):
+    """ 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
+    """
+    key = "cell{}_1x_nid".format(cell_no)
+    nid = VzW_CDMA1X_NID if sim_card == VzW12349 else DEFAULT_CDMA1X_NID
+    return user_params.get(key, nid)
+
+
+def get_evdo_channel(user_params, cell_no, sim_card):
+    """ Returns the EVDO 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:
+        EVDO Channel to be used
+    """
+    key = "cell{}_evdo_channel".format(cell_no)
+    ch = VzW_EVDO_CH if sim_card == VzW12349 else DEFAULT_EVDO_CH
+    return user_params.get(key, ch)
+
+
+def get_evdo_sid(user_params, cell_no, sim_card):
+    """ Returns the EVDO 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:
+        EVDO SID to be used
+    """
+    key = "cell{}_evdo_sid".format(cell_no)
+    return user_params.get(key, DEFAULT_EVDO_SECTOR_ID)
+    sid = VzW_EVDO_SECTOR_ID if sim_card == VzW12349 else DEFAULT_EVDO_SECTOR_ID
+    return user_params.get(key, sid)
+
+
+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
+    """
+    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_data_utils.py b/acts/framework/acts/test_utils/tel/tel_data_utils.py
index 1238efb..ac0bfd4 100644
--- a/acts/framework/acts/test_utils/tel/tel_data_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_data_utils.py
@@ -24,18 +24,23 @@
 from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
 from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
 from acts.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
-from acts.test_utils.tel.tel_test_utils import WifiUtils
+from acts.test_utils.tel.tel_test_utils import start_wifi_tethering
+from acts.test_utils.tel.tel_test_utils import stop_wifi_tethering
 from acts.test_utils.tel.tel_test_utils import ensure_network_generation_for_subscription
 from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
 from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
 from acts.test_utils.tel.tel_test_utils import get_network_rat_for_subscription
 from acts.test_utils.tel.tel_test_utils import is_droid_in_network_generation_for_subscription
 from acts.test_utils.tel.tel_test_utils import rat_generation_from_rat
+from acts.test_utils.tel.tel_test_utils import set_wifi_to_default
 from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
+from acts.test_utils.tel.tel_test_utils import verify_internet_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
 from acts.test_utils.tel.tel_test_utils import wait_for_data_attach_for_subscription
+from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
 
 
 def wifi_tethering_cleanup(log, provider, client_list):
@@ -54,14 +59,12 @@
     """
     for client in client_list:
         client.droid.telephonyToggleDataConnection(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.")
+        set_wifi_to_default(log, client)
+    if not stop_wifi_tethering(log, provider):
+        provider.log.error("Provider stop WiFi tethering failed.")
         return False
-    if not WifiUtils.stop_wifi_tethering(log, provider):
-        log.error("Provider strop WiFi tethering failed.")
+    if provider.droid.wifiIsApEnabled():
+        provider.log.error("Provider WiFi tethering is still enabled.")
         return False
     return True
 
@@ -69,7 +72,7 @@
 def wifi_tethering_setup_teardown(log,
                                   provider,
                                   client_list,
-                                  ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                                  ap_band=WIFI_CONFIG_APBAND_2G,
                                   check_interval=30,
                                   check_iteration=4,
                                   do_cleanup=True,
@@ -90,7 +93,7 @@
         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
+            This is optional, default value is 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.
@@ -110,7 +113,8 @@
     log.info("--->Start wifi_tethering_setup_teardown<---")
     log.info("Provider: {}".format(provider.serial))
     if not provider.droid.connectivityIsTetheringSupported():
-        log.error("Provider does not support tethering. Stop tethering test.")
+        provider.log.error(
+            "Provider does not support tethering. Stop tethering test.")
         return False
 
     if ssid is None:
@@ -125,54 +129,49 @@
     try:
         for client in client_list:
             log.info("Client: {}".format(client.serial))
-            WifiUtils.wifi_toggle_state(log, client, False)
+            wifi_toggle_state(log, client, False)
             client.droid.telephonyToggleDataConnection(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))
+            if verify_internet_connection(log, client):
+                client.log.error("Turn off Data on client fail")
                 return False
 
-        log.info(
-            "WiFI Tethering: Turn on WiFi tethering on {}. SSID: {}, password: {}".format(
-                provider.serial, ssid, password))
+        provider.log.info(
+            "Provider turn on WiFi tethering. SSID: %s, password: %s", ssid,
+            password)
 
-        if not WifiUtils.start_wifi_tethering(log, provider, ssid, password,
-                                              ap_band):
-            log.error("Provider start WiFi tethering failed.")
+        if not start_wifi_tethering(log, provider, ssid, password, ap_band):
+            provider.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):
+        provider.log.info("Provider check Internet connection.")
+        if not verify_internet_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))
+            client.log.info(
+                "Client connect to WiFi and verify AP band correct.")
             if not ensure_wifi_connected(log, client, ssid, password):
-                log.error("Client connect to WiFi failed.")
+                client.log.error("Client connect to WiFi failed.")
                 return False
 
             wifi_info = client.droid.wifiGetConnectionInfo()
-            if ap_band == WifiUtils.WIFI_CONFIG_APBAND_5G:
+            if ap_band == WIFI_CONFIG_APBAND_5G:
                 if wifi_info["is_24ghz"]:
-                    log.error("Expected 5g network. WiFi Info: {}".format(
-                        wifi_info))
+                    client.log.error("Expected 5g network. WiFi Info: %s",
+                                     wifi_info)
                     return False
             else:
                 if wifi_info["is_5ghz"]:
-                    log.error("Expected 2g network. WiFi Info: {}".format(
-                        wifi_info))
+                    client.log.error("Expected 2g network. WiFi Info: %s",
+                                     wifi_info)
                     return False
 
-            log.info("Client{} check Internet connection.".format(
-                client.serial))
+            client.log.info("Client check Internet connection.")
             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))
+                    not verify_internet_connection(log, client)):
+                client.log.error("No WiFi Data on client")
                 return False
 
         if not tethering_check_internet_connection(
@@ -205,20 +204,24 @@
     Returns:
         True if no error happened. False otherwise.
     """
-    for i in range(1, check_iteration):
+    for i in range(1, check_iteration + 1):
+        result = True
         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
+        provider.log.info(
+            "Provider check Internet connection after %s seconds.",
+            check_interval * i)
+        if not verify_internet_connection(log, provider):
+            result = False
+            continue
         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
+            client.log.info(
+                "Client check Internet connection after %s seconds.",
+                check_interval * i)
+            if not verify_internet_connection(log, client):
+                result = False
+                break
+        if result: return result
+    return False
 
 
 def wifi_cell_switching(log, ad, wifi_network_ssid, wifi_network_pass, nw_gen):
@@ -244,52 +247,53 @@
     try:
 
         if not ensure_network_generation_for_subscription(
-                log, ad, get_default_data_sub_id(ad), nw_gen,
+                log, ad,
+                get_default_data_sub_id(ad), nw_gen,
                 MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
-            log.error("Device failed to register in {}".format(nw_gen))
+            ad.log.error("Device failed to register in %s", nw_gen)
             return False
 
         # Ensure WiFi can connect to live network
-        log.info("Make sure phone can connect to live network by WIFI")
+        ad.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.")
+            ad.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)
+        wifi_toggle_state(log, ad, True)
         ad.droid.telephonyToggleDataConnection(True)
         if (not wait_for_wifi_data_connection(log, ad, True) or
-                not verify_http_connection(log, ad)):
-            log.error("Data is not on WiFi")
+                not verify_internet_connection(log, ad)):
+            ad.log.error("Data is not on WiFi")
             return False
 
         log.info("Step2 WiFi is Off, Data is on Cell.")
-        WifiUtils.wifi_toggle_state(log, ad, False)
+        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")
+                not verify_internet_connection(log, ad)):
+            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)
+        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")
+                not verify_internet_connection(log, ad)):
+            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)
+        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")
+                not verify_internet_connection(log, ad)):
+            ad.log.error("Data did not return to cell")
             return False
         return True
 
     finally:
-        WifiUtils.wifi_toggle_state(log, ad, False)
+        wifi_toggle_state(log, ad, False)
 
 
 def airplane_mode_test(log, ad):
@@ -313,40 +317,40 @@
 
     try:
         ad.droid.telephonyToggleDataConnection(True)
-        WifiUtils.wifi_toggle_state(log, ad, False)
+        wifi_toggle_state(log, ad, False)
 
         log.info("Step1: ensure attach")
         if not toggle_airplane_mode(log, ad, False):
-            log.error("Failed initial attach")
+            ad.log.error("Failed initial attach")
             return False
-        if not verify_http_connection(log, ad):
-            log.error("Data not available on cell.")
+        if not verify_internet_connection(log, ad):
+            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")
+            ad.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")
+            ad.log.error("Failed to disable cell data connection")
             return False
-        if verify_http_connection(log, ad):
-            log.error("Data available in airplane mode.")
+        if verify_internet_connection(log, ad):
+            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")
+            ad.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")
+            ad.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)
+        return verify_internet_connection(log, ad)
     finally:
         toggle_airplane_mode(log, ad, False)
 
@@ -370,12 +374,15 @@
         False if failed.
     """
     ensure_phones_idle(log, [ad])
-
+    wait_time = MAX_WAIT_TIME_NW_SELECTION
+    if getattr(ad, 'roaming', False):
+        wait_time = 2 * wait_time
     if not ensure_network_generation_for_subscription(
-            log, ad, get_default_data_sub_id(ad), nw_gen,
-            MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
-        log.error("Device failed to reselect in {}s.".format(
-            MAX_WAIT_TIME_NW_SELECTION))
+            log, ad,
+            get_default_data_sub_id(ad), nw_gen, MAX_WAIT_TIME_NW_SELECTION,
+            NETWORK_SERVICE_DATA):
+        ad.log.error("Device failed to connect to %s in %s seconds.", nw_gen,
+                     wait_time)
         return False
 
     try:
@@ -383,42 +390,43 @@
         toggle_airplane_mode(log, ad, False)
         ad.droid.telephonyToggleDataConnection(True)
         if not wait_for_cell_data_connection(log, ad, True):
-            log.error("Failed to enable data connection.")
+            ad.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.")
+        if not verify_internet_connection(log, ad):
+            ad.log.error("Data not available on cell.")
             return False
 
         log.info("Step3 Turn off data and verify not connected.")
         ad.droid.telephonyToggleDataConnection(False)
         if not wait_for_cell_data_connection(log, ad, False):
-            log.error("Step3 Failed to disable data connection.")
+            ad.log.error("Step3 Failed to disable data connection.")
             return False
 
-        if verify_http_connection(log, ad):
-            log.error("Step3 Data still available when disabled.")
+        if verify_internet_connection(log, ad):
+            ad.log.error("Step3 Data still available when disabled.")
             return False
 
         log.info("Step4 Re-enable data.")
         ad.droid.telephonyToggleDataConnection(True)
         if not wait_for_cell_data_connection(log, ad, True):
-            log.error("Step4 failed to re-enable data.")
+            ad.log.error("Step4 failed to re-enable data.")
             return False
-        if not verify_http_connection(log, ad):
-            log.error("Data not available on cell.")
+        if not verify_internet_connection(log, ad):
+            ad.log.error("Data not available on cell.")
             return False
 
         if not is_droid_in_network_generation_for_subscription(
-                log, ad, get_default_data_sub_id(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_rat(
+                log, ad,
+                get_default_data_sub_id(ad), nw_gen, NETWORK_SERVICE_DATA):
+            ad.log.error("Failed: droid is no longer on correct network")
+            ad.log.info(
+                "Expected:%s, Current:%s", nw_gen,
+                rat_generation_from_rat(
                     get_network_rat_for_subscription(
-                        log, ad, get_default_data_sub_id(
-                            ad), NETWORK_SERVICE_DATA))))
+                        log, ad,
+                        get_default_data_sub_id(ad), NETWORK_SERVICE_DATA)))
             return False
         return True
     finally:
@@ -437,13 +445,13 @@
         Data SIM changed successfully, data attached and Internet access is OK.
     """
     sub_id = get_subid_from_slot_index(log, ad, sim_slot)
-    log.info("Change Data to subId: {}, SIM slot: {}".format(sub_id, sim_slot))
+    ad.log.info("Change Data to subId: %s, SIM slot: %s", sub_id, sim_slot)
     set_subid_for_data(ad, sub_id)
     if not wait_for_data_attach_for_subscription(log, ad, sub_id,
                                                  MAX_WAIT_TIME_NW_SELECTION):
-        log.error("Failed to attach data on subId:{}".format(sub_id))
+        ad.log.error("Failed to attach data on subId:%s", sub_id)
         return False
-    if not verify_http_connection(log, ad):
-        log.error("No Internet access after changing Data SIM.")
+    if not verify_internet_connection(log, ad):
+        ad.log.error("No Internet access after changing Data SIM.")
         return False
     return True
diff --git a/acts/framework/acts/test_utils/tel/tel_defines.py b/acts/framework/acts/test_utils/tel/tel_defines.py
index 5a96055..f19737d 100644
--- a/acts/framework/acts/test_utils/tel/tel_defines.py
+++ b/acts/framework/acts/test_utils/tel/tel_defines.py
@@ -21,14 +21,26 @@
 MAX_WAIT_TIME_CONNECTION_STATE_UPDATE = 20
 
 # Max time to wait for network reselection
-MAX_WAIT_TIME_NW_SELECTION = 120
+MAX_WAIT_TIME_NW_SELECTION = 180
 
 # Max time to wait for call drop
 MAX_WAIT_TIME_CALL_DROP = 60
 
+# Wait time between state check retry
+WAIT_TIME_BETWEEN_STATE_CHECK = 5
+
 # Max time to wait after caller make a call and before
 # callee start ringing
-MAX_WAIT_TIME_CALLEE_RINGING = 30
+MAX_WAIT_TIME_CALLEE_RINGING = 90
+
+# country code list
+COUNTRY_CODE_LIST = [
+    "+1", "+44", "+39", "+61", "+49", "+34", "+33", "+47", "+246", "+86",
+    "+850", "+81"
+]
+
+# Wait time after enterring puk code
+WAIT_TIME_SUPPLY_PUK_CODE = 30
 
 # Max time to wait after caller make a call and before
 # callee start ringing
@@ -60,10 +72,10 @@
 # be used for wait after IMS registration.
 
 # Max time to wait for VoLTE enabled flag to be True
-MAX_WAIT_TIME_VOLTE_ENABLED = MAX_WAIT_TIME_IMS_REGISTRATION + 20
+MAX_WAIT_TIME_VOLTE_ENABLED = MAX_WAIT_TIME_IMS_REGISTRATION + 60
 
 # Max time to wait for WFC enabled flag to be True
-MAX_WAIT_TIME_WFC_ENABLED = MAX_WAIT_TIME_IMS_REGISTRATION + 50
+MAX_WAIT_TIME_WFC_ENABLED = MAX_WAIT_TIME_IMS_REGISTRATION + 120
 
 # Max time to wait for WFC enabled flag to be False
 MAX_WAIT_TIME_WFC_DISABLED = 60
@@ -82,7 +94,7 @@
 MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK = 15
 
 # Max time to wait for voice mail count report correct result.
-MAX_WAIT_TIME_VOICE_MAIL_COUNT = 30
+MAX_WAIT_TIME_VOICE_MAIL_COUNT = 90
 
 # Max time to wait for data SIM change
 MAX_WAIT_TIME_DATA_SUB_CHANGE = 150
@@ -176,24 +188,25 @@
 INVALID_WIFI_RSSI = -127
 
 # MAX and MIN value for attenuator settings
-ATTEN_MAX_VALUE = 90
+ATTEN_MAX_VALUE = 95
 ATTEN_MIN_VALUE = 0
 
 MAX_RSSI_RESERVED_VALUE = 100
 MIN_RSSI_RESERVED_VALUE = -200
 
 # cellular weak RSSI value
-CELL_WEAK_RSSI_VALUE = -120
+CELL_WEAK_RSSI_VALUE = -105
 # cellular strong RSSI value
 CELL_STRONG_RSSI_VALUE = -70
 # WiFi weak RSSI value
-WIFI_WEAK_RSSI_VALUE = -80
+WIFI_WEAK_RSSI_VALUE = -63
 
 # Emergency call number
 DEFAULT_EMERGENCY_CALL_NUMBER = "911"
 
-EMERGENCY_CALL_NUMBERS = ["08", "000", "110", "112", "118", "119", "911",
-                          "999", "*911", "#911"]
+EMERGENCY_CALL_NUMBERS = [
+    "08", "000", "110", "112", "118", "119", "911", "999", "*911", "#911"
+]
 
 AOSP_PREFIX = "aosp_"
 
@@ -225,6 +238,12 @@
 CARRIER_EEUK = 'eeuk'
 CARRIER_VFUK = 'vfuk'
 CARRIER_UNKNOWN = 'unknown'
+CARRIER_GMBH = 'gmbh'
+CARRIER_ITA = 'ita'
+CARRIER_ESP = 'esp'
+CARRIER_ORG = 'org'
+CARRIER_TEL = 'tel'
+CARRIER_TSA = 'tsa'
 
 RAT_FAMILY_CDMA = 'cdma'
 RAT_FAMILY_CDMA2000 = 'cdma2000'
@@ -416,6 +435,10 @@
 DATA_STATE_SUSPENDED = "SUSPENDED"
 DATA_STATE_UNKNOWN = "UNKNOWN"
 
+# Constant for Data Roaming State
+DATA_ROAMING_ENABLE = 1
+DATA_ROAMING_DISABLE = 0
+
 # Constant for Telephony Manager Call State
 TELEPHONY_STATE_RINGING = "RINGING"
 TELEPHONY_STATE_IDLE = "IDLE"
@@ -447,6 +470,10 @@
 PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING = "RINGING"
 PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND = "BACKGROUND"
 
+# Constants used to register or de-register for call callback events
+EVENT_CALL_STATE_CHANGED = "EVENT_STATE_CHANGED"
+EVENT_CALL_CHILDREN_CHANGED = "EVENT_CHILDREN_CHANGED"
+
 # Constants used to register or de-register for video call callback events
 EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED = "EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED"
 EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED = "EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED"
@@ -504,7 +531,11 @@
 EventCmasReceived = "CmasReceived"
 EventEtwsReceived = "EtwsReceived"
 
-# Constant for Telecom Call Event Name
+# Constants for Telecom Call Management Event Name (see InCallService.java).
+EventTelecomCallAdded = "TelecomCallAdded"
+EventTelecomCallRemoved = "TelecomCallRemoved"
+
+# Constant for Telecom Call Event Name (see Call.java)
 EventTelecomCallStateChanged = "TelecomCallStateChanged"
 EventTelecomCallParentChanged = "TelecomCallParentChanged"
 EventTelecomCallChildrenChanged = "TelecomCallChildrenChanged"
diff --git a/acts/framework/acts/test_utils/tel/tel_lookup_tables.py b/acts/framework/acts/test_utils/tel/tel_lookup_tables.py
index 274ee7f..a33569c 100644
--- a/acts/framework/acts/test_utils/tel/tel_lookup_tables.py
+++ b/acts/framework/acts/test_utils/tel/tel_lookup_tables.py
@@ -25,9 +25,13 @@
     return _TelTables.technology_tbl[rat_type]['generation']
 
 
-def network_preference_for_generaton(generation, operator):
-    return _TelTables.operator_network_tbl[operator][generation][
-        'network_preference']
+def network_preference_for_generaton(generation, operator, phone_type=None):
+    if not phone_type:
+        return _TelTables.operator_network_tbl[operator][generation][
+            'network_preference']
+    else:
+        return _TelTables.operator_network_tbl_by_phone_type[phone_type][
+            generation]['network_preference']
 
 
 def rat_families_for_network_preference(network_preference):
@@ -35,8 +39,13 @@
         'rat_family_list']
 
 
-def rat_family_for_generation(generation, operator):
-    return _TelTables.operator_network_tbl[operator][generation]['rat_family']
+def rat_family_for_generation(generation, operator, phone_type=None):
+    if not phone_type:
+        return _TelTables.operator_network_tbl[operator][generation][
+            'rat_family']
+    else:
+        return _TelTables.operator_network_tbl_by_phone_type[phone_type][
+            generation]['rat_family']
 
 
 def operator_name_from_plmn_id(plmn_id):
@@ -90,6 +99,10 @@
     return "123"
 
 
+def get_vzw_voice_mail_number():
+    return "*86"
+
+
 # For ATT, get the voice mail number
 def get_att_voice_mail_number():
     return None
@@ -100,16 +113,29 @@
     return None
 
 
+def get_ee_voice_mail_number():
+    return "+447953222222"
+
+
 def get_voice_mail_number_function(operator):
-    return _TelTables.voice_mail_number_get_function_tbl[operator]
+    return _TelTables.voice_mail_number_get_function_tbl.get(operator)
 
 
 def get_voice_mail_count_check_function(operator):
-    return _TelTables.voice_mail_count_check_function_tbl[operator]
+    return _TelTables.voice_mail_count_check_function_tbl.get(
+        operator, check_tmo_voice_mail_count)
 
 
-def get_allowable_network_preference(operator):
-    return _TelTables.allowable_network_preference_tbl[operator]
+def get_voice_mail_delete_digit(operator):
+    return _TelTables.voice_mail_delete_digit_tbl.get(operator, "7")
+
+
+def get_allowable_network_preference(operator, phone_type=None):
+    if not phone_type:
+        return _TelTables.allowable_network_preference_tbl[operator]
+    else:
+        return _TelTables.allowable_network_preference_tbl_by_phone_type[
+            phone_type]
 
 
 class _ConnectionTables():
@@ -203,8 +229,8 @@
         '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
+        '311180': tel_defines.CARRIER_ATT,
+        #Cingular Licensee Pacific Telesis Mobile Services, LLC
 
         #Sprint (and Sprint-Nextel)
         '310120': tel_defines.CARRIER_SPT,
@@ -218,11 +244,39 @@
         '23430': tel_defines.CARRIER_EEUK,  #T-Mobile UK
         '23431': tel_defines.CARRIER_EEUK,  #Virgin Mobile (MVNO)
         '23432': tel_defines.CARRIER_EEUK,  #Virgin Mobile (MVNO)
-        '23415': tel_defines.CARRIER_VFUK
+        '23415': tel_defines.CARRIER_VFUK,
+
+        #Vodafone (Germany)
+        '26202': tel_defines.CARRIER_GMBH,
+        '26204': tel_defines.CARRIER_GMBH,
+        '26209': tel_defines.CARRIER_GMBH,
+        '26242': tel_defines.CARRIER_GMBH,
+        '26243': tel_defines.CARRIER_GMBH,
+
+        #Vodafone (Italy)
+        '22206': tel_defines.CARRIER_ITA,
+        '22210': tel_defines.CARRIER_ITA,
+
+        #Vodafone (Spain)
+        '21401': tel_defines.CARRIER_ESP,
+        '20406': tel_defines.CARRIER_ESP,
+
+        #Orange (France)
+        '20801': tel_defines.CARRIER_ORG,
+        '20802': tel_defines.CARRIER_ORG,
+        '20891': tel_defines.CARRIER_ORG,
+
+        #Telenor (Norway)
+        '24201': tel_defines.CARRIER_TEL,
+        '24212': tel_defines.CARRIER_TEL,
+
+        #Telstra (Australia)
+        '50501': tel_defines.CARRIER_TSA
     }
 
-    technology_gen_tbl = [tel_defines.GEN_2G, tel_defines.GEN_3G,
-                          tel_defines.GEN_4G]
+    technology_gen_tbl = [
+        tel_defines.GEN_2G, tel_defines.GEN_3G, tel_defines.GEN_4G
+    ]
 
     technology_tbl = {
         tel_defines.RAT_1XRTT: {
@@ -390,33 +444,35 @@
 
     network_preference_tbl = {
         tel_defines.NETWORK_MODE_LTE_GSM_WCDMA: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_LTE,
-                                tel_defines.RAT_FAMILY_WCDMA,
-                                tel_defines.RAT_FAMILY_GSM]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_WCDMA,
+                tel_defines.RAT_FAMILY_GSM
+            ]
         },
         tel_defines.NETWORK_MODE_GSM_UMTS: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_WCDMA,
-                                tel_defines.RAT_FAMILY_GSM]
+            'rat_family_list':
+            [tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_GSM]
         },
         tel_defines.NETWORK_MODE_GSM_ONLY: {
             'rat_family_list': [tel_defines.RAT_FAMILY_GSM]
         },
         tel_defines.NETWORK_MODE_LTE_CDMA_EVDO: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_LTE,
-                                tel_defines.RAT_FAMILY_CDMA2000,
-                                tel_defines.RAT_FAMILY_CDMA]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_CDMA2000,
+                tel_defines.RAT_FAMILY_CDMA
+            ]
         },
         tel_defines.NETWORK_MODE_CDMA: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_CDMA2000,
-                                tel_defines.RAT_FAMILY_CDMA]
+            'rat_family_list':
+            [tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA]
         },
         tel_defines.NETWORK_MODE_CDMA_NO_EVDO: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_CDMA2000,
-                                tel_defines.RAT_FAMILY_CDMA]
+            'rat_family_list':
+            [tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA]
         },
         tel_defines.NETWORK_MODE_WCDMA_PREF: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_WCDMA,
-                                tel_defines.RAT_FAMILY_GSM]
+            'rat_family_list':
+            [tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_GSM]
         },
         tel_defines.NETWORK_MODE_WCDMA_ONLY: {
             'rat_family_list': [tel_defines.RAT_FAMILY_WCDMA]
@@ -425,79 +481,88 @@
             'rat_family_list': [tel_defines.RAT_FAMILY_CDMA2000]
         },
         tel_defines.NETWORK_MODE_GLOBAL: {
-            'rat_family_list':
-            [tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_TDSCDMA,
-             tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_GSM,
-             tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_TDSCDMA,
+                tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_GSM,
+                tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA
+            ]
         },
         tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA: {
-            'rat_family_list':
-            [tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_WCDMA,
-             tel_defines.RAT_FAMILY_GSM, tel_defines.RAT_FAMILY_CDMA2000,
-             tel_defines.RAT_FAMILY_CDMA]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_WCDMA,
+                tel_defines.RAT_FAMILY_GSM, tel_defines.RAT_FAMILY_CDMA2000,
+                tel_defines.RAT_FAMILY_CDMA
+            ]
         },
         tel_defines.NETWORK_MODE_LTE_ONLY: {
             'rat_family_list': [tel_defines.RAT_FAMILY_LTE]
         },
         tel_defines.NETWORK_MODE_LTE_WCDMA: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_LTE,
-                                tel_defines.RAT_FAMILY_WCDMA]
+            'rat_family_list':
+            [tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_WCDMA]
         },
         tel_defines.NETWORK_MODE_TDSCDMA_ONLY: {
             'rat_family_list': [tel_defines.RAT_FAMILY_TDSCDMA]
         },
         tel_defines.NETWORK_MODE_TDSCDMA_WCDMA: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_TDSCDMA,
-                                tel_defines.RAT_FAMILY_WCDMA]
+            'rat_family_list':
+            [tel_defines.RAT_FAMILY_TDSCDMA, tel_defines.RAT_FAMILY_WCDMA]
         },
         tel_defines.NETWORK_MODE_LTE_TDSCDMA: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_LTE,
-                                tel_defines.RAT_FAMILY_TDSCDMA]
+            'rat_family_list':
+            [tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_TDSCDMA]
         },
         tel_defines.NETWORK_MODE_TDSCDMA_GSM: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_TDSCDMA,
-                                tel_defines.RAT_FAMILY_GSM]
+            'rat_family_list':
+            [tel_defines.RAT_FAMILY_TDSCDMA, tel_defines.RAT_FAMILY_GSM]
         },
         tel_defines.NETWORK_MODE_LTE_TDSCDMA_GSM: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_LTE,
-                                tel_defines.RAT_FAMILY_TDSCDMA,
-                                tel_defines.RAT_FAMILY_GSM]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_TDSCDMA,
+                tel_defines.RAT_FAMILY_GSM
+            ]
         },
         tel_defines.NETWORK_MODE_TDSCDMA_GSM_WCDMA: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_WCDMA,
-                                tel_defines.RAT_FAMILY_TDSCDMA,
-                                tel_defines.RAT_FAMILY_GSM]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+                tel_defines.RAT_FAMILY_GSM
+            ]
         },
         tel_defines.NETWORK_MODE_LTE_TDSCDMA_WCDMA: {
-            'rat_family_list': [tel_defines.RAT_FAMILY_WCDMA,
-                                tel_defines.RAT_FAMILY_TDSCDMA,
-                                tel_defines.RAT_FAMILY_LTE]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+                tel_defines.RAT_FAMILY_LTE
+            ]
         },
         tel_defines.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA: {
-            'rat_family_list':
-            [tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
-             tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_GSM]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+                tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_GSM
+            ]
         },
         tel_defines.NETWORK_MODE_TDSCDMA_CDMA_EVDO_WCDMA: {
-            'rat_family_list':
-            [tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
-             tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+                tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA
+            ]
         },
         tel_defines.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: {
-            'rat_family_list':
-            [tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
-             tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_GSM,
-             tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA]
+            'rat_family_list': [
+                tel_defines.RAT_FAMILY_WCDMA, tel_defines.RAT_FAMILY_TDSCDMA,
+                tel_defines.RAT_FAMILY_LTE, tel_defines.RAT_FAMILY_GSM,
+                tel_defines.RAT_FAMILY_CDMA2000, tel_defines.RAT_FAMILY_CDMA
+            ]
         }
     }
     default_umts_operator_network_tbl = {
         tel_defines.GEN_4G: {
             'rat_family': tel_defines.RAT_FAMILY_LTE,
-            'network_preference': tel_defines.NETWORK_MODE_LTE_GSM_WCDMA
+            'network_preference':
+            tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
         },
         tel_defines.GEN_3G: {
             'rat_family': tel_defines.RAT_FAMILY_WCDMA,
-            'network_preference': tel_defines.NETWORK_MODE_GSM_UMTS
+            'network_preference': tel_defines.NETWORK_MODE_WCDMA_ONLY
         },
         tel_defines.GEN_2G: {
             'rat_family': tel_defines.RAT_FAMILY_GSM,
@@ -507,7 +572,8 @@
     default_cdma_operator_network_tbl = {
         tel_defines.GEN_4G: {
             'rat_family': tel_defines.RAT_FAMILY_LTE,
-            'network_preference': tel_defines.NETWORK_MODE_LTE_CDMA_EVDO
+            'network_preference':
+            tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
         },
         tel_defines.GEN_3G: {
             'rat_family': tel_defines.RAT_FAMILY_CDMA2000,
@@ -524,16 +590,31 @@
         tel_defines.CARRIER_VZW: default_cdma_operator_network_tbl,
         tel_defines.CARRIER_SPT: default_cdma_operator_network_tbl,
         tel_defines.CARRIER_EEUK: default_umts_operator_network_tbl,
-        tel_defines.CARRIER_VFUK: default_umts_operator_network_tbl
+        tel_defines.CARRIER_VFUK: default_umts_operator_network_tbl,
+        tel_defines.CARRIER_GMBH: default_umts_operator_network_tbl,
+        tel_defines.CARRIER_ITA: default_umts_operator_network_tbl,
+        tel_defines.CARRIER_ESP: default_umts_operator_network_tbl,
+        tel_defines.CARRIER_ORG: default_umts_operator_network_tbl,
+        tel_defines.CARRIER_TEL: default_umts_operator_network_tbl,
+        tel_defines.CARRIER_TSA: default_umts_operator_network_tbl
+    }
+    operator_network_tbl_by_phone_type = {
+        tel_defines.PHONE_TYPE_GSM: default_umts_operator_network_tbl,
+        tel_defines.PHONE_TYPE_CDMA: default_cdma_operator_network_tbl
     }
 
     umts_allowable_network_preference_tbl = \
-        [tel_defines.NETWORK_MODE_LTE_GSM_WCDMA,
+        [tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+         tel_defines.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+         tel_defines.NETWORK_MODE_LTE_GSM_WCDMA,
          tel_defines.NETWORK_MODE_WCDMA_PREF,
+         tel_defines.NETWORK_MODE_WCDMA_ONLY,
          tel_defines.NETWORK_MODE_GSM_ONLY]
 
     cdma_allowable_network_preference_tbl = \
-        [tel_defines.NETWORK_MODE_LTE_CDMA_EVDO,
+        [tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+         tel_defines.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+         tel_defines.NETWORK_MODE_LTE_CDMA_EVDO,
          tel_defines.NETWORK_MODE_CDMA,
          tel_defines.NETWORK_MODE_CDMA_NO_EVDO,
          tel_defines.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA]
@@ -546,11 +627,17 @@
         tel_defines.CARRIER_EEUK: umts_allowable_network_preference_tbl,
         tel_defines.CARRIER_VFUK: umts_allowable_network_preference_tbl
     }
+    allowable_network_preference_tbl_by_phone_type = {
+        tel_defines.PHONE_TYPE_GSM: umts_allowable_network_preference_tbl,
+        tel_defines.PHONE_TYPE_CDMA: cdma_allowable_network_preference_tbl
+    }
 
     voice_mail_number_get_function_tbl = {
         tel_defines.CARRIER_TMO: get_tmo_voice_mail_number,
+        tel_defines.CARRIER_VZW: get_vzw_voice_mail_number,
         tel_defines.CARRIER_ATT: get_att_voice_mail_number,
-        tel_defines.CARRIER_SPT: get_spt_voice_mail_number
+        tel_defines.CARRIER_SPT: get_spt_voice_mail_number,
+        tel_defines.CARRIER_EEUK: get_ee_voice_mail_number
     }
 
     voice_mail_count_check_function_tbl = {
@@ -559,48 +646,52 @@
         tel_defines.CARRIER_SPT: check_spt_voice_mail_count
     }
 
+    voice_mail_delete_digit_tbl = {tel_defines.CARRIER_EEUK: "3"}
+
 
 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_OMADM,
-     tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC],
-    NexusModelNames.N6: [tel_defines.CAPABILITY_PHONE,
-                         tel_defines.CAPABILITY_OMADM,
-                         tel_defines.CAPABILITY_VOLTE,
-                         tel_defines.CAPABILITY_WFC],
-    NexusModelNames.N6v2: [tel_defines.CAPABILITY_PHONE,
-                           tel_defines.CAPABILITY_OMADM,
-                           tel_defines.CAPABILITY_VOLTE,
-                           tel_defines.CAPABILITY_WFC],
-    NexusModelNames.N5v3: [tel_defines.CAPABILITY_PHONE,
-                           tel_defines.CAPABILITY_OMADM,
-                           tel_defines.CAPABILITY_VOLTE,
-                           tel_defines.CAPABILITY_WFC,
-                           tel_defines.CAPABILITY_VT],
-    NexusModelNames.N6v3: [tel_defines.CAPABILITY_PHONE,
-                           tel_defines.CAPABILITY_OMADM,
-                           tel_defines.CAPABILITY_VOLTE,
-                           tel_defines.CAPABILITY_WFC,
-                           tel_defines.CAPABILITY_VT]
+    NexusModelNames.N5v2: [
+        tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+        tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC
+    ],
+    NexusModelNames.N6: [
+        tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+        tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC
+    ],
+    NexusModelNames.N6v2: [
+        tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+        tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC
+    ],
+    NexusModelNames.N5v3: [
+        tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+        tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
+        tel_defines.CAPABILITY_VT
+    ],
+    NexusModelNames.N6v3: [
+        tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+        tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
+        tel_defines.CAPABILITY_VT
+    ]
 }
 
 operator_capabilities = {
-    tel_defines.CARRIER_VZW: [tel_defines.CAPABILITY_PHONE,
-                              tel_defines.CAPABILITY_OMADM,
-                              tel_defines.CAPABILITY_VOLTE,
-                              tel_defines.CAPABILITY_WFC,
-                              tel_defines.CAPABILITY_VT],
+    tel_defines.CARRIER_VZW: [
+        tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_OMADM,
+        tel_defines.CAPABILITY_VOLTE, tel_defines.CAPABILITY_WFC,
+        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.CAPABILITY_VT],
+    tel_defines.CARRIER_TMO: [
+        tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE,
+        tel_defines.CAPABILITY_WFC, tel_defines.CAPABILITY_VT
+    ],
     tel_defines.CARRIER_SPT: [tel_defines.CAPABILITY_PHONE],
-    tel_defines.CARRIER_EEUK: [tel_defines.CAPABILITY_PHONE,
-                              tel_defines.CAPABILITY_VOLTE,
-                              tel_defines.CAPABILITY_WFC],
+    tel_defines.CARRIER_EEUK: [
+        tel_defines.CAPABILITY_PHONE, tel_defines.CAPABILITY_VOLTE,
+        tel_defines.CAPABILITY_WFC
+    ],
     tel_defines.CARRIER_VFUK: [tel_defines.CAPABILITY_PHONE]
 }
diff --git a/acts/framework/acts/test_utils/tel/tel_test_utils.py b/acts/framework/acts/test_utils/tel/tel_test_utils.py
index 8e67dc1..c7960a9 100644
--- a/acts/framework/acts/test_utils/tel/tel_test_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_test_utils.py
@@ -18,16 +18,24 @@
 standard_library.install_aliases()
 
 import concurrent.futures
+import json
+import logging
+import os
 import urllib.parse
 import time
 
 from queue import Empty
+from acts.asserts import abort_all
+from acts.controllers.adb import AdbError
 from acts.controllers.android_device import AndroidDevice
 from acts.controllers.event_dispatcher import EventDispatcher
 from acts.test_utils.tel.tel_defines import AOSP_PREFIX
 from acts.test_utils.tel.tel_defines import CARRIER_UNKNOWN
+from acts.test_utils.tel.tel_defines import COUNTRY_CODE_LIST
 from acts.test_utils.tel.tel_defines import DATA_STATE_CONNECTED
 from acts.test_utils.tel.tel_defines import DATA_STATE_DISCONNECTED
+from acts.test_utils.tel.tel_defines import DATA_ROAMING_ENABLE
+from acts.test_utils.tel.tel_defines import DATA_ROAMING_DISABLE
 from acts.test_utils.tel.tel_defines import GEN_4G
 from acts.test_utils.tel.tel_defines import GEN_UNKNOWN
 from acts.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_BACKGROUND
@@ -72,12 +80,14 @@
 from acts.test_utils.tel.tel_defines import SERVICE_STATE_OUT_OF_SERVICE
 from acts.test_utils.tel.tel_defines import SERVICE_STATE_POWER_OFF
 from acts.test_utils.tel.tel_defines import SIM_STATE_READY
+from acts.test_utils.tel.tel_defines import WAIT_TIME_SUPPLY_PUK_CODE
 from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_IDLE
 from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_OFFHOOK
 from acts.test_utils.tel.tel_defines import TELEPHONY_STATE_RINGING
 from acts.test_utils.tel.tel_defines import VOICEMAIL_DELETE_DIGIT
 from acts.test_utils.tel.tel_defines import WAIT_TIME_1XRTT_VOICE_ATTACH
 from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
 from acts.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID
 from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
 from acts.test_utils.tel.tel_defines import WAIT_TIME_LEAVE_VOICE_MAIL
@@ -106,6 +116,7 @@
 from acts.test_utils.tel.tel_lookup_tables import \
     get_voice_mail_count_check_function
 from acts.test_utils.tel.tel_lookup_tables import get_voice_mail_number_function
+from acts.test_utils.tel.tel_lookup_tables import get_voice_mail_delete_digit
 from acts.test_utils.tel.tel_lookup_tables import \
     network_preference_for_generaton
 from acts.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
@@ -124,75 +135,134 @@
     get_incoming_voice_sub_id
 from acts.test_utils.tel.tel_subscription_utils import \
     get_incoming_message_sub_id
+from acts.test_utils.wifi import wifi_test_utils
+from acts.test_utils.wifi import wifi_constants
+from acts.utils import adb_shell_ping
 from acts.utils import load_config
-from acts.logger import LoggerProxy
-log = LoggerProxy()
+
+WIFI_SSID_KEY = wifi_test_utils.WifiEnums.SSID_KEY
+WIFI_PWD_KEY = wifi_test_utils.WifiEnums.PWD_KEY
+WIFI_CONFIG_APBAND_2G = wifi_test_utils.WifiEnums.WIFI_CONFIG_APBAND_2G
+WIFI_CONFIG_APBAND_5G = wifi_test_utils.WifiEnums.WIFI_CONFIG_APBAND_5G
+log = logging
+
+
+class _CallSequenceException(Exception):
+    pass
 
 
 class TelTestUtilsError(Exception):
     pass
 
 
-def setup_droid_properties(log, ad, sim_filename):
+def abort_all_tests(log, msg):
+    log.error("Aborting all ongoing tests due to: %s.", msg)
+    abort_all(msg)
+
+
+def get_phone_number_by_adb(ad):
+    return phone_number_formatter(
+        ad.adb.shell("service call iphonesubinfo 13"))
+
+
+def get_iccid_by_adb(ad):
+    return ad.adb.shell("service call iphonesubinfo 11")
+
+
+def get_operator_by_adb(ad):
+    return ad.adb.getprop("gsm.sim.operator.alpha")
+
+
+def get_sub_id_by_adb(ad):
+    return ad.adb.shell("service call iphonesubinfo 5")
+
+
+def setup_droid_properties_by_adb(log, ad, sim_filename=None):
 
     # Check to see if droid already has this property
     if hasattr(ad, 'cfg'):
         return
 
+    sim_data = None
+    if sim_filename:
+        try:
+            sim_data = load_config(sim_filename)
+        except Exception:
+            log.warning("Failed to load %s!", sim_filename)
+
+    sub_id = get_sub_id_by_adb(ad)
+    if iccid in sim_data and sim_data[iccid].get("phone_num"):
+        phone_number = phone_number_formatter(sim_data[iccid]["phone_num"])
+    else:
+        phone_number = get_phone_number_by_adb(ad)
+    if not phone_number:
+        abort_all_tests(ad.log, "Failed to find valid phone number")
+    sim_record = {
+        'phone_num': phone_number,
+        'iccid': get_iccid_by_adb(ad),
+        'sim_operator_name': get_operator_by_adb(ad)
+    }
+    device_props = {'subscription': {sub_id: sim_record}}
+    ad.log.info("subId %s SIM record: %s", sub_id, sim_record)
+    setattr(ad, 'cfg', device_props)
+
+
+def setup_droid_properties(log, ad, sim_filename=None):
+
+    # Check to see if droid already has this property
+    if hasattr(ad, 'cfg'):
+        return
+
+    if ad.skip_sl4a:
+        return setup_droid_properties_by_adb(
+            log, ad, sim_filename=sim_filename)
+
+    refresh_droid_config(log, ad)
     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.telephonyGetSimSerialNumberForSubscription(
-                    sub_id)
-                if not sim_serial:
-                    ad.log.error("Unable to find ICC-ID for SIM.")
-                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.telephonyGetLine1NumberForSubscription(
-                    sub_id)
-            except Exception as e:
-                log.error("Failed to setup_droid_property with {}".format(e))
-                raise
-            if not number or number == "":
-                raise TelTestUtilsError(
-                    "Failed to find valid phone number for {}"
-                    .format(ad.serial))
+    sim_data = {}
+    if sim_filename:
+        try:
+            sim_data = load_config(sim_filename)
+        except Exception:
+            log.warning("Failed to load %s!", sim_filename)
+    if not ad.cfg["subscription"]:
+        abort_all_tests(ad.log, "No Valid SIMs found in device")
+    for sub_id, sub_info in ad.cfg["subscription"].items():
+        sub_info["operator"] = get_operator_name(log, ad, sub_id)
+        iccid = sub_info["iccid"]
+        if not iccid:
+            ad.log.warn("Unable to find ICC-ID for SIM")
+            continue
+        if not sub_info["phone_num"]:
+            if iccid not in sim_data or not sim_data[iccid].get("phone_num"):
+                abort_all_tests(
+                    ad.log,
+                    "Failed to find valid phone number for ICCID %s in %s" %
+                    (iccid, sim_filename))
+            else:
+                sub_info["phone_num"] = sim_data[iccid]["phone_num"]
+        elif sim_data.get(iccid) and sim_data[iccid].get("phone_num"):
+            if not check_phone_number_match(sub_info["phone_num"],
+                                            sim_data[iccid]["phone_num"]):
+                ad.log.error(
+                    "ICCID %s phone number is %s in %s, does not match "
+                    "the number %s retrieved from the phone", iccid,
+                    sim_data[iccid]["phone_num"], sim_filename,
+                    sub_info["phone_num"])
+            sub_info["phone_num"] = sim_data[iccid]["phone_num"]
+        if sub_info["sim_operator_name"] != sub_info["network_operator_name"]:
+            setattr(ad, 'roaming', True)
+    if getattr(ad, 'roaming', False):
+        ad.log.info("Enable cell data roaming")
+        toggle_cell_data_roaming(ad, True)
 
-            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.
-                    telephonyGetSimSerialNumberForSubscription(sub_id)))
-
-    if found_sims == 0:
-        ad.log.warning("No Valid SIMs found in device")
-
-    setattr(ad, 'cfg', device_props)
+    ad.log.info("cfg = %s", ad.cfg)
 
 
 def refresh_droid_config(log, ad):
     """ Update Android Device cfg records for each sub_id.
-    1. Update Phone Number using Line1Number (if Line1Number is valid).
-    2. Update Operator name.
 
     Args:
         log: log object
@@ -201,17 +271,53 @@
     Returns:
         None
     """
-    if not hasattr(ad, 'cfg'):
-        return
-    for sub_id in ad.cfg['subscription']:
-        # Update Phone number
-        number = ad.droid.telephonyGetLine1NumberForSubscription(sub_id)
-        if number:
-            number = phone_number_formatter(number)
-            ad.cfg['subscription'][sub_id]['phone_num'] = number
-        # Update Operator Name
-        ad.cfg['subscription'][sub_id]['operator'] = get_operator_name(log, ad,
-                                                                       sub_id)
+    cfg = {"subscription": {}}
+    droid = ad.droid
+    sub_info_list = droid.subscriptionGetAllSubInfoList()
+    for sub_info in sub_info_list:
+        sub_id = sub_info["subscriptionId"]
+        sim_slot = sub_info["simSlotIndex"]
+        if sim_slot != INVALID_SIM_SLOT_INDEX:
+            sim_serial = droid.telephonyGetSimSerialNumberForSubscription(
+                sub_id)
+            if not sim_serial:
+                ad.log.error("Unable to find ICC-ID for SIM in slot %s",
+                             sim_slot)
+            sim_record = {}
+            sim_record["iccid"] = sim_serial
+            sim_record["sim_slot"] = sim_slot
+            try:
+                sim_record[
+                    "phone_type"] = droid.telephonyGetPhoneTypeForSubscription(
+                        sub_id)
+            except:
+                sim_record["phone_type"] = droid.telephonyGetPhoneType()
+            sim_record[
+                "sim_plmn"] = droid.telephonyGetSimOperatorForSubscription(
+                    sub_id)
+            sim_record[
+                "sim_operator_name"] = droid.telephonyGetSimOperatorNameForSubscription(
+                    sub_id)
+            sim_record[
+                "network_plmn"] = droid.telephonyGetNetworkOperatorForSubscription(
+                    sub_id)
+            sim_record[
+                "network_operator_name"] = droid.telephonyGetNetworkOperatorNameForSubscription(
+                    sub_id)
+            sim_record[
+                "network_type"] = droid.telephonyGetNetworkTypeForSubscription(
+                    sub_id)
+            sim_record[
+                "sim_country"] = droid.telephonyGetSimCountryIsoForSubscription(
+                    sub_id)
+            sim_record["phone_num"] = phone_number_formatter(
+                droid.telephonyGetLine1NumberForSubscription(sub_id))
+            sim_record[
+                "phone_tag"] = droid.telephonyGetLine1AlphaTagForSubscription(
+                    sub_id)
+            cfg['subscription'][sub_id] = sim_record
+            ad.log.info("SubId %s SIM record: %s", sub_id, sim_record)
+    setattr(ad, 'cfg', cfg)
 
 
 def get_slot_index_from_subid(log, ad, sub_id):
@@ -244,6 +350,31 @@
     return len(valid_sims.keys())
 
 
+def toggle_airplane_mode_by_adb(log, ad, new_state=None):
+    """ Toggle the state of airplane mode.
+
+    Args:
+        log: log handler.
+        ad: android_device object.
+        new_state: Airplane mode state to set to.
+            If None, opposite of the current state.
+        strict_checking: Whether to turn on strict checking that checks all features.
+
+    Returns:
+        result: True if operation succeed. False if error happens.
+    """
+    cur_state = bool(int(ad.adb.shell("settings get global airplane_mode_on")))
+    if new_state == cur_state:
+        ad.log.info("Airplane mode already in %s", new_state)
+        return True
+    elif new_state is None:
+        new_state = not cur_state
+
+    ad.adb.shell("settings put global airplane_mode_on %s" % int(new_state))
+    ad.adb.shell("am broadcast -a android.intent.action.AIRPLANE_MODE")
+    return True
+
+
 def toggle_airplane_mode(log, ad, new_state=None, strict_checking=True):
     """ Toggle the state of airplane mode.
 
@@ -257,8 +388,11 @@
     Returns:
         result: True if operation succeed. False if error happens.
     """
-    return toggle_airplane_mode_msim(log, ad, new_state,
-                                     strict_checking=strict_checking)
+    if ad.skip_sl4a:
+        return toggle_airplane_mode_by_adb(log, ad, new_state)
+    else:
+        return toggle_airplane_mode_msim(
+            log, ad, new_state, strict_checking=strict_checking)
 
 
 def is_expected_event(event_to_check, events_list):
@@ -340,8 +474,8 @@
         if bt_state == state:
             return True
         if max_wait <= 0:
-            log.error("Time out: bluetooth state still {}, expecting {}".
-                      format(bt_state, state))
+            ad.log.error("Time out: bluetooth state still %s, expecting %s",
+                         bt_state, state)
             return False
 
         event = {
@@ -351,8 +485,8 @@
         ad.ed.pop_event(event, max_wait)
         return True
     except Empty:
-        log.error("Time out: bluetooth state still {}, expecting {}".format(
-            bt_state, state))
+        ad.log.error("Time out: bluetooth state still in %s, expecting %s",
+                     bt_state, state)
         return False
     finally:
         ad.droid.bluetoothStopListeningForAdapterStateChange()
@@ -389,10 +523,10 @@
 
     cur_state = ad.droid.connectivityCheckAirplaneMode()
     if cur_state == new_state:
-        ad.log.info("Airplane mode already <{}>".format(new_state))
+        ad.log.info("Airplane mode already in %s", new_state)
         return True
     elif new_state is None:
-        log.info("Current State {} New state {}".format(cur_state, new_state))
+        ad.log.info("APM Current State %s New state %s", cur_state, new_state)
 
     if new_state is None:
         new_state = not cur_state
@@ -409,7 +543,7 @@
         # NO SIM, or Dead SIM, or no Roaming coverage.
         service_state_list.append(SERVICE_STATE_OUT_OF_SERVICE)
         service_state_list.append(SERVICE_STATE_EMERGENCY_ONLY)
-        ad.log.info("Turn off airplane mode.")
+        ad.log.info("Turn off airplane mode")
 
     for sub_id in sub_id_list:
         ad.droid.telephonyStartTrackingServiceStateChangeForSubscription(
@@ -431,11 +565,11 @@
         except Empty:
             pass
         if event is None:
-            ad.log.error("Did not get expected service state {}".format(
-                service_state_list))
+            ad.log.error("Did not get expected service state %s",
+                         service_state_list)
             return False
         else:
-            ad.log.info("Received event: {}".format(event))
+            ad.log.info("Received event: %s", event)
     finally:
         for sub_id in sub_id_list:
             ad.droid.telephonyStopTrackingServiceStateChangeForSubscription(
@@ -445,23 +579,22 @@
     try:
         if new_state and not _wait_for_bluetooth_in_state(
                 log, ad, False, timeout_time - time.time()):
-            log.error(
-                "Failed waiting for bluetooth during airplane mode toggle on {}".
-                format(ad.serial))
+            ad.log.error(
+                "Failed waiting for bluetooth during airplane mode toggle")
             if strict_checking: return False
     except Exception as e:
-        log.error("Failed to check bluetooth state due to {}".format(e))
+        ad.log.error("Failed to check bluetooth state due to %s", e)
         if strict_checking:
             raise
 
     # APM on (new_state=True) will turn off wifi but may not turn it on
     if new_state and not _wait_for_wifi_in_state(log, ad, False,
                                                  timeout_time - time.time()):
-        ad.log.error("Failed waiting for wifi during airplane mode toggle.")
-        return False
+        ad.log.error("Failed waiting for wifi during airplane mode toggle on")
+        if strict_checking: return False
 
     if ad.droid.connectivityCheckAirplaneMode() != new_state:
-        ad.log.error("Airplane mode {} failed".format("ON" if new_state else "OFF"))
+        ad.log.error("Set airplane mode to %s failed", new_state)
         return False
     return True
 
@@ -469,7 +602,8 @@
 def wait_and_answer_call(log,
                          ad,
                          incoming_number=None,
-                         incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+                         incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+                         caller=None):
     """Wait for an incoming call on default voice subscription and
        accepts the call.
 
@@ -488,8 +622,12 @@
         False: for errors
         """
     return wait_and_answer_call_for_subscription(
-        log, ad, get_incoming_voice_sub_id(ad), incoming_number,
-        incall_ui_display)
+        log,
+        ad,
+        get_incoming_voice_sub_id(ad),
+        incoming_number,
+        incall_ui_display=incall_ui_display,
+        caller=caller)
 
 
 def wait_for_ringing_event(log, ad, wait_time):
@@ -509,8 +647,6 @@
         event_ringing if received ringing event.
         otherwise return None.
     """
-    log.info("Wait for ringing.")
-    start_time = time.time()
     event_ringing = None
 
     try:
@@ -520,12 +656,9 @@
             timeout=wait_time,
             field=CallStateContainer.CALL_STATE,
             value=TELEPHONY_STATE_RINGING)
+        ad.log.info("Receive ringing event")
     except Empty:
-        if ad.droid.telecomIsRinging():
-            log.error("No Ringing event. But Callee in Ringing state.")
-            log.error("Event may have been dropped.")
-        else:
-            log.error("No Ringing Event, Callee not in ringing state.")
+        ad.log.info("No Ringing Event")
     finally:
         return event_ringing
 
@@ -548,48 +681,92 @@
         log, ad, get_incoming_voice_sub_id(ad), incoming_number)
 
 
-def wait_for_ringing_call_for_subscription(log,
-                                           ad,
-                                           sub_id,
-                                           incoming_number=None):
+def wait_for_ringing_call_for_subscription(
+        log,
+        ad,
+        sub_id,
+        incoming_number=None,
+        caller=None,
+        event_tracking_started=False,
+        timeout=MAX_WAIT_TIME_CALLEE_RINGING):
     """Wait for an incoming call on specified subscription.
 
     Args:
         log: log object.
         ad: android device object.
         sub_id: subscription ID
-        incoming_number: Expected incoming number.
-            Optional. Default is None
+        incoming_number: Expected incoming number. Default is None
+        event_tracking_started: True if event tracking already state outside
+        timeout: time to wait for ring
 
     Returns:
         True: if incoming call is received and answered successfully.
         False: for errors
     """
-    if (not ad.droid.telecomIsRinging() and
-            ad.droid.telephonyGetCallStateForSubscription(sub_id) !=
-            TELEPHONY_STATE_RINGING):
+    if not event_tracking_started:
         ad.ed.clear_all_events()
         ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
-        event_ringing = _wait_for_ringing_event(log, ad,
-                                                MAX_WAIT_TIME_CALLEE_RINGING)
+    event_ringing = _wait_for_ringing_event(log, ad, timeout)
+    if not event_tracking_started:
         ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
-        if event_ringing is None:
-            log.error("No Ringing Event.")
-            return False
+    if caller and not caller.droid.telecomIsInCall():
+        caller.log.error("Caller not in call state")
+        raise _CallSequenceException("Caller not in call state")
+    if not event_ringing and not (
+            ad.droid.telephonyGetCallStateForSubscription(sub_id) ==
+            TELEPHONY_STATE_RINGING or ad.droid.telecomIsRinging()):
+        ad.log.info("Not in call ringing state")
+        return False
+    if not incoming_number:
+        return True
 
-        if not incoming_number:
-            result = True
-        else:
-            result = check_phone_number_match(
-                event_ringing['data'][CallStateContainer.INCOMING_NUMBER],
-                incoming_number)
+    result = check_phone_number_match(
+        event_ringing['data'][CallStateContainer.INCOMING_NUMBER],
+        incoming_number)
+    if not result:
+        ad.log.error(
+            "Incoming Number not match. Expected number:%s, actual number:%s",
+            incoming_number,
+            event_ringing['data'][CallStateContainer.INCOMING_NUMBER])
+        return False
+    return True
 
-        if not result:
-            log.error("Incoming Number not match")
-            log.error("Expected number:{}, actual number:{}".format(
-                incoming_number, event_ringing['data'][
-                    CallStateContainer.INCOMING_NUMBER]))
-            return False
+
+def wait_for_call_offhook_event(
+        log,
+        ad,
+        sub_id,
+        event_tracking_started=False,
+        timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT):
+    """Wait for an incoming call on specified subscription.
+
+    Args:
+        log: log object.
+        ad: android device object.
+        event_tracking_started: True if event tracking already state outside
+        timeout: time to wait for event
+
+    Returns:
+        True: if call offhook event is received.
+        False: if call offhook event is not received.
+    """
+    if not event_tracking_started:
+        ad.ed.clear_all_events()
+        ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
+    try:
+        ad.ed.wait_for_event(
+            EventCallStateChanged,
+            is_event_match,
+            timeout=timeout,
+            field=CallStateContainer.CALL_STATE,
+            value=TELEPHONY_STATE_OFFHOOK)
+    except Empty:
+        ad.log.info("No event for call state change to OFFHOOK")
+        return False
+    finally:
+        if not event_tracking_started:
+            ad.droid.telephonyStopTrackingCallStateChangeForSubscription(
+                sub_id)
     return True
 
 
@@ -598,7 +775,9 @@
         ad,
         sub_id,
         incoming_number=None,
-        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND):
+        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+        timeout=MAX_WAIT_TIME_CALLEE_RINGING,
+        caller=None):
     """Wait for an incoming call on specified subscription and
        accepts the call.
 
@@ -618,37 +797,42 @@
         True: if incoming call is received and answered successfully.
         False: for errors
     """
-
-    if not wait_for_ringing_call_for_subscription(log, ad, sub_id,
-                                                  incoming_number):
-        log.error("Could not answer a call: phone never rang.")
-        return False
-
     ad.ed.clear_all_events()
     ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
-    if not wait_for_telecom_ringing(log, ad, MAX_WAIT_TIME_TELECOM_RINGING):
-        log.error("Telecom is not ringing.")
-        return False
-    log.info("Accept on callee.")
-    ad.droid.telecomAcceptRingingCall()
     try:
-        ad.ed.wait_for_event(
-            EventCallStateChanged,
-            is_event_match,
-            timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
-            field=CallStateContainer.CALL_STATE,
-            value=TELEPHONY_STATE_OFFHOOK)
-    except Empty:
-        if not ad.droid.telecomIsInCall():
-            log.error("Accept call failed.")
+        if not _wait_for_droid_in_state(
+                log,
+                ad,
+                timeout,
+                wait_for_ringing_call_for_subscription,
+                sub_id,
+                incoming_number=None,
+                caller=caller,
+                event_tracking_started=True,
+                timeout=WAIT_TIME_BETWEEN_STATE_CHECK):
+            ad.log.info("Could not answer a call: phone never rang.")
             return False
+        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+        ad.log.info("Accept the ring call")
+        ad.droid.telecomAcceptRingingCall()
+
+        if ad.droid.telecomIsInCall() or wait_for_call_offhook_event(
+                log, ad, sub_id, event_tracking_started=True,
+                timeout=timeout) or ad.droid.telecomIsInCall():
+            ad.log.info("Call answered successfully.")
+            return True
+        else:
+            ad.log.error("Could not answer the call.")
+            return False
+    except Exception as e:
+        log.error(e)
+        return False
     finally:
         ad.droid.telephonyStopTrackingCallStateChangeForSubscription(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
+        if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
+            ad.droid.telecomShowInCallScreen()
+        elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
+            ad.droid.showHomeScreen()
 
 
 def wait_and_reject_call(log,
@@ -672,8 +856,8 @@
         False: for errors
     """
     return wait_and_reject_call_for_subscription(
-        log, ad, get_incoming_voice_sub_id(ad), incoming_number, delay_reject,
-        reject)
+        log, ad,
+        get_incoming_voice_sub_id(ad), incoming_number, delay_reject, reject)
 
 
 def wait_and_reject_call_for_subscription(log,
@@ -701,7 +885,7 @@
 
     if not wait_for_ringing_call_for_subscription(log, ad, sub_id,
                                                   incoming_number):
-        log.error("Could not reject a call: phone never rang.")
+        ad.log.error("Could not reject a call: phone never rang.")
         return False
 
     ad.ed.clear_all_events()
@@ -709,7 +893,6 @@
     if reject is True:
         # Delay between ringing and reject.
         time.sleep(delay_reject)
-        log.info("Reject on callee.")
         is_find = False
         # Loop the call list and find the matched one to disconnect.
         for call in ad.droid.telecomCallGetCallIds():
@@ -717,13 +900,14 @@
                     get_number_from_tel_uri(get_call_uri(ad, call)),
                     incoming_number):
                 ad.droid.telecomCallDisconnect(call)
+                ad.log.info("Callee reject the call")
                 is_find = True
         if is_find is False:
-            log.error("Did not find matching call to reject.")
+            ad.log.error("Callee did not find matching call to reject.")
             return False
     else:
         # don't reject on callee. Just ignore the incoming call.
-        log.info("Received incoming call. Ignore it.")
+        ad.log.info("Callee received incoming call. Ignore it.")
     try:
         ad.ed.wait_for_event(
             EventCallStateChanged,
@@ -732,7 +916,7 @@
             field=CallStateContainer.CALL_STATE,
             value_list=[TELEPHONY_STATE_IDLE, TELEPHONY_STATE_OFFHOOK])
     except Empty:
-        log.error("No onCallStateChangedIdle event received.")
+        ad.log.error("No onCallStateChangedIdle event received.")
         return False
     finally:
         ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
@@ -803,8 +987,7 @@
     """
     if number is None:
         return None, None
-    country_code_list = ["+1", "+44"]
-    for country_code in country_code_list:
+    for country_code in COUNTRY_CODE_LIST:
         if number.startswith(country_code):
             return number[len(country_code):], country_code
     if number[0] == "1" or number[0] == "0":
@@ -840,19 +1023,24 @@
     Returns:
         True if two phone numbers match. Otherwise False.
     """
-    # Remove country code and prefix
-    number1, country_code1 = _phone_number_remove_prefix(number1)
-    number2, country_code2 = _phone_number_remove_prefix(number2)
-    if ((country_code1 is not None) and (country_code2 is not None) and
-        (country_code1 != country_code2)):
-        return False
-    # Remove white spaces, dashes, dots
     number1 = phone_number_formatter(number1)
     number2 = phone_number_formatter(number2)
-    return number1 == number2
+    # Handle extra country code attachment when matching phone number
+    if number1.replace("+", "") in number2 or number2.replace("+",
+                                                              "") in number1:
+        return True
+    else:
+        logging.info("phone number1 %s and number2 %s does not match" %
+                     (number1, number2))
+        return False
 
 
-def initiate_call(log, ad_caller, callee_number, emergency=False):
+def initiate_call(log,
+                  ad,
+                  callee_number,
+                  emergency=False,
+                  timeout=MAX_WAIT_TIME_CALL_INITIATION,
+                  checking_interval=5):
     """Make phone call from caller to callee.
 
     Args:
@@ -864,48 +1052,36 @@
     Returns:
         result: if phone call is placed successfully.
     """
-    ad_caller.ed.clear_all_events()
-    sub_id = get_outgoing_voice_sub_id(ad_caller)
-    ad_caller.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
-
-    wait_time_for_incall_state = MAX_WAIT_TIME_CALL_INITIATION
+    ad.ed.clear_all_events()
+    sub_id = get_outgoing_voice_sub_id(ad)
+    ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
 
     try:
         # Make a Call
+        ad.log.info("Make a phone call to %s", callee_number)
         if emergency:
-            ad_caller.droid.telecomCallEmergencyNumber(callee_number)
+            ad.droid.telecomCallEmergencyNumber(callee_number)
         else:
-            ad_caller.droid.telecomCallNumber(callee_number)
+            ad.droid.telecomCallNumber(callee_number)
 
         # Verify OFFHOOK event
-        if ad_caller.droid.telephonyGetCallState() != TELEPHONY_STATE_OFFHOOK:
-            event_offhook = ad_caller.ed.wait_for_event(
-                EventCallStateChanged,
-                is_event_match,
-                timeout=wait_time_for_incall_state,
-                field=CallStateContainer.CALL_STATE,
-                value=TELEPHONY_STATE_OFFHOOK)
-    except Empty:
-        log.error("initiate_call did not receive Telephony OFFHOOK event.")
+        checking_retries = int(timeout / checking_interval)
+        for i in range(checking_retries):
+            if (ad.droid.telecomIsInCall() and
+                    ad.droid.telephonyGetCallState() == TELEPHONY_STATE_OFFHOOK
+                    and
+                    ad.droid.telecomGetCallState() == TELEPHONY_STATE_OFFHOOK
+                ) or wait_for_call_offhook_event(log, ad, sub_id, True,
+                                                 checking_interval):
+                return True
+        ad.log.error(
+            "Make call to %s fail. telecomIsInCall:%s, Telecom State:%s,"
+            " Telephony State:%s", callee_number,
+            ad.droid.telecomIsInCall(),
+            ad.droid.telephonyGetCallState(), ad.droid.telecomGetCallState())
         return False
     finally:
-        ad_caller.droid.telephonyStopTrackingCallStateChangeForSubscription(
-            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.telephonyGetCallState() == 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.telephonyGetCallState(
-              ), ad_caller.droid.telecomGetCallState()))
-    return False
+        ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
 
 
 def call_reject(log, ad_caller, ad_callee, reject=True):
@@ -915,8 +1091,8 @@
     """
     subid_caller = ad_caller.droid.subscriptionGetDefaultVoiceSubId()
     subid_callee = ad_callee.incoming_voice_sub_id
-    log.info("Sub-ID Caller {}, Sub-ID Callee {}".format(subid_caller,
-                                                         subid_callee))
+    ad_caller.log.info("Sub-ID Caller %s, Sub-ID Callee %s", subid_caller,
+                       subid_callee)
     return call_reject_for_subscription(log, ad_caller, ad_callee,
                                         subid_caller, subid_callee, reject)
 
@@ -930,13 +1106,10 @@
     """
     """
 
-    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))
+    ad_caller.log.info("Call from %s to %s", caller_number, callee_number)
     try:
         if not initiate_call(log, ad_caller, callee_number):
             raise _CallSequenceException("Initiate call failed.")
@@ -1020,8 +1193,6 @@
         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"
@@ -1029,9 +1200,16 @@
     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))
+    ad_caller.log.info("Call from %s to %s", caller_number, callee_number)
 
     try:
+        voice_mail_count_before = ad_callee.droid.telephonyGetVoiceMailCountForSubscription(
+            subid_callee)
+        ad_callee.log.info("voice mail count is %s", voice_mail_count_before)
+        # -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.--")
 
         if not initiate_call(log, ad_caller, callee_number):
             raise _CallSequenceException("Initiate call failed.")
@@ -1042,13 +1220,6 @@
 
         ad_callee.droid.telephonyStartTrackingVoiceMailStateChangeForSubscription(
             subid_callee)
-        voice_mail_count_before = ad_callee.droid.telephonyGetVoiceMailCountForSubscription(
-            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)
@@ -1059,7 +1230,6 @@
 
         # TODO: b/26293512 Need to play some sound to leave message.
         # Otherwise carrier voice mail server may drop this voice mail.
-
         time.sleep(wait_time_in_call)
 
         if not verify_caller_func:
@@ -1068,8 +1238,8 @@
             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))
+                "Caller %s not in correct state after %s seconds" %
+                (ad_caller.serial, wait_time_in_call))
 
         if not hangup_call(log, ad_caller):
             raise _CallSequenceException("Error in Hanging-Up Call")
@@ -1081,12 +1251,15 @@
                 _is_on_message_waiting_event_true)
             log.info(event)
         except Empty:
+            ad_callee.log.warning("No expected event %s",
+                                  EventMessageWaitingIndicatorChanged)
             raise _CallSequenceException("No expected event {}.".format(
                 EventMessageWaitingIndicatorChanged))
         voice_mail_count_after = ad_callee.droid.telephonyGetVoiceMailCountForSubscription(
             subid_callee)
-        log.info("telephonyGetVoiceMailCount output - before: {}, after: {}".
-                 format(voice_mail_count_before, voice_mail_count_after))
+        ad_callee.log.info(
+            "telephonyGetVoiceMailCount output - before: %s, after: %s",
+            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]
@@ -1094,7 +1267,7 @@
         # -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("telephonyGetVoiceMailCount output is incorrect.")
+            log.error("before and after voice mail count is not incorrect.")
             return False
 
     except _CallSequenceException as e:
@@ -1132,26 +1305,33 @@
         False if error happens. True is succeed.
     """
     log.info("Erase all pending voice mail.")
-    if ad.droid.telephonyGetVoiceMailCount() == 0:
-        log.info("No Pending voice mail.")
+    count = ad.droid.telephonyGetVoiceMailCount()
+    if count == 0:
+        ad.log.info("No Pending voice mail.")
         return True
+    if count == -1:
+        ad.log.info("There is pending voice mail, but the count is unknown")
+        count = MAX_SAVED_VOICE_MAIL
+    else:
+        ad.log.info("There are %s voicemails", count)
 
     voice_mail_number = get_voice_mail_number(log, ad)
-
+    delete_digit = get_voice_mail_delete_digit(get_operator_name(log, ad))
     if not initiate_call(log, ad, voice_mail_number):
-        log.error("Initiate call failed.")
+        log.error("Initiate call to voice mail failed.")
         return False
     time.sleep(WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE)
     callId = ad.droid.telecomCallGetCallIds()[0]
     time.sleep(WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE)
-    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.log.info("Press %s to delete voice mail.", delete_digit)
+        ad.droid.telecomCallPlayDtmfTone(callId, delete_digit)
         ad.droid.telecomCallStopDtmfTone(callId)
         time.sleep(WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE)
         count -= 1
-    log.info("Voice mail server dropped this call.")
+    if is_phone_in_call(log, ad):
+        hangup_call(log, ad)
+
     # wait for telephonyGetVoiceMailCount to update correct result
     remaining_time = MAX_WAIT_TIME_VOICE_MAIL_COUNT
     while ((remaining_time > 0) and
@@ -1159,7 +1339,7 @@
         time.sleep(1)
         remaining_time -= 1
     current_voice_mail_count = ad.droid.telephonyGetVoiceMailCount()
-    log.info("telephonyGetVoiceMailCount: {}".format(current_voice_mail_count))
+    ad.log.info("telephonyGetVoiceMailCount: %s", current_voice_mail_count)
     return (current_voice_mail_count == 0)
 
 
@@ -1206,8 +1386,6 @@
     """
     subid_caller = get_outgoing_voice_sub_id(ad_caller)
     subid_callee = get_incoming_voice_sub_id(ad_callee)
-    log.info("Sub-ID Caller {}, Sub-ID Callee {}".format(subid_caller,
-                                                         subid_callee))
     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,
@@ -1254,14 +1432,12 @@
 
     """
     CHECK_INTERVAL = 3
-
-    class _CallSequenceException(Exception):
-        pass
+    result = True
 
     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))
+    ad_caller.log.info("Call from %s to %s", caller_number, callee_number)
 
     try:
         if not initiate_call(log, ad_caller, callee_number):
@@ -1272,6 +1448,7 @@
                 ad_callee,
                 subid_callee,
                 incoming_number=caller_number,
+                caller=ad_caller,
                 incall_ui_display=incall_ui_display):
             raise _CallSequenceException("Answer call fail.")
 
@@ -1316,9 +1493,10 @@
 
     except _CallSequenceException as e:
         log.error(e)
+        result = False
         return False
     finally:
-        if ad_hangup:
+        if ad_hangup or not result:
             for ad in [ad_caller, ad_callee]:
                 try:
                     if ad.droid.telecomIsInCall():
@@ -1327,17 +1505,17 @@
                     log.error(str(e))
 
 
-def phone_number_formatter(input_string, format=None):
+def phone_number_formatter(input_string, formatter=None):
     """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.
+        formatter: (int) expected format, this could be 7/10/11/12
+            if formatter is 7: output string would be 7 digital number.
+            if formatter is 10: output string would be 10 digital (standard) number.
+            if formatter is 11: output string would be "1" + 10 digital number.
+            if formatter is 12: output string would be "+1" + 10 digital number.
 
     Returns:
         If no error happen, return phone number in expected format.
@@ -1345,9 +1523,9 @@
     """
     # make sure input_string is 10 digital
     # Remove white spaces, dashes, dots
-    input_string = input_string.replace(" ", "").replace("-", "").replace(".",
-                                                                          "")
-    if not format:
+    input_string = input_string.replace(" ", "").replace("-", "").replace(
+        ".", "").lstrip("0")
+    if not formatter:
         return input_string
     # Remove "1"  or "+1"from front
     if (len(input_string) == PHONE_NUMBER_STRING_FORMAT_11_DIGIT and
@@ -1357,18 +1535,18 @@
           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):
+          formatter == 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:
+    if formatter == PHONE_NUMBER_STRING_FORMAT_12_DIGIT:
         input_string = "+1" + input_string
-    elif format == PHONE_NUMBER_STRING_FORMAT_11_DIGIT:
+    elif formatter == PHONE_NUMBER_STRING_FORMAT_11_DIGIT:
         input_string = "1" + input_string
-    elif format == PHONE_NUMBER_STRING_FORMAT_10_DIGIT:
+    elif formatter == PHONE_NUMBER_STRING_FORMAT_10_DIGIT:
         input_string = input_string
-    elif format == PHONE_NUMBER_STRING_FORMAT_7_DIGIT:
+    elif formatter == PHONE_NUMBER_STRING_FORMAT_7_DIGIT:
         input_string = input_string[3:]
     else:
         return None
@@ -1393,8 +1571,8 @@
 def verify_http_connection(log,
                            ad,
                            url="http://www.google.com/",
-                           retry=3,
-                           retry_interval=5):
+                           retry=5,
+                           retry_interval=15):
     """Make ping request and return status.
 
     Args:
@@ -1415,17 +1593,293 @@
         # 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.")
+            ad.log.info("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))
+    ad.log.info("Verify Internet retry failed after %s second",
+                i * retry_interval)
     return False
 
 
+def _generate_file_name_and_out_path(url, out_path):
+    file_name = url.split("/")[-1]
+    if not out_path:
+        out_path = "/sdcard/Download/%s" % file_name
+    elif out_path.endswith("/"):
+        out_path = os.path.join(out_path, file_name)
+    else:
+        file_name = out_path.split("/")[-1]
+    return file_name, out_path
+
+
+def _check_file_existance(ad, file_name, out_path, expected_file_size=None):
+    """Check file existance by file_name and out_path. If expected_file_size
+       is provided, then also check if the file meet the file size requirement.
+    """
+    if out_path.endswith("/"):
+        out_path = os.path.join(out_path, file_name)
+    out = ad.adb.shell("ls -l %s" % out_path, ignore_status=True)
+    # Handle some old version adb returns error message "No such" into std_out
+    if out and "No such" not in out and file_name in out:
+        if expected_file_size:
+            file_size = int(out.split(" ")[4])
+            if file_size >= expected_file_size:
+                ad.log.info("File %s of size %s is in %s", file_name,
+                            file_size, out_path)
+                return True
+            else:
+                ad.log.info(
+                    "File size for %s in %s is %s does not meet expected %s",
+                    file_name, out_path, file_size, expected_file_size)
+                return False
+        else:
+            ad.log.info("File %s is in %s", file_name, out_path)
+            return True
+    else:
+        ad.log.info("File %s does not exist in %s.", file_name, out_path)
+        return False
+
+
+def active_file_download_task(log, ad, file_name="5MB"):
+    curl_capable = True
+    try:
+        out = ad.adb.shell("curl --version")
+        if "not found" in out:
+            curl_capable = False
+    except AdbError:
+        curl_capable = False
+    # files available for download on the same website:
+    # 1GB.zip, 512MB.zip, 200MB.zip, 50MB.zip, 20MB.zip, 10MB.zip, 5MB.zip
+    # download file by adb command, as phone call will use sl4a
+    url = "http://download.thinkbroadband.com/" + file_name + ".zip"
+    file_map_dict = {
+        '5MB': 5000000,
+        '10MB': 10000000,
+        '20MB': 20000000,
+        '50MB': 50000000,
+        '200MB': 200000000,
+        '512MB': 512000000,
+        '1GB': 1000000000
+    }
+    file_size = file_map_dict.get(file_name)
+    if not file_size:
+        log.warning("file_name %s for download is not available", file_name)
+        return False
+    timeout = min(max(file_size / 100000, 60), 3600)
+    output_path = "/sdcard/Download/" + file_name + ".zip"
+    if not curl_capable:
+        return (http_file_download_by_chrome, (ad, url, file_size, True,
+                                               timeout))
+    else:
+        return (http_file_download_by_curl, (ad, url, output_path, file_size,
+                                             True, timeout))
+
+
+def active_file_download_test(log, ad, file_name="5MB"):
+    task = active_file_download_task(log, ad, file_name)
+    return task[0](*task[1])
+
+
+def verify_internet_connection(log, ad):
+    """Verify internet connection by ping test.
+
+    Args:
+        log: log object
+        ad: Android Device Object.
+
+    """
+    ad.log.info("Verify internet connection")
+    return adb_shell_ping(ad, count=5, timeout=60, loss_tolerance=40)
+
+
+def iperf_test_by_adb(log,
+                      ad,
+                      iperf_server,
+                      port_num=None,
+                      reverse=False,
+                      timeout=180,
+                      limit_rate=None,
+                      omit=10,
+                      ipv6=False,
+                      rate_dict=None):
+    """Iperf test by adb.
+
+    Args:
+        log: log object
+        ad: Android Device Object.
+        iperf_Server: The iperf host url".
+        port_num: TCP/UDP server port
+        timeout: timeout for file download to complete.
+        limit_rate: iperf bandwidth option. None by default
+        omit: the omit option provided in iperf command.
+    """
+    iperf_option = "-t %s -O %s -J" % (timeout, omit)
+    if limit_rate: iperf_option += " -b %s" % limit_rate
+    if port_num: iperf_option += " -p %s" % port_num
+    if ipv6: iperf_option += " -6"
+    if reverse: iperf_option += " -R"
+    try:
+        ad.log.info("Running adb iperf test with server %s", iperf_server)
+        result, data = ad.run_iperf_client(
+            iperf_server, iperf_option, timeout=timeout + 60)
+        ad.log.info("Iperf test result with server %s is %s", iperf_server,
+                    result)
+        if result:
+            data_json = json.loads(''.join(data))
+            tx_rate = data_json['end']['sum_sent']['bits_per_second']
+            rx_rate = data_json['end']['sum_received']['bits_per_second']
+            ad.log.info(
+                'iPerf3 upload speed is %sbps, download speed is %sbps',
+                tx_rate, rx_rate)
+            if rate_dict is not None:
+                rate_dict["Uplink"] = tx_rate
+                rate_dict["Downlink"] = rx_rate
+        return result
+    except Exception as e:
+        ad.log.warning("Fail to run iperf test with exception %s", e)
+        return False
+
+
+def http_file_download_by_curl(ad,
+                               url,
+                               out_path=None,
+                               expected_file_size=None,
+                               remove_file_after_check=True,
+                               timeout=900,
+                               limit_rate=None,
+                               retry=3):
+    """Download http file by adb curl.
+
+    Args:
+        ad: Android Device Object.
+        url: The url that file to be downloaded from".
+        out_path: Optional. Where to download file to.
+                  out_path is /sdcard/Download/ by default.
+        expected_file_size: Optional. Provided if checking the download file meet
+                            expected file size in unit of byte.
+        remove_file_after_check: Whether to remove the downloaded file after
+                                 check.
+        timeout: timeout for file download to complete.
+        limit_rate: download rate in bps. None, if do not apply rate limit.
+        retry: the retry request times provided in curl command.
+    """
+    file_name, out_path = _generate_file_name_and_out_path(url, out_path)
+    curl_cmd = "curl"
+    if limit_rate:
+        curl_cmd += " --limit-rate %s" % limit_rate
+    if retry:
+        curl_cmd += " --retry %s" % retry
+    curl_cmd += " --url %s > %s" % (url, out_path)
+    try:
+        ad.log.info("Download file from %s to %s by adb shell command %s", url,
+                    out_path, curl_cmd)
+        ad.adb.shell(curl_cmd, timeout=timeout)
+        if _check_file_existance(ad, file_name, out_path, expected_file_size):
+            ad.log.info("File %s is downloaded from %s successfully",
+                        file_name, url)
+            return True
+        else:
+            ad.log.warning("Fail to download file from %s", url)
+            return False
+    except Exception as e:
+        ad.log.warning("Download file from %s failed with exception %s", url,
+                       e)
+        return False
+    finally:
+        if remove_file_after_check:
+            ad.log.info("Remove the downloaded file %s", out_path)
+            ad.adb.shell("rm %s" % out_path, ignore_status=True)
+
+
+def http_file_download_by_chrome(ad,
+                                 url,
+                                 expected_file_size=None,
+                                 remove_file_after_check=True,
+                                 timeout=900):
+    """Download http file by chrome.
+
+    Args:
+        ad: Android Device Object.
+        url: The url that file to be downloaded from".
+        expected_file_size: Optional. Provided if checking the download file meet
+                            expected file size in unit of byte.
+        remove_file_after_check: Whether to remove the downloaded file after
+                                 check.
+        timeout: timeout for file download to complete.
+    """
+    file_name, out_path = _generate_file_name_and_out_path(url,
+                                                           "/sdcard/Download/")
+    for cmd in ("am set-debug-app --persistent com.android.chrome",
+                'echo "chrome --no-default-browser-check --no-first-run '
+                '--disable-fre" > /data/local/chrome-command-line',
+                "pm grant com.android.chrome "
+                "android.permission.READ_EXTERNAL_STORAGE",
+                "pm grant com.android.chrome "
+                "android.permission.WRITE_EXTERNAL_STORAGE",
+                'am start -a android.intent.action.VIEW -d "%s"' % url):
+        ad.adb.shell(cmd)
+    ad.log.info("Download %s from %s with timeout %s", file_name, url, timeout)
+    elapse_time = 0
+    while elapse_time < timeout:
+        time.sleep(30)
+        if _check_file_existance(ad, file_name, out_path, expected_file_size):
+            ad.log.info("File %s is downloaded from %s successfully",
+                        file_name, url)
+            if remove_file_after_check:
+                ad.log.info("Remove the downloaded file %s", out_path)
+                ad.adb.shell("rm %s" % out_path, ignore_status=True)
+            return True
+        else:
+            elapse_time += 30
+    ad.log.warning("Fail to download file from %s", url)
+    ad.adb.shell("rm %s" % out_path, ignore_status=True)
+    return False
+
+
+def http_file_download_by_sl4a(log,
+                               ad,
+                               url,
+                               out_path=None,
+                               expected_file_size=None,
+                               remove_file_after_check=True,
+                               timeout=300):
+    """Download http file by sl4a RPC call.
+
+    Args:
+        log: log object
+        ad: Android Device Object.
+        url: The url that file to be downloaded from".
+        out_path: Optional. Where to download file to.
+                  out_path is /sdcard/Download/ by default.
+        expected_file_size: Optional. Provided if checking the download file meet
+                            expected file size in unit of byte.
+        remove_file_after_check: Whether to remove the downloaded file after
+                                 check.
+        timeout: timeout for file download to complete.
+    """
+    file_name, out_path = _generate_file_name_and_out_path(url, out_path)
+    try:
+        ad.log.info("Download file from %s to %s by sl4a RPC call", url,
+                    out_path)
+        ad.droid.httpDownloadFile(url, out_path, timeout=timeout)
+        if _check_file_existance(ad, file_name, out_path, expected_file_size):
+            ad.log.info("File %s is downloaded from %s successfully",
+                        file_name, url)
+            return True
+        else:
+            ad.log.warning("Fail to download file from %s", url)
+            return False
+    except Exception as e:
+        ad.log.error("Download file from %s failed with exception %s", url, e)
+        raise
+    finally:
+        if remove_file_after_check:
+            ad.log.info("Remove the downloaded file %s", out_path)
+            ad.adb.shell("rm %s" % out_path, ignore_status=True)
+
+
 def _connection_state_change(_event, target_state, connection_type):
     if connection_type:
         if 'TypeName' not in _event['data']:
@@ -1435,9 +1889,8 @@
             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))
+                "_connection_state_change expect: %s, received: %s <type %s>",
+                connection_type, connection_type_string_in_event, cur_type)
             return False
 
     if 'isConnected' in _event['data'] and _event['data'][
@@ -1510,6 +1963,9 @@
         False: DATA_STATE_DISCONNECTED
     }[state]
 
+    data_state = ad.droid.telephonyGetDataConnectionState()
+    if not state and ad.droid.telephonyGetDataConnectionState() == state_str:
+        return True
     ad.ed.clear_all_events()
     ad.droid.telephonyStartTrackingDataConnectionStateChangeForSubscription(
         sub_id)
@@ -1530,8 +1986,8 @@
                 field=DataConnectionStateContainer.DATA_CONNECTION_STATE,
                 value=state_str)
         except Empty:
-            log.debug("No expected event EventDataConnectionStateChanged {}.".
-                      format(state_str))
+            ad.log.info("No expected event EventDataConnectionStateChanged %s",
+                        state_str)
 
         # TODO: Wait for <MAX_WAIT_TIME_CONNECTION_STATE_UPDATE> seconds for
         # data connection state.
@@ -1635,32 +2091,26 @@
         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))
+            ad.log.info("current data connection type: %s", current_type)
             if not connection_type:
                 return True
             else:
                 if not is_connected and current_type != connection_type:
-                    ad.log.info(
-                        "wait_for_nw_data_connection success: data not on {}!".
-                        format(connection_type))
+                    ad.log.info("data connection not on %s!", connection_type)
                     return True
                 elif is_connected and current_type == connection_type:
-                    ad.log.info(
-                        "wait_for_nw_data_connection success: data on {}!".
-                        format(connection_type))
+                    ad.log.info("data connection on %s as expected",
+                                connection_type)
                     return True
         else:
-            ad.log.info("{} current state: {} target: {}".format(
-                ad.serial, cur_data_connection_state, is_connected))
+            ad.log.info("current data connection state: %s target: %s",
+                        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))
+            ad.log.info("received event: %s", event)
         except Empty:
             pass
 
@@ -1674,34 +2124,59 @@
                 log, ad, MAX_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))
+            ad.log.info("current data connection type: %s", 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))
+                    ad.log.info("data connection not on %s", 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))
+                    ad.log.info("after event wait, data connection on %s",
+                                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)))
+        ad.log.error("Exception error %s", str(e))
         return False
     finally:
         ad.droid.connectivityStopTrackingConnectivityStateChange()
 
 
+def toggle_cell_data_roaming(ad, state):
+    """Enable cell data roaming for default data subscription.
+
+    Wait for the data roaming status to be DATA_STATE_CONNECTED
+        or DATA_STATE_DISCONNECTED.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        state: True or False for enable or disable cell data roaming.
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    state_int = {True: DATA_ROAMING_ENABLE, False: DATA_ROAMING_DISABLE}[state]
+    action_str = {True: "Enable", False: "Disable"}[state]
+    if ad.droid.connectivityCheckDataRoamingMode() == state:
+        ad.log.info("Data roaming is already in state %s", state)
+        return True
+    if not ad.droid.connectivitySetDataRoaming(state_int):
+        ad.error.info("Fail to config data roaming into state %s", state)
+        return False
+    if ad.droid.connectivityCheckDataRoamingMode() == state:
+        ad.log.info("Data roaming is configured into state %s", state)
+        return True
+    else:
+        ad.log.error("Data roaming is not configured into state %s", state)
+        return False
+
+
 def verify_incall_state(log, ads, expected_status):
     """Verify phones in incall state or not.
 
@@ -1717,8 +2192,8 @@
     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))
+            ad.log.error("InCall status:%s, expected:%s",
+                         ad.droid.telecomIsInCall(), expected_status)
             result = False
     return result
 
@@ -1739,9 +2214,8 @@
     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))
+        ad.log.error("Active Call number is %s, expecting", actual_number,
+                     expected_number)
         return False
     return True
 
@@ -1772,8 +2246,9 @@
     Raises:
         TelTestUtilsError if platform does not support VoLTE.
     """
-    return toggle_volte_for_subscription(
-        log, ad, get_outgoing_voice_sub_id(ad), new_state)
+    return toggle_volte_for_subscription(log, ad,
+                                         get_outgoing_voice_sub_id(ad),
+                                         new_state)
 
 
 def toggle_volte_for_subscription(log, ad, sub_id, new_state=None):
@@ -1814,7 +2289,7 @@
         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))
+        ad.log.info("Set wfc mode to %s", wfc_mode)
         if not ad.droid.imsIsWfcEnabledByPlatform():
             if wfc_mode == WFC_MODE_DISABLED:
                 return True
@@ -1863,8 +2338,8 @@
         if state_check_func(log, ad, *args, **kwargs):
             return True
 
-        time.sleep(1)
-        max_time -= 1
+        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+        max_time -= WAIT_TIME_BETWEEN_STATE_CHECK
 
     return False
 
@@ -1875,8 +2350,8 @@
         if state_check_func(log, ad, sub_id, *args, **kwargs):
             return True
 
-        time.sleep(1)
-        max_time -= 1
+        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+        max_time -= WAIT_TIME_BETWEEN_STATE_CHECK
 
     return False
 
@@ -1892,8 +2367,8 @@
         if success:
             return True
 
-        time.sleep(1)
-        max_time -= 1
+        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+        max_time -= WAIT_TIME_BETWEEN_STATE_CHECK
 
     return False
 
@@ -1972,11 +2447,10 @@
 
 
 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
+    rat = get_network_rat_for_subscription(log, ad, sub_id, voice_or_data)
+    ad.log.info("Sub_id %s network rate is %s for %s", sub_id, rat,
+                voice_or_data)
+    return rat != RAT_UNKNOWN
 
 
 def wait_for_voice_attach(log, ad, max_time):
@@ -2095,9 +2569,8 @@
         Return False if VoLTE feature bit is False or IMS not registered.
     """
     volte_status = ad.droid.telephonyIsVolteAvailable()
-    ims_status = is_ims_registered(log, ad)
-    if volte_status is True and ims_status is False:
-        log.error("Error! VoLTE is Available, but IMS is not registered.")
+    if volte_status is True and is_ims_registered(log, ad) is False:
+        ad.log.error("Error! VoLTE is Available, but IMS is not registered.")
         return False
     return volte_status
 
@@ -2114,8 +2587,7 @@
         Return False if Video Calling feature bit is False or IMS not registered.
     """
     video_status = ad.droid.telephonyIsVideoCallingAvailable()
-    ims_status = is_ims_registered(log, ad)
-    if video_status is True and ims_status is False:
+    if video_status is True and is_ims_registered(log, ad) is False:
         log.error("Error! Video Call is Available, but IMS is not registered.")
         return False
     return video_status
@@ -2163,8 +2635,7 @@
         Return False if WiFi Calling feature bit is False or IMS not registered.
     """
     wfc_status = ad.droid.telephonyIsWifiCallingAvailable()
-    ims_status = is_ims_registered(log, ad)
-    if wfc_status is True and ims_status is False:
+    if wfc_status is True and is_ims_registered(log, ad) is False:
         log.error(
             "Error! WiFi Calling is Available, but IMS is not registered.")
         return False
@@ -2248,8 +2719,9 @@
     Returns:
         True if success.
     """
-    return set_phone_number_for_subscription(
-        log, ad, get_outgoing_voice_sub_id(ad), phone_num)
+    return set_phone_number_for_subscription(log, ad,
+                                             get_outgoing_voice_sub_id(ad),
+                                             phone_num)
 
 
 def set_phone_number_for_subscription(log, ad, subid, phone_num):
@@ -2285,10 +2757,10 @@
     try:
         if subId is not None:
             result = operator_name_from_plmn_id(
-                ad.droid.telephonyGetSimOperatorForSubscription(subId))
+                ad.droid.telephonyGetNetworkOperatorForSubscription(subId))
         else:
             result = operator_name_from_plmn_id(
-                ad.droid.telephonyGetSimOperator())
+                ad.droid.telephonyGetNetworkOperator())
     except KeyError:
         result = CARRIER_UNKNOWN
     return result
@@ -2390,7 +2862,7 @@
                                     text)
             return True
         except Empty:
-            log.error("No matched SMS received event.")
+            ad_rx.log.error("No matched SMS received event.")
             return False
     else:
         try:
@@ -2403,10 +2875,10 @@
                 received_sms += event['data']['Text']
             return True
         except Empty:
-            log.error("No matched SMS received event.")
+            ad_rx.log.error("No matched SMS received event.")
             if received_sms != '':
-                log.error("Only received partial matched SMS: {}".format(
-                    received_sms))
+                ad_rx.log.error("Only received partial matched SMS: %s",
+                                received_sms)
             return False
 
 
@@ -2448,7 +2920,7 @@
                                 text)
         return True
     except Empty:
-        log.warn("No matched MMS downloaded event.")
+        ad_rx.log.warning("No matched MMS downloaded event.")
         return False
 
 
@@ -2472,8 +2944,9 @@
     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))
+        length = len(text)
+        ad_tx.log.info("Sending SMS from %s to %s, len: %s, content: %s.",
+                       phonenumber_tx, phonenumber_rx, length, text)
         result = False
         ad_rx.ed.clear_all_events()
         ad_rx.droid.smsStartTrackingIncomingSmsMessage()
@@ -2484,7 +2957,8 @@
                 ad_tx.ed.pop_event(EventSmsSentSuccess,
                                    MAX_WAIT_TIME_SMS_SENT_SUCCESS)
             except Empty:
-                log.error("No sent_success event.")
+                ad_tx.log.error("No sent_success event for SMS of length %s.",
+                                length)
                 return False
 
             if not wait_for_matching_sms(
@@ -2493,6 +2967,8 @@
                     phonenumber_tx,
                     text,
                     allow_multi_part_long_sms=True):
+                ad_rx.log.error("No matching received SMS of length %s.",
+                                length)
                 return False
         finally:
             ad_rx.droid.smsStopTrackingIncomingSmsMessage()
@@ -2513,7 +2989,8 @@
         array_message: the array of message to send/receive
     """
     return mms_send_receive_verify_for_subscription(
-        log, ad_tx, ad_rx, get_outgoing_message_sub_id(ad_tx),
+        log, ad_tx, ad_rx,
+        get_outgoing_message_sub_id(ad_tx),
         get_incoming_message_sub_id(ad_rx), array_message)
 
 
@@ -2538,9 +3015,9 @@
     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: {}, file: {}.".format(
-                phonenumber_tx, phonenumber_rx, subject, message, filename))
+        ad_tx.log.info(
+            "Sending MMS from %s to %s, subject: %s, message: %s, file: %s.",
+            phonenumber_tx, phonenumber_rx, subject, message, filename)
         result = False
         ad_rx.ed.clear_all_events()
         ad_rx.droid.smsStartTrackingIncomingMmsMessage()
@@ -2551,7 +3028,7 @@
                 ad_tx.ed.pop_event(EventMmsSentSuccess,
                                    MAX_WAIT_TIME_SMS_SENT_SUCCESS)
             except Empty:
-                log.warn("No sent_success event.")
+                ad_tx.log.warning("No sent_success event.")
                 return False
 
             if not wait_for_matching_mms(log, ad_rx, phonenumber_tx, message):
@@ -2575,7 +3052,8 @@
         array_message: the array of message to send/receive
     """
     return mms_receive_verify_after_call_hangup_for_subscription(
-        log, ad_tx, ad_rx, get_outgoing_message_sub_id(ad_tx),
+        log, ad_tx, ad_rx,
+        get_outgoing_message_sub_id(ad_tx),
         get_incoming_message_sub_id(ad_rx), array_message)
 
 
@@ -2600,9 +3078,9 @@
     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(
-            "Waiting MMS {} to {}, subject: {}, message: {}, file: {}.".format(
-                phonenumber_tx, phonenumber_rx, subject, message, filename))
+        ad_rx.log.info(
+            "Waiting MMS from %s to %s, subject: %s, message: %s, file: %s.",
+            phonenumber_tx, phonenumber_rx, subject, message, filename)
         result = False
         ad_rx.ed.clear_all_events()
         ad_rx.droid.smsStartTrackingIncomingMmsMessage()
@@ -2614,7 +3092,7 @@
                 ad_tx.ed.pop_event(EventMmsSentSuccess,
                                    MAX_WAIT_TIME_SMS_SENT_SUCCESS)
             except Empty:
-                log.warn("No sent_success event.")
+                log.warning("No sent_success event.")
                 return False
 
             if not wait_for_matching_mms(log, ad_rx, phonenumber_tx, message):
@@ -2634,8 +3112,9 @@
     """Ensure ad's current network is in expected rat_family.
     """
     return ensure_network_rat_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), network_preference,
-        rat_family, voice_or_data, max_wait_time, toggle_apm_after_setting)
+        log, ad,
+        ad.droid.subscriptionGetDefaultSubId(), network_preference, rat_family,
+        voice_or_data, max_wait_time, toggle_apm_after_setting)
 
 
 def ensure_network_rat_for_subscription(
@@ -2651,33 +3130,36 @@
     """
     if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
             network_preference, sub_id):
-        log.error("Set Preferred Networks failed.")
+        ad.log.error("Set sub_id %s Preferred Networks Type %s failed.",
+                     sub_id, network_preference)
         return False
     if is_droid_in_rat_family_for_subscription(log, ad, sub_id, rat_family,
                                                voice_or_data):
+        ad.log.info("Sub_id %s in RAT %s for %s", sub_id, rat_family,
+                    voice_or_data)
         return True
 
     if toggle_apm_after_setting:
-        toggle_airplane_mode(log, ad, True)
+        toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
         time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-        toggle_airplane_mode(log, ad, False)
+        toggle_airplane_mode(log, ad, new_state=None, strict_checking=False)
 
     result = wait_for_network_rat_for_subscription(
         log, ad, sub_id, rat_family, max_wait_time, voice_or_data)
 
     log.info(
-        "End of ensure_network_rat_for_subscription for {}. "
-        "Setting to {}, Expecting {} {}. Current: voice: {}(family: {}), "
-        "data: {}(family: {})".format(
-            ad.serial, network_preference, rat_family, voice_or_data, ad.droid.
-            telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
-            rat_family_from_rat(
-                ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
-                    sub_id)),
+        "End of ensure_network_rat_for_subscription for %s. "
+        "Setting to %s, Expecting %s %s. Current: voice: %s(family: %s), "
+        "data: %s(family: %s)", ad.serial, network_preference, rat_family,
+        voice_or_data,
+        ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
+        rat_family_from_rat(
+            ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
+                sub_id)),
+        ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id),
+        rat_family_from_rat(
             ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
-                sub_id), rat_family_from_rat(
-                    ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
-                        sub_id))))
+                sub_id)))
     return result
 
 
@@ -2690,7 +3172,8 @@
     """Ensure that current rat is within the device's preferred network rats.
     """
     return ensure_network_preference_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), network_preference,
+        log, ad,
+        ad.droid.subscriptionGetDefaultSubId(), network_preference,
         voice_or_data, max_wait_time, toggle_apm_after_setting)
 
 
@@ -2714,26 +3197,26 @@
         return True
 
     if toggle_apm_after_setting:
-        toggle_airplane_mode(log, ad, True)
+        toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
         time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-        toggle_airplane_mode(log, ad, False)
+        toggle_airplane_mode(log, ad, new_state=False, strict_checking=False)
 
     result = wait_for_preferred_network_for_subscription(
         log, ad, sub_id, network_preference, max_wait_time, voice_or_data)
 
-    log.info(
-        "End of ensure_network_preference_for_subscription for {}. "
-        "Setting to {}, Expecting {} {}. Current: voice: {}(family: {}), "
-        "data: {}(family: {})".format(
-            ad.serial, network_preference, rat_family_list, voice_or_data, ad.
-            droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
-            rat_family_from_rat(
-                ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
-                    sub_id)),
+    ad.log.info(
+        "End of ensure_network_preference_for_subscription. "
+        "Setting to %s, Expecting %s %s. Current: voice: %s(family: %s), "
+        "data: %s(family: %s)", network_preference, rat_family_list,
+        voice_or_data,
+        ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
+        rat_family_from_rat(
+            ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
+                sub_id)),
+        ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id),
+        rat_family_from_rat(
             ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
-                sub_id), rat_family_from_rat(
-                    ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
-                        sub_id))))
+                sub_id)))
     return result
 
 
@@ -2750,8 +3233,9 @@
     Wait for ad in expected network type.
     """
     return ensure_network_generation_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), generation,
-        max_wait_time, voice_or_data, toggle_apm_after_setting)
+        log, ad,
+        ad.droid.subscriptionGetDefaultSubId(), generation, max_wait_time,
+        voice_or_data, toggle_apm_after_setting)
 
 
 def ensure_network_generation_for_subscription(
@@ -2768,27 +3252,43 @@
     Toggle ON/OFF airplane mode if necessary.
     Wait for ad in expected network type.
     """
-    operator = get_operator_name(log, ad, sub_id)
+    ad.log.info(
+        "RAT network type voice: %s, data: %s",
+        ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
+        ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id))
+    if is_droid_in_network_generation_for_subscription(
+            log, ad, sub_id, generation, voice_or_data):
+        return True
+
     try:
-        network_preference = network_preference_for_generaton(generation,
-                                                              operator)
-        rat_family = rat_family_for_generation(generation, operator)
-    except KeyError:
-        log.error("Failed to find a rat_family entry for "
-                  "PLMN: {}, operator:{}, generation: {}".format(
-                      ad.droid.telephonyGetSimOperatorForSubscription(sub_id),
-                      operator, generation))
+        ad.log.info("Finding the network preference for generation %s for "
+                    "operator %s phone type %s", generation,
+                    ad.cfg["subscription"][sub_id]["operator"],
+                    ad.cfg["subscription"][sub_id]["phone_type"])
+        network_preference = network_preference_for_generaton(
+            generation, ad.cfg["subscription"][sub_id]["operator"],
+            ad.cfg["subscription"][sub_id]["phone_type"])
+        ad.log.info("Network preference for %s is %s", generation,
+                    network_preference)
+        rat_family = rat_family_for_generation(
+            generation, ad.cfg["subscription"][sub_id]["operator"],
+            ad.cfg["subscription"][sub_id]["phone_type"])
+    except KeyError as e:
+        ad.log.error("Failed to find a rat_family entry for generation %s"
+                     " for subscriber %s with error %s", generation,
+                     ad.cfg["subscription"][sub_id], e)
         return False
 
     current_network_preference = \
             ad.droid.telephonyGetPreferredNetworkTypesForSubscription(
                 sub_id)
 
-    # Update the setting iff necessary
     if (current_network_preference is not network_preference and
             not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
                 network_preference, sub_id)):
-        log.error("Set Preferred Networks failed.")
+        ad.log.error(
+            "Network preference is %s. Set Preferred Networks to %s failed.",
+            current_network_preference, network_preference)
         return False
 
     if is_droid_in_network_generation_for_subscription(
@@ -2796,26 +3296,25 @@
         return True
 
     if toggle_apm_after_setting:
-        toggle_airplane_mode(log, ad, True)
+        toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
         time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-        toggle_airplane_mode(log, ad, False)
+        toggle_airplane_mode(log, ad, new_state=False, strict_checking=False)
 
     result = wait_for_network_generation_for_subscription(
         log, ad, sub_id, generation, max_wait_time, voice_or_data)
 
-    log.info(
-        "End of ensure_network_generation_for_subscription for {}. "
-        "Setting to {}, Expecting {} {}. Current: voice: {}(family: {}), "
-        "data: {}(family: {})".format(
-            ad.serial, network_preference, generation, voice_or_data, ad.droid.
-            telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
-            rat_generation_from_rat(
-                ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
-                    sub_id)),
+    ad.log.info(
+        "Ensure network %s %s %s. With network preference %s, "
+        "current: voice: %s(family: %s), data: %s(family: %s)", generation,
+        voice_or_data, result, network_preference,
+        ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
+        rat_generation_from_rat(
+            ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
+                sub_id)),
+        ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id),
+        rat_generation_from_rat(
             ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
-                sub_id), rat_generation_from_rat(
-                    ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
-                        sub_id))))
+                sub_id)))
 
     return result
 
@@ -2826,8 +3325,9 @@
                          max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
                          voice_or_data=None):
     return wait_for_network_rat_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
-        max_wait_time, voice_or_data)
+        log, ad,
+        ad.droid.subscriptionGetDefaultSubId(), rat_family, max_wait_time,
+        voice_or_data)
 
 
 def wait_for_network_rat_for_subscription(
@@ -2848,8 +3348,9 @@
                              max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
                              voice_or_data=None):
     return wait_for_not_network_rat_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
-        max_wait_time, voice_or_data)
+        log, ad,
+        ad.droid.subscriptionGetDefaultSubId(), rat_family, max_wait_time,
+        voice_or_data)
 
 
 def wait_for_not_network_rat_for_subscription(
@@ -2861,7 +3362,7 @@
         voice_or_data=None):
     return _wait_for_droid_in_state_for_subscription(
         log, ad, sub_id, max_wait_time,
-        lambda log, ad, sub_id, * args, ** kwargs: not is_droid_in_rat_family_for_subscription(log, ad, sub_id, rat_family, voice_or_data)
+        lambda log, ad, sub_id, *args, **kwargs: not is_droid_in_rat_family_for_subscription(log, ad, sub_id, rat_family, voice_or_data)
     )
 
 
@@ -2871,7 +3372,8 @@
                                max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
                                voice_or_data=None):
     return wait_for_preferred_network_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), network_preference,
+        log, ad,
+        ad.droid.subscriptionGetDefaultSubId(), network_preference,
         max_wait_time, voice_or_data)
 
 
@@ -2895,8 +3397,9 @@
                                 max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
                                 voice_or_data=None):
     return wait_for_network_generation_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), generation,
-        max_wait_time, voice_or_data)
+        log, ad,
+        ad.droid.subscriptionGetDefaultSubId(), generation, max_wait_time,
+        voice_or_data)
 
 
 def wait_for_network_generation_for_subscription(
@@ -2914,8 +3417,8 @@
 
 def is_droid_in_rat_family(log, ad, rat_family, voice_or_data=None):
     return is_droid_in_rat_family_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
-        voice_or_data)
+        log, ad,
+        ad.droid.subscriptionGetDefaultSubId(), rat_family, voice_or_data)
 
 
 def is_droid_in_rat_family_for_subscription(log,
@@ -2929,8 +3432,8 @@
 
 def is_droid_in_rat_familiy_list(log, ad, rat_family_list, voice_or_data=None):
     return is_droid_in_rat_family_list_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family_list,
-        voice_or_data)
+        log, ad,
+        ad.droid.subscriptionGetDefaultSubId(), rat_family_list, voice_or_data)
 
 
 def is_droid_in_rat_family_list_for_subscription(log,
@@ -2991,13 +3494,16 @@
 
     for service in service_list:
         nw_rat = get_network_rat_for_subscription(log, ad, sub_id, service)
-
+        ad.log.info("network rat for %s is %s", service, nw_rat)
         if nw_rat == RAT_UNKNOWN or not is_valid_rat(nw_rat):
             continue
 
         if rat_generation_from_rat(nw_rat) == nw_gen:
+            ad.log.info("network rat %s is expected %s", nw_rat, nw_gen)
             return True
         else:
+            ad.log.info("network rat %s is %s, expecting %s", nw_rat,
+                        rat_generation_from_rat(nw_rat), nw_gen)
             return False
 
     return False
@@ -3077,11 +3583,8 @@
     try:
         return rat_generation_from_rat(
             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)))
+    except KeyError as e:
+        ad.log.error("KeyError %s", e)
         return GEN_UNKNOWN
 
 
@@ -3103,70 +3606,91 @@
     return voice_mail_number
 
 
-def ensure_phones_idle(log,
-                       ads,
-                       settling_time=WAIT_TIME_ANDROID_STATE_SETTLING):
+def ensure_phones_idle(log, ads, max_time=MAX_WAIT_TIME_CALL_DROP):
     """Ensure ads idle (not in call).
     """
+    result = True
     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)
+        if not ensure_phone_idle(log, ad, max_time=max_time):
+            result = False
+    return result
+
+
+def ensure_phone_idle(log, ad, max_time=MAX_WAIT_TIME_CALL_DROP):
+    """Ensure ad idle (not in call).
+    """
+    if ad.droid.telecomIsInCall():
+        ad.droid.telecomEndCall()
+    if not wait_for_droid_not_in_call(log, ad, max_time=max_time):
+        ad.log.error("Failed to end call on %s")
+        return False
     return True
 
 
-def ensure_phone_idle(log, ad, settling_time=WAIT_TIME_ANDROID_STATE_SETTLING):
-    """Ensure ad idle (not in call).
+def ensure_phone_subscription(log, ad):
+    """Ensure Phone Subscription.
     """
-    return ensure_phones_idle(log, [ad], settling_time)
+    #check for sim and service
+    subInfo = ad.droid.subscriptionGetAllSubInfoList()
+    if not subInfo or len(subInfo) < 1:
+        ad.log.error("Unable to find A valid subscription!")
+        return False
+    if ad.droid.subscriptionGetDefaultDataSubId() <= INVALID_SUB_ID:
+        ad.log.error("No Default Data Sub ID")
+        return False
+    elif ad.droid.subscriptionGetDefaultVoiceSubId() <= INVALID_SUB_ID:
+        ad.log.error("No Valid Voice Sub ID")
+        return False
+    sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+    if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
+                                                  MAX_WAIT_TIME_NW_SELECTION):
+        ad.log.error("Did Not Attach For Voice Services")
+        return False
+    return True
 
 
-def ensure_phone_default_state(log, ad):
+def ensure_phone_default_state(log, ad, check_subscription=True):
     """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()
 
-    if not wait_for_droid_not_in_call(log, ad):
-        log.error("Failed to end call on {} "
-                  "while ensuring phone in default state.".format(ad.serial))
+    try:
+        ad.droid.telephonyFactoryReset()
+        ad.droid.imsFactoryReset()
+        if ad.droid.telecomIsInCall():
+            ad.droid.telecomEndCall()
+        if not wait_for_droid_not_in_call(log, ad):
+            ad.log.error("Failed to end call on %s")
+    except Exception as e:
+        ad.log.error("Failure %s, toggle APM instead", e)
+        toggle_airplane_mode(log, ad, True, False)
+        ad.droid.telephonyToggleDataConnection(True)
+        set_wfc_mode(log, ad, WFC_MODE_DISABLED)
+
+    if not toggle_airplane_mode(log, ad, False, False):
+        ad.log.error("Fail to turn off airplane mode")
         result = False
-
-    set_wfc_mode(log, ad, WFC_MODE_DISABLED)
-
-    toggle_video_calling(log, ad, False)
+    set_wifi_to_default(log, ad)
 
     if not wait_for_not_network_rat(
             log, ad, RAT_FAMILY_WLAN, voice_or_data=NETWORK_SERVICE_DATA):
-        log.error(
-            "ensure_phones_default_state: wait_for_droid_not_in iwlan fail {}.".
-            format(ad.serial))
+        ad.log.error("%s still in %s", NETWORK_SERVICE_DATA, RAT_FAMILY_WLAN)
         result = False
 
-    if (not WifiUtils.wifi_reset(log, ad)):
-        log.error("ensure_phones_default_state:reset WiFi fail {}.".format(
-            ad.serial))
+    if getattr(ad, 'data_roaming', False):
+        ad.log.info("Enable cell data roaming")
+        toggle_cell_data_roaming(ad, True)
+    if check_subscription and not ensure_phone_subscription(log, ad):
+        ad.log.error("Unable to find a valid subscription!")
         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.telephonyToggleDataConnection(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):
+def ensure_phones_default_state(log, ads, check_subscription=True):
     """Ensure ads in default state.
     Phone not in call.
     Phone have no stored WiFi network and WiFi disconnected.
@@ -3178,34 +3702,179 @@
     """
     tasks = []
     for ad in ads:
-        tasks.append((ensure_phone_default_state, (log, ad)))
+        tasks.append((ensure_phone_default_state, (log, ad,
+                                                   check_subscription)))
     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=0):
-    """Ensure ad connected to wifi.
+def check_is_wifi_connected(log, ad, wifi_ssid):
+    """Check if ad is connected to wifi wifi_ssid.
 
     Args:
         log: Log object.
         ad: Android device object.
         wifi_ssid: WiFi network SSID.
-        wifi_pwd: WiFi network password. This is optional.
 
+    Returns:
+        True if wifi is connected to wifi_ssid
+        False if wifi is not connected to wifi_ssid
     """
-    while (retry >= 0):
-        WifiUtils.wifi_reset(log, ad)
-        WifiUtils.wifi_toggle_state(log, ad, True)
-        if WifiUtils.wifi_connect(log, ad, wifi_ssid, wifi_pwd):
+    wifi_info = ad.droid.wifiGetConnectionInfo()
+    if wifi_info["supplicant_state"] == "completed" and wifi_info[
+            "SSID"] == wifi_ssid:
+        ad.log.info("Wifi is connected to %s", wifi_ssid)
+        return True
+    else:
+        ad.log.info("Wifi is not connected to %s", wifi_ssid)
+        ad.log.debug("Wifi connection_info=%s", wifi_info)
+        return False
+
+
+def ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd=None, retries=3):
+    """Ensure ad connected to wifi on network wifi_ssid.
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        wifi_ssid: WiFi network SSID.
+        wifi_pwd: optional secure network password.
+        retries: the number of retries.
+
+    Returns:
+        True if wifi is connected to wifi_ssid
+        False if wifi is not connected to wifi_ssid
+    """
+    network = {WIFI_SSID_KEY: wifi_ssid}
+    if wifi_pwd:
+        network[WIFI_PWD_KEY] = wifi_pwd
+    for i in range(retries):
+        if not ad.droid.wifiCheckState():
+            ad.log.info("Wifi state is down. Turn on Wifi")
+            ad.droid.wifiToggleState(True)
+        if check_is_wifi_connected(log, ad, wifi_ssid):
+            ad.log.info("Wifi is connected to %s", wifi_ssid)
             return True
         else:
-            log.info("ensure_wifi_connected: Connect WiFi failed")
-            retry -= 1
+            ad.log.info("Connecting to wifi %s", wifi_ssid)
+            ad.droid.wifiConnectByConfig(network)
+            time.sleep(20)
+            if check_is_wifi_connected(log, ad, wifi_ssid):
+                ad.log.info("Connected to Wifi %s", wifi_ssid)
+                return True
+    ad.log.info("Fail to connected to wifi %s", wifi_ssid)
     return False
 
 
+def forget_all_wifi_networks(log, ad):
+    """Forget all stored wifi network information
+
+    Args:
+        log: log object
+        ad: AndroidDevice object
+
+    Returns:
+        boolean success (True) or failure (False)
+    """
+    if not ad.droid.wifiGetConfiguredNetworks():
+        return True
+    try:
+        old_state = ad.droid.wifiCheckState()
+        wifi_test_utils.reset_wifi(ad)
+        wifi_toggle_state(log, ad, old_state)
+    except Exception as e:
+        log.error("forget_all_wifi_networks with exception: %s", e)
+        return False
+    return True
+
+
+def wifi_reset(log, ad, disable_wifi=True):
+    """Forget all stored wifi networks and (optionally) disable WiFi
+
+    Args:
+        log: log object
+        ad: AndroidDevice object
+        disable_wifi: boolean to disable wifi, defaults to True
+    Returns:
+        boolean success (True) or failure (False)
+    """
+    if not forget_all_wifi_networks(log, ad):
+        ad.log.error("Unable to forget all networks")
+        return False
+    if not wifi_toggle_state(log, ad, not disable_wifi):
+        ad.log.error("Failed to toggle WiFi state to %s!", not disable_wifi)
+        return False
+    return True
+
+
+def set_wifi_to_default(log, ad):
+    """Set wifi to default state (Wifi disabled and no configured network)
+
+    Args:
+        log: log object
+        ad: AndroidDevice object
+
+    Returns:
+        boolean success (True) or failure (False)
+    """
+    ad.droid.wifiFactoryReset()
+    ad.droid.wifiToggleState(False)
+
+
+def wifi_toggle_state(log, ad, state, retries=3):
+    """Toggle the WiFi State
+
+    Args:
+        log: log object
+        ad: AndroidDevice object
+        state: True, False, or None
+
+    Returns:
+        boolean success (True) or failure (False)
+    """
+    for i in range(retries):
+        if wifi_test_utils.wifi_toggle_state(ad, state, assert_on_fail=False):
+            return True
+        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+    return False
+
+
+def start_wifi_tethering(log, ad, ssid, password, ap_band=None):
+    """Start a Tethering Session
+
+    Args:
+        log: log object
+        ad: AndroidDevice object
+        ssid: the name of the WiFi network
+        password: optional password, used for secure networks.
+        ap_band=DEPRECATED specification of 2G or 5G tethering
+    Returns:
+        boolean success (True) or failure (False)
+    """
+    return wifi_test_utils._assert_on_fail_handler(
+        wifi_test_utils.start_wifi_tethering,
+        False,
+        ad,
+        ssid,
+        password,
+        band=ap_band)
+
+
+def stop_wifi_tethering(log, ad):
+    """Stop a Tethering Session
+
+    Args:
+        log: log object
+        ad: AndroidDevice object
+    Returns:
+        boolean success (True) or failure (False)
+    """
+    return wifi_test_utils._assert_on_fail_handler(
+        wifi_test_utils.stop_wifi_tethering, False, ad)
+
+
 def reset_preferred_network_type_to_allowable_range(log, ad):
     """If preferred network type is not in allowable range, reset to GEN_4G
     preferred network type.
@@ -3217,17 +3886,15 @@
     Returns:
         None
     """
-    sub_info_list = ad.droid.subscriptionGetAllSubInfoList()
-    for sub_info in sub_info_list:
-        sub_id = sub_info['subscriptionId']
-        operator = get_operator_name(log, ad, sub_id)
+    for sub_id, sub_info in ad.cfg["subscription"].items():
         current_preference = \
             ad.droid.telephonyGetPreferredNetworkTypesForSubscription(sub_id)
+        ad.log.debug("sub_id network preference is %s", current_preference)
         try:
             if current_preference not in get_allowable_network_preference(
-                    operator):
-                network_preference = network_preference_for_generaton(GEN_4G,
-                                                                      operator)
+                    sub_info["operator"], sub_info["phone_type"]):
+                network_preference = network_preference_for_generaton(
+                    GEN_4G, sub_info["operator"], sub_info["phone_type"])
                 ad.droid.telephonySetPreferredNetworkTypesForSubscription(
                     network_preference, sub_id)
         except KeyError:
@@ -3249,6 +3916,27 @@
     return func(*params)
 
 
+def run_multithread_func(log, tasks):
+    """Run multi-thread functions and return results.
+
+    Args:
+        log: log object.
+        tasks: a list of tasks to be executed in parallel.
+
+    Returns:
+        results for tasks.
+    """
+    MAX_NUMBER_OF_WORKERS = 5
+    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 %s result: %s",
+             [task[0].__name__ for task in tasks], results)
+    return results
+
+
 def multithread_func(log, tasks):
     """Multi-thread function wrapper.
 
@@ -3260,19 +3948,37 @@
         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))
+    results = run_multithread_func(log, tasks)
     for r in results:
         if not r:
             return False
     return True
 
 
+def multithread_func_and_check_results(log, tasks, expected_results):
+    """Multi-thread function wrapper.
+
+    Args:
+        log: log object.
+        tasks: tasks to be executed in parallel.
+        expected_results: check if the results from tasks match expected_results.
+
+    Returns:
+        True if expected_results are met.
+        False if expected_results are not met.
+    """
+    return_value = True
+    results = run_multithread_func(log, tasks)
+    log.info("multithread_func result: %s, expecting %s", results,
+             expected_results)
+    for task, result, expected_result in zip(tasks, results, expected_results):
+        if result != expected_result:
+            logging.info("Result for task %s is %s, expecting %s", task[0],
+                         result, expected_result)
+            return_value = False
+    return return_value
+
+
 def set_phone_screen_on(log, ad, screen_on_time=MAX_SCREEN_ON_TIME):
     """Set phone screen on time.
 
@@ -3300,6 +4006,10 @@
         True if set successfully.
     """
     ad.droid.toggleRingerSilentMode(silent_mode)
+    ad.droid.setMediaVolume(0)
+    ad.droid.setVoiceCallVolume(0)
+    ad.droid.setAlarmVolume(0)
+
     return silent_mode == ad.droid.checkRingerSilentMode()
 
 
@@ -3312,8 +4022,7 @@
         sub_id :Subscription ID.
 
     """
-    log.info("Setting subscription:{} as Message SIM for {}".format(sub_id,
-                                                                    ad.serial))
+    ad.log.info("Setting subscription %s as preferred SMS SIM", sub_id)
     ad.droid.subscriptionSetDefaultSmsSubId(sub_id)
     # Wait to make sure settings take effect
     time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
@@ -3329,8 +4038,7 @@
         sub_id :Subscription ID.
 
     """
-    log.info("Setting subscription:{} as Data SIM for {}".format(sub_id,
-                                                                 ad.serial))
+    ad.log.info("Setting subscription %s as preferred Data SIM", sub_id)
     ad.droid.subscriptionSetDefaultDataSubId(sub_id)
     time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
     # Wait to make sure settings take effect
@@ -3352,8 +4060,7 @@
         sub_id :Subscription ID.
 
     """
-    log.info("Setting subscription:{} as Voice SIM for {}".format(sub_id,
-                                                                  ad.serial))
+    ad.log.info("Setting subscription %s as Voice SIM", sub_id)
     ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
     ad.droid.telecomSetUserSelectedOutgoingPhoneAccountBySubId(sub_id)
     # Wait to make sure settings take effect
@@ -3456,9 +4163,8 @@
     try:
         return (
             (network_callback_id == event['data'][NetworkCallbackContainer.ID])
-            and
-            (network_callback_event ==
-             event['data'][NetworkCallbackContainer.NETWORK_CALLBACK_EVENT]))
+            and (network_callback_event == event['data'][
+                NetworkCallbackContainer.NETWORK_CALLBACK_EVENT]))
     except KeyError:
         return False
 
@@ -3477,8 +4183,7 @@
     """
     actual_bid = ad.droid.getBuildID()
 
-    log.info("{} BUILD DISPLAY: {}"
-             .format(ad.serial, ad.droid.getBuildDisplay()))
+    ad.log.info("BUILD DISPLAY: %s", 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: {} "
@@ -3488,7 +4193,7 @@
     #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))
+        ad.log.error("%s: Incorrect Build ID", ad.model)
         return False
     return True
 
@@ -3559,170 +4264,3 @@
         return uri_number
     else:
         return None
-
-
-# TODO: b/26294018 Remove wrapper class once wifi_utils methods updated
-class WifiUtils():
-
-    from acts.test_utils.wifi.wifi_test_utils \
-        import reset_wifi as _reset_wifi
-    from acts.test_utils.wifi.wifi_test_utils \
-        import wifi_connect as _wifi_connect
-    from acts.test_utils.wifi.wifi_test_utils \
-        import wifi_toggle_state as _wifi_toggle_state
-    from acts.test_utils.wifi.wifi_test_utils \
-        import start_wifi_tethering as _start_wifi_tethering
-    from acts.test_utils.wifi.wifi_test_utils \
-        import stop_wifi_tethering as _stop_wifi_tethering
-    from acts.test_utils.wifi.wifi_test_utils \
-        import WifiEnums as _WifiEnums
-    from acts.test_utils.wifi.wifi_test_utils \
-        import WifiEventNames as _WifiEventNames
-
-    WIFI_CONFIG_APBAND_2G = _WifiEnums.WIFI_CONFIG_APBAND_2G
-    WIFI_CONFIG_APBAND_5G = _WifiEnums.WIFI_CONFIG_APBAND_5G
-    SSID_KEY = _WifiEnums.SSID_KEY
-    PWD_KEY = _WifiEnums.PWD_KEY
-
-    @staticmethod
-    def wifi_toggle_state(log, ad, state):
-        """Toggle the WiFi State
-
-        Args:
-            log: log object
-            ad: AndroidDevice object
-            state: True, False, or None
-
-        Returns:
-            boolean success (True) or failure (False)
-        """
-        try:
-            WifiUtils._wifi_toggle_state(ad, state)
-        except Exception as e:
-            log.error("WifiUtils.wifi_toggle_state exception: {}".format(e))
-            return False
-        return True
-
-    @staticmethod
-    def forget_all_networks(log, ad):
-        """Forget all stored wifi network information
-
-        Args:
-            log: log object
-            ad: AndroidDevice object
-
-        Returns:
-            boolean success (True) or failure (False)
-        """
-        networks = ad.droid.wifiGetConfiguredNetworks()
-        if networks is None:
-            return True
-        for network in networks:
-            ad.droid.wifiForgetNetwork(network['networkId'])
-            try:
-                event = ad.ed.pop_event(
-                    WifiUtils._WifiEventNames.WIFI_FORGET_NW_SUCCESS)
-            except Empty:
-                log.warning("Could not confirm the removal of network {}.".
-                            format(network))
-        networks = ad.droid.wifiGetConfiguredNetworks()
-        if len(networks):
-            log.error("Failed to forget all networks {}.".format(networks))
-            return False
-        return True
-
-    @staticmethod
-    def wifi_reset(log, ad, disable_wifi=True):
-        """Forget all stored wifi networks and (optionally) disable WiFi
-
-        Args:
-            log: log object
-            ad: AndroidDevice object
-            disable_wifi: boolean to disable wifi, defaults to True
-        Returns:
-            boolean success (True) or failure (False)
-        """
-        if disable_wifi is True:
-            if not WifiUtils.wifi_toggle_state(log, ad, False):
-                log.error("Failed to disable WiFi during reset!")
-                return False
-            # Ensure toggle state has human-time to take effect
-            time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-        return WifiUtils.forget_all_networks(log, ad)
-
-    @staticmethod
-    def wifi_connect(log, ad, ssid, password=None):
-        """Connect to a WiFi network with a provided SSID and Password
-
-        Args:
-            log: log object
-            ad: AndroidDevice object
-            ssid: the name of the WiFi network
-            password: optional password, used for secure networks.
-        Returns:
-            boolean success (True) or failure (False)
-        """
-        if password == "":
-            password = None
-        try:
-            network = {WifiUtils.SSID_KEY: ssid}
-            if password:
-                network[WifiUtils.PWD_KEY] = password
-            WifiUtils._wifi_connect(ad, network)
-        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 failed with {}.".format(e))
-                return False
-        except Exception as e:
-            log.error("WifiUtils.wifi_connect exception: {}".format(e))
-            return False
-        return True
-
-    @staticmethod
-    def start_wifi_tethering(log, ad, ssid, password, ap_band=None):
-        """Start a Tethering Session
-
-        Args:
-            log: log object
-            ad: AndroidDevice object
-            ssid: the name of the WiFi network
-            password: optional password, used for secure networks.
-            ap_band=DEPRECATED specification of 2G or 5G tethering
-        Returns:
-            boolean success (True) or failure (False)
-        """
-        try:
-            WifiUtils._start_wifi_tethering(ad, ssid, password, ap_band)
-        except Exception as e:
-            log.error("WifiUtils.start_wifi_tethering exception: {}".format(e))
-            return False
-        return True
-
-    @staticmethod
-    def stop_wifi_tethering(log, ad):
-        """Stop a Tethering Session
-
-        Args:
-            log: log object
-            ad: AndroidDevice object
-        Returns:
-            boolean success (True) or failure (False)
-        """
-        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
index 2567741..e670bd3 100644
--- a/acts/framework/acts/test_utils/tel/tel_video_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_video_utils.py
@@ -59,7 +59,6 @@
 from acts.test_utils.tel.tel_test_utils import hangup_call
 from acts.test_utils.tel.tel_test_utils import set_wfc_mode
 from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts.test_utils.tel.tel_test_utils import toggle_video_calling
 from acts.test_utils.tel.tel_test_utils import toggle_volte
 from acts.test_utils.tel.tel_test_utils import verify_incall_state
 from acts.test_utils.tel.tel_test_utils import wait_for_network_generation
@@ -83,10 +82,15 @@
     Returns:
         True if ad (default sub_id) is setup correctly and idle for video call.
     """
-    return phone_setup_video_for_subscription(log, ad, get_outgoing_voice_sub_id(ad), wfc_mode)
+    return phone_setup_video_for_subscription(log, ad,
+                                              get_outgoing_voice_sub_id(ad),
+                                              wfc_mode)
 
 
-def phone_setup_video_for_subscription(log, ad, sub_id, wfc_mode=WFC_MODE_DISABLED):
+def phone_setup_video_for_subscription(log,
+                                       ad,
+                                       sub_id,
+                                       wfc_mode=WFC_MODE_DISABLED):
     """Setup phone sub_id to make video call
 
     Args:
@@ -103,16 +107,13 @@
 
     toggle_airplane_mode(log, ad, False)
     if not set_wfc_mode(log, ad, wfc_mode):
-        log.error("{} WFC mode failed to be set to {}.".format(ad.serial, wfc_mode))
+        log.error(
+            "{} WFC mode failed to be set to {}.".format(ad.serial, wfc_mode))
         return False
     toggle_volte(log, ad, True)
 
-    toggle_video_calling(log, ad, True)
-
-    if not ensure_network_generation(log,
-                                     ad,
-                                     GEN_4G,
-                                     voice_or_data=NETWORK_SERVICE_DATA):
+    if not ensure_network_generation(
+            log, ad, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
         log.error("{} voice not in LTE mode.".format(ad.serial))
         return False
 
@@ -150,9 +151,8 @@
         return False
 
     if not wait_for_video_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED):
-        log.error(
-                "{} failed to <report video calling enabled> within {}s.".format(
-                        ad.serial, MAX_WAIT_TIME_VOLTE_ENABLED))
+        log.error("{} failed to <report video calling enabled> within {}s.".
+                  format(ad.serial, MAX_WAIT_TIME_VOLTE_ENABLED))
         return False
     return True
 
@@ -172,7 +172,7 @@
         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, get_outgoing_voice_sub_id(ad))
+        log, ad, get_outgoing_voice_sub_id(ad))
 
 
 def is_phone_in_call_video_for_subscription(log, ad, sub_id, video_state=None):
@@ -191,8 +191,8 @@
     """
 
     if video_state is None:
-        log.info("Verify if {}(subid {}) in video call.".format(ad.serial,
-                                                                sub_id))
+        log.info(
+            "Verify if {}(subid {}) in video call.".format(ad.serial, sub_id))
     if not ad.droid.telecomIsInCall():
         log.error("{} not in call.".format(ad.serial))
         return False
@@ -201,14 +201,14 @@
         state = ad.droid.telecomCallVideoGetState(call)
         if video_state is None:
             if {
-                VT_STATE_AUDIO_ONLY: False,
-                VT_STATE_TX_ENABLED: True,
-                VT_STATE_TX_PAUSED: True,
-                VT_STATE_RX_ENABLED: True,
-                VT_STATE_RX_PAUSED: True,
-                VT_STATE_BIDIRECTIONAL: True,
-                VT_STATE_BIDIRECTIONAL_PAUSED: True,
-                VT_STATE_STATE_INVALID: False
+                    VT_STATE_AUDIO_ONLY: False,
+                    VT_STATE_TX_ENABLED: True,
+                    VT_STATE_TX_PAUSED: True,
+                    VT_STATE_RX_ENABLED: True,
+                    VT_STATE_RX_PAUSED: True,
+                    VT_STATE_BIDIRECTIONAL: True,
+                    VT_STATE_BIDIRECTIONAL_PAUSED: True,
+                    VT_STATE_STATE_INVALID: False
             }[state]:
                 return True
         else:
@@ -230,7 +230,7 @@
         True if phone in bi-directional video call.
     """
     return is_phone_in_call_video_bidirectional_for_subscription(
-            log, ad, get_outgoing_voice_sub_id(ad))
+        log, ad, get_outgoing_voice_sub_id(ad))
 
 
 def is_phone_in_call_video_bidirectional_for_subscription(log, ad, sub_id):
@@ -245,7 +245,7 @@
         True if phone in bi-directional video call.
     """
     log.info("Verify if {}(subid {}) in bi-directional video call.".format(
-            ad.serial, sub_id))
+        ad.serial, sub_id))
     return is_phone_in_call_video_for_subscription(log, ad, sub_id,
                                                    VT_STATE_BIDIRECTIONAL)
 
@@ -261,7 +261,7 @@
         True if phone in tx_enabled video call.
     """
     return is_phone_in_call_video_tx_enabled_for_subscription(
-            log, ad, get_outgoing_voice_sub_id(ad))
+        log, ad, get_outgoing_voice_sub_id(ad))
 
 
 def is_phone_in_call_video_tx_enabled_for_subscription(log, ad, sub_id):
@@ -276,7 +276,7 @@
         True if phone in tx_enabled video call.
     """
     log.info("Verify if {}(subid {}) in tx_enabled video call.".format(
-            ad.serial, sub_id))
+        ad.serial, sub_id))
     return is_phone_in_call_video_for_subscription(log, ad, sub_id,
                                                    VT_STATE_TX_ENABLED)
 
@@ -292,7 +292,7 @@
         True if phone in rx_enabled video call.
     """
     return is_phone_in_call_video_rx_enabled_for_subscription(
-            log, ad, get_outgoing_voice_sub_id(ad))
+        log, ad, get_outgoing_voice_sub_id(ad))
 
 
 def is_phone_in_call_video_rx_enabled_for_subscription(log, ad, sub_id):
@@ -307,7 +307,7 @@
         True if phone in rx_enabled video call.
     """
     log.info("Verify if {}(subid {}) in rx_enabled video call.".format(
-            ad.serial, sub_id))
+        ad.serial, sub_id))
     return is_phone_in_call_video_for_subscription(log, ad, sub_id,
                                                    VT_STATE_RX_ENABLED)
 
@@ -323,7 +323,7 @@
         True if phone in hd voice call.
     """
     return is_phone_in_call_voice_hd_for_subscription(
-            log, ad, get_outgoing_voice_sub_id(ad))
+        log, ad, get_outgoing_voice_sub_id(ad))
 
 
 def is_phone_in_call_voice_hd_for_subscription(log, ad, sub_id):
@@ -337,8 +337,8 @@
     Returns:
         True if phone in hd voice call.
     """
-    log.info("Verify if {}(subid {}) in hd voice call.".format(ad.serial,
-                                                               sub_id))
+    log.info(
+        "Verify if {}(subid {}) in hd voice call.".format(ad.serial, sub_id))
     if not ad.droid.telecomIsInCall():
         log.error("{} not in call.".format(ad.serial))
         return False
@@ -347,7 +347,7 @@
         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)))
+            state, ad.droid.telecomCallGetProperties(call)))
     return False
 
 
@@ -398,8 +398,9 @@
         False: for errors
     """
     return wait_and_answer_video_call_for_subscription(
-            log, ad, get_outgoing_voice_sub_id(ad), incoming_number, video_state,
-            incall_ui_display)
+        log, ad,
+        get_outgoing_voice_sub_id(ad), incoming_number, video_state,
+        incall_ui_display)
 
 
 def wait_and_answer_video_call_for_subscription(
@@ -429,9 +430,8 @@
     """
 
     if not wait_for_ringing_call(log, ad, incoming_number):
-        log.error(
-                "Video call could not be established: <{}> never rang.".format(
-                        ad.serial))
+        log.error("Video call could not be established: <{}> never rang.".
+                  format(ad.serial))
         return False
 
     ad.ed.clear_all_events()
@@ -447,11 +447,11 @@
 
     try:
         ad.ed.wait_for_event(
-                EventCallStateChanged,
-                is_event_match,
-                timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
-                field=CallStateContainer.CALL_STATE,
-                value=TELEPHONY_STATE_OFFHOOK)
+            EventCallStateChanged,
+            is_event_match,
+            timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
+            field=CallStateContainer.CALL_STATE,
+            value=TELEPHONY_STATE_OFFHOOK)
     except Empty:
         if not ad.droid.telecomIsInCall():
             log.error("Accept call failed.")
@@ -505,11 +505,11 @@
 
     """
     return video_call_setup_teardown_for_subscription(
-            log, ad_caller, ad_callee, get_outgoing_voice_sub_id(ad_caller),
-            get_incoming_voice_sub_id(ad_callee), ad_hangup, video_state,
-            verify_caller_func, verify_callee_func, wait_time_in_call,
-            incall_ui_display)
-
+        log, ad_caller, ad_callee,
+        get_outgoing_voice_sub_id(ad_caller),
+        get_incoming_voice_sub_id(ad_callee), ad_hangup, video_state,
+        verify_caller_func, verify_callee_func, wait_time_in_call,
+        incall_ui_display)
 
 # TODO: b/26337151 Might be able to re-factor call_setup_teardown and add.
 # Minimal changes.
@@ -557,6 +557,7 @@
         False if error happened.
 
     """
+    CHECK_INTERVAL = 60
 
     class _CallSequenceException(Exception):
         pass
@@ -592,32 +593,45 @@
         if verify_caller_func and not verify_caller_func(log, ad_caller):
             raise _CallSequenceException("Caller not in correct state!")
 
+        time.sleep(5)
+        ad_caller.adb.shell("input keyevent 22",timeout=3)
+        ad_callee.adb.shell("input keyevent 22",timeout=3)
+        ad_caller.adb.shell("input keyevent 22",timeout=3)
+        ad_callee.adb.shell("input keyevent 22",timeout=3)
+        ad_caller.adb.shell("input keyevent 66",timeout=3)
+        ad_callee.adb.shell("input keyevent 66",timeout=3)
+
         # TODO: b/26291165 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)
+        elapsed_time = 0
+        while (elapsed_time < wait_time_in_call):
+            CHECK_INTERVAL = min(CHECK_INTERVAL,
+                                 wait_time_in_call - elapsed_time)
+            time.sleep(CHECK_INTERVAL)
+            elapsed_time += CHECK_INTERVAL
 
-        # 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))
+            # 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 at <{}>/<{}> seconds"
+                    .format(elapsed_time, 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_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 at <{}>/<{}> seconds"
+                    .format(elapsed_time, wait_time_in_call))
 
         if not ad_hangup:
             return True
@@ -638,6 +652,113 @@
                 except Exception as e:
                     log.error(str(e))
 
+def video_call_setup(log,
+                     ad_caller,
+                     ad_callee,
+                     video_state=VT_STATE_BIDIRECTIONAL,
+                     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>,
+    accept the call, (optional)then hang up from <droid_hangup>.
+
+    Args:
+        ad_caller: Caller Android Device Object.
+        ad_callee: Callee Android Device Object.
+        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_for_subscription(
+        log, ad_caller, ad_callee,
+        get_outgoing_voice_sub_id(ad_caller),
+        get_incoming_voice_sub_id(ad_callee),
+        video_state, incall_ui_display)
+
+def video_call_setup_for_subscription(
+        log,
+        ad_caller,
+        ad_callee,
+        subid_caller,
+        subid_callee,
+        video_state=VT_STATE_BIDIRECTIONAL,
+        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>,
+    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.
+        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
+        time.sleep(WAIT_TIME_ACCEPT_VIDEO_CALL_TO_CHECK_STATE)
+
+        # Below step is needed for allow camera pop-up
+        time.sleep(5)
+        ad_caller.adb.shell("input keyevent 22",timeout=3)
+        ad_callee.adb.shell("input keyevent 22",timeout=3)
+        ad_caller.adb.shell("input keyevent 22",timeout=3)
+        ad_callee.adb.shell("input keyevent 22",timeout=3)
+        ad_caller.adb.shell("input keyevent 66",timeout=3)
+        ad_callee.adb.shell("input keyevent 66",timeout=3)
+
+        # TODO: b/26291165 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)
+
+        return True
+
+    except _CallSequenceException as e:
+        log.error(e)
+        return False
 
 def video_call_modify_video(log,
                             ad_requester,
@@ -675,7 +796,7 @@
         video_quality_response = video_quality_request
 
     cur_video_state = ad_requester.droid.telecomCallVideoGetState(
-            call_id_requester)
+        call_id_requester)
 
     log.info("State change request from {} to {} requested"
              .format(cur_video_state, video_state_request))
@@ -684,25 +805,25 @@
         return True
 
     ad_responder.ed.clear_events(
-            EventTelecomVideoCallSessionModifyRequestReceived)
+        EventTelecomVideoCallSessionModifyRequestReceived)
 
     ad_responder.droid.telecomCallVideoStartListeningForEvent(
-            call_id_responder, EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED)
+        call_id_responder, EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED)
 
     ad_requester.droid.telecomCallVideoSendSessionModifyRequest(
-            call_id_requester, video_state_request, video_quality_request)
+        call_id_requester, video_state_request, video_quality_request)
 
     try:
         request_event = ad_responder.ed.pop_event(
-                EventTelecomVideoCallSessionModifyRequestReceived,
-                MAX_WAIT_TIME_VIDEO_SESSION_EVENT)
+            EventTelecomVideoCallSessionModifyRequestReceived,
+            MAX_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, EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED)
+            call_id_responder, EVENT_VIDEO_SESSION_MODIFY_REQUEST_RECEIVED)
 
     if (verify_func_between_request_and_response and
             not verify_func_between_request_and_response()):
@@ -714,22 +835,22 @@
     ad_requester.droid.telecomCallSetAudioRoute(AUDIO_ROUTE_EARPIECE)
 
     ad_requester.droid.telecomCallVideoStartListeningForEvent(
-            call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
+        call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
 
     ad_responder.droid.telecomCallVideoSendSessionModifyResponse(
-            call_id_responder, video_state_response, video_quality_response)
+        call_id_responder, video_state_response, video_quality_response)
 
     try:
         response_event = ad_requester.ed.pop_event(
-                EventTelecomVideoCallSessionModifyResponseReceived,
-                MAX_WAIT_TIME_VIDEO_SESSION_EVENT)
+            EventTelecomVideoCallSessionModifyResponseReceived,
+            MAX_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, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
+            call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
 
     # TODO: b/26291165 Replace with reducing the volume as we want
     # to test route switching
@@ -804,21 +925,21 @@
     """
     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))
+            call_id_requester, call_id_responder))
         return False
     current_video_state_requester = ad_requester.droid.telecomCallVideoGetState(
-            call_id_requester)
+        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):
+                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):
+              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))
+                ad_requester.serial, current_video_state_requester))
             return False
     expected_video_state_responder = {
         VT_STATE_AUDIO_ONLY: VT_STATE_AUDIO_ONLY,
@@ -826,22 +947,22 @@
     }[video_state_request]
 
     ad_requester.droid.telecomCallVideoStartListeningForEvent(
-            call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
+        call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
 
     ad_requester.droid.telecomCallVideoSendSessionModifyRequest(
-            call_id_requester, video_state_request, video_quality_request)
+        call_id_requester, video_state_request, video_quality_request)
 
     try:
         response_event = ad_requester.ed.pop_event(
-                EventTelecomVideoCallSessionModifyResponseReceived,
-                MAX_WAIT_TIME_VIDEO_SESSION_EVENT)
+            EventTelecomVideoCallSessionModifyResponseReceived,
+            MAX_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, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
+            call_id_requester, EVENT_VIDEO_SESSION_MODIFY_RESPONSE_RECEIVED)
 
     time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
     # TODO: b/26291165 Replace with reducing the volume as we want
@@ -855,15 +976,14 @@
         log.error("requester not in correct state. expected:{}, current:{}"
                   .format(video_state_request,
                           ad_requester.droid.telecomCallVideoGetState(
-                                  call_id_requester)))
+                              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)))
+        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
@@ -885,10 +1005,10 @@
     """
     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)))
+            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)))
+            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
index 30dbf09..4997873 100644
--- a/acts/framework/acts/test_utils/tel/tel_voice_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_voice_utils.py
@@ -69,12 +69,15 @@
 from acts.test_utils.tel.tel_test_utils import verify_incall_state
 from acts.test_utils.tel.tel_test_utils import \
     wait_for_data_attach_for_subscription
+from acts.test_utils.tel.tel_test_utils import wait_for_network_generation
 from acts.test_utils.tel.tel_test_utils import \
     wait_for_network_generation_for_subscription
 from acts.test_utils.tel.tel_test_utils import wait_for_not_network_rat
 from acts.test_utils.tel.tel_test_utils import wait_for_network_rat
 from acts.test_utils.tel.tel_test_utils import \
     wait_for_network_rat_for_subscription
+from acts.test_utils.tel.tel_test_utils import \
+     wait_for_not_network_rat_for_subscription
 from acts.test_utils.tel.tel_test_utils import wait_for_volte_enabled
 from acts.test_utils.tel.tel_test_utils import \
     wait_for_voice_attach_for_subscription
@@ -116,10 +119,10 @@
     # 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")
+        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")
+        callee.log.error("Callee Failed to Reselect")
         return False
 
     # TODO: b/26337871 Need to use proper API to check phone registered.
@@ -167,30 +170,31 @@
     """
     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), ]
+    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")
+            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")
+            phone_b.log.error("Phone B Failed to Reselect")
             return False
 
         # TODO: b/26337871 Need to use proper API to check phone registered.
         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.info("---> Call test: %s to %s <---", 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
 
@@ -237,39 +241,41 @@
     """
     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), ]
+    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")
+            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")
+            phone_b.log.error("Phone B Failed to Reselect")
             return False
 
         # TODO: b/26337871 Need to use proper API to check phone registered.
         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.info("---> Call test: %s to %s <---", 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_iwlan(log,
                       ad,
                       is_airplane_mode,
@@ -295,9 +301,10 @@
     Returns:
         True if success. False if fail.
     """
-    return phone_setup_iwlan_for_subscription(
-        log, ad, get_outgoing_voice_sub_id(ad), is_airplane_mode, wfc_mode,
-        wifi_ssid, wifi_pwd)
+    return phone_setup_iwlan_for_subscription(log, ad,
+                                              get_outgoing_voice_sub_id(ad),
+                                              is_airplane_mode, wfc_mode,
+                                              wifi_ssid, wifi_pwd)
 
 
 def phone_setup_iwlan_for_subscription(log,
@@ -327,36 +334,40 @@
     Returns:
         True if success. False if fail.
     """
+    toggle_airplane_mode(log, ad, is_airplane_mode, strict_checking=False)
 
-    # VoLTE settings are unavailable in airplane mode
-    toggle_airplane_mode(log, ad, False)
-
-    # Now that we are out of APM, toggle VoLTE if necessary
-    if ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform():
-        toggle_volte(log, ad, True)
-
-    if not is_airplane_mode and not ensure_network_generation_for_subscription(
-            log,
-            ad,
-            sub_id,
-            GEN_4G,
-            voice_or_data=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
-
-    if not toggle_airplane_mode(log, ad, is_airplane_mode):
-        log.error("Failed to enable airplane mode on {}".format(ad.serial))
+    # check if WFC supported phones
+    if wfc_mode != WFC_MODE_DISABLED and not ad.droid.imsIsWfcEnabledByPlatform(
+    ):
+        ad.log.error("WFC is not enabled on this device by checking "
+                     "ImsManager.isWfcEnabledByPlatform")
         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))
+            ad.log.error("Fail to connect to WiFi %s.", wifi_ssid)
             return False
 
-    return phone_idle_iwlan_for_subscription(log, ad, sub_id)
+    if not set_wfc_mode(log, ad, wfc_mode):
+        ad.log.error("Unable to set WFC mode to %s.", wfc_mode)
+        return False
+
+    if not wait_for_wfc_enabled(log, ad, max_time=MAX_WAIT_TIME_WFC_ENABLED):
+        ad.log.error("WFC is not enabled")
+        return False
+
+    if wait_for_network_rat_for_subscription(
+            log, ad, sub_id, RAT_FAMILY_WLAN,
+            voice_or_data=NETWORK_SERVICE_DATA):
+        ad.log.info(
+            "Data rat is in iwlan mode successfully with APM %s WFC %s",
+            is_airplane_mode, wfc_mode)
+        return True
+    else:
+        ad.log.error(
+            "Unable to bring data rat in iwlan mode with APM %s WFC %s",
+            is_airplane_mode, wfc_mode)
+        return False
 
 
 def phone_setup_iwlan_cellular_preferred(log,
@@ -380,32 +391,36 @@
     Returns:
         True if success. False if fail.
     """
-    toggle_airplane_mode(log, ad, False)
-    toggle_volte(log, ad, True)
-    if not ensure_network_generation(log,
-                                     ad,
-                                     GEN_4G,
-                                     voice_or_data=NETWORK_SERVICE_DATA):
-        return False
+    toggle_airplane_mode(log, ad, False, strict_checking=False)
+    try:
+        toggle_volte(log, ad, True)
+        if not wait_for_network_generation(
+                log, ad, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+            if not ensure_network_generation(
+                    log, ad, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+                ad.log.error("Fail to ensure data in 4G")
+                return False
+    except Exception as e:
+        ad.log.error(e)
+        ad.droid.telephonyToggleDataConnection(True)
     if not set_wfc_mode(log, ad, WFC_MODE_CELLULAR_PREFERRED):
-        log.error("{} set WFC mode failed.".format(ad.serial))
+        ad.log.error("Set WFC mode failed.")
         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))
+            ad.log.error("Connect to WiFi failed.")
             return False
-    if not wait_for_not_network_rat(log,
-                                    ad,
-                                    RAT_FAMILY_WLAN,
-                                    voice_or_data=NETWORK_SERVICE_DATA):
-        log.error("{} data rat in iwlan mode.".format(ad.serial))
+    if not wait_for_not_network_rat(
+            log, ad, RAT_FAMILY_WLAN, voice_or_data=NETWORK_SERVICE_DATA):
+        ad.log.error("Data rat in iwlan mode.")
         return False
     elif not wait_for_wfc_disabled(log, ad, MAX_WAIT_TIME_WFC_ENABLED):
-        log.error("{} should report wifi calling disabled within {}s.".format(
-            ad.serial, MAX_WAIT_TIME_WFC_ENABLED))
+        ad.log.error("Should report wifi calling disabled within %s.",
+                     MAX_WAIT_TIME_WFC_ENABLED)
         return False
     return True
 
+
 def phone_setup_data_for_subscription(log, ad, sub_id, network_generation):
     """Setup Phone <sub_id> Data to <network_generation>
 
@@ -418,9 +433,9 @@
     Returns:
         True if success, False if fail.
     """
-    toggle_airplane_mode(log, ad, False)
+    toggle_airplane_mode(log, ad, False, strict_checking=False)
     if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
-        log.error("{} Disable WFC failed.".format(ad.serial))
+        ad.log.error("Disable WFC failed.")
         return False
     if not ensure_network_generation_for_subscription(
             log,
@@ -431,6 +446,7 @@
         return False
     return True
 
+
 def phone_setup_4g(log, ad):
     """Setup Phone default data sub_id data to 4G.
 
@@ -441,8 +457,9 @@
     Returns:
         True if success, False if fail.
     """
-    return phone_setup_4g_for_subscription(
-        log, ad, get_default_data_sub_id(ad))
+    return phone_setup_4g_for_subscription(log, ad,
+                                           get_default_data_sub_id(ad))
+
 
 def phone_setup_4g_for_subscription(log, ad, sub_id):
     """Setup Phone <sub_id> Data to 4G.
@@ -457,6 +474,7 @@
     """
     return phone_setup_data_for_subscription(log, ad, sub_id, GEN_4G)
 
+
 def phone_setup_3g(log, ad):
     """Setup Phone default data sub_id data to 3G.
 
@@ -467,8 +485,9 @@
     Returns:
         True if success, False if fail.
     """
-    return phone_setup_3g_for_subscription(
-        log, ad, get_default_data_sub_id(ad))
+    return phone_setup_3g_for_subscription(log, ad,
+                                           get_default_data_sub_id(ad))
+
 
 def phone_setup_3g_for_subscription(log, ad, sub_id):
     """Setup Phone <sub_id> Data to 3G.
@@ -483,6 +502,7 @@
     """
     return phone_setup_data_for_subscription(log, ad, sub_id, GEN_3G)
 
+
 def phone_setup_2g(log, ad):
     """Setup Phone default data sub_id data to 2G.
 
@@ -493,8 +513,9 @@
     Returns:
         True if success, False if fail.
     """
-    return phone_setup_2g_for_subscription(
-        log, ad, get_default_data_sub_id(ad))
+    return phone_setup_2g_for_subscription(log, ad,
+                                           get_default_data_sub_id(ad))
+
 
 def phone_setup_2g_for_subscription(log, ad, sub_id):
     """Setup Phone <sub_id> Data to 3G.
@@ -509,6 +530,7 @@
     """
     return phone_setup_data_for_subscription(log, ad, sub_id, GEN_2G)
 
+
 def phone_setup_csfb(log, ad):
     """Setup phone for CSFB call test.
 
@@ -543,16 +565,12 @@
         False for errors.
     """
     if not phone_setup_4g_for_subscription(log, ad, sub_id):
-        log.error("{} failed to set to 4G data.".format(ad.serial))
+        ad.log.error("Failed to set to 4G data.")
         return False
     if ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform():
         toggle_volte(log, ad, False)
     if not ensure_network_generation_for_subscription(
-            log,
-            ad,
-            sub_id,
-            GEN_4G,
-            voice_or_data=NETWORK_SERVICE_DATA):
+            log, ad, sub_id, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
         return False
 
     if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
@@ -561,6 +579,7 @@
 
     return phone_idle_csfb_for_subscription(log, ad, sub_id)
 
+
 def phone_setup_volte(log, ad):
     """Setup VoLTE enable.
 
@@ -572,8 +591,8 @@
         True: if VoLTE is enabled successfully.
         False: for errors
     """
-    return phone_setup_volte_for_subscription(
-        log, ad, get_outgoing_voice_sub_id(ad))
+    return phone_setup_volte_for_subscription(log, ad,
+                                              get_outgoing_voice_sub_id(ad))
 
 
 def phone_setup_volte_for_subscription(log, ad, sub_id):
@@ -589,11 +608,12 @@
         False: for errors
     """
     if not phone_setup_4g_for_subscription(log, ad, sub_id):
-        log.error("{} failed to set to 4G data.".format(ad.serial))
+        ad.log.error("Failed to set to 4G data.")
         return False
     toggle_volte_for_subscription(log, ad, sub_id, True)
     return phone_idle_volte_for_subscription(log, ad, sub_id)
 
+
 def phone_setup_voice_3g(log, ad):
     """Setup phone voice to 3G.
 
@@ -608,6 +628,7 @@
     return phone_setup_voice_3g_for_subscription(log, ad,
                                                  get_outgoing_voice_sub_id(ad))
 
+
 def phone_setup_voice_3g_for_subscription(log, ad, sub_id):
     """Setup phone voice to 3G for subscription id.
 
@@ -621,13 +642,14 @@
         False for errors.
     """
     if not phone_setup_3g_for_subscription(log, ad, sub_id):
-        log.error("{} failed to set to 3G data.".format(ad.serial))
+        ad.log.error("Failed to set to 3G data.")
         return False
     if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
                                                   MAX_WAIT_TIME_NW_SELECTION):
         return False
     return phone_idle_3g_for_subscription(log, ad, sub_id)
 
+
 def phone_setup_voice_2g(log, ad):
     """Setup phone voice to 2G.
 
@@ -639,8 +661,8 @@
         True if setup successfully.
         False for errors.
     """
-    return phone_setup_voice_2g_for_subscription(
-        log, ad, get_outgoing_voice_sub_id(ad))
+    return phone_setup_voice_2g_for_subscription(log, ad,
+                                                 get_outgoing_voice_sub_id(ad))
 
 
 def phone_setup_voice_2g_for_subscription(log, ad, sub_id):
@@ -656,13 +678,14 @@
         False for errors.
     """
     if not phone_setup_2g_for_subscription(log, ad, sub_id):
-        log.error("{} failed to set to 2G data.".format(ad.serial))
+        ad.log.error("Failed to set to 2G data.")
         return False
     if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
                                                   MAX_WAIT_TIME_NW_SELECTION):
         return False
     return phone_idle_2g_for_subscription(log, ad, sub_id)
 
+
 def phone_setup_voice_general(log, ad):
     """Setup phone for voice general call test.
 
@@ -694,13 +717,14 @@
         True if setup successfully.
         False for errors.
     """
-    toggle_airplane_mode(log, ad, False)
+    toggle_airplane_mode(log, ad, False, strict_checking=False)
     if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
                                                   MAX_WAIT_TIME_NW_SELECTION):
         # if phone can not attach voice, try phone_setup_voice_3g
         return phone_setup_voice_3g_for_subscription(log, ad, sub_id)
     return True
 
+
 def phone_setup_data_general(log, ad):
     """Setup phone for data general test.
 
@@ -717,6 +741,7 @@
     return phone_setup_data_general_for_subscription(
         log, ad, ad.droid.subscriptionGetDefaultDataSubId())
 
+
 def phone_setup_data_general_for_subscription(log, ad, sub_id):
     """Setup phone for data general test for subscription id.
 
@@ -731,7 +756,7 @@
         True if setup successfully.
         False for errors.
     """
-    toggle_airplane_mode(log, ad, False)
+    toggle_airplane_mode(log, ad, False, strict_checking=False)
     if not wait_for_data_attach_for_subscription(log, ad, sub_id,
                                                  MAX_WAIT_TIME_NW_SELECTION):
         # if phone can not attach data, try reset network preference settings
@@ -740,11 +765,12 @@
     return wait_for_data_attach_for_subscription(log, ad, sub_id,
                                                  MAX_WAIT_TIME_NW_SELECTION)
 
+
 def phone_setup_rat_for_subscription(log, ad, sub_id, network_preference,
                                      rat_family):
-    toggle_airplane_mode(log, ad, False)
+    toggle_airplane_mode(log, ad, False, strict_checking=False)
     if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
-        log.error("{} Disable WFC failed.".format(ad.serial))
+        ad.log.error("Disable WFC failed.")
         return False
     return ensure_network_rat_for_subscription(log, ad, sub_id,
                                                network_preference, rat_family)
@@ -818,17 +844,14 @@
         sub_id: subscription id.
     """
     if not wait_for_network_rat_for_subscription(
-            log,
-            ad,
-            sub_id,
-            RAT_FAMILY_LTE,
+            log, ad, sub_id, RAT_FAMILY_LTE,
             voice_or_data=NETWORK_SERVICE_VOICE):
-        log.error("{} voice rat not in LTE mode.".format(ad.serial))
+        ad.log.error("Voice rat not in LTE mode.")
         return False
     if not wait_for_volte_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED):
-        log.error(
-            "{} failed to <report volte enabled true> within {}s.".format(
-                ad.serial, MAX_WAIT_TIME_VOLTE_ENABLED))
+        ad.log.error(
+            "Failed to <report volte enabled true> within %s seconds.",
+            MAX_WAIT_TIME_VOLTE_ENABLED)
         return False
     return True
 
@@ -851,16 +874,38 @@
         sub_id: subscription id.
     """
     if not wait_for_network_rat_for_subscription(
-            log,
-            ad,
-            sub_id,
-            RAT_FAMILY_WLAN,
+            log, ad, sub_id, RAT_FAMILY_WLAN,
             voice_or_data=NETWORK_SERVICE_DATA):
-        log.error("{} data rat not in iwlan mode.".format(ad.serial))
+        ad.log.error("data rat not in iwlan mode.")
         return False
     if not wait_for_wfc_enabled(log, ad, MAX_WAIT_TIME_WFC_ENABLED):
-        log.error("{} failed to <report wfc enabled true> within {}s.".format(
-            ad.serial, MAX_WAIT_TIME_WFC_ENABLED))
+        ad.log.error("Failed to <report wfc enabled true> within %s seconds.",
+                     MAX_WAIT_TIME_WFC_ENABLED)
+        return False
+    return True
+
+
+def phone_idle_not_iwlan(log, ad):
+    """Return if phone is idle for non WiFi calling call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_not_iwlan_for_subscription(log, ad,
+                                                 get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_not_iwlan_for_subscription(log, ad, sub_id):
+    """Return if phone is idle for non WiFi calling call test for sub id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not wait_for_not_network_rat_for_subscription(
+            log, ad, sub_id, RAT_FAMILY_WLAN,
+            voice_or_data=NETWORK_SERVICE_DATA):
+        log.error("{} data rat in iwlan mode.".format(ad.serial))
         return False
     return True
 
@@ -883,12 +928,9 @@
         sub_id: subscription id.
     """
     if not wait_for_network_rat_for_subscription(
-            log,
-            ad,
-            sub_id,
-            RAT_FAMILY_LTE,
+            log, ad, sub_id, RAT_FAMILY_LTE,
             voice_or_data=NETWORK_SERVICE_DATA):
-        log.error("{} data rat not in lte mode.".format(ad.serial))
+        ad.log.error("Data rat not in lte mode.")
         return False
     return True
 
@@ -911,11 +953,7 @@
         sub_id: subscription id.
     """
     return wait_for_network_generation_for_subscription(
-        log,
-        ad,
-        sub_id,
-        GEN_3G,
-        voice_or_data=NETWORK_SERVICE_VOICE)
+        log, ad, sub_id, GEN_3G, voice_or_data=NETWORK_SERVICE_VOICE)
 
 
 def phone_idle_2g(log, ad):
@@ -936,11 +974,7 @@
         sub_id: subscription id.
     """
     return wait_for_network_generation_for_subscription(
-        log,
-        ad,
-        sub_id,
-        GEN_2G,
-        voice_or_data=NETWORK_SERVICE_VOICE)
+        log, ad, sub_id, GEN_2G, voice_or_data=NETWORK_SERVICE_VOICE)
 
 
 def is_phone_in_call_volte(log, ad):
@@ -961,13 +995,12 @@
         sub_id: subscription id.
     """
     if not ad.droid.telecomIsInCall():
-        log.error("{} not in call.".format(ad.serial))
+        ad.log.error("Not in call.")
         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))
+        ad.log.error("Voice rat on: %s. Expected: LTE", nw_type)
         return False
     return True
 
@@ -990,13 +1023,12 @@
         sub_id: subscription id.
     """
     if not ad.droid.telecomIsInCall():
-        log.error("{} not in call.".format(ad.serial))
+        ad.log.error("Not in call.")
         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))
+        ad.log.error("Voice rat on: %s. Expected: not LTE", nw_type)
         return False
     return True
 
@@ -1019,13 +1051,12 @@
         sub_id: subscription id.
     """
     if not ad.droid.telecomIsInCall():
-        log.error("{} not in call.".format(ad.serial))
+        ad.log.error("Not in call.")
         return False
     nw_gen = get_network_gen_for_subscription(log, ad, sub_id,
                                               NETWORK_SERVICE_VOICE)
     if nw_gen != GEN_3G:
-        log.error("{} voice rat on: {}. Expected: 3g".format(ad.serial,
-                                                             nw_gen))
+        ad.log.error("Voice rat on: %s. Expected: 3g", nw_gen)
         return False
     return True
 
@@ -1048,13 +1079,12 @@
         sub_id: subscription id.
     """
     if not ad.droid.telecomIsInCall():
-        log.error("{} not in call.".format(ad.serial))
+        ad.log.error("Not in call.")
         return False
     nw_gen = get_network_gen_for_subscription(log, ad, sub_id,
                                               NETWORK_SERVICE_VOICE)
     if nw_gen != GEN_2G:
-        log.error("{} voice rat on: {}. Expected: 2g".format(ad.serial,
-                                                             nw_gen))
+        ad.log.error("Voice rat on: %s. Expected: 2g", nw_gen)
         return False
     return True
 
@@ -1077,13 +1107,12 @@
         sub_id: subscription id.
     """
     if not ad.droid.telecomIsInCall():
-        log.error("{} not in call.".format(ad.serial))
+        ad.log.error("Not in call.")
         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))
+        ad.log.error("Voice rat on: %s. Expected: 1xrtt", nw_type)
         return False
     return True
 
@@ -1108,13 +1137,12 @@
     # Currently checking 'umts'.
     # Changes may needed in the future.
     if not ad.droid.telecomIsInCall():
-        log.error("{} not in call.".format(ad.serial))
+        ad.log.error("Not in call.")
         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))
+        ad.log.error("%s voice rat on: %s. Expected: umts", nw_type)
         return False
     return True
 
@@ -1126,15 +1154,14 @@
         ad: Android device object.
     """
     if not ad.droid.telecomIsInCall():
-        log.error("{} not in call.".format(ad.serial))
+        ad.log.error("Not in call.")
         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))
+        ad.log.error("Data rat on: %s. Expected: iwlan", nw_type)
         return False
     if not is_wfc_enabled(log, ad):
-        log.error("{} WiFi Calling feature bit is False.".format(ad.serial))
+        ad.log.error("WiFi Calling feature bit is False.")
         return False
     return True
 
@@ -1147,15 +1174,14 @@
         sub_id: subscription id.
     """
     if not ad.droid.telecomIsInCall():
-        log.error("{} not in call.".format(ad.serial))
+        ad.log.error("Not in call.")
         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))
+        ad.log.error("Data rat on: %s. Expected: not iwlan", nw_type)
         return False
     if is_wfc_enabled(log, ad):
-        log.error("{} WiFi Calling feature bit is True.".format(ad.serial))
+        ad.log.error("WiFi Calling feature bit is True.")
         return False
     return True
 
@@ -1192,21 +1218,20 @@
         # 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)))
+            ads[0].log.error(
+                "Call_id:%s, state:%s, expected: STATE_ACTIVE", 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)))
+            ads[0].log.error(
+                "Call_id:%s, state:%s, expected: STATE_HOLDING", 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].log.info("swap_test %s: swap and check call status.", i)
         ads[0].droid.telecomCallHold(call_active_id)
         time.sleep(WAIT_TIME_IN_CALL)
         # Swap object reference
@@ -1215,17 +1240,17 @@
             # 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)))
+                ads[0].log.error(
+                    "Call_id:%s, state:%s, expected: STATE_ACTIVE",
+                    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)))
+                ads[0].log.error(
+                    "Call_id:%s, state:%s, expected: STATE_HOLDING",
+                    call_hold_id,
+                    ads[0].droid.telecomCallGetCallState(call_hold_id))
                 return False
         # TODO: b/26296375 add voice check.
 
diff --git a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
new file mode 100755
index 0000000..9ff8f5b
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+    Base Class for Defining Common WiFi Test Functionality
+"""
+
+from acts import asserts
+from acts import utils
+from acts.base_test import BaseTestClass
+from acts.signals import TestSignal
+from acts.controllers import android_device
+from acts.controllers.ap_lib import hostapd_ap_preset
+from acts.controllers.ap_lib import hostapd_bss_settings
+from acts.controllers.ap_lib import hostapd_constants
+from acts.controllers.ap_lib import hostapd_security
+
+
+class WifiBaseTest(BaseTestClass):
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+
+    def legacy_configure_ap_and_start(
+            self,
+            channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
+            channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
+            max_2g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_2G,
+            max_5g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_5G,
+            ap_ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
+            ap_passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
+            ap_ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
+            ap_passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G,
+    ):
+        asserts.assert_true(
+            len(self.user_params["AccessPoint"]) == 2,
+            "Exactly two access points must be specified. \
+            If the access point has two radios, the configuration \
+            can be repeated for the second radio.")
+        network_list_2g = []
+        network_list_5g = []
+        self.access_point_2g = self.access_points[0]
+        self.access_point_5g = self.access_points[1]
+        network_list_2g.append({"channel": channel_2g})
+        network_list_5g.append({"channel": channel_5g})
+
+        if "reference_networks" in self.user_params:
+            pass
+        else:
+            ref_5g_security = hostapd_constants.WPA2_STRING
+            ref_2g_security = hostapd_constants.WPA2_STRING
+            self.user_params["reference_networks"] = []
+            for x in range(0, 2):
+                ref_2g_ssid = '2g_%s' % utils.rand_ascii_str(ap_ssid_length_2g)
+                ref_2g_passphrase = utils.rand_ascii_str(
+                    ap_passphrase_length_2g)
+                ref_5g_ssid = '5g_%s' % utils.rand_ascii_str(ap_ssid_length_5g)
+                ref_5g_passphrase = utils.rand_ascii_str(
+                    ap_passphrase_length_5g)
+                network_list_2g.append({
+                    "ssid": ref_2g_ssid,
+                    "security": ref_2g_security,
+                    "passphrase": ref_2g_passphrase
+                })
+                network_list_5g.append({
+                    "ssid": ref_5g_ssid,
+                    "security": ref_5g_security,
+                    "passphrase": ref_5g_passphrase
+                })
+                self.user_params["reference_networks"].append({
+                    "2g": {
+                        "SSID": ref_2g_ssid,
+                        "password": ref_2g_passphrase
+                    },
+                    "5g": {
+                        "SSID": ref_5g_ssid,
+                        "password": ref_5g_passphrase
+                    }
+                })
+            self.reference_networks = self.user_params["reference_networks"]
+
+        if "open_network" in self.user_params:
+            pass
+        else:
+            self.user_params["open_network"] = []
+            open_2g_ssid = '2g_%s' % utils.rand_ascii_str(8)
+            network_list_2g.append({"ssid": open_2g_ssid, "security": 'none'})
+            self.user_params["open_network"] = {"SSID": open_2g_ssid}
+            self.open_network = self.user_params["open_network"]
+
+        if "config_store_networks" in self.user_params:
+            pass
+        else:
+            self.user_params["config_store_networks"] = []
+            self.user_params["config_store_networks"].append(self.open_network)
+            config_store_2g_security = 'wpa2'
+            for x in range(0, 4):
+                config_store_2g_ssid = '2g_%s' % utils.rand_ascii_str(8)
+                config_store_2g_passphrase = utils.rand_ascii_str(10)
+                network_list_2g.append({
+                    "ssid": config_store_2g_ssid,
+                    "security": config_store_2g_security,
+                    "passphrase": config_store_2g_passphrase
+                })
+                self.user_params["config_store_networks"].append({
+                    "SSID": config_store_2g_ssid,
+                    "password": config_store_2g_passphrase
+                })
+            self.config_store_networks = self.user_params[
+                "config_store_networks"]
+
+        if "iot_networks" in self.user_params:
+            pass
+        else:
+            self.user_params["iot_networks"] = []
+            for iot_network in self.config_store_networks:
+                if "password" in iot_network:
+                    self.user_params["iot_networks"].append(iot_network)
+            iot_2g_security = 'wpa2'
+            for iot_network_2g in range(
+                    0, (max_2g_networks - len(network_list_2g)) + 1):
+                iot_2g_ssid = '2g_%s' % utils.rand_ascii_str(8)
+                iot_2g_passphrase = utils.rand_ascii_str(10)
+                network_list_2g.append({
+                    "ssid": iot_2g_ssid,
+                    "security": iot_2g_security,
+                    "passphrase": iot_2g_passphrase
+                })
+                self.user_params["iot_networks"].append({
+                    "SSID": iot_2g_ssid,
+                    "password": iot_2g_passphrase
+                })
+            iot_5g_security = 'wpa2'
+            for iot_network_5g in range(
+                    0, (max_5g_networks - len(network_list_5g)) + 1):
+                iot_5g_ssid = '5g_%s' % utils.rand_ascii_str(8)
+                iot_5g_passphrase = utils.rand_ascii_str(10)
+                network_list_5g.append({
+                    "ssid": iot_5g_ssid,
+                    "security": iot_5g_security,
+                    "passphrase": iot_5g_passphrase
+                })
+                self.user_params["iot_networks"].append({
+                    "SSID": iot_5g_ssid,
+                    "password": iot_5g_passphrase
+                })
+            self.iot_networks = self.user_params["iot_networks"]
+
+        if len(network_list_5g) > 1:
+            self.config_5g = self._generate_legacy_ap_config(network_list_5g)
+            self.access_point_5g.start_ap(self.config_5g)
+
+        if len(network_list_2g) > 1:
+            self.config_2g = self._generate_legacy_ap_config(network_list_2g)
+            self.access_point_2g.start_ap(self.config_2g)
+
+    def _generate_legacy_ap_config(self, network_list):
+        bss_settings = []
+        ap_settings = network_list.pop(0)
+        hostapd_config_settings = network_list.pop(0)
+        for network in network_list:
+            if "passphrase" in network:
+                bss_settings.append(
+                    hostapd_bss_settings.BssSettings(
+                        name=network["ssid"],
+                        ssid=network["ssid"],
+                        security=hostapd_security.Security(
+                            security_mode=network["security"],
+                            password=network["passphrase"])))
+            else:
+                bss_settings.append(
+                    hostapd_bss_settings.BssSettings(
+                        name=network["ssid"], ssid=network["ssid"]))
+        if "passphrase" in hostapd_config_settings:
+            config = hostapd_ap_preset.create_ap_preset(
+                channel=ap_settings["channel"],
+                ssid=hostapd_config_settings["ssid"],
+                security=hostapd_security.Security(
+                    security_mode=hostapd_config_settings["security"],
+                    password=hostapd_config_settings["passphrase"]),
+                bss_settings=bss_settings,
+                profile_name='whirlwind')
+        else:
+            config = hostapd_ap_preset.create_ap_preset(
+                channel=ap_settings["channel"],
+                ssid=hostapd_config_settings["ssid"],
+                bss_settings=bss_settings,
+                profile_name='whirlwind')
+
+        return config
diff --git a/acts/framework/acts/test_utils/wifi/wifi_aware_const.py b/acts/framework/acts/test_utils/wifi/wifi_aware_const.py
new file mode 100644
index 0000000..7e495f5
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/wifi_aware_const.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+######################################################
+# Broadcast events
+######################################################
+BROADCAST_WIFI_AWARE_AVAILABLE = "WifiAwareAvailable"
+BROADCAST_WIFI_AWARE_NOT_AVAILABLE = "WifiAwareNotAvailable"
+
+######################################################
+# ConfigRequest keys
+######################################################
+
+CONFIG_KEY_5G_BAND = "Support5gBand"
+CONFIG_KEY_MASTER_PREF = "MasterPreference"
+CONFIG_KEY_CLUSTER_LOW = "ClusterLow"
+CONFIG_KEY_CLUSTER_HIGH = "ClusterHigh"
+CONFIG_KEY_ENABLE_IDEN_CB = "EnableIdentityChangeCallback"
+
+######################################################
+# PublishConfig keys
+######################################################
+
+PUBLISH_KEY_SERVICE_NAME = "ServiceName"
+PUBLISH_KEY_SSI = "ServiceSpecificInfo"
+PUBLISH_KEY_MATCH_FILTER = "MatchFilter"
+PUBLISH_KEY_TYPE = "PublishType"
+PUBLISH_KEY_COUNT = "PublishCount"
+PUBLISH_KEY_TTL = "TtlSec"
+PUBLISH_KEY_TERM_CB_ENABLED = "TerminateNotificationEnabled"
+
+######################################################
+# SubscribeConfig keys
+######################################################
+
+SUBSCRIBE_KEY_SERVICE_NAME = "ServiceName"
+SUBSCRIBE_KEY_SSI = "ServiceSpecificInfo"
+SUBSCRIBE_KEY_MATCH_FILTER = "MatchFilter"
+SUBSCRIBE_KEY_TYPE = "SubscribeType"
+SUBSCRIBE_KEY_COUNT = "SubscribeCount"
+SUBSCRIBE_KEY_TTL = "TtlSec"
+SUBSCRIBE_KEY_STYLE = "MatchStyle"
+SUBSCRIBE_KEY_ENABLE_TERM_CB = "EnableTerminateNotification"
+
+######################################################
+# WifiAwareAttachCallback events
+######################################################
+EVENT_CB_ON_ATTACHED = "WifiAwareOnAttached"
+EVENT_CB_ON_ATTACH_FAILED = "WifiAwareOnAttachFailed"
+
+######################################################
+# WifiAwareIdentityChangedListener events
+######################################################
+EVENT_CB_ON_IDENTITY_CHANGED = "WifiAwareOnIdentityChanged"
+
+# WifiAwareAttachCallback & WifiAwareIdentityChangedListener events keys
+EVENT_CB_KEY_REASON = "reason"
+EVENT_CB_KEY_MAC = "mac"
+EVENT_CB_KEY_LATENCY_MS = "latencyMs"
+EVENT_CB_KEY_TIMESTAMP_MS = "timestampMs"
+
+######################################################
+# WifiAwareDiscoverySessionCallback events
+######################################################
+SESSION_CB_ON_PUBLISH_STARTED = "WifiAwareSessionOnPublishStarted"
+SESSION_CB_ON_SUBSCRIBE_STARTED = "WifiAwareSessionOnSubscribeStarted"
+SESSION_CB_ON_SESSION_CONFIG_UPDATED = "WifiAwareSessionOnSessionConfigUpdated"
+SESSION_CB_ON_SESSION_CONFIG_FAILED = "WifiAwareSessionOnSessionConfigFailed"
+SESSION_CB_ON_SESSION_TERMINATED = "WifiAwareSessionOnSessionTerminated"
+SESSION_CB_ON_SERVICE_DISCOVERED = "WifiAwareSessionOnServiceDiscovered"
+SESSION_CB_ON_MESSAGE_SENT = "WifiAwareSessionOnMessageSent"
+SESSION_CB_ON_MESSAGE_SEND_FAILED = "WifiAwareSessionOnMessageSendFailed"
+SESSION_CB_ON_MESSAGE_RECEIVED = "WifiAwareSessionOnMessageReceived"
+
+# WifiAwareDiscoverySessionCallback events keys
+SESSION_CB_KEY_CB_ID = "callbackId"
+SESSION_CB_KEY_SESSION_ID = "sessionId"
+SESSION_CB_KEY_REASON = "reason"
+SESSION_CB_KEY_PEER_ID = "peerId"
+SESSION_CB_KEY_SERVICE_SPECIFIC_INFO = "serviceSpecificInfo"
+SESSION_CB_KEY_MATCH_FILTER = "matchFilter"
+SESSION_CB_KEY_MESSAGE = "message"
+SESSION_CB_KEY_MESSAGE_ID = "messageId"
+SESSION_CB_KEY_MESSAGE_AS_STRING = "messageAsString"
+SESSION_CB_KEY_LATENCY_MS = "latencyMs"
+SESSION_CB_KEY_TIMESTAMP_MS = "timestampMs"
+
+######################################################
+# WifiAwareRangingListener events (RttManager.RttListener)
+######################################################
+RTT_LISTENER_CB_ON_SUCCESS = "WifiAwareRangingListenerOnSuccess"
+RTT_LISTENER_CB_ON_FAILURE = "WifiAwareRangingListenerOnFailure"
+RTT_LISTENER_CB_ON_ABORT = "WifiAwareRangingListenerOnAborted"
+
+# WifiAwareRangingListener events (RttManager.RttListener) keys
+RTT_LISTENER_CB_KEY_CB_ID = "callbackId"
+RTT_LISTENER_CB_KEY_SESSION_ID = "sessionId"
+RTT_LISTENER_CB_KEY_RESULTS = "Results"
+RTT_LISTENER_CB_KEY_REASON = "reason"
+RTT_LISTENER_CB_KEY_DESCRIPTION = "description"
+
+######################################################
+
+# Aware Data-Path Constants
+DATA_PATH_INITIATOR = 0
+DATA_PATH_RESPONDER = 1
+
+# Maximum send retry
+MAX_TX_RETRIES = 5
diff --git a/acts/framework/acts/test_utils/wifi/wifi_constants.py b/acts/framework/acts/test_utils/wifi/wifi_constants.py
new file mode 100644
index 0000000..f51c336
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/wifi_constants.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - 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.
+
+# Constants for Wifi related events.
+WIFI_CONNECTED = "WifiNetworkConnected"
+SUPPLICANT_CON_CHANGED = "SupplicantConnectionChanged"
+WIFI_FORGET_NW_SUCCESS = "WifiManagerForgetNetworkOnSuccess"
+
+# These constants will be used by the ACTS wifi tests.
+CONNECT_BY_CONFIG_SUCCESS = 'WifiManagerConnectByConfigOnSuccess'
+CONNECT_BY_NETID_SUCCESS = 'WifiManagerConnectByNetIdOnSuccess'
diff --git a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
index 385f324..2fac55e 100755
--- a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
@@ -14,6 +14,7 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import logging
 import time
 import pprint
 
@@ -22,15 +23,14 @@
 
 from acts import asserts
 from acts import signals
-from acts.logger import LoggerProxy
+from acts import utils
+from acts.controllers import attenuator
+from acts.test_utils.wifi import wifi_constants
 from acts.test_utils.tel import tel_defines
-from acts.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()
-
+# Default timeout used for reboot, toggle WiFi and Airplane mode,
+# for the system to settle down after the operation.
+DEFAULT_TIMEOUT = 10
 # 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.
@@ -48,9 +48,11 @@
 
 DEFAULT_PING_ADDR = "http://www.google.com/robots.txt"
 
+
 class WifiEnums():
 
     SSID_KEY = "SSID"
+    NETID_KEY = "network_id"
     BSSID_KEY = "BSSID"
     PWD_KEY = "password"
     frequency_key = "frequency"
@@ -59,11 +61,11 @@
     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;
+    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"
@@ -77,43 +79,43 @@
     class Eap(IntEnum):
         NONE = -1
         PEAP = 0
-        TLS  = 1
+        TLS = 1
         TTLS = 2
-        PWD  = 3
-        SIM  = 4
-        AKA  = 5
+        PWD = 3
+        SIM = 4
+        AKA = 5
         AKA_PRIME = 6
         UNAUTH_TLS = 7
 
     # EAP Phase2 types
     class EapPhase2(IntEnum):
-        NONE        = 0
-        PAP         = 1
-        MSCHAP      = 2
-        MSCHAPV2    = 3
-        GTC         = 4
+        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"
+        # 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"
+        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.
@@ -131,51 +133,51 @@
 
     # Macros for wifi rtt.
     class RttType(IntEnum):
-        TYPE_ONE_SIDED      = 1
-        TYPE_TWO_SIDED      = 2
+        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
+        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
+        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_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
+        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
+        REASON_UNSPECIFIED = -1
+        REASON_NOT_AVAILABLE = -2
+        REASON_INVALID_LISTENER = -3
+        REASON_INVALID_REQUEST = -4
 
     class RttParam:
         device_type = "deviceType"
@@ -204,13 +206,13 @@
     }
 
     # 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
+    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
@@ -226,12 +228,12 @@
     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
+        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.
@@ -357,13 +359,6 @@
         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 = []
@@ -378,114 +373,241 @@
             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
+            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]
+    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:
+        if model and utils.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.MIX_CHANNEL_SCAN = [2412, 2437, 2462, 5180, 5200, 5240, 5745,
+                                     5765]
+        elif model and utils.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:
+        elif model and utils.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]
+                                       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]
+        elif model and utils.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.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 _assert_on_fail_handler(func, assert_on_fail, *args, **kwargs):
+    """Wrapper function that handles the bahevior of assert_on_fail.
+
+    When assert_on_fail is True, let all test signals through, which can
+    terminate test cases directly. When assert_on_fail is False, the wrapper
+    raises no test signals and reports operation status by returning True or
+    False.
+
+    Args:
+        func: The function to wrap. This function reports operation status by
+              raising test signals.
+        assert_on_fail: A boolean that specifies if the output of the wrapper
+                        is test signal based or return value based.
+        args: Positional args for func.
+        kwargs: Name args for func.
+
+    Returns:
+        If assert_on_fail is True, returns True/False to signal operation
+        status, otherwise return nothing.
+    """
+    try:
+        func(*args, **kwargs)
+        if not assert_on_fail:
+            return True
+    except signals.TestSignal:
+        if assert_on_fail:
+            raise
+        return False
+
+
+def assert_network_in_list(target, network_list):
+    """Makes sure a specified target Wi-Fi network exists in a list of Wi-Fi
+    networks.
+
+    Args:
+        target: A dict representing a Wi-Fi network.
+                E.g. {WifiEnums.SSID_KEY: "SomeNetwork"}
+        network_list: A list of dicts, each representing a Wi-Fi network.
+    """
+    match_results = match_networks(target, network_list)
+    asserts.assert_true(
+        match_results, "Target network %s, does not exist in network list %s" %
+        (target, network_list))
+
+
 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.
+    To be considered a match, the network should contain every key-value pair
+    of target_params
 
     Args:
-        target_params: The target parameters to match networks against.
+        target_params: A dict with 1 or more key-value pairs representing a Wi-Fi network.
+                       E.g { 'SSID': 'wh_ap1_5g', 'BSSID': '30:b5:c2:33:e4:47' }
         networks: A list of dict objects representing WiFi networks.
 
     Returns:
         The networks that match the target parameters.
     """
     results = []
+    asserts.assert_true(target_params,
+                        "Expected networks object 'target_params' is empty")
     for n in networks:
+        add_network = 1
         for k, v in target_params.items():
             if k not in n:
-                continue
+                add_network = 0
+                break
             if n[k] != v:
-                continue
+                add_network = 0
+                break
+        if add_network:
             results.append(n)
     return results
 
-def wifi_toggle_state(ad, new_state=None):
+
+def wifi_toggle_state(ad, new_state=None, assert_on_fail=True):
     """Toggles the state of wifi.
 
     Args:
         ad: An AndroidDevice object.
         new_state: Wifi state to set to. If None, opposite of the current state.
+        assert_on_fail: If True, error checks in this function will raise test
+                        failure signals.
 
     Returns:
-        True if the toggle was successful, False otherwise.
+        If assert_on_fail is False, function returns True if the toggle was
+        successful, False otherwise. If assert_on_fail is True, no return value.
     """
-    # Check if the new_state is already achieved, so we don't wait for the
-    # state change event by mistake.
-    if new_state == ad.droid.wifiCheckState():
-        return True
+    return _assert_on_fail_handler(
+        _wifi_toggle_state, assert_on_fail, ad, new_state=new_state)
+
+
+def _wifi_toggle_state(ad, new_state=None):
+    """Toggles the state of wifi.
+
+    TestFailure signals are raised when something goes wrong.
+
+    Args:
+        ad: An AndroidDevice object.
+        new_state: The state to set Wi-Fi to. If None, opposite of the current
+                   state will be set.
+    """
+    if new_state is None:
+        new_state = not ad.droid.wifiCheckState()
+    elif new_state == ad.droid.wifiCheckState():
+        # Check if the new_state is already achieved, so we don't wait for the
+        # state change event by mistake.
+        return
     ad.droid.wifiStartTrackingStateChange()
-    log.info("Setting wifi state to {}".format(new_state))
+    ad.log.info("Setting Wi-Fi state to %s.", new_state)
+    # Setting wifi state.
     ad.droid.wifiToggleState(new_state)
+    fail_msg = "Failed to set Wi-Fi state to %s on %s." % (new_state,
+                                                           ad.serial)
     try:
-        event = ad.ed.pop_event(WifiEventNames.SUPPLICANT_CON_CHANGED, SHORT_TIMEOUT)
-        return event['data']['Connected'] == new_state
+        event = ad.ed.pop_event(wifi_constants.SUPPLICANT_CON_CHANGED,
+                                SHORT_TIMEOUT)
+        asserts.assert_equal(event['data']['Connected'], new_state, fail_msg)
     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 == ad.droid.wifiCheckState()
+        # Supplicant connection event is not always reliable. We double check
+        # here and call it a success as long as the new state equals the
+        # expected state.
+        time.sleep(5)
+        asserts.assert_equal(new_state, ad.droid.wifiCheckState(), fail_msg)
     finally:
         ad.droid.wifiStopTrackingStateChange()
 
+
 def reset_wifi(ad):
-    """Clears all saved networks on a device.
+    """Clears all saved Wi-Fi networks on a device.
+
+    This will turn Wi-Fi on.
 
     Args:
         ad: An AndroidDevice object.
 
-    Raises:
-        WifiTestUtilsError is raised if forget network operation failed.
     """
-    ad.droid.wifiToggleState(True)
+    # TODO(gmoturu): need to remove wifi_toggle_state() in reset_wifi() when
+    # bug: 32809235 is fixed
+    wifi_toggle_state(ad, True)
     networks = ad.droid.wifiGetConfiguredNetworks()
     if not networks:
         return
     for n in networks:
         ad.droid.wifiForgetNetwork(n['networkId'])
         try:
-            event = ad.ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
-              SHORT_TIMEOUT)
+            event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
+                                    SHORT_TIMEOUT)
         except Empty:
-            raise WifiTestUtilsError("Failed to remove network {}.".format(n))
+            logging.warning("Could not confirm the removal of network %s.", n)
+    # Check again to see if there's any network left.
+    asserts.assert_true(
+        not ad.droid.wifiGetConfiguredNetworks(),
+        "Failed to remove these configured Wi-Fi networks: %s" % networks)
+
+
+def toggle_airplane_mode_on_and_off(ad):
+    """Turn ON and OFF Airplane mode.
+
+    ad: An AndroidDevice object.
+    Returns: Assert if turning on/off Airplane mode fails.
+
+    """
+    ad.log.debug("Toggling Airplane mode ON.")
+    asserts.assert_true(
+        utils.force_airplane_mode(ad, True),
+        "Can not turn on airplane mode on: %s" % ad.serial)
+    time.sleep(DEFAULT_TIMEOUT)
+    ad.log.debug("Toggling Airplane mode OFF.")
+    asserts.assert_true(
+        utils.force_airplane_mode(ad, False),
+        "Can not turn on airplane mode on: %s" % ad.serial)
+    time.sleep(DEFAULT_TIMEOUT)
+
+
+def toggle_wifi_off_and_on(ad):
+    """Turn OFF and ON WiFi.
+
+    ad: An AndroidDevice object.
+    Returns: Assert if turning off/on WiFi fails.
+
+    """
+    ad.log.debug("Toggling wifi OFF.")
+    wifi_toggle_state(ad, False)
+    time.sleep(DEFAULT_TIMEOUT)
+    ad.log.debug("Toggling wifi ON.")
+    wifi_toggle_state(ad, True)
+    time.sleep(DEFAULT_TIMEOUT)
+
 
 def wifi_forget_network(ad, net_ssid):
     """Remove configured Wifi network on an android device.
@@ -494,22 +616,20 @@
         ad: android_device object for forget network.
         net_ssid: ssid of network to be forget
 
-    Raises:
-        WifiTestUtilsError is raised if forget network operation failed.
     """
-    droid, ed = ad.droid, ad.ed
-    droid.wifiToggleState(True)
-    networks = droid.wifiGetConfiguredNetworks()
+    ad.droid.wifiToggleState(True)
+    networks = ad.droid.wifiGetConfiguredNetworks()
     if not networks:
         return
     for n in networks:
         if net_ssid in n[WifiEnums.SSID_KEY]:
-            droid.wifiForgetNetwork(n['networkId'])
+            ad.droid.wifiForgetNetwork(n['networkId'])
             try:
-                event = ed.pop_event(WifiEventNames.WIFI_FORGET_NW_SUCCESS,
-                        SHORT_TIMEOUT)
+                event = ad.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS,
+                                        SHORT_TIMEOUT)
             except Empty:
-                raise WifiTestUtilsError("Failed to remove network %s." % n)
+                asserts.fail("Failed to remove network %s." % n)
+
 
 def wifi_test_device_init(ad):
     """Initializes an android device for wifi testing.
@@ -522,38 +642,29 @@
     5. Enable WiFi verbose logging.
     6. Sync device time with computer time.
     7. Turn off cellular data.
+    8. Turn off ambient display.
     """
-    require_sl4a((ad,))
+    utils.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, True), msg
+    asserts.assert_true(not ad.droid.wifiScannerIsAlwaysAvailable(), msg)
+    wifi_toggle_state(ad, True)
     reset_wifi(ad)
-    msg = "Failed to clear configured networks."
-    assert not ad.droid.wifiGetConfiguredNetworks(), msg
     ad.droid.wifiEnableVerboseLogging(1)
     msg = "Failed to enable WiFi verbose logging."
-    assert ad.droid.wifiGetVerboseLoggingLevel() == 1, msg
-    ad.droid.wifiScannerToggleAlwaysAvailable(False)
+    asserts.assert_equal(ad.droid.wifiGetVerboseLoggingLevel(), 1, msg)
     # We don't verify the following settings since they are not critical.
-    sync_device_time(ad)
+    # Set wpa_supplicant log level to EXCESSIVE.
+    output = ad.adb.shell("wpa_cli -i wlan0 -p -g@android:wpa_wlan0 IFNAME="
+                          "wlan0 log_level EXCESSIVE")
+    ad.log.info("wpa_supplicant log change status: %s", output)
+    utils.sync_device_time(ad)
     ad.droid.telephonyToggleDataConnection(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)
+    utils.set_ambient_display(ad, False)
 
-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(ad):
     """Starts a wifi connection scan and wait for results to become available.
@@ -562,7 +673,11 @@
         ad: An AndroidDevice object.
     """
     ad.droid.wifiStartScan()
-    ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
+    try:
+        ad.ed.pop_event("WifiManagerScanResultsAvailable", 60)
+    except Empty:
+        asserts.fail("Wi-Fi results did not become available within 60s.")
+
 
 def start_wifi_background_scan(ad, scan_setting):
     """Starts wifi background scan.
@@ -574,12 +689,12 @@
     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)
+    idx = ad.droid.wifiScannerStartBackgroundScan(scan_setting)
+    event = ad.ed.pop_event("WifiScannerScan{}onSuccess".format(idx),
+                            SHORT_TIMEOUT)
     return event['data']
 
+
 def start_wifi_tethering(ad, ssid, password, band=None):
     """Starts wifi tethering on an android_device.
 
@@ -591,31 +706,29 @@
             WifiEnums.WIFI_CONFIG_APBAND_2G or WifiEnums.WIFI_CONFIG_APBAND_5G.
 
     Returns:
-        True if soft AP was started successfully, False otherwise.
+        No return value. Error checks in this function will raise test failure signals
     """
-    droid, ed = ad.droid, ad.ed
-    config = {
-        WifiEnums.SSID_KEY: ssid
-    }
+    config = {WifiEnums.SSID_KEY: ssid}
     if password:
         config[WifiEnums.PWD_KEY] = password
     if band:
         config[WifiEnums.APBAND_KEY] = band
-    if not droid.wifiSetWifiApConfiguration(config):
-        log.error("Failed to update WifiAp Configuration")
-        return False
-    droid.wifiStartTrackingTetherStateChange()
-    droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
+    asserts.assert_true(
+        ad.droid.wifiSetWifiApConfiguration(config),
+        "Failed to update WifiAp Configuration")
+    ad.droid.wifiStartTrackingTetherStateChange()
+    ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
     try:
-        ed.pop_event("ConnectivityManagerOnTetheringStarted")
-        ed.wait_for_event("TetherStateChanged",
-                          lambda x : x["data"]["ACTIVE_TETHER"], 30)
+        ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
+        ad.ed.wait_for_event("TetherStateChanged",
+                             lambda x: x["data"]["ACTIVE_TETHER"], 30)
+        ad.log.debug("Tethering started successfully.")
     except Empty:
         msg = "Failed to receive confirmation of wifi tethering starting"
         asserts.fail(msg)
     finally:
-        droid.wifiStopTrackingTetherStateChange()
-    return True
+        ad.droid.wifiStopTrackingTetherStateChange()
+
 
 def stop_wifi_tethering(ad):
     """Stops wifi tethering on an android_device.
@@ -623,45 +736,420 @@
     Args:
         ad: android_device to stop wifi tethering on.
     """
-    droid, ed = ad.droid, ad.ed
-    droid.wifiStartTrackingTetherStateChange()
-    droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
+    ad.droid.wifiStartTrackingTetherStateChange()
+    ad.droid.connectivityStopTethering(tel_defines.TETHERING_WIFI)
+    ad.droid.wifiSetApEnabled(False, None)
     try:
-        ed.pop_event("WifiManagerApDisabled", 30)
-        ed.wait_for_event("TetherStateChanged",
-                          lambda x : not x["data"]["ACTIVE_TETHER"], 30)
+        ad.ed.pop_event("WifiManagerApDisabled", 30)
+        ad.ed.wait_for_event("TetherStateChanged",
+                             lambda x: not x["data"]["ACTIVE_TETHER"], 30)
     except Empty:
         msg = "Failed to receive confirmation of wifi tethering stopping"
         asserts.fail(msg)
     finally:
-        droid.wifiStopTrackingTetherStateChange()
+        ad.droid.wifiStopTrackingTetherStateChange()
 
-def wifi_connect(ad, network):
+
+def toggle_wifi_and_wait_for_reconnection(ad,
+                                          network,
+                                          num_of_tries=1,
+                                          assert_on_fail=True):
+    """Toggle wifi state and then wait for Android device to reconnect to
+    the provided wifi network.
+
+    This expects the device to be already connected to the provided network.
+
+    Logic steps are
+     1. Ensure that we're connected to the network.
+     2. Turn wifi off.
+     3. Wait for 10 seconds.
+     4. Turn wifi on.
+     5. Wait for the "connected" event, then confirm the connected ssid is the
+        one requested.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        network: A dictionary representing the network to await connection. The
+                 dictionary must have the key "SSID".
+        num_of_tries: An integer that is the number of times to try before
+                      delaring failure. Default is 1.
+        assert_on_fail: If True, error checks in this function will raise test
+                        failure signals.
+
+    Returns:
+        If assert_on_fail is False, function returns True if the toggle was
+        successful, False otherwise. If assert_on_fail is True, no return value.
+    """
+    return _assert_on_fail_handler(
+        _toggle_wifi_and_wait_for_reconnection,
+        assert_on_fail,
+        ad,
+        network,
+        num_of_tries=num_of_tries)
+
+
+def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=1):
+    """Toggle wifi state and then wait for Android device to reconnect to
+    the provided wifi network.
+
+    This expects the device to be already connected to the provided network.
+
+    Logic steps are
+     1. Ensure that we're connected to the network.
+     2. Turn wifi off.
+     3. Wait for 10 seconds.
+     4. Turn wifi on.
+     5. Wait for the "connected" event, then confirm the connected ssid is the
+        one requested.
+
+    This will directly fail a test if anything goes wrong.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        network: A dictionary representing the network to await connection. The
+                 dictionary must have the key "SSID".
+        num_of_tries: An integer that is the number of times to try before
+                      delaring failure. Default is 1.
+    """
+    expected_ssid = network[WifiEnums.SSID_KEY]
+    # First ensure that we're already connected to the provided network.
+    verify_con = {WifiEnums.SSID_KEY: expected_ssid}
+    verify_wifi_connection_info(ad, verify_con)
+    # Now toggle wifi state and wait for the connection event.
+    wifi_toggle_state(ad, False)
+    time.sleep(10)
+    wifi_toggle_state(ad, True)
+    ad.droid.wifiStartTrackingStateChange()
+    try:
+        connect_result = None
+        for i in range(num_of_tries):
+            try:
+                connect_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED,
+                                                 30)
+                break
+            except Empty:
+                pass
+        asserts.assert_true(connect_result,
+                            "Failed to connect to Wi-Fi network %s on %s" %
+                            (network, ad.serial))
+        logging.debug("Connection result on %s: %s.", ad.serial,
+                      connect_result)
+        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
+        asserts.assert_equal(actual_ssid, expected_ssid,
+                             "Connected to the wrong network on %s."
+                             "Expected %s, but got %s." %
+                             (ad.serial, expected_ssid, actual_ssid))
+        logging.info("Connected to Wi-Fi network %s on %s", actual_ssid,
+                     ad.serial)
+    finally:
+        ad.droid.wifiStopTrackingStateChange()
+
+
+def wait_for_connect(ad, ssid=None, id=None, tries=1):
+    """Wait for a connect event on queue and pop when available.
+
+    Args:
+        ad: An Android device object.
+        ssid: SSID of the network to connect to.
+        id: Network Id of the network to connect to.
+        tries: An integer that is the number of times to try before failing.
+
+    Returns:
+        A dict with details of the connection data, which looks like this:
+        {
+         'time': 1485460337798,
+         'name': 'WifiNetworkConnected',
+         'data': {
+                  'rssi': -27,
+                  'is_24ghz': True,
+                  'mac_address': '02:00:00:00:00:00',
+                  'network_id': 1,
+                  'BSSID': '30:b5:c2:33:d3:fc',
+                  'ip_address': 117483712,
+                  'link_speed': 54,
+                  'supplicant_state': 'completed',
+                  'hidden_ssid': False,
+                  'SSID': 'wh_ap1_2g',
+                  'is_5ghz': False}
+        }
+
+    """
+    conn_result = None
+
+    # If ssid and network id is None, just wait for any connect event.
+    if id is None and ssid is None:
+        for i in range(tries):
+            try:
+                conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
+                break
+            except Empty:
+                pass
+    else:
+    # If ssid or network id is specified, wait for specific connect event.
+        for i in range(tries):
+            try:
+                conn_result = ad.ed.pop_event(wifi_constants.WIFI_CONNECTED, 30)
+                if id and conn_result['data'][WifiEnums.NETID_KEY] == id:
+                    break
+                elif ssid and conn_result['data'][WifiEnums.SSID_KEY] == ssid:
+                    break
+            except Empty:
+                pass
+
+    return conn_result
+
+
+def wait_for_disconnect(ad):
+    """Wait for a Disconnect event from the supplicant.
+
+    Args:
+        ad: Android device object.
+
+    """
+    try:
+        ad.droid.wifiStartTrackingStateChange()
+        event = ad.ed.pop_event("WifiNetworkDisconnected", 10)
+        ad.droid.wifiStopTrackingStateChange()
+    except queue.Empty:
+        raise signals.TestFailure("Device did not disconnect from the network")
+
+
+def wifi_connect(ad, network, num_of_tries=1, assert_on_fail=True):
     """Connect an Android device to a wifi network.
 
     Initiate connection to a wifi network, wait for the "connected" event, then
     confirm the connected ssid is the one requested.
 
+    This will directly fail a test if anything goes wrong.
+
     Args:
         ad: android_device object to initiate connection on.
         network: A dictionary representing the network to connect to. The
-            dictionary must have the key "SSID".
+                 dictionary must have the key "SSID".
+        num_of_tries: An integer that is the number of times to try before
+                      delaring failure. Default is 1.
+        assert_on_fail: If True, error checks in this function will raise test
+                        failure signals.
+
+    Returns:
+        Returns a value only if assert_on_fail is false.
+        Returns True if the connection was successful, False otherwise.
     """
-    assert WifiEnums.SSID_KEY in network, ("Key '%s' must be present in "
-        "network definition.") % WifiEnums.SSID_KEY
+    return _assert_on_fail_handler(
+        _wifi_connect, assert_on_fail, ad, network, num_of_tries=num_of_tries)
+
+
+def _wifi_connect(ad, network, num_of_tries=1):
+    """Connect an Android device to a wifi network.
+
+    Initiate connection to a wifi network, wait for the "connected" event, then
+    confirm the connected ssid is the one requested.
+
+    This will directly fail a test if anything goes wrong.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        network: A dictionary representing the network to connect to. The
+                 dictionary must have the key "SSID".
+        num_of_tries: An integer that is the number of times to try before
+                      delaring failure. Default is 1.
+    """
+    asserts.assert_true(WifiEnums.SSID_KEY in network,
+                        "Key '%s' must be present in network definition." %
+                        WifiEnums.SSID_KEY)
     ad.droid.wifiStartTrackingStateChange()
+    expected_ssid = network[WifiEnums.SSID_KEY]
+    ad.droid.wifiConnectByConfig(network)
+    ad.log.info("Starting connection process to %s", expected_ssid)
     try:
-        assert ad.droid.wifiConnect(network), "WiFi connect returned false."
-        connect_result = ad.ed.pop_event(WifiEventNames.WIFI_CONNECTED)
-        log.debug("Connection result: %s." % connect_result)
-        expected_ssid = network[WifiEnums.SSID_KEY]
+        event = ad.ed.pop_event(wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 30)
+        connect_result = wait_for_connect(ad, ssid=expected_ssid, tries=num_of_tries)
+        asserts.assert_true(connect_result,
+                            "Failed to connect to Wi-Fi network %s on %s" %
+                            (network, ad.serial))
+        ad.log.debug("Wi-Fi connection result: %s.", connect_result)
         actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
-        assert actual_ssid == expected_ssid, ("Expected to connect to %s, "
-            "connected to %s") % (expected_ssid, actual_ssid)
-        log.info("Successfully connected to %s" % actual_ssid)
+        asserts.assert_equal(actual_ssid, expected_ssid,
+                             "Connected to the wrong network on %s." %
+                             ad.serial)
+        ad.log.info("Connected to Wi-Fi network %s.", actual_ssid)
+
+        # Wait for data connection to stabilize.
+        time.sleep(5)
+
+        internet = validate_connection(ad, DEFAULT_PING_ADDR)
+        if not internet:
+            raise signals.TestFailure("Failed to connect to internet on %s" %
+                                      expected_ssid)
+    except Empty:
+        asserts.fail("Failed to start connection process to %s on %s" %
+                     (network, ad.serial))
+    except Exception as error:
+        ad.log.error("Failed to connect to %s with error %s", expected_ssid,
+                     error)
+        raise signals.TestFailure("Failed to connect to %s network" % network)
+
     finally:
         ad.droid.wifiStopTrackingStateChange()
 
+
+def wifi_connect_by_id(ad, network_id, num_of_tries=3, assert_on_fail=True):
+    """Connect an Android device to a wifi network using network Id.
+
+    Start connection to the wifi network, with the given network Id, wait for
+    the "connected" event, then verify the connected network is the one requested.
+
+    This will directly fail a test if anything goes wrong.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        network_id: Integer specifying the network id of the network.
+        num_of_tries: An integer that is the number of times to try before
+                      delaring failure. Default is 1.
+        assert_on_fail: If True, error checks in this function will raise test
+                        failure signals.
+
+    Returns:
+        Returns a value only if assert_on_fail is false.
+        Returns True if the connection was successful, False otherwise.
+    """
+    _assert_on_fail_handler(_wifi_connect_by_id, assert_on_fail, ad,
+                            network_id, num_of_tries)
+
+
+def _wifi_connect_by_id(ad, network_id, num_of_tries=1):
+    """Connect an Android device to a wifi network using it's network id.
+
+    Start connection to the wifi network, with the given network id, wait for
+    the "connected" event, then verify the connected network is the one requested.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        network_id: Integer specifying the network id of the network.
+        num_of_tries: An integer that is the number of times to try before
+                      delaring failure. Default is 1.
+    """
+    ad.droid.wifiStartTrackingStateChange()
+    # Clear all previous events.
+    ad.ed.clear_all_events()
+    ad.droid.wifiConnectByNetworkId(network_id)
+    ad.log.info("Starting connection to network with id %d", network_id)
+    try:
+        event = ad.ed.pop_event(wifi_constants.CONNECT_BY_NETID_SUCCESS, 60)
+        connect_result = wait_for_connect(ad, id=network_id, tries=num_of_tries)
+        asserts.assert_true(connect_result,
+                            "Failed to connect to Wi-Fi network using network id")
+        ad.log.debug("Wi-Fi connection result: %s", connect_result)
+        actual_id = connect_result['data'][WifiEnums.NETID_KEY]
+        asserts.assert_equal(actual_id, network_id,
+                             "Connected to the wrong network on %s."
+                             "Expected network id = %d, but got %d." %
+                             (ad.serial, network_id, actual_id))
+        expected_ssid = connect_result['data'][WifiEnums.SSID_KEY]
+        ad.log.info("Connected to Wi-Fi network %s with %d network id.",
+                     expected_ssid, network_id)
+
+        # Wait for data connection to stabilize.
+        time.sleep(5)
+
+        internet = validate_connection(ad, DEFAULT_PING_ADDR)
+        if not internet:
+            raise signals.TestFailure("Failed to connect to internet on %s" %
+                                      expected_ssid)
+    except Empty:
+        asserts.fail("Failed to connect to network with id %d on %s" %
+                    (network_id, ad.serial))
+    except Exception as error:
+        ad.log.error("Failed to connect to network with id %d with error %s",
+                      network_id, error)
+        raise signals.TestFailure("Failed to connect to network with network"
+                                  " id %d" % network_id)
+    finally:
+        ad.droid.wifiStopTrackingStateChange()
+
+
+def wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1,
+                           assert_on_fail=True):
+    """Connect an Android device to a wifi network.
+
+    Initiate connection to a wifi network, wait for the "connected" event, then
+    confirm the connected ssid is the one requested.
+
+    This will directly fail a test if anything goes wrong.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        passpoint_network: SSID of the Passpoint network to connect to.
+        num_of_tries: An integer that is the number of times to try before
+                      delaring failure. Default is 1.
+        assert_on_fail: If True, error checks in this function will raise test
+                        failure signals.
+
+    Returns:
+        If assert_on_fail is False, function returns network id, if the connect was
+        successful, False otherwise. If assert_on_fail is True, no return value.
+    """
+    _assert_on_fail_handler(_wifi_passpoint_connect, assert_on_fail, ad,
+                            passpoint_network, num_of_tries = num_of_tries)
+
+
+def _wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1):
+    """Connect an Android device to a wifi network.
+
+    Initiate connection to a wifi network, wait for the "connected" event, then
+    confirm the connected ssid is the one requested.
+
+    This will directly fail a test if anything goes wrong.
+
+    Args:
+        ad: android_device object to initiate connection on.
+        passpoint_network: SSID of the Passpoint network to connect to.
+        num_of_tries: An integer that is the number of times to try before
+                      delaring failure. Default is 1.
+    """
+    ad.droid.wifiStartTrackingStateChange()
+    expected_ssid = passpoint_network
+    ad.log.info("Starting connection process to passpoint %s", expected_ssid)
+
+    try:
+        connect_result = wait_for_connect(ad, expected_ssid, num_of_tries)
+        asserts.assert_true(connect_result,
+                            "Failed to connect to WiFi passpoint network %s on"
+                            " %s" % (expected_ssid, ad.serial))
+        ad.log.info("Wi-Fi connection result: %s.", connect_result)
+        actual_ssid = connect_result['data'][WifiEnums.SSID_KEY]
+        asserts.assert_equal(actual_ssid, expected_ssid,
+                             "Connected to the wrong network on %s." % ad.serial)
+        ad.log.info("Connected to Wi-Fi passpoint network %s.", actual_ssid)
+
+        # Wait for data connection to stabilize.
+        time.sleep(5)
+
+        internet = validate_connection(ad, DEFAULT_PING_ADDR)
+        if not internet:
+            raise signals.TestFailure("Failed to connect to internet on %s" %
+                                      expected_ssid)
+    except Exception as error:
+        ad.log.error("Failed to connect to passpoint network %s with error %s",
+                      expected_ssid, error)
+        raise signals.TestFailure("Failed to connect to %s passpoint network" %
+                                   expected_ssid)
+
+    finally:
+        ad.droid.wifiStopTrackingStateChange()
+
+
+def delete_passpoint(ad, fqdn):
+    """Delete a required Passpoint configuration."""
+    try:
+        ad.droid.removePasspointConfig(fqdn)
+        return True
+    except Exception as error:
+        ad.log.error("Failed to remove passpoint configuration with FQDN=%s "
+                     "and error=%s" , fqdn, error)
+        return False
+
+
 def start_wifi_single_scan(ad, scan_setting):
     """Starts wifi single shot scan.
 
@@ -672,13 +1160,12 @@
     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))
+    idx = ad.droid.wifiScannerStartScan(scan_setting)
+    event = ad.ed.pop_event("WifiScannerScan%sonSuccess" % idx, SHORT_TIMEOUT)
+    ad.log.debug("Got event %s", event)
     return event['data']
 
+
 def track_connection(ad, network_ssid, check_connection_count):
     """Track wifi connection to network changes for given number of counts
 
@@ -686,23 +1173,22 @@
         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.
+                                check.
     Returns:
-
         True if connection to given network happen, else return False.
     """
-    droid, ed = ad.droid, ad.ed
-    droid.wifiStartTrackingStateChange()
+    ad.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
+        connect_network = ad.ed.pop_event("WifiNetworkConnected", 120)
+        ad.log.info("Connected to network %s", 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()
+    ad.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
@@ -725,9 +1211,10 @@
     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
+            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.
 
@@ -738,15 +1225,13 @@
     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)
+    idx = ad.droid.wifiScannerStartTrackingBssids(
+        track_setting["bssidInfos"], track_setting["apLostThreshold"])
+    event = ad.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.
@@ -759,11 +1244,15 @@
         out_file: The full path to the converted key file, including
         filename.
     """
+    asserts.assert_true(in_file.endswith(".pem"), "Input file has to be .pem.")
+    asserts.assert_true(
+        out_file.endswith(".der"), "Output file has to be .der.")
     cmd = ("openssl pkcs8 -inform PEM -in {} -outform DER -out {} -nocrypt"
            " -topk8").format(in_file, out_file)
-    exe_cmd(cmd)
+    utils.exe_cmd(cmd)
 
-def check_internet_connection(ad, ping_addr):
+
+def validate_connection(ad, ping_addr):
     """Validate internet connection by pinging the address provided.
 
     Args:
@@ -771,13 +1260,13 @@
         ping_addr: address on internet for pinging.
 
     Returns:
-        True, if address ping successful
+        ping output if successful, NULL otherwise.
     """
-    droid, ed = ad.droid, ad.ed
-    ping = droid.httpPing(ping_addr)
-    log.info("Http ping result: {}".format(ping))
+    ping = ad.droid.httpPing(ping_addr)
+    ad.log.info("Http ping result: %s.", 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):
@@ -790,13 +1279,13 @@
     """
     current_con = ad.droid.wifiGetConnectionInfo()
     case_insensitive = ["BSSID", "supplicant_state"]
-    log.debug("Current connection: %s" % current_con)
+    ad.log.debug("Current connection: %s", current_con)
     for k, expected_v in expected_con.items():
         # Do not verify authentication related fields.
         if k == "password":
             continue
-        msg = "Field %s does not exist in wifi connection info %s." % (k,
-            current_con)
+        msg = "Field %s does not exist in wifi connection info %s." % (
+            k, current_con)
         if k not in current_con:
             raise signals.TestFailure(msg)
         actual_v = current_con[k]
@@ -804,53 +1293,10 @@
             actual_v = actual_v.lower()
             expected_v = expected_v.lower()
         msg = "Expected %s to be %s, actual %s is %s." % (k, expected_v, k,
-            actual_v)
+                                                          actual_v)
         if actual_v != expected_v:
             raise signals.TestFailure(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(ad)
-    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))
-    ad.droid.wifiEnterpriseConnect(config)
-    try:
-        event = ed.pop_event("WifiManagerEnterpriseConnectOnSuccess", 30)
-        log.info("Started connecting...")
-        event = ed.pop_event(WifiEventNames.WIFI_CONNECTED, 60)
-    except Empty:
-        asserts.fail("Failed to connect to %s" % config)
-    log.debug(event)
-    if expect_ssid:
-        actual_ssid = event["data"][WifiEnums.SSID_KEY]
-        asserts.assert_equal(expect_ssid, actual_ssid, "SSID mismatch.")
-    else:
-        log.info("Connected to %s." % expect_ssid)
-    if validate_con:
-        log.info("Checking Internet access.")
-        # Wait for data connection to stabilize.
-        time.sleep(4)
-        ping = ad.droid.httpPing(ping_addr)
-        log.info("Http ping result: {}".format(ping))
-        asserts.assert_true(ping, "No Internet access.")
 
 def expand_enterprise_config_by_phase2(config):
     """Take an enterprise config and generate a list of configs, each with
@@ -870,13 +1316,14 @@
     for phase2_type in phase2_types:
         # Skip a special case for passpoint TTLS.
         if (WifiEnums.Enterprise.FQDN in config and
-            phase2_type == WifiEnums.EapPhase2.GTC):
+                phase2_type == WifiEnums.EapPhase2.GTC):
             continue
         c = dict(config)
         c[WifiEnums.Enterprise.PHASE2] = phase2_type.value
         results.append(c)
     return results
 
+
 def generate_eap_test_name(config, ad=None):
     """ Generates a test case name based on an EAP configuration.
 
@@ -908,3 +1355,43 @@
                 name += "-{}".format(e.name)
                 break
     return name
+
+
+def group_attenuators(attenuators):
+    """Groups a list of attenuators into attenuator groups for backward
+    compatibility reasons.
+
+    Most legacy Wi-Fi setups have two attenuators each connected to a separate
+    AP. The new Wi-Fi setup has four attenuators, each connected to one channel
+    on an AP, so two of them are connected to one AP.
+
+    To make the existing scripts work in the new setup, when the script needs
+    to attenuate one AP, it needs to set attenuation on both attenuators
+    connected to the same AP.
+
+    This function groups attenuators properly so the scripts work in both
+    legacy and new Wi-Fi setups.
+
+    Args:
+        attenuators: A list of attenuator objects, either two or four in length.
+
+    Raises:
+        signals.TestFailure is raised if the attenuator list does not have two
+        or four objects.
+    """
+    attn0 = attenuator.AttenuatorGroup("AP0")
+    attn1 = attenuator.AttenuatorGroup("AP1")
+    # Legacy testbed setup has two attenuation channels.
+    num_of_attns = len(attenuators)
+    if num_of_attns == 2:
+        attn0.add(attenuators[0])
+        attn1.add(attenuators[1])
+    elif num_of_attns == 4:
+        attn0.add(attenuators[0])
+        attn0.add(attenuators[1])
+        attn1.add(attenuators[2])
+        attn1.add(attenuators[3])
+    else:
+        asserts.fail(("Either two or four attenuators are required for this "
+                      "test, but found %s") % num_of_attns)
+    return [attn0, attn1]
diff --git a/acts/framework/acts/tracelogger.py b/acts/framework/acts/tracelogger.py
new file mode 100644
index 0000000..c700243
--- /dev/null
+++ b/acts/framework/acts/tracelogger.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import inspect
+import os
+
+
+class TraceLogger():
+    def __init__(self, logger):
+        self._logger = logger
+
+    @staticmethod
+    def _get_trace_info(level=1):
+        # We want the stack frame above this and above the error/warning/info
+        inspect_stack = inspect.stack()
+        trace_info = ""
+        for i in range(level):
+            try:
+                stack_frames = inspect_stack[2 + i]
+                info = inspect.getframeinfo(stack_frames[0])
+                trace_info = "%s[%s:%s:%s]" % (trace_info,
+                                               os.path.basename(info.filename),
+                                               info.function, info.lineno)
+            except IndexError:
+                break
+        return trace_info
+
+    def debug(self, msg, *args, **kwargs):
+        trace_info = TraceLogger._get_trace_info(level=3)
+        self._logger.debug("%s %s" % (msg, trace_info), *args, **kwargs)
+
+    def error(self, msg, *args, **kwargs):
+        trace_info = TraceLogger._get_trace_info(level=3)
+        self._logger.error("%s %s" % (msg, trace_info), *args, **kwargs)
+
+    def warn(self, msg, *args, **kwargs):
+        trace_info = TraceLogger._get_trace_info(level=1)
+        self._logger.warn("%s %s" % (msg, trace_info), *args, **kwargs)
+
+    def warning(self, msg, *args, **kwargs):
+        trace_info = TraceLogger._get_trace_info(level=1)
+        self._logger.warning("%s %s" % (msg, trace_info), *args, **kwargs)
+
+    def info(self, msg, *args, **kwargs):
+        trace_info = TraceLogger._get_trace_info(level=1)
+        self._logger.info("%s %s" % (msg, trace_info), *args, **kwargs)
+
+    def __getattr__(self, name):
+        return getattr(self._logger, name)
diff --git a/acts/framework/acts/utils.py b/acts/framework/acts/utils.py
index af66d98..c8d7ba5 100755
--- a/acts/framework/acts/utils.py
+++ b/acts/framework/acts/utils.py
@@ -19,6 +19,7 @@
 import datetime
 import json
 import functools
+import logging
 import os
 import random
 import re
@@ -33,6 +34,10 @@
 MAX_FILENAME_LEN = 255
 
 
+class ActsUtilsError(Exception):
+    """Generic error raised for exceptions in ACTS utils."""
+
+
 class NexusModelNames:
     # TODO(angli): This will be fixed later by angli.
     ONE = 'sprout'
@@ -43,11 +48,22 @@
     N6v3 = 'marlin'
     N5v3 = 'sailfish'
 
+
 class DozeModeStatus:
     ACTIVE = "ACTIVE"
     IDLE = "IDLE"
 
 
+class CapablityPerDevice:
+    energy_info_models = [
+        "shamu", "volantis", "volantisg", "angler", "bullhead", "ryu",
+        "marlin", "sailfish"
+    ]
+    tdls_models = [
+        "shamu", "hammerhead", "angler", "bullhead", "marlin", "sailfish"
+    ]
+
+
 ascii_letters_and_digits = string.ascii_letters + string.digits
 valid_filename_chars = "-_." + ascii_letters_and_digits
 
@@ -183,6 +199,8 @@
         A list of files that match the predicate.
     """
     file_list = []
+    if not isinstance(paths, list):
+        paths = [paths]
     for path in paths:
         p = abs_path(path)
         for dirPath, subdirList, fileList in os.walk(p):
@@ -220,6 +238,20 @@
         return base64_str
 
 
+def dump_string_to_file(content, file_path, mode='w'):
+    """ Dump content of a string to
+
+    Args:
+        content: content to be dumped to file
+        file_path: full path to the file including the file name.
+        mode: file open mode, 'w' (truncating file) by default
+    :return:
+    """
+    full_path = abs_path(file_path)
+    with open(full_path, mode) as f:
+        f.write(content)
+
+
 def find_field(item_list, cond, comparator, target_field):
     """Finds the value of a field in a dict object that satisfies certain
     conditions.
@@ -324,43 +356,76 @@
         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.
+def _assert_subprocess_running(proc):
+    """Checks if a subprocess has terminated on its own.
 
     Args:
-        cmd: Command to start the subprocess with.
+        proc: A subprocess returned by subprocess.Popen.
+
+    Raises:
+        ActsUtilsError is raised if the subprocess has stopped.
+    """
+    ret = proc.poll()
+    if ret is not None:
+        out, err = proc.communicate()
+        raise ActsUtilsError("Process %d has terminated. ret: %d, stderr: %s,"
+                             " stdout: %s" % (proc.pid, ret, err, out))
+
+
+def start_standing_subprocess(cmd, check_health_delay=0):
+    """Starts a long-running subprocess.
+
+    This is not a blocking call and the subprocess started by it should be
+    explicitly terminated with stop_standing_subprocess.
+
+    For short-running commands, you should use exe_cmd, which blocks.
+
+    You can specify a health check after the subprocess is started to make sure
+    it did not stop prematurely.
+
+    Args:
+        cmd: string, the command to start the subprocess with.
+        check_health_delay: float, the number of seconds to wait after the
+                            subprocess starts to check its health. Default is 0,
+                            which means no check.
 
     Returns:
         The subprocess that got started.
     """
-    p = subprocess.Popen(cmd,
-                         stdout=subprocess.PIPE,
-                         stderr=subprocess.PIPE,
-                         shell=True,
-                         preexec_fn=os.setpgrp)
-    return p
+    proc = subprocess.Popen(
+        cmd,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        shell=True,
+        preexec_fn=os.setpgrp)
+    logging.debug("Start standing subprocess with cmd: %s", cmd)
+    if check_health_delay > 0:
+        time.sleep(check_health_delay)
+        _assert_subprocess_running(proc)
+    return proc
 
 
-def stop_standing_subprocess(p, kill_signal=signal.SIGTERM):
+def stop_standing_subprocess(proc, kill_signal=signal.SIGTERM):
     """Stops a subprocess started by start_standing_subprocess.
 
+    Before killing the process, we check if the process is running, if it has
+    terminated, ActsUtilsError is raised.
+
     Catches and ignores the PermissionError which only happens on Macs.
 
     Args:
-        p: Subprocess to terminate.
+        proc: Subprocess to terminate.
     """
+    pid = proc.pid
+    logging.debug("Stop standing subprocess %d", pid)
+    _assert_subprocess_running(proc)
     try:
-        os.killpg(p.pid, kill_signal)
+        os.killpg(pid, kill_signal)
     except PermissionError:
         pass
 
 
-def wait_for_standing_subprocess(p, timeout=None):
+def wait_for_standing_subprocess(proc, timeout=None):
     """Waits for a subprocess started by start_standing_subprocess to finish
     or times out.
 
@@ -380,7 +445,7 @@
         p: Subprocess to wait for.
         timeout: An integer number of seconds to wait before timing out.
     """
-    p.wait(timeout)
+    proc.wait(timeout)
 
 
 def sync_device_time(ad):
@@ -412,6 +477,9 @@
 def timeout(sec):
     """A decorator used to add time out check to a function.
 
+    This only works in main thread due to its dependency on signal module.
+    Do NOT use it if the decorated funtion does not run in the Main thread.
+
     Args:
         sec: Number of seconds to wait before the function times out.
             No timeout if set to 0
@@ -494,6 +562,7 @@
         wait_for_device_with_timeout(ad)
         ad.adb.shell("settings put global airplane_mode_on {}".format(
             1 if new_state else 0))
+        ad.adb.shell("am broadcast -a android.intent.action.AIRPLANE_MODE")
     except TimeoutError:
         # adb wait for device timeout
         return False
@@ -597,8 +666,8 @@
         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))
+    ad.adb.shell(
+        "settings put secure doze_enabled {}".format(1 if new_state else 0))
 
 
 def set_adaptive_brightness(ad, new_state):
@@ -664,7 +733,8 @@
         False if failed.
     """
     ad.adb.shell(
-        "am start -n \"com.google.android.setupwizard/.SetupWizardExitActivity\"")
+        "am start -n \"com.google.android.setupwizard/.SetupWizardExitActivity\""
+    )
     # magical sleep to wait for the gservices override broadcast to complete
     time.sleep(3)
     provisioned_state = int(
@@ -673,3 +743,67 @@
         logging.error("Failed to bypass setup wizard.")
         return False
     return True
+
+def parse_ping_ouput(ad, count, out, loss_tolerance=20):
+    """Ping Parsing util.
+
+    Args:
+        ad: Android Device Object.
+        count: Number of ICMP packets sent
+        out: shell output text of ping operation
+        loss_tolerance: Threshold after which flag test as false
+    Returns:
+        False: if packet loss is more than loss_tolerance%
+        True: if all good
+    """
+    out = out.split('\n')[-3:]
+    stats = out[1].split(',')
+    # For failure case, line of interest becomes the last line
+    if len(stats) != 4:
+        stats = out[2].split(',')
+    packet_loss = float(stats[2].split('%')[0])
+    packet_xmit = int(stats[0].split()[0])
+    packet_rcvd = int(stats[1].split()[0])
+    min_packet_xmit_rcvd = (100 - loss_tolerance) * 0.01
+
+    if (packet_loss >= loss_tolerance or
+            packet_xmit < count * min_packet_xmit_rcvd or
+            packet_rcvd < count * min_packet_xmit_rcvd):
+        ad.log.error(
+            "More than %d %% packet loss seen, Expected Packet_count %d \
+            Packet loss %.2f%% Packets_xmitted %d Packets_rcvd %d",
+            loss_tolerance, count, packet_loss, packet_xmit, packet_rcvd)
+        return False
+    ad.log.info("Pkt_count %d Pkt_loss %.2f%% Pkt_xmit %d Pkt_rcvd %d", count,
+                packet_loss, packet_xmit, packet_rcvd)
+    return True
+
+
+def adb_shell_ping(ad, count=120, dest_ip="www.google.com", timeout=200,
+                   loss_tolerance=20):
+    """Ping utility using adb shell.
+
+    Args:
+        ad: Android Device Object.
+        count: Number of ICMP packets to send
+        dest_ip: hostname or IP address
+                 default www.google.com
+        timeout: timeout for icmp pings to complete.
+    """
+    ping_cmd = "ping -W 1"
+    if count:
+        ping_cmd += " -c %d" % count
+    if dest_ip:
+        ping_cmd += " %s | tee /data/ping.txt" % dest_ip
+    try:
+        ad.log.info("Starting ping test to %s using adb command %s", dest_ip,
+                    ping_cmd)
+        out = ad.adb.shell(ping_cmd, timeout=timeout)
+        if not parse_ping_ouput(ad, count, out, loss_tolerance):
+            return False
+        return True
+    except Exception as e:
+        ad.log.warn("Ping Test to %s failed with exception %s", dest_ip, e)
+        return False
+    finally:
+        ad.adb.shell("rm /data/ping.txt", timeout=10, ignore_status=True)
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
index dd8cba0..72fbb8b 100755
--- a/acts/framework/setup.py
+++ b/acts/framework/setup.py
@@ -17,24 +17,29 @@
 from distutils import cmd
 from distutils import log
 import os
+import pip
 import shutil
 import setuptools
 from setuptools.command import test
 import sys
 
-
 install_requires = [
-    'contextlib2',
     'future',
     # mock-1.0.1 is the last version compatible with setuptools <17.1,
     # which is what comes with Ubuntu 14.04 LTS.
     'mock<=1.0.1',
     'pyserial',
+    'shellescape',
+    'protobuf',
 ]
-if sys.version_info < (3,):
+
+if sys.version_info < (3, ):
     install_requires.append('enum34')
+    install_requires.append('statistics')
     # "futures" is needed for py2 compatibility and it only works in 2.7
     install_requires.append('futures')
+    install_requires.append('py2-ipaddress')
+    install_requires.append('subprocess32')
 
 
 class PyTest(test.test):
@@ -71,8 +76,6 @@
         pass
 
     def run(self):
-        import pip
-
         required_packages = self.distribution.install_requires
 
         for package in required_packages:
@@ -107,7 +110,8 @@
             acts_module: The acts module to uninstall.
         """
         for acts_install_dir in acts_module.__path__:
-            self.announce('Deleting acts from: %s' % acts_install_dir, log.INFO)
+            self.announce('Deleting acts from: %s' % acts_install_dir,
+                          log.INFO)
             shutil.rmtree(acts_install_dir)
 
     def run(self):
@@ -124,8 +128,9 @@
         try:
             import acts as acts_module
         except ImportError:
-            self.announce('Acts is not installed, nothing to uninstall.',
-                          level=log.ERROR)
+            self.announce(
+                'Acts is not installed, nothing to uninstall.',
+                level=log.ERROR)
             return
 
         while acts_module:
@@ -140,19 +145,22 @@
 
 
 def main():
-    setuptools.setup(name='acts',
-                     version='0.9',
-                     description='Android Comms Test Suite',
-                     license='Apache2.0',
-                     packages=setuptools.find_packages(),
-                     include_package_data=False,
-                     tests_require=['pytest'],
-                     install_requires=install_requires,
-                     scripts=['acts/bin/act.py', 'acts/bin/monsoon.py'],
-                     cmdclass={'test': PyTest,
-                               'install_deps': ActsInstallDependencies,
-                               'uninstall': ActsUninstall},
-                     url="http://www.android.com/")
+    setuptools.setup(
+        name='acts',
+        version='0.9',
+        description='Android Comms Test Suite',
+        license='Apache2.0',
+        packages=setuptools.find_packages(),
+        include_package_data=False,
+        tests_require=['pytest'],
+        install_requires=install_requires,
+        scripts=['acts/bin/act.py', 'acts/bin/monsoon.py'],
+        cmdclass={
+            'test': PyTest,
+            'install_deps': ActsInstallDependencies,
+            'uninstall': ActsUninstall
+        },
+        url="http://www.android.com/")
 
 
 if __name__ == '__main__':
diff --git a/acts/framework/tests/AttenuatorSanityTest.py b/acts/framework/tests/AttenuatorSanityTest.py
index 8ce90d7..b2f5f47 100644
--- a/acts/framework/tests/AttenuatorSanityTest.py
+++ b/acts/framework/tests/AttenuatorSanityTest.py
@@ -20,14 +20,12 @@
 CONSERVATIVE_MAX_ATTEN_VALUE = 10
 MIN_ATTEN_VALUE = 0
 
-class AttenuatorSanityTest(BaseTestClass):
 
+class AttenuatorSanityTest(BaseTestClass):
     def __init__(self, controllers):
         BaseTestClass.__init__(self, controllers)
-        self.tests = (
-            "test_attenuator_validation",
-            "test_attenuator_get_max_value",
-        )
+        self.tests = ("test_attenuator_validation",
+                      "test_attenuator_get_max_value", )
         self.number_of_iteration = 2
 
     def test_attenuator_validation(self):
@@ -44,7 +42,8 @@
 
             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)))
+                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))
diff --git a/acts/framework/tests/IntegrationTest.py b/acts/framework/tests/IntegrationTest.py
index 519dd46..c8bdf2f 100755
--- a/acts/framework/tests/IntegrationTest.py
+++ b/acts/framework/tests/IntegrationTest.py
@@ -16,11 +16,12 @@
 
 from acts import asserts
 from acts import base_test
+from acts import test_runner
 
 import mock_controller
 
-class IntegrationTest(base_test.BaseTestClass):
 
+class IntegrationTest(base_test.BaseTestClass):
     def setup_class(self):
         self.register_controller(mock_controller)
 
@@ -29,4 +30,8 @@
         asserts.assert_equal(self.user_params["extra_param"], "haha")
         self.log.info("This is a bare minimal test to make sure the basic ACTS"
                       "test flow works.")
-        asserts.explicit_pass("Hello World")
\ No newline at end of file
+        asserts.explicit_pass("Hello World")
+
+
+if __name__ == "__main__":
+    test_runner.main()
diff --git a/acts/framework/tests/Sl4aSanityTest.py b/acts/framework/tests/Sl4aSanityTest.py
index 2f5add6..c3671a3 100644
--- a/acts/framework/tests/Sl4aSanityTest.py
+++ b/acts/framework/tests/Sl4aSanityTest.py
@@ -19,6 +19,7 @@
 from acts.test_utils.wifi.wifi_test_utils import wifi_toggle_state
 from acts.test_utils.wifi.wifi_test_utils import WifiEnums
 
+
 class Sl4aSanityTest(BaseTestClass):
     """Tests for sl4a basic sanity.
 
@@ -27,10 +28,8 @@
 
     def __init__(self, controllers):
         BaseTestClass.__init__(self, controllers)
-        self.tests = (
-            "test_bring_up_and_shutdown",
-            "test_message_then_shutdown_stress"
-        )
+        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.
diff --git a/acts/framework/tests/SnifferSanityTest.py b/acts/framework/tests/SnifferSanityTest.py
index 88e8563..fa9ed8b 100644
--- a/acts/framework/tests/SnifferSanityTest.py
+++ b/acts/framework/tests/SnifferSanityTest.py
@@ -17,8 +17,8 @@
 from acts import base_test
 from acts.controllers.sniffer import Sniffer
 
-class SnifferSanityTest(base_test.BaseTestClass):
 
+class SnifferSanityTest(base_test.BaseTestClass):
     def setup_class(self):
         self._channels = [6, 44]
 
@@ -38,9 +38,9 @@
         for sniffer in self.sniffers:
             for channel in self._channels:
                 with sniffer.start_capture(
-                         override_configs={Sniffer.CONFIG_KEY_CHANNEL:channel},
-                                           duration=self._capture_sec,
-                                           packet_count=self._packet_count):
+                        override_configs={Sniffer.CONFIG_KEY_CHANNEL: channel},
+                        duration=self._capture_sec,
+                        packet_count=self._packet_count):
                     self.log.info("Capture: %s", sniffer.get_capture_file())
 
     def test_sniffer_validation_manual(self):
@@ -56,8 +56,8 @@
         for sniffer in self.sniffers:
             for channel in self._channels:
                 sniffer.start_capture(
-                          override_configs={Sniffer.CONFIG_KEY_CHANNEL:channel},
-                                      packet_count=self._packet_count)
+                    override_configs={Sniffer.CONFIG_KEY_CHANNEL: channel},
+                    packet_count=self._packet_count)
                 self.log.info("Capture: %s", sniffer.get_capture_file())
                 sniffer.wait_for_capture(timeout=self._capture_sec)
 
@@ -68,9 +68,8 @@
         for sniffer in self.sniffers:
             for channel in self._channels:
                 with sniffer.start_capture(
-                         override_configs={Sniffer.CONFIG_KEY_CHANNEL:channel},
-                                           duration=self._capture_sec,
-                                           packet_count=3,
-                                           additional_args=self._filter[
-                                                       sniffer.get_subtype()]):
+                        override_configs={Sniffer.CONFIG_KEY_CHANNEL: channel},
+                        duration=self._capture_sec,
+                        packet_count=3,
+                        additional_args=self._filter[sniffer.get_subtype()]):
                     self.log.info("Capture: %s", sniffer.get_capture_file())
diff --git a/acts/framework/tests/acts_adb_test.py b/acts/framework/tests/acts_adb_test.py
deleted file mode 100755
index be9cb5e..0000000
--- a/acts/framework/tests/acts_adb_test.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python3.4
-#
-#   Copyright 2016 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-import socket
-import unittest
-
-from acts.controllers import adb
-
-class ActsAdbTest(unittest.TestCase):
-    """This test class has unit tests for the implementation of everything
-    under acts.controllers.adb.
-    """
-    def test_is_port_available_positive(self):
-        test_s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        test_s.bind(('localhost', 0))
-        port = test_s.getsockname()[1]
-        test_s.close()
-        self.assertTrue(adb.is_port_available(port))
-
-    def test_is_port_available_negative(self):
-        test_s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        test_s.bind(('localhost', 0))
-        port = test_s.getsockname()[1]
-        try:
-            self.assertFalse(adb.is_port_available(port))
-        finally:
-            test_s.close()
-
-if __name__ == "__main__":
-   unittest.main()
\ No newline at end of file
diff --git a/acts/framework/tests/acts_android_device_test.py b/acts/framework/tests/acts_android_device_test.py
index 4eff212..b79b7f6 100755
--- a/acts/framework/tests/acts_android_device_test.py
+++ b/acts/framework/tests/acts_android_device_test.py
@@ -29,18 +29,19 @@
 # The expected result of the cat adb operation.
 MOCK_ADB_LOGCAT_CAT_RESULT = [
     "02-29 14:02:21.456  4454  Something\n",
-    "02-29 14:02:21.789  4454  Something again\n"]
+    "02-29 14:02:21.789  4454  Something again\n"
+]
 # A mockd piece of adb logcat output.
-MOCK_ADB_LOGCAT = (
-    "02-29 14:02:19.123  4454  Nothing\n"
-    "%s"
-    "02-29 14:02:22.123  4454  Something again and again\n"
-    ) % ''.join(MOCK_ADB_LOGCAT_CAT_RESULT)
+MOCK_ADB_LOGCAT = ("02-29 14:02:19.123  4454  Nothing\n"
+                   "%s"
+                   "02-29 14:02:22.123  4454  Something again and again\n"
+                   ) % ''.join(MOCK_ADB_LOGCAT_CAT_RESULT)
 # Mock start and end time of the adb cat.
 MOCK_ADB_LOGCAT_BEGIN_TIME = "02-29 14:02:20.123"
 MOCK_ADB_LOGCAT_END_TIME = "02-29 14:02:22.000"
 
-def get_mock_ads(num, logger=None):
+
+def get_mock_ads(num):
     """Generates a list of mock AndroidDevice objects.
 
     The serial number of each device will be integer 0 through num - 1.
@@ -51,40 +52,53 @@
     """
     ads = []
     for i in range(num):
-        ad = mock.MagicMock(name="AndroidDevice",
-                            logger=logger,
-                            serial=i,
-                            h_port=None)
+        ad = mock.MagicMock(name="AndroidDevice", serial=i, h_port=None)
         ads.append(ad)
     return ads
 
-def get_mock_logger():
-    return mock.MagicMock(name="Logger", log_path=MOCK_LOG_PATH)
 
-def mock_get_all_instances(logger=None):
-    return get_mock_ads(5, logger=logger)
+def mock_get_all_instances():
+    return get_mock_ads(5)
+
 
 def mock_list_adb_devices():
     return [ad.serial for ad in get_mock_ads(5)]
 
+
 class MockAdbProxy():
     """Mock class that swaps out calls to adb with mock calls."""
 
-    def __init__(self, serial):
+    def __init__(self, serial, fail_br=False, fail_br_before_N=False):
         self.serial = serial
+        self.fail_br = fail_br
+        self.fail_br_before_N = fail_br_before_N
 
-    def shell(self, params):
+    def shell(self, params, ignore_status=False, timeout=60):
         if params == "id -u":
-            return b"root"
-        if (params == "getprop | grep ro.build.product" or
-            params == "getprop | grep ro.product.name"):
-            return b"[ro.build.product]: [FakeModel]"
-        elif params == "getprop sys.boot_completed":
-            return b"1"
+            return "root"
         elif params == "bugreportz":
-            return b'OK:/path/bugreport.zip\n'
+            if self.fail_br:
+                return "OMG I died!\n"
+            return "OK:/path/bugreport.zip\n"
+        elif params == "bugreportz -v":
+            if self.fail_br_before_N:
+                return "/system/bin/sh: bugreportz: not found"
+            return "1.1"
 
-    def bugreport(self, params):
+    def getprop(self, params):
+        if params == "ro.build.id":
+            return "AB42"
+        elif params == "ro.build.type":
+            return "userdebug"
+        elif params == "ro.build.product" or params == "ro.product.name":
+            return "FakeModel"
+        elif params == "sys.boot_completed":
+            return "1"
+
+    def devices(self):
+        return "\t".join([str(self.serial), "device"])
+
+    def bugreport(self, params, timeout=android_device.BUG_REPORT_TIMEOUT):
         expected = os.path.join(logging.log_path,
                                 "AndroidDevice%s" % self.serial, "BugReports",
                                 "test_something,sometime,%s" % (self.serial))
@@ -95,20 +109,41 @@
         """All calls to the none-existent functions in adb proxy would
         simply return the adb command string.
         """
-        def adb_call(*args):
-            clean_name = name.replace('_', '-')
+
+        def adb_call(*args, **kwargs):
             arg_str = ' '.join(str(elem) for elem in args)
             return arg_str
+
         return adb_call
 
+
+class MockFastbootProxy():
+    """Mock class that swaps out calls to adb with mock calls."""
+
+    def __init__(self, serial):
+        self.serial = serial
+
+    def devices(self):
+        return "xxxx\tdevice\nyyyy\tdevice"
+
+    def __getattr__(self, name):
+        def fastboot_call(*args):
+            arg_str = ' '.join(str(elem) for elem in args)
+            return arg_str
+
+        return fastboot_call
+
+
 class ActsAndroidDeviceTest(unittest.TestCase):
     """This test class has unit tests for the implementation of everything
     under acts.controllers.android_device.
     """
 
     def setUp(self):
-        """Creates a temp dir to be used by tests in this test class.
-        """
+        # Set log_path to logging since acts logger setup is not called.
+        if not hasattr(logging, "log_path"):
+            setattr(logging, "log_path", "/tmp/logs")
+        # Creates a temp dir to be used by tests in this test class.
         self.tmp_dir = tempfile.mkdtemp()
 
     def tearDown(self):
@@ -119,27 +154,27 @@
     # Tests for android_device module functions.
     # These tests use mock AndroidDevice instances.
 
-    @mock.patch.object(android_device, "get_all_instances",
-                       new=mock_get_all_instances)
-    @mock.patch.object(android_device, "list_adb_devices",
-                       new=mock_list_adb_devices)
+    @mock.patch.object(
+        android_device, "get_all_instances", new=mock_get_all_instances)
+    @mock.patch.object(
+        android_device, "list_adb_devices", new=mock_list_adb_devices)
     def test_create_with_pickup_all(self):
         pick_all_token = android_device.ANDROID_DEVICE_PICK_ALL_TOKEN
-        actual_ads = android_device.create(pick_all_token, logging)
+        actual_ads = android_device.create(pick_all_token)
         for actual, expected in zip(actual_ads, get_mock_ads(5)):
             self.assertEqual(actual.serial, expected.serial)
 
     def test_create_with_empty_config(self):
         expected_msg = android_device.ANDROID_DEVICE_EMPTY_CONFIG_MSG
-        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
-                                     expected_msg):
-            android_device.create([], logging)
+        with self.assertRaisesRegex(android_device.AndroidDeviceError,
+                                    expected_msg):
+            android_device.create([])
 
     def test_create_with_not_list_config(self):
         expected_msg = android_device.ANDROID_DEVICE_NOT_LIST_CONFIG_MSG
-        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
-                                     expected_msg):
-            android_device.create("HAHA", logging)
+        with self.assertRaisesRegex(android_device.AndroidDeviceError,
+                                    expected_msg):
+            android_device.create("HAHA")
 
     def test_get_device_success_with_serial(self):
         ads = get_mock_ads(5)
@@ -152,9 +187,8 @@
         expected_serial = 1
         expected_h_port = 5555
         ads[1].h_port = expected_h_port
-        ad = android_device.get_device(ads,
-                                       serial=expected_serial,
-                                       h_port=expected_h_port)
+        ad = android_device.get_device(
+            ads, serial=expected_serial, h_port=expected_h_port)
         self.assertEqual(ad.serial, expected_serial)
         self.assertEqual(ad.h_port, expected_h_port)
 
@@ -162,16 +196,16 @@
         ads = get_mock_ads(5)
         expected_msg = ("Could not find a target device that matches condition"
                         ": {'serial': 5}.")
-        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
-                                     expected_msg):
+        with self.assertRaisesRegex(android_device.AndroidDeviceError,
+                                    expected_msg):
             ad = android_device.get_device(ads, serial=len(ads))
 
     def test_get_device_too_many_matches(self):
         ads = get_mock_ads(5)
         target_serial = ads[1].serial = ads[0].serial
         expected_msg = "More than one device matched: \[0, 0\]"
-        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
-                                     expected_msg):
+        with self.assertRaisesRegex(android_device.AndroidDeviceError,
+                                    expected_msg):
             ad = android_device.get_device(ads, serial=target_serial)
 
     def test_start_services_on_ads(self):
@@ -187,7 +221,7 @@
         ads[2].start_services = mock.MagicMock(
             side_effect=android_device.AndroidDeviceError(msg))
         ads[2].clean_up = mock.MagicMock()
-        with self.assertRaisesRegexp(android_device.AndroidDeviceError, msg):
+        with self.assertRaisesRegex(android_device.AndroidDeviceError, msg):
             android_device._start_services_on_ads(ads)
         ads[0].clean_up.assert_called_once_with()
         ads[1].clean_up.assert_called_once_with()
@@ -198,79 +232,134 @@
     # in AndroidDeivce.
 
     @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
-    def test_AndroidDevice_instantiation(self, MockAdbProxy):
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=MockFastbootProxy(1))
+    def test_AndroidDevice_instantiation(self, MockFastboot, MockAdbProxy):
         """Verifies the AndroidDevice object's basic attributes are correctly
         set after instantiation.
         """
         mock_serial = 1
-        ml = get_mock_logger()
-        ad = android_device.AndroidDevice(serial=mock_serial, logger=ml)
+        ad = android_device.AndroidDevice(serial=mock_serial)
         self.assertEqual(ad.serial, 1)
         self.assertEqual(ad.model, "fakemodel")
         self.assertIsNone(ad.adb_logcat_process)
         self.assertIsNone(ad.adb_logcat_file_path)
-        expected_lp = os.path.join(ml.log_path,
+        expected_lp = os.path.join(logging.log_path,
                                    "AndroidDevice%s" % mock_serial)
         self.assertEqual(ad.log_path, expected_lp)
 
     @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=MockFastbootProxy(1))
+    def test_AndroidDevice_build_info(self, MockFastboot, MockAdbProxy):
+        """Verifies the AndroidDevice object's basic attributes are correctly
+        set after instantiation.
+        """
+        ad = android_device.AndroidDevice(serial=1)
+        build_info = ad.build_info
+        self.assertEqual(build_info["build_id"], "AB42")
+        self.assertEqual(build_info["build_type"], "userdebug")
+
+    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=MockFastbootProxy(1))
     @mock.patch('acts.utils.create_dir')
     @mock.patch('acts.utils.exe_cmd')
-    def test_AndroidDevice_take_bug_report(self,
-                                           exe_mock,
-                                           create_dir_mock,
-                                           MockAdbProxy):
+    def test_AndroidDevice_take_bug_report(self, exe_mock, create_dir_mock,
+                                           FastbootProxy, MockAdbProxy):
         """Verifies AndroidDevice.take_bug_report calls the correct adb command
         and writes the bugreport file to the correct path.
         """
         mock_serial = 1
-        ml = get_mock_logger()
-        ad = android_device.AndroidDevice(serial=mock_serial, logger=ml)
+        ad = android_device.AndroidDevice(serial=mock_serial)
         ad.take_bug_report("test_something", "sometime")
-        expected_path = os.path.join(MOCK_LOG_PATH,
-                                     "AndroidDevice%s" % ad.serial,
-                                     "BugReports")
+        expected_path = os.path.join(logging.log_path, "AndroidDevice%s" %
+                                     ad.serial, "BugReports")
+        create_dir_mock.assert_called_with(expected_path)
+
+    @mock.patch(
+        'acts.controllers.adb.AdbProxy',
+        return_value=MockAdbProxy(
+            1, fail_br=True))
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=MockFastbootProxy(1))
+    @mock.patch('acts.utils.create_dir')
+    @mock.patch('acts.utils.exe_cmd')
+    def test_AndroidDevice_take_bug_report_fail(
+            self, exe_mock, create_dir_mock, FastbootProxy, MockAdbProxy):
+        """Verifies AndroidDevice.take_bug_report writes out the correct message
+        when taking bugreport fails.
+        """
+        mock_serial = 1
+        ad = android_device.AndroidDevice(serial=mock_serial)
+        expected_msg = "Failed to take bugreport on 1: OMG I died!"
+        with self.assertRaisesRegex(android_device.AndroidDeviceError,
+                                    expected_msg):
+            ad.take_bug_report("test_something", "sometime")
+
+    @mock.patch(
+        'acts.controllers.adb.AdbProxy',
+        return_value=MockAdbProxy(
+            1, fail_br_before_N=True))
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=MockFastbootProxy(1))
+    @mock.patch('acts.utils.create_dir')
+    @mock.patch('acts.utils.exe_cmd')
+    def test_AndroidDevice_take_bug_report_fallback(
+            self, exe_mock, create_dir_mock, FastbootProxy, MockAdbProxy):
+        """Verifies AndroidDevice.take_bug_report falls back to traditional
+        bugreport on builds that do not have bugreportz.
+        """
+        mock_serial = 1
+        ad = android_device.AndroidDevice(serial=mock_serial)
+        ad.take_bug_report("test_something", "sometime")
+        expected_path = os.path.join(logging.log_path, "AndroidDevice%s" %
+                                     ad.serial, "BugReports")
         create_dir_mock.assert_called_with(expected_path)
 
     @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=MockFastbootProxy(1))
     @mock.patch('acts.utils.create_dir')
     @mock.patch('acts.utils.start_standing_subprocess', return_value="process")
     @mock.patch('acts.utils.stop_standing_subprocess')
-    def test_AndroidDevice_take_logcat(self,
-                                       stop_proc_mock,
-                                       start_proc_mock,
-                                       creat_dir_mock,
+    def test_AndroidDevice_take_logcat(self, stop_proc_mock, start_proc_mock,
+                                       creat_dir_mock, FastbootProxy,
                                        MockAdbProxy):
         """Verifies the steps of collecting adb logcat on an AndroidDevice
         object, including various function calls and the expected behaviors of
         the calls.
         """
         mock_serial = 1
-        ml = get_mock_logger()
-        ad = android_device.AndroidDevice(serial=mock_serial, logger=ml)
+        ad = android_device.AndroidDevice(serial=mock_serial)
         expected_msg = ("Android device .* does not have an ongoing adb logcat"
                         " collection.")
         # Expect error if stop is called before start.
-        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
-                                     expected_msg):
+        with self.assertRaisesRegex(android_device.AndroidDeviceError,
+                                    expected_msg):
             ad.stop_adb_logcat()
         ad.start_adb_logcat()
         # Verify start did the correct operations.
         self.assertTrue(ad.adb_logcat_process)
-        expected_log_path = os.path.join(
-                                    MOCK_LOG_PATH,
-                                    "AndroidDevice%s" % ad.serial,
-                                    "adblog,fakemodel,%s.txt" % ad.serial)
+        expected_log_path = os.path.join(logging.log_path,
+                                         "AndroidDevice%s" % ad.serial,
+                                         "adblog,fakemodel,%s.txt" % ad.serial)
         creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
-        adb_cmd = 'adb -s %s logcat -v threadtime  >> %s'
+        adb_cmd = 'adb -s %s logcat -v threadtime -b all >> %s'
         start_proc_mock.assert_called_with(adb_cmd % (ad.serial,
                                                       expected_log_path))
         self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
         expected_msg = ("Android device .* already has an adb logcat thread "
                         "going on. Cannot start another one.")
         # Expect error if start is called back to back.
-        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
-                                     expected_msg):
+        with self.assertRaisesRegex(android_device.AndroidDeviceError,
+                                    expected_msg):
             ad.start_adb_logcat()
         # Verify stop did the correct operations.
         ad.stop_adb_logcat()
@@ -279,27 +368,63 @@
         self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
 
     @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=MockFastbootProxy(1))
+    @mock.patch('acts.utils.create_dir')
     @mock.patch('acts.utils.start_standing_subprocess', return_value="process")
     @mock.patch('acts.utils.stop_standing_subprocess')
-    @mock.patch('acts.logger.get_log_line_timestamp',
-                return_value=MOCK_ADB_LOGCAT_END_TIME)
-    def test_AndroidDevice_cat_adb_log(self,
-                                       mock_timestamp_getter,
-                                       stop_proc_mock,
-                                       start_proc_mock,
-                                       MockAdbProxy):
+    def test_AndroidDevice_take_logcat_with_user_param(
+            self, stop_proc_mock, start_proc_mock, creat_dir_mock,
+            FastbootProxy, MockAdbProxy):
+        """Verifies the steps of collecting adb logcat on an AndroidDevice
+        object, including various function calls and the expected behaviors of
+        the calls.
+        """
+        mock_serial = 1
+        ad = android_device.AndroidDevice(serial=mock_serial)
+        ad.adb_logcat_param = "-b radio"
+        expected_msg = ("Android device .* does not have an ongoing adb logcat"
+                        " collection.")
+        # Expect error if stop is called before start.
+        with self.assertRaisesRegex(android_device.AndroidDeviceError,
+                                    expected_msg):
+            ad.stop_adb_logcat()
+        ad.start_adb_logcat()
+        # Verify start did the correct operations.
+        self.assertTrue(ad.adb_logcat_process)
+        expected_log_path = os.path.join(logging.log_path,
+                                         "AndroidDevice%s" % ad.serial,
+                                         "adblog,fakemodel,%s.txt" % ad.serial)
+        creat_dir_mock.assert_called_with(os.path.dirname(expected_log_path))
+        adb_cmd = 'adb -s %s logcat -v threadtime -b radio >> %s'
+        start_proc_mock.assert_called_with(adb_cmd % (ad.serial,
+                                                      expected_log_path))
+        self.assertEqual(ad.adb_logcat_file_path, expected_log_path)
+
+    @mock.patch('acts.controllers.adb.AdbProxy', return_value=MockAdbProxy(1))
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=MockFastbootProxy(1))
+    @mock.patch('acts.utils.start_standing_subprocess', return_value="process")
+    @mock.patch('acts.utils.stop_standing_subprocess')
+    @mock.patch(
+        'acts.logger.get_log_line_timestamp',
+        return_value=MOCK_ADB_LOGCAT_END_TIME)
+    def test_AndroidDevice_cat_adb_log(self, mock_timestamp_getter,
+                                       stop_proc_mock, start_proc_mock,
+                                       FastbootProxy, MockAdbProxy):
         """Verifies that AndroidDevice.cat_adb_log loads the correct adb log
         file, locates the correct adb log lines within the given time range,
         and writes the lines to the correct output file.
         """
         mock_serial = 1
-        ml = get_mock_logger()
-        ad = android_device.AndroidDevice(serial=mock_serial, logger=ml)
+        ad = android_device.AndroidDevice(serial=mock_serial)
         # Expect error if attempted to cat adb log before starting adb logcat.
         expected_msg = ("Attempting to cat adb log when none has been "
                         "collected on Android device .*")
-        with self.assertRaisesRegexp(android_device.AndroidDeviceError,
-                                     expected_msg):
+        with self.assertRaisesRegex(android_device.AndroidDeviceError,
+                                    expected_msg):
             ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME)
         ad.start_adb_logcat()
         # Direct the log path of the ad to a temp dir to avoid racing.
@@ -309,15 +434,14 @@
         with open(mock_adb_log_path, 'w') as f:
             f.write(MOCK_ADB_LOGCAT)
         ad.cat_adb_log("some_test", MOCK_ADB_LOGCAT_BEGIN_TIME)
-        cat_file_path = os.path.join(ad.log_path,
-                                     "AdbLogExcerpts",
-                                     ("some_test,02-29 14:02:20.123,%s,%s.txt"
-                                     ) % (ad.model, ad.serial))
+        cat_file_path = os.path.join(ad.log_path, "AdbLogExcerpts", (
+            "some_test,02-29 14:02:20.123,%s,%s.txt") % (ad.model, ad.serial))
         with open(cat_file_path, 'r') as f:
             actual_cat = f.read()
         self.assertEqual(actual_cat, ''.join(MOCK_ADB_LOGCAT_CAT_RESULT))
         # Stops adb logcat.
         ad.stop_adb_logcat()
 
+
 if __name__ == "__main__":
-   unittest.main()
+    unittest.main()
diff --git a/acts/framework/tests/acts_asserts_test.py b/acts/framework/tests/acts_asserts_test.py
new file mode 100755
index 0000000..dbf39d9
--- /dev/null
+++ b/acts/framework/tests/acts_asserts_test.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import unittest
+
+from acts import asserts
+from acts import signals
+
+MSG_EXPECTED_EXCEPTION = "This is an expected exception."
+
+
+class ActsAssertsTest(unittest.TestCase):
+    """Verifies that asserts.xxx functions raise the correct test signals.
+    """
+
+    def test_assert_false(self):
+        asserts.assert_false(False, MSG_EXPECTED_EXCEPTION)
+        with self.assertRaisesRegexp(signals.TestFailure,
+                                     MSG_EXPECTED_EXCEPTION):
+            asserts.assert_false(True, MSG_EXPECTED_EXCEPTION)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/acts/framework/tests/acts_base_class_test.py b/acts/framework/tests/acts_base_class_test.py
index 684c481..53849ed 100755
--- a/acts/framework/tests/acts_base_class_test.py
+++ b/acts/framework/tests/acts_base_class_test.py
@@ -28,30 +28,35 @@
 
 MOCK_EXTRA = {"key": "value", "answer_to_everything": 42}
 
+
 def never_call():
     raise Exception(MSG_UNEXPECTED_EXCEPTION)
 
+
 class SomeError(Exception):
     """A custom exception class used for tests in this module."""
 
-class ActsBaseClassTest(unittest.TestCase):
 
+class ActsBaseClassTest(unittest.TestCase):
     def setUp(self):
         self.mock_test_cls_configs = {
             'reporter': mock.MagicMock(),
             'log': mock.MagicMock(),
             'log_path': '/tmp',
             'cli_args': None,
-            'user_params': {}
+            'user_params': {
+                "some_param": "hahaha"
+            }
         }
         self.mock_test_name = "test_something"
 
     def test_current_test_case_name(self):
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
-                asserts.assert_true(self.current_test_name == "test_func", ("Got "
-                                 "unexpected test name %s."
-                                 ) % self.current_test_name)
+                asserts.assert_true(self.current_test_name == "test_func", (
+                    "Got "
+                    "unexpected test name %s.") % self.current_test_name)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_func"])
         actual_record = bt_cls.results.passed[0]
@@ -63,12 +68,15 @@
         class MockBaseTest(base_test.BaseTestClass):
             def __init__(self, controllers):
                 super(MockBaseTest, self).__init__(controllers)
-                self.tests = ("test_something",)
+                self.tests = ("test_something", )
+
             def test_something(self):
                 pass
+
             def test_never(self):
                 # This should not execute it's not on default test list.
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.passed[0]
@@ -78,29 +86,34 @@
         class MockBaseTest(base_test.BaseTestClass):
             def __init__(self, controllers):
                 super(MockBaseTest, self).__init__(controllers)
-                self.tests = ("not_a_test_something",)
+                self.tests = ("not_a_test_something", )
+
             def not_a_test_something(self):
                 pass
+
             def test_never(self):
                 # This should not execute it's not on default test list.
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         expected_msg = ("Test case name not_a_test_something does not follow "
                         "naming convention test_\*, abort.")
-        with self.assertRaisesRegexp(test_runner.USERError,
-                                     expected_msg):
+        with self.assertRaisesRegexp(base_test.Error, expected_msg):
             bt_cls.run()
 
     def test_cli_test_selection_override_self_tests_list(self):
         class MockBaseTest(base_test.BaseTestClass):
             def __init__(self, controllers):
                 super(MockBaseTest, self).__init__(controllers)
-                self.tests = ("test_never",)
+                self.tests = ("test_never", )
+
             def test_something(self):
                 pass
+
             def test_never(self):
                 # This should not execute it's not selected by cmd line input.
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_something"])
         actual_record = bt_cls.results.passed[0]
@@ -110,91 +123,118 @@
         class MockBaseTest(base_test.BaseTestClass):
             def __init__(self, controllers):
                 super(MockBaseTest, self).__init__(controllers)
-                self.tests = ("not_a_test_something",)
+                self.tests = ("not_a_test_something", )
+
             def not_a_test_something(self):
                 pass
+
             def test_never(self):
                 # This should not execute it's not selected by cmd line input.
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         expected_msg = ("Test case name not_a_test_something does not follow "
                         "naming convention test_*, abort.")
-        with self.assertRaises(test_runner.USERError, msg=expected_msg):
+        with self.assertRaises(base_test.Error, msg=expected_msg):
             bt_cls.run(test_names=["not_a_test_something"])
 
     def test_default_execution_of_all_tests(self):
         class MockBaseTest(base_test.BaseTestClass):
             def test_something(self):
                 pass
+
             def not_a_test(self):
                 # This should not execute its name doesn't follow test case
                 # naming convention.
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_something"])
         actual_record = bt_cls.results.passed[0]
         self.assertEqual(actual_record.test_name, "test_something")
 
+    def test_missing_requested_test_func(self):
+        class MockBaseTest(base_test.BaseTestClass):
+            pass
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run(test_names=["test_something"])
+        self.assertFalse(bt_cls.results.executed)
+        self.assertTrue(bt_cls.results.skipped)
+
     def test_setup_class_fail_by_exception(self):
+        call_check = mock.MagicMock()
+
         class MockBaseTest(base_test.BaseTestClass):
             def setup_class(self):
                 raise Exception(MSG_EXPECTED_EXCEPTION)
+
             def test_something(self):
                 # This should not execute because setup_class failed.
                 never_call()
+
+            def on_blocked(self, test_name, begin_time):
+                call_check("haha")
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
-        bt_cls.run(test_names=["test_func"])
-        actual_record = bt_cls.results.failed[0]
-        self.assertEqual(actual_record.test_name, "")
-        expected_msg = "setup_class failed for MockBaseTest: %s" % (
-                       MSG_EXPECTED_EXCEPTION)
-        self.assertEqual(actual_record.details, expected_msg)
-        self.assertIsNone(actual_record.extras)
-        expected_summary = ("Executed 1, Failed 1, Passed 0, Requested 1, "
-                            "Skipped 0, Unknown 0")
+        bt_cls.run()
+        actual_record = bt_cls.results.blocked[0]
+        self.assertEqual(actual_record.test_name, "test_something")
+        expected_summary = (
+            "Blocked 1, ControllerInfo {}, Executed 0, Failed 0, Passed 0,"
+            " Requested 1, Skipped 0, Unknown 0")
         self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+        call_check.assert_called_once_with("haha")
 
     def test_setup_test_fail_by_exception(self):
         class MockBaseTest(base_test.BaseTestClass):
             def setup_test(self):
                 raise Exception(MSG_EXPECTED_EXCEPTION)
+
             def test_something(self):
                 # This should not execute because setup_test failed.
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_something"])
         actual_record = bt_cls.results.unknown[0]
         self.assertEqual(actual_record.test_name, self.mock_test_name)
         self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
         self.assertIsNone(actual_record.extras)
-        expected_summary = ("Executed 1, Failed 0, Passed 0, Requested 1, "
-                            "Skipped 0, Unknown 1")
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
         self.assertEqual(bt_cls.results.summary_str(), expected_summary)
 
     def test_setup_test_fail_by_test_signal(self):
         class MockBaseTest(base_test.BaseTestClass):
             def setup_test(self):
                 raise signals.TestFailure(MSG_EXPECTED_EXCEPTION)
+
             def test_something(self):
                 # This should not execute because setup_test failed.
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_something"])
         actual_record = bt_cls.results.failed[0]
         self.assertEqual(actual_record.test_name, self.mock_test_name)
         self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
         self.assertIsNone(actual_record.extras)
-        expected_summary = ("Executed 1, Failed 1, Passed 0, Requested 1, "
-                            "Skipped 0, Unknown 0")
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 1, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 0")
         self.assertEqual(bt_cls.results.summary_str(), expected_summary)
 
     def test_setup_test_fail_by_return_False(self):
         class MockBaseTest(base_test.BaseTestClass):
             def setup_test(self):
                 return False
+
             def test_something(self):
                 # This should not execute because setup_test failed.
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_something"])
         actual_record = bt_cls.results.failed[0]
@@ -202,48 +242,315 @@
         self.assertEqual(actual_record.test_name, self.mock_test_name)
         self.assertEqual(actual_record.details, expected_msg)
         self.assertIsNone(actual_record.extras)
-        expected_summary = ("Executed 1, Failed 1, Passed 0, Requested 1, "
-                            "Skipped 0, Unknown 0")
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 1, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 0")
         self.assertEqual(bt_cls.results.summary_str(), expected_summary)
 
     def test_teardown_test_assert_fail(self):
         class MockBaseTest(base_test.BaseTestClass):
             def teardown_test(self):
                 asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
+
             def test_something(self):
                 pass
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
-        actual_record = bt_cls.results.failed[0]
+        actual_record = bt_cls.results.unknown[0]
         self.assertEqual(actual_record.test_name, self.mock_test_name)
-        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
+        self.assertIsNone(actual_record.details)
         self.assertIsNone(actual_record.extras)
-        expected_summary = ("Executed 1, Failed 1, Passed 0, Requested 1, "
-                            "Skipped 0, Unknown 0")
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
         self.assertEqual(bt_cls.results.summary_str(), expected_summary)
 
     def test_teardown_test_raise_exception(self):
         class MockBaseTest(base_test.BaseTestClass):
             def teardown_test(self):
                 raise Exception(MSG_EXPECTED_EXCEPTION)
+
             def test_something(self):
                 pass
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.unknown[0]
         self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertIsNone(actual_record.details)
+        self.assertIsNone(actual_record.extras)
+        expected_extra_error = {"teardown_test": MSG_EXPECTED_EXCEPTION}
+        self.assertEqual(actual_record.extra_errors, expected_extra_error)
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_teardown_test_executed_if_test_pass(self):
+        my_mock = mock.MagicMock()
+
+        class MockBaseTest(base_test.BaseTestClass):
+            def teardown_test(self):
+                my_mock("teardown_test")
+
+            def test_something(self):
+                pass
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        actual_record = bt_cls.results.passed[0]
+        my_mock.assert_called_once_with("teardown_test")
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertIsNone(actual_record.details)
+        self.assertIsNone(actual_record.extras)
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 1, "
+            "Requested 1, Skipped 0, Unknown 0")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_teardown_test_executed_if_setup_test_fails(self):
+        my_mock = mock.MagicMock()
+
+        class MockBaseTest(base_test.BaseTestClass):
+            def setup_test(self):
+                raise Exception(MSG_EXPECTED_EXCEPTION)
+
+            def teardown_test(self):
+                my_mock("teardown_test")
+
+            def test_something(self):
+                pass
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        actual_record = bt_cls.results.unknown[0]
+        my_mock.assert_called_once_with("teardown_test")
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
         self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
         self.assertIsNone(actual_record.extras)
-        expected_summary = ("Executed 1, Failed 0, Passed 0, Requested 1, "
-                            "Skipped 0, Unknown 1")
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_teardown_test_executed_if_test_fails(self):
+        my_mock = mock.MagicMock()
+
+        class MockBaseTest(base_test.BaseTestClass):
+            def teardown_test(self):
+                my_mock("teardown_test")
+
+            def test_something(self):
+                raise Exception(MSG_EXPECTED_EXCEPTION)
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        actual_record = bt_cls.results.unknown[0]
+        my_mock.assert_called_once_with("teardown_test")
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
+        self.assertIsNone(actual_record.extras)
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_on_exception_executed_if_teardown_test_fails(self):
+        my_mock = mock.MagicMock()
+
+        class MockBaseTest(base_test.BaseTestClass):
+            def on_exception(self, test_name, begin_time):
+                my_mock("on_exception")
+
+            def teardown_test(self):
+                raise Exception(MSG_EXPECTED_EXCEPTION)
+
+            def test_something(self):
+                pass
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        my_mock.assert_called_once_with("on_exception")
+        actual_record = bt_cls.results.unknown[0]
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertIsNone(actual_record.details)
+        self.assertIsNone(actual_record.extras)
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_on_fail_executed_if_test_fails(self):
+        my_mock = mock.MagicMock()
+
+        class MockBaseTest(base_test.BaseTestClass):
+            def on_fail(self, test_name, begin_time):
+                my_mock("on_fail")
+
+            def test_something(self):
+                asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        my_mock.assert_called_once_with("on_fail")
+        actual_record = bt_cls.results.failed[0]
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
+        self.assertIsNone(actual_record.extras)
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 1, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 0")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_on_fail_executed_if_test_setup_fails_by_exception(self):
+        my_mock = mock.MagicMock()
+
+        class MockBaseTest(base_test.BaseTestClass):
+            def setup_test(self):
+                raise Exception(MSG_EXPECTED_EXCEPTION)
+
+            def on_fail(self, test_name, begin_time):
+                my_mock("on_fail")
+
+            def test_something(self):
+                pass
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        my_mock.assert_called_once_with("on_fail")
+        actual_record = bt_cls.results.unknown[0]
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
+        self.assertIsNone(actual_record.extras)
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_on_fail_executed_if_test_setup_fails_by_return_False(self):
+        my_mock = mock.MagicMock()
+
+        class MockBaseTest(base_test.BaseTestClass):
+            def setup_test(self):
+                return False
+
+            def on_fail(self, test_name, begin_time):
+                my_mock("on_fail")
+
+            def test_something(self):
+                pass
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        my_mock.assert_called_once_with("on_fail")
+        actual_record = bt_cls.results.failed[0]
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertEqual(actual_record.details,
+                         'Setup for test_something failed.')
+        self.assertIsNone(actual_record.extras)
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 1, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 0")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_failure_to_call_procedure_function_is_recorded(self):
+        class MockBaseTest(base_test.BaseTestClass):
+            def on_fail(self):
+                pass
+
+            def test_something(self):
+                asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        actual_record = bt_cls.results.unknown[0]
+        self.assertIn('_on_fail', actual_record.extra_errors)
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
+        self.assertIsNone(actual_record.extras)
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_failure_in_procedure_functions_is_recorded(self):
+        expected_msg = "Something failed in on_pass."
+
+        class MockBaseTest(base_test.BaseTestClass):
+            def on_pass(self, test_name, begin_time):
+                raise Exception(expected_msg)
+
+            def test_something(self):
+                asserts.explicit_pass(MSG_EXPECTED_EXCEPTION)
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        actual_record = bt_cls.results.unknown[0]
+        expected_extra_error = {'_on_pass': expected_msg}
+        self.assertEqual(actual_record.extra_errors, expected_extra_error)
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
+        self.assertIsNone(actual_record.extras)
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_both_teardown_and_test_body_raise_exceptions(self):
+        class MockBaseTest(base_test.BaseTestClass):
+            def teardown_test(self):
+                asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
+
+            def test_something(self):
+                raise Exception("Test Body Exception.")
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        actual_record = bt_cls.results.unknown[0]
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertEqual(actual_record.details, "Test Body Exception.")
+        self.assertIsNone(actual_record.extras)
+        self.assertEqual(actual_record.extra_errors["teardown_test"],
+                         "Details=This is an expected exception., Extras=None")
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
+        self.assertEqual(bt_cls.results.summary_str(), expected_summary)
+
+    def test_explicit_pass_but_teardown_test_raises_an_exception(self):
+        """Test record result should be marked as UNKNOWN as opposed to PASS.
+        """
+
+        class MockBaseTest(base_test.BaseTestClass):
+            def teardown_test(self):
+                asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
+
+            def test_something(self):
+                asserts.explicit_pass("Test Passed!")
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        actual_record = bt_cls.results.unknown[0]
+        self.assertEqual(actual_record.test_name, self.mock_test_name)
+        self.assertEqual(actual_record.details, "Test Passed!")
+        self.assertIsNone(actual_record.extras)
+        self.assertEqual(actual_record.extra_errors["teardown_test"],
+                         "Details=This is an expected exception., Extras=None")
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
         self.assertEqual(bt_cls.results.summary_str(), expected_summary)
 
     def test_on_pass_raise_exception(self):
         class MockBaseTest(base_test.BaseTestClass):
             def on_pass(self, test_name, begin_time):
                 raise Exception(MSG_EXPECTED_EXCEPTION)
+
             def test_something(self):
-                asserts.explicit_pass(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
+                asserts.explicit_pass(
+                    MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.unknown[0]
@@ -252,16 +559,19 @@
         self.assertEqual(actual_record.extras, MOCK_EXTRA)
         self.assertEqual(actual_record.extra_errors,
                          {'_on_pass': MSG_EXPECTED_EXCEPTION})
-        expected_summary = ("Executed 1, Failed 0, Passed 0, Requested 1, "
-                            "Skipped 0, Unknown 1")
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
         self.assertEqual(bt_cls.results.summary_str(), expected_summary)
 
     def test_on_fail_raise_exception(self):
         class MockBaseTest(base_test.BaseTestClass):
             def on_fail(self, test_name, begin_time):
                 raise Exception(MSG_EXPECTED_EXCEPTION)
+
             def test_something(self):
                 asserts.fail(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.unknown[0]
@@ -271,34 +581,38 @@
         self.assertEqual(actual_record.extras, MOCK_EXTRA)
         self.assertEqual(actual_record.extra_errors,
                          {'_on_fail': MSG_EXPECTED_EXCEPTION})
-        expected_summary = ("Executed 1, Failed 0, Passed 0, Requested 1, "
-                            "Skipped 0, Unknown 1")
+        expected_summary = (
+            "Blocked 0, ControllerInfo {}, Executed 1, Failed 0, Passed 0, "
+            "Requested 1, Skipped 0, Unknown 1")
         self.assertEqual(bt_cls.results.summary_str(), expected_summary)
 
     def test_abort_class(self):
         class MockBaseTest(base_test.BaseTestClass):
             def test_1(self):
                 pass
+
             def test_2(self):
                 asserts.abort_class(MSG_EXPECTED_EXCEPTION)
                 never_call()
+
             def test_3(self):
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_1", "test_2", "test_3"])
-        self.assertEqual(bt_cls.results.passed[0].test_name,
-                         "test_1")
+        self.assertEqual(bt_cls.results.passed[0].test_name, "test_1")
         self.assertEqual(bt_cls.results.failed[0].details,
                          MSG_EXPECTED_EXCEPTION)
-        self.assertEqual(bt_cls.results.summary_str(),
-                         ("Executed 2, Failed 1, Passed 1, Requested 3, "
-                          "Skipped 0, Unknown 0"))
+        self.assertEqual(bt_cls.results.summary_str(), (
+            "Blocked 0, ControllerInfo {}, Executed 2, Failed 1, Passed 1, "
+            "Requested 3, Skipped 0, Unknown 0"))
 
     def test_uncaught_exception(self):
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
                 raise Exception(MSG_EXPECTED_EXCEPTION)
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_func"])
         actual_record = bt_cls.results.unknown[0]
@@ -311,6 +625,7 @@
             def test_func(self):
                 asserts.fail(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_func"])
         actual_record = bt_cls.results.failed[0]
@@ -321,9 +636,10 @@
     def test_assert_true(self):
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
-                asserts.assert_true(False, MSG_EXPECTED_EXCEPTION,
-                                 extras=MOCK_EXTRA)
+                asserts.assert_true(
+                    False, MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_func"])
         actual_record = bt_cls.results.failed[0]
@@ -335,6 +651,7 @@
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
                 asserts.assert_equal(1, 1, extras=MOCK_EXTRA)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.passed[0]
@@ -346,6 +663,7 @@
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
                 asserts.assert_equal(1, 2, extras=MOCK_EXTRA)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.failed[0]
@@ -356,8 +674,9 @@
     def test_assert_equal_fail_with_msg(self):
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
-                asserts.assert_equal(1, 2, msg=MSG_EXPECTED_EXCEPTION,
-                                     extras=MOCK_EXTRA)
+                asserts.assert_equal(
+                    1, 2, msg=MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.failed[0]
@@ -371,6 +690,7 @@
             def test_func(self):
                 with asserts.assert_raises(SomeError, extras=MOCK_EXTRA):
                     raise SomeError(MSG_EXPECTED_EXCEPTION)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.passed[0]
@@ -383,6 +703,7 @@
             def test_func(self):
                 with asserts.assert_raises(SomeError, extras=MOCK_EXTRA):
                     pass
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.failed[0]
@@ -395,6 +716,7 @@
             def test_func(self):
                 with asserts.assert_raises(SomeError, extras=MOCK_EXTRA):
                     raise AttributeError(MSG_UNEXPECTED_EXCEPTION)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.unknown[0]
@@ -410,6 +732,7 @@
                         expected_regex=MSG_EXPECTED_EXCEPTION,
                         extras=MOCK_EXTRA):
                     raise SomeError(MSG_EXPECTED_EXCEPTION)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.passed[0]
@@ -425,6 +748,7 @@
                         expected_regex=MSG_EXPECTED_EXCEPTION,
                         extras=MOCK_EXTRA):
                     pass
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.failed[0]
@@ -434,6 +758,7 @@
 
     def test_assert_raises_fail_with_wrong_regex(self):
         wrong_msg = "ha"
+
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
                 with asserts.assert_raises_regex(
@@ -441,6 +766,7 @@
                         expected_regex=MSG_EXPECTED_EXCEPTION,
                         extras=MOCK_EXTRA):
                     raise SomeError(wrong_msg)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.failed[0]
@@ -458,6 +784,7 @@
                         expected_regex=MSG_EXPECTED_EXCEPTION,
                         extras=MOCK_EXTRA):
                     raise AttributeError(MSG_UNEXPECTED_EXCEPTION)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run()
         actual_record = bt_cls.results.unknown[0]
@@ -468,9 +795,10 @@
     def test_explicit_pass(self):
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
-                asserts.explicit_pass(MSG_EXPECTED_EXCEPTION,
-                                      extras=MOCK_EXTRA)
+                asserts.explicit_pass(
+                    MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_func"])
         actual_record = bt_cls.results.passed[0]
@@ -482,6 +810,7 @@
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
                 pass
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_func"])
         actual_record = bt_cls.results.passed[0]
@@ -494,6 +823,7 @@
             def test_func(self):
                 asserts.skip(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_func"])
         actual_record = bt_cls.results.skipped[0]
@@ -505,9 +835,10 @@
         class MockBaseTest(base_test.BaseTestClass):
             def test_func(self):
                 asserts.skip_if(False, MSG_UNEXPECTED_EXCEPTION)
-                asserts.skip_if(True, MSG_EXPECTED_EXCEPTION,
-                                extras=MOCK_EXTRA)
+                asserts.skip_if(
+                    True, MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
                 never_call()
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_func"])
         actual_record = bt_cls.results.skipped[0]
@@ -517,14 +848,63 @@
 
     def test_unpack_userparams_required(self):
         """Missing a required param should raise an error."""
+        required = ["some_param"]
+        bc = base_test.BaseTestClass(self.mock_test_cls_configs)
+        bc.unpack_userparams(required)
+        expected_value = self.mock_test_cls_configs["user_params"][
+            "some_param"]
+        self.assertEqual(bc.some_param, expected_value)
+
+    def test_unpack_userparams_required_missing(self):
+        """Missing a required param should raise an error."""
         required = ["something"]
         bc = base_test.BaseTestClass(self.mock_test_cls_configs)
         expected_msg = ("Missing required user param '%s' in test "
                         "configuration.") % required[0]
-        with self.assertRaises(base_test.BaseTestError, msg=expected_msg):
+        with self.assertRaises(base_test.Error, msg=expected_msg):
             bc.unpack_userparams(required)
 
     def test_unpack_userparams_optional(self):
+        """If an optional param is specified, the value should be what's in the
+        config.
+        """
+        opt = ["some_param"]
+        bc = base_test.BaseTestClass(self.mock_test_cls_configs)
+        bc.unpack_userparams(opt_param_names=opt)
+        expected_value = self.mock_test_cls_configs["user_params"][
+            "some_param"]
+        self.assertEqual(bc.some_param, expected_value)
+
+    def test_unpack_userparams_optional_with_default(self):
+        """If an optional param is specified with a default value, and the
+        param is not in the config, the value should be the default value.
+        """
+        bc = base_test.BaseTestClass(self.mock_test_cls_configs)
+        bc.unpack_userparams(optional_thing="whatever")
+        self.assertEqual(bc.optional_thing, "whatever")
+
+    def test_unpack_userparams_default_overwrite_by_optional_param_list(self):
+        """If an optional param is specified in kwargs, and the param is in the
+        config, the value should be the one in the config.
+        """
+        bc = base_test.BaseTestClass(self.mock_test_cls_configs)
+        bc.unpack_userparams(some_param="whatever")
+        expected_value = self.mock_test_cls_configs["user_params"][
+            "some_param"]
+        self.assertEqual(bc.some_param, expected_value)
+
+    def test_unpack_userparams_default_overwrite_by_required_param_list(self):
+        """If an optional param is specified in kwargs, the param is in the
+        required param list, and the param is not specified in the config, the
+        param's alue should be the default value and there should be no error
+        thrown.
+        """
+        bc = base_test.BaseTestClass(self.mock_test_cls_configs)
+        bc.unpack_userparams(
+            req_param_names=['a_kwarg_param'], a_kwarg_param="whatever")
+        self.assertEqual(bc.a_kwarg_param, "whatever")
+
+    def test_unpack_userparams_optional_missing(self):
         """Missing an optional param should not raise an error."""
         opt = ["something"]
         bc = base_test.BaseTestClass(self.mock_test_cls_configs)
@@ -538,8 +918,8 @@
         configs["user_params"]["something"] = 42
         configs["user_params"]["something_else"] = 53
         bc = base_test.BaseTestClass(configs)
-        bc.unpack_userparams(req_param_names=required,
-                             opt_param_names=optional)
+        bc.unpack_userparams(
+            req_param_names=required, opt_param_names=optional)
         self.assertEqual(bc.something, 42)
         self.assertEqual(bc.something_else, 53)
 
@@ -550,8 +930,7 @@
         configs = dict(self.mock_test_cls_configs)
         configs["user_params"][arg_name] = actual_arg_val
         bc = base_test.BaseTestClass(configs)
-        bc.unpack_userparams(opt_param_names=[arg_name],
-                             arg1=default_arg_val)
+        bc.unpack_userparams(opt_param_names=[arg_name], arg1=default_arg_val)
         self.assertEqual(bc.arg1, actual_arg_val)
 
     def test_unpack_userparams_default_None(self):
@@ -571,33 +950,35 @@
         static_arg = "haha"
         static_kwarg = "meh"
         itrs = ["pass", "fail", "skip"]
+
         class MockBaseTest(base_test.BaseTestClass):
             def name_gen(self, setting, arg, special_arg=None):
                 return "test_%s_%s" % (setting, arg)
+
             def logic(self, setting, arg, special_arg=None):
-                asserts.assert_true(setting in itrs,
-                                 ("%s is not in acceptable settings range %s"
-                                 ) % (setting, itrs))
+                asserts.assert_true(setting in itrs, (
+                    "%s is not in acceptable settings range %s") %
+                                    (setting, itrs))
                 asserts.assert_true(arg == static_arg,
-                                 "Expected %s, got %s" % (static_arg, arg))
-                asserts.assert_true(arg == static_arg,
-                                 "Expected %s, got %s" % (static_kwarg,
-                                                          special_arg))
+                                    "Expected %s, got %s" % (static_arg, arg))
+                asserts.assert_true(arg == static_arg, "Expected %s, got %s" %
+                                    (static_kwarg, special_arg))
                 if setting == "pass":
-                    asserts.explicit_pass(MSG_EXPECTED_EXCEPTION,
-                                          extras=MOCK_EXTRA)
+                    asserts.explicit_pass(
+                        MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
                 elif setting == "fail":
                     asserts.fail(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
                 elif setting == "skip":
                     asserts.skip(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
+
             @signals.generated_test
             def test_func(self):
                 self.run_generated_testcases(
                     test_func=self.logic,
                     settings=itrs,
-                    args=(static_arg,),
-                    name_func=self.name_gen
-                )
+                    args=(static_arg, ),
+                    name_func=self.name_gen)
+
         bt_cls = MockBaseTest(self.mock_test_cls_configs)
         bt_cls.run(test_names=["test_func"])
         self.assertEqual(len(bt_cls.results.requested), 3)
@@ -614,5 +995,6 @@
         self.assertEqual(fail_record.details, MSG_EXPECTED_EXCEPTION)
         self.assertEqual(fail_record.extras, MOCK_EXTRA)
 
+
 if __name__ == "__main__":
-   unittest.main()
\ No newline at end of file
+    unittest.main()
diff --git a/acts/framework/tests/acts_host_utils_test.py b/acts/framework/tests/acts_host_utils_test.py
new file mode 100755
index 0000000..e75d27a
--- /dev/null
+++ b/acts/framework/tests/acts_host_utils_test.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import mock
+import socket
+import unittest
+
+from acts.controllers.utils_lib import host_utils
+
+
+class ActsHostUtilsTest(unittest.TestCase):
+    """This test class has unit tests for the implementation of everything
+    under acts.controllers.adb.
+    """
+
+    def test_is_port_available_positive(self):
+        # Unfortunately, we cannot do this test reliably for SOCK_STREAM
+        # because the kernel allow this socket to linger about for some
+        # small amount of time.  We're not using SO_REUSEADDR because we
+        # are working around behavior on darwin where binding to localhost
+        # on some port and then binding again to the wildcard address
+        # with SO_REUSEADDR seems to be allowed.
+        test_s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        test_s.bind(('localhost', 0))
+        port = test_s.getsockname()[1]
+        test_s.close()
+        self.assertTrue(host_utils.is_port_available(port))
+
+    def test_detects_udp_port_in_use(self):
+        test_s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        test_s.bind(('localhost', 0))
+        port = test_s.getsockname()[1]
+        try:
+            self.assertFalse(host_utils.is_port_available(port))
+        finally:
+            test_s.close()
+
+    def test_detects_tcp_port_in_use(self):
+        test_s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        test_s.bind(('localhost', 0))
+        port = test_s.getsockname()[1]
+        try:
+            self.assertFalse(host_utils.is_port_available(port))
+        finally:
+            test_s.close()
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/acts/framework/tests/acts_import_test_utils_test.py b/acts/framework/tests/acts_import_test_utils_test.py
new file mode 100755
index 0000000..4cfa93d
--- /dev/null
+++ b/acts/framework/tests/acts_import_test_utils_test.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import unittest
+
+from acts import utils
+from acts.test_utils.bt import BleEnum
+from acts.test_utils.bt import BluetoothBaseTest
+from acts.test_utils.bt import BluetoothCarHfpBaseTest
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt import GattConnectedBaseTest
+from acts.test_utils.bt import GattEnum
+from acts.test_utils.bt import bt_contacts_utils
+from acts.test_utils.bt import bt_gatt_utils
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.bt import native_bt_test_utils
+
+from acts.test_utils.car import car_bt_utils
+from acts.test_utils.car import car_media_utils
+from acts.test_utils.car import car_telecom_utils
+from acts.test_utils.car import tel_telecom_utils
+
+from acts.test_utils.net import connectivity_const
+from acts.test_utils.net import connectivity_const
+
+from acts.test_utils.tel import TelephonyBaseTest
+from acts.test_utils.tel import tel_atten_utils
+from acts.test_utils.tel import tel_data_utils
+from acts.test_utils.tel import tel_defines
+from acts.test_utils.tel import tel_lookup_tables
+from acts.test_utils.tel import tel_subscription_utils
+from acts.test_utils.tel import tel_test_utils
+from acts.test_utils.tel import tel_video_utils
+from acts.test_utils.tel import tel_voice_utils
+
+from acts.test_utils.wifi import wifi_aware_const
+from acts.test_utils.wifi import wifi_constants
+from acts.test_utils.wifi import wifi_test_utils
+
+
+class ActsImportTestUtilsTest(unittest.TestCase):
+    """This test class has unit tests for the implementation of everything
+    under acts.test_utils.*
+    """
+
+    def test_import_successful(self):
+        """ Test to return true if all imports were successful.
+
+        This test will fail if any import was unsuccessful.
+        """
+        self.assertTrue(True)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/acts/framework/tests/acts_import_unit_test.py b/acts/framework/tests/acts_import_unit_test.py
new file mode 100755
index 0000000..164020c
--- /dev/null
+++ b/acts/framework/tests/acts_import_unit_test.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import os
+import re
+import sys
+import uuid
+
+if sys.version_info < (3,):
+    import warnings
+
+    with warnings.catch_warnings():
+        warnings.filterwarnings('ignore', category=PendingDeprecationWarning)
+        import imp
+
+    import importlib
+    import unittest2 as unittest
+
+    def import_module(name, path):
+        return imp.load_source(name, path)
+
+    def import_acts():
+        return importlib.import_module('acts')
+else:
+    import importlib.machinery
+    import unittest
+
+    def import_module(name, path):
+        return importlib.machinery.SourceFileLoader(name, path).load_module()
+
+    def import_acts():
+        return importlib.import_module('acts')
+
+
+PY_FILE_REGEX = re.compile('.+\.py$')
+
+BLACKLIST = [
+    'acts/controllers/native.py', 'acts/controllers/native_android_device.py'
+]
+
+
+class ActsImportTestUtilsTest(unittest.TestCase):
+    """Test that all acts framework imports work.
+    """
+
+    def test_import_acts_successful(self):
+        """ Test that importing acts works.
+        """
+        acts = import_acts()
+        self.assertIsNotNone(acts)
+
+    def test_import_framework_successful(self):
+        """ Dynamically test all imports from the framework.
+        """
+        acts = import_acts()
+        if hasattr(acts, '__path__') and len(acts.__path__) > 0:
+            acts_path = acts.__path__[0]
+        else:
+            acts_path = os.path.dirname(acts.__file__)
+
+        for root, _, files in os.walk(acts_path):
+            for f in files:
+                full_path = os.path.join(root, f)
+                if any(full_path.endswith(e) for e in BLACKLIST):
+                    continue
+
+                path = os.path.relpath(os.path.join(root, f), os.getcwd())
+
+                if PY_FILE_REGEX.match(full_path):
+                    with self.subTest(msg='import %s' % path):
+                        fake_module_name = str(uuid.uuid4())
+                        module = import_module(fake_module_name, path)
+
+                        self.assertIsNotNone(module)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/acts/framework/tests/acts_integration_test.py b/acts/framework/tests/acts_integration_test.py
deleted file mode 100644
index fa344a1..0000000
--- a/acts/framework/tests/acts_integration_test.py
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env python3.4
-#
-#   Copyright 2016 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-import json
-import subprocess
-import unittest
-
-class ActsIntegrationTest(unittest.TestCase):
-    """Execute a simple ACTS test to make sure the basic workflow of ACTS is
-    intact.
-    """
-    def test_acts(self):
-        cmd = "act.py -c acts_sanity_test_config.json -tc IntegrationTest"
-        subprocess.check_call([cmd], shell=True)
-        with open("/tmp/logs/Sanity/latest/test_run_summary.json", 'r') as f:
-            results = json.load(f)
-        self.assertEqual(results["Results"][0]["Test Name"], "test_hello_world")
-        self.assertEqual(results["Results"][0]["Result"], "PASS")
diff --git a/acts/framework/tests/acts_job_test.py b/acts/framework/tests/acts_job_test.py
new file mode 100755
index 0000000..f93bf3d
--- /dev/null
+++ b/acts/framework/tests/acts_job_test.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3.4
+
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Import the python3 compatible bytes()
+from builtins import bytes
+
+import mock
+import os
+import sys
+import unittest
+
+from acts.libs.proc import job
+
+if os.name == 'posix' and sys.version_info[0] < 3:
+    import subprocess32 as subprocess
+else:
+    import subprocess
+
+
+class FakePopen(object):
+    """A fake version of the object returned from subprocess.Popen()."""
+
+    def __init__(self,
+                 stdout=None,
+                 stderr=None,
+                 returncode=0,
+                 will_timeout=False):
+        self.returncode = returncode
+        self._stdout = bytes(stdout,
+                             'utf-8') if stdout is not None else bytes()
+        self._stderr = bytes(stderr,
+                             'utf-8') if stderr is not None else bytes()
+        self._will_timeout = will_timeout
+
+    def communicate(self, timeout=None):
+        if self._will_timeout:
+            raise subprocess.TimeoutExpired(
+                -1, 'Timed out according to test logic')
+        return self._stdout, self._stderr
+
+    def kill(self):
+        pass
+
+    def wait(self):
+        pass
+
+
+class JobTestCases(unittest.TestCase):
+    @mock.patch(
+        'acts.libs.proc.job.subprocess.Popen',
+        return_value=FakePopen(stdout='TEST\n'))
+    def test_run_success(self, popen):
+        """Test running a simple shell command."""
+        result = job.run('echo TEST')
+        self.assertTrue(result.stdout.startswith('TEST'))
+
+    @mock.patch(
+        'acts.libs.proc.job.subprocess.Popen',
+        return_value=FakePopen(stderr='TEST\n'))
+    def test_run_stderr(self, popen):
+        """Test that we can read process stderr."""
+        result = job.run('echo TEST 1>&2')
+        self.assertEqual(len(result.stdout), 0)
+        self.assertTrue(result.stderr.startswith('TEST'))
+        self.assertFalse(result.stdout)
+
+    @mock.patch(
+        'acts.libs.proc.job.subprocess.Popen',
+        return_value=FakePopen(returncode=1))
+    def test_run_error(self, popen):
+        """Test that we raise on non-zero exit statuses."""
+        self.assertRaises(job.Error, job.run, 'exit 1')
+
+    @mock.patch(
+        'acts.libs.proc.job.subprocess.Popen',
+        return_value=FakePopen(returncode=1))
+    def test_run_with_ignored_error(self, popen):
+        """Test that we can ignore exit status on request."""
+        result = job.run('exit 1', ignore_status=True)
+        self.assertEqual(result.exit_status, 1)
+
+    @mock.patch(
+        'acts.libs.proc.job.subprocess.Popen',
+        return_value=FakePopen(will_timeout=True))
+    def test_run_timeout(self, popen):
+        """Test that we correctly implement command timeouts."""
+        self.assertRaises(job.Error, job.run, 'sleep 5', timeout=0.1)
+
+    @mock.patch(
+        'acts.libs.proc.job.subprocess.Popen',
+        return_value=FakePopen(stdout='TEST\n'))
+    def test_run_no_shell(self, popen):
+        """Test that we handle running without a wrapping shell."""
+        result = job.run(['echo', 'TEST'])
+        self.assertTrue(result.stdout.startswith('TEST'))
+
+    @mock.patch(
+        'acts.libs.proc.job.subprocess.Popen',
+        return_value=FakePopen(stdout='TEST\n'))
+    def test_job_env(self, popen):
+        """Test that we can set environment variables correctly."""
+        test_env = {'MYTESTVAR': '20'}
+        result = job.run('printenv', env=test_env.copy())
+        popen.assert_called_once()
+        _, kwargs = popen.call_args
+        self.assertTrue('env' in kwargs)
+        self.assertEqual(kwargs['env'], test_env)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/acts/framework/tests/acts_logger_test.py b/acts/framework/tests/acts_logger_test.py
new file mode 100755
index 0000000..8d26778
--- /dev/null
+++ b/acts/framework/tests/acts_logger_test.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import unittest
+
+from acts import logger
+
+
+class ActsLoggerTest(unittest.TestCase):
+    """Verifies code in acts.logger module.
+    """
+
+    def test_epoch_to_log_line_timestamp(self):
+        actual_stamp = logger.epoch_to_log_line_timestamp(1469134262116)
+        self.assertEqual("07-21 13:51:02.116", actual_stamp)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/acts/framework/tests/acts_proto_utils_test.proto b/acts/framework/tests/acts_proto_utils_test.proto
new file mode 100644
index 0000000..a40e8af
--- /dev/null
+++ b/acts/framework/tests/acts_proto_utils_test.proto
@@ -0,0 +1,22 @@
+syntax = "proto2";
+
+package bt_metrics_utils_test;
+
+message TestProtoEntry {
+  message NestedType {
+    enum EnumType {
+      AAA = 0;
+      BBB = 1;
+    }
+    optional string name = 1;
+    optional EnumType type = 2;
+  }
+
+  optional string name = 1;
+  optional int32 id = 2;
+  repeated NestedType nested = 3;
+}
+
+message TestProto {
+  repeated TestProtoEntry entries = 1;
+}
diff --git a/acts/framework/tests/acts_proto_utils_test.py b/acts/framework/tests/acts_proto_utils_test.py
new file mode 100644
index 0000000..d39a680
--- /dev/null
+++ b/acts/framework/tests/acts_proto_utils_test.py
@@ -0,0 +1,105 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+import unittest
+import logging
+import os
+import sys
+import tempfile
+import shutil
+from importlib import import_module
+
+from google.protobuf import text_format
+
+from acts.libs.proto.proto_utils import compile_proto
+from acts.libs.proto.proto_utils import compile_import_proto
+
+TEST_PROTO_NAME = "acts_proto_utils_test.proto"
+TEST_PROTO_GENERATED_NAME = "acts_proto_utils_test_pb2"
+TEST_PROTO_DATA_NAME = "acts_proto_utils_test_data.txt"
+TEST_NAME = "test_name"
+TEST_ID = 42
+
+
+class BtMetricsUtilsTest(unittest.TestCase):
+    """This test class has unit tests for the implementation of everything
+    under acts.controllers.android_device.
+    """
+
+    def setUp(self):
+        # Set log_path to logging since acts logger setup is not called.
+        if not hasattr(logging, "log_path"):
+            setattr(logging, "log_path", "/tmp/logs")
+        # Creates a temp dir to be used by tests in this test class.
+        self.tmp_dir = tempfile.mkdtemp()
+
+    def tearDown(self):
+        """Removes the temp dir.
+        """
+        shutil.rmtree(self.tmp_dir)
+
+    def getResource(self, relative_path_to_test):
+        return os.path.join(
+            os.path.dirname(os.path.realpath(__file__)), relative_path_to_test)
+
+    def compare_test_entry(self, entry, name, id, nested):
+        self.assertEqual(entry.name, name)
+        self.assertEqual(entry.id, id)
+        self.assertEqual(len(entry.nested), len(nested))
+        for i in range(len(entry.nested)):
+            self.assertEqual(entry.nested[i].name, nested[i][0])
+            self.assertEqual(entry.nested[i].type, nested[i][1])
+
+    def test_compile_proto(self):
+        proto_path = self.getResource(TEST_PROTO_NAME)
+        output_module_name = compile_proto(proto_path, self.tmp_dir)
+        self.assertIsNotNone(output_module_name)
+        self.assertEqual(output_module_name, TEST_PROTO_GENERATED_NAME)
+        self.assertTrue(
+            os.path.exists(
+                os.path.join(self.tmp_dir, output_module_name) + ".py"))
+        sys.path.append(self.tmp_dir)
+        bt_metrics_utils_test_pb2 = None
+        try:
+            bt_metrics_utils_test_pb2 = import_module(output_module_name)
+        except ImportError:
+            self.fail("Cannot import generated py-proto %s" %
+                      (output_module_name))
+        test_proto = bt_metrics_utils_test_pb2.TestProto()
+        test_proto_entry = test_proto.entries.add()
+        test_proto_entry.id = TEST_ID
+        test_proto_entry.name = TEST_NAME
+        self.assertEqual(test_proto.entries[0].id, TEST_ID)
+        self.assertEqual(test_proto.entries[0].name, TEST_NAME)
+
+    def test_parse_proto(self):
+        proto_path = self.getResource(TEST_PROTO_NAME)
+        output_module = compile_import_proto(self.tmp_dir, proto_path)
+        self.assertIsNotNone(output_module)
+        AAA = output_module.TestProtoEntry.NestedType.AAA
+        BBB = output_module.TestProtoEntry.NestedType.BBB
+        test_proto = output_module.TestProto()
+        proto_data_path = self.getResource(TEST_PROTO_DATA_NAME)
+        self.assertTrue(os.path.exists(proto_data_path))
+        self.assertTrue(os.path.isfile(proto_data_path))
+        with open(proto_data_path) as f:
+            text_format.Merge(f.read(), test_proto)
+        self.assertEqual(len(test_proto.entries), 2)
+        entry1 = test_proto.entries[0]
+        self.compare_test_entry(entry1, "TestName1", 42,
+                                [("NestedA", AAA), ("NestedB", BBB),
+                                 ("NestedA", AAA)])
+        entry2 = test_proto.entries[1]
+        self.compare_test_entry(entry2, "TestName2", 43,
+                                [("NestedB", BBB), ("NestedA", AAA),
+                                 ("NestedB", BBB)])
diff --git a/acts/framework/tests/acts_proto_utils_test_data.txt b/acts/framework/tests/acts_proto_utils_test_data.txt
new file mode 100644
index 0000000..db164d2
--- /dev/null
+++ b/acts/framework/tests/acts_proto_utils_test_data.txt
@@ -0,0 +1,33 @@
+entries {
+  name: 'TestName1'
+  id: 42
+  nested {
+    name: "NestedA"
+    type: AAA
+  }
+  nested {
+    name: "NestedB"
+    type: BBB
+  }
+  nested {
+    name: "NestedA"
+    type: AAA
+  }
+}
+
+entries {
+  name: 'TestName2'
+  id: 43
+  nested: {
+    name: "NestedB"
+    type: BBB
+  }
+  nested: {
+    name: "NestedA"
+    type: AAA
+  }
+  nested {
+    name: "NestedB"
+    type: BBB
+  }
+}
diff --git a/acts/framework/tests/acts_records_test.py b/acts/framework/tests/acts_records_test.py
old mode 100644
new mode 100755
index a19163d..24ba656
--- a/acts/framework/tests/acts_records_test.py
+++ b/acts/framework/tests/acts_records_test.py
@@ -19,6 +19,7 @@
 from acts import records
 from acts import signals
 
+
 class ActsRecordsTest(unittest.TestCase):
     """This test class tests the implementation of classes in acts.records.
     """
@@ -57,95 +58,165 @@
         self.assertTrue(str(record), "str of the record should not be empty.")
         self.assertTrue(repr(record), "the record's repr shouldn't be empty.")
         self.assertTrue(record.json_str(), ("json str of the record should "
-                         "not be empty."))
+                                            "not be empty."))
 
     """ Begin of Tests """
+
     def test_result_record_pass_none(self):
         record = records.TestResultRecord(self.tn)
         record.test_begin()
         record.test_pass()
-        self.verify_record(record=record,
-                           result=records.TestResultEnums.TEST_RESULT_PASS,
-                           details=None,
-                           extras=None)
+        self.verify_record(
+            record=record,
+            result=records.TestResultEnums.TEST_RESULT_PASS,
+            details=None,
+            extras=None)
 
     def test_result_record_pass_with_float_extra(self):
         record = records.TestResultRecord(self.tn)
         record.test_begin()
         s = signals.TestPass(self.details, self.float_extra)
         record.test_pass(s)
-        self.verify_record(record=record,
-                           result=records.TestResultEnums.TEST_RESULT_PASS,
-                           details=self.details,
-                           extras=self.float_extra)
+        self.verify_record(
+            record=record,
+            result=records.TestResultEnums.TEST_RESULT_PASS,
+            details=self.details,
+            extras=self.float_extra)
 
     def test_result_record_pass_with_json_extra(self):
         record = records.TestResultRecord(self.tn)
         record.test_begin()
         s = signals.TestPass(self.details, self.json_extra)
         record.test_pass(s)
-        self.verify_record(record=record,
-                           result=records.TestResultEnums.TEST_RESULT_PASS,
-                           details=self.details,
-                           extras=self.json_extra)
+        self.verify_record(
+            record=record,
+            result=records.TestResultEnums.TEST_RESULT_PASS,
+            details=self.details,
+            extras=self.json_extra)
 
     def test_result_record_fail_none(self):
         record = records.TestResultRecord(self.tn)
         record.test_begin()
         record.test_fail()
-        self.verify_record(record=record,
-                           result=records.TestResultEnums.TEST_RESULT_FAIL,
-                           details=None,
-                           extras=None)
+        self.verify_record(
+            record=record,
+            result=records.TestResultEnums.TEST_RESULT_FAIL,
+            details=None,
+            extras=None)
 
     def test_result_record_fail_with_float_extra(self):
         record = records.TestResultRecord(self.tn)
         record.test_begin()
         s = signals.TestFailure(self.details, self.float_extra)
         record.test_fail(s)
-        self.verify_record(record=record,
-                           result=records.TestResultEnums.TEST_RESULT_FAIL,
-                           details=self.details,
-                           extras=self.float_extra)
+        self.verify_record(
+            record=record,
+            result=records.TestResultEnums.TEST_RESULT_FAIL,
+            details=self.details,
+            extras=self.float_extra)
 
     def test_result_record_fail_with_json_extra(self):
         record = records.TestResultRecord(self.tn)
         record.test_begin()
         s = signals.TestFailure(self.details, self.json_extra)
         record.test_fail(s)
-        self.verify_record(record=record,
-                           result=records.TestResultEnums.TEST_RESULT_FAIL,
-                           details=self.details,
-                           extras=self.json_extra)
+        self.verify_record(
+            record=record,
+            result=records.TestResultEnums.TEST_RESULT_FAIL,
+            details=self.details,
+            extras=self.json_extra)
 
     def test_result_record_skip_none(self):
         record = records.TestResultRecord(self.tn)
         record.test_begin()
         record.test_skip()
-        self.verify_record(record=record,
-                           result=records.TestResultEnums.TEST_RESULT_SKIP,
-                           details=None,
-                           extras=None)
+        self.verify_record(
+            record=record,
+            result=records.TestResultEnums.TEST_RESULT_SKIP,
+            details=None,
+            extras=None)
 
     def test_result_record_skip_with_float_extra(self):
         record = records.TestResultRecord(self.tn)
         record.test_begin()
         s = signals.TestSkip(self.details, self.float_extra)
         record.test_skip(s)
-        self.verify_record(record=record,
-                           result=records.TestResultEnums.TEST_RESULT_SKIP,
-                           details=self.details,
-                           extras=self.float_extra)
+        self.verify_record(
+            record=record,
+            result=records.TestResultEnums.TEST_RESULT_SKIP,
+            details=self.details,
+            extras=self.float_extra)
 
     def test_result_record_skip_with_json_extra(self):
         record = records.TestResultRecord(self.tn)
         record.test_begin()
         s = signals.TestSkip(self.details, self.json_extra)
         record.test_skip(s)
-        self.verify_record(record=record,
-                           result=records.TestResultEnums.TEST_RESULT_SKIP,
-                           details=self.details,
-                           extras=self.json_extra)
+        self.verify_record(
+            record=record,
+            result=records.TestResultEnums.TEST_RESULT_SKIP,
+            details=self.details,
+            extras=self.json_extra)
+
+    def test_result_add_operator_success(self):
+        record1 = records.TestResultRecord(self.tn)
+        record1.test_begin()
+        s = signals.TestPass(self.details, self.float_extra)
+        record1.test_pass(s)
+        tr1 = records.TestResult()
+        tr1.add_record(record1)
+        tr1.add_controller_info("MockDevice", ["magicA", "magicB"])
+        record2 = records.TestResultRecord(self.tn)
+        record2.test_begin()
+        s = signals.TestPass(self.details, self.json_extra)
+        record2.test_pass(s)
+        tr2 = records.TestResult()
+        tr2.add_record(record2)
+        tr2.add_controller_info("MockDevice", ["magicC"])
+        tr2 += tr1
+        self.assertTrue(tr2.passed, [tr1, tr2])
+        self.assertTrue(tr2.controller_info, {"MockDevice": ["magicC"]})
+
+    def test_result_add_operator_type_mismatch(self):
+        record1 = records.TestResultRecord(self.tn)
+        record1.test_begin()
+        s = signals.TestPass(self.details, self.float_extra)
+        record1.test_pass(s)
+        tr1 = records.TestResult()
+        tr1.add_record(record1)
+        expected_msg = "Operand .* of type .* is not a TestResult."
+        with self.assertRaisesRegexp(TypeError, expected_msg):
+            tr1 += "haha"
+
+    def test_is_all_pass(self):
+        s = signals.TestPass(self.details, self.float_extra)
+        record1 = records.TestResultRecord(self.tn)
+        record1.test_begin()
+        record1.test_pass(s)
+        s = signals.TestSkip(self.details, self.float_extra)
+        record2 = records.TestResultRecord(self.tn)
+        record2.test_begin()
+        record2.test_skip(s)
+        tr = records.TestResult()
+        tr.add_record(record1)
+        tr.add_record(record2)
+        tr.add_record(record1)
+        self.assertEqual(len(tr.passed), 2)
+        self.assertTrue(tr.is_all_pass)
+
+    def test_is_all_pass_negative(self):
+        s = signals.TestFailure(self.details, self.float_extra)
+        record1 = records.TestResultRecord(self.tn)
+        record1.test_begin()
+        record1.test_fail(s)
+        record2 = records.TestResultRecord(self.tn)
+        record2.test_begin()
+        record2.test_unknown(s)
+        tr = records.TestResult()
+        tr.add_record(record1)
+        tr.add_record(record2)
+        self.assertFalse(tr.is_all_pass)
+
 
 if __name__ == "__main__":
-   unittest.main()
\ No newline at end of file
+    unittest.main()
\ No newline at end of file
diff --git a/acts/framework/tests/acts_sl4a_client_test.py b/acts/framework/tests/acts_sl4a_client_test.py
new file mode 100755
index 0000000..1754d4f
--- /dev/null
+++ b/acts/framework/tests/acts_sl4a_client_test.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from builtins import str
+
+import json
+import mock
+import socket
+import unittest
+
+from acts.controllers import sl4a_client
+
+MOCK_RESP = b'{"id": 0, "result": 123, "error": null, "status": 1, "uid": 1}'
+MOCK_RESP_TEMPLATE = '{"id": %d, "result": 123, "error": null, "status": 1, "uid": 1}'
+MOCK_RESP_UNKWN_STATUS = b'{"id": 0, "result": 123, "error": null, "status": 0}'
+MOCK_RESP_WITH_ERROR = b'{"id": 0, "error": 1, "status": 1, "uid": 1}'
+
+
+class MockSocketFile(object):
+    def __init__(self, resp):
+        self.resp = resp
+        self.last_write = None
+
+    def write(self, msg):
+        self.last_write = msg
+
+    def readline(self):
+        return self.resp
+
+    def flush(self):
+        pass
+
+
+class ActsSl4aClientTest(unittest.TestCase):
+    """This test class has unit tests for the implementation of everything
+    under acts.controllers.android, which is the RPC client module for sl4a.
+    """
+
+    def setup_mock_socket_file(self, mock_create_connection):
+        """Sets up a fake socket file from the mock connection.
+
+        Args:
+            mock_create_connection: The mock method for creating a method.
+
+        Returns:
+            The mock file that will be injected into the code.
+        """
+        fake_file = MockSocketFile(MOCK_RESP)
+        fake_conn = mock.MagicMock()
+        fake_conn.makefile.return_value = fake_file
+        mock_create_connection.return_value = fake_conn
+
+        return fake_file
+
+    @mock.patch('socket.create_connection')
+    def test_open_timeout_io_error(self, mock_create_connection):
+        """Test socket timeout with io error
+
+        Test that if the net socket gives an io error, then the sl4a client
+        will eventually exit with an IOError.
+        """
+        mock_create_connection.side_effect = IOError()
+
+        with self.assertRaises(IOError):
+            client = sl4a_client.Sl4aClient()
+            client.open(connection_timeout=0.1)
+
+    @mock.patch('socket.create_connection')
+    def test_open_timeout(self, mock_create_connection):
+        """Test socket timeout
+
+        Test that a timeout exception will be raised if the socket gives a
+        timeout.
+        """
+        mock_create_connection.side_effect = socket.timeout
+
+        with self.assertRaises(socket.timeout):
+            client = sl4a_client.Sl4aClient()
+            client.open(connection_timeout=0.1)
+
+    @mock.patch('socket.create_connection')
+    def test_handshake_error(self, mock_create_connection):
+        """Test error in sl4a handshake
+
+        Test that if there is an error in the sl4a handshake then a protocol
+        error will be raised.
+        """
+        fake_conn = mock.MagicMock()
+        fake_conn.makefile.return_value = MockSocketFile(None)
+        mock_create_connection.return_value = fake_conn
+
+        with self.assertRaises(sl4a_client.Sl4aProtocolError):
+            client = sl4a_client.Sl4aClient()
+            client.open()
+
+    @mock.patch('socket.create_connection')
+    def test_open_handshake(self, mock_create_connection):
+        """Test sl4a client handshake
+
+        Test that at the end of a handshake with no errors the client object
+        has the correct parameters.
+        """
+        fake_conn = mock.MagicMock()
+        fake_conn.makefile.return_value = MockSocketFile(MOCK_RESP)
+        mock_create_connection.return_value = fake_conn
+
+        client = sl4a_client.Sl4aClient()
+        client.open()
+
+        self.assertEqual(client.uid, 1)
+
+    @mock.patch('socket.create_connection')
+    def test_open_handshake_unknown_status(self, mock_create_connection):
+        """Test handshake with unknown status response
+
+        Test that when the handshake is given an unknown status then the client
+        will not be given a uid.
+        """
+        fake_conn = mock.MagicMock()
+        fake_conn.makefile.return_value = MockSocketFile(
+            MOCK_RESP_UNKWN_STATUS)
+        mock_create_connection.return_value = fake_conn
+
+        client = sl4a_client.Sl4aClient()
+        client.open()
+
+        self.assertEqual(client.uid, sl4a_client.UNKNOWN_UID)
+
+    @mock.patch('socket.create_connection')
+    def test_open_no_response(self, mock_create_connection):
+        """Test handshake no response
+
+        Test that if a handshake recieves no response then it will give a
+        protocol error.
+        """
+        fake_file = self.setup_mock_socket_file(mock_create_connection)
+
+        client = sl4a_client.Sl4aClient()
+        client.open()
+
+        fake_file.resp = None
+
+        with self.assertRaises(
+                sl4a_client.Sl4aProtocolError,
+                msg=sl4a_client.Sl4aProtocolError.NO_RESPONSE_FROM_HANDSHAKE):
+            client.some_rpc(1, 2, 3)
+
+    @mock.patch('socket.create_connection')
+    def test_rpc_error_response(self, mock_create_connection):
+        """Test rpc that is given an error response
+
+        Test that when an rpc recieves a reponse with an error will raised
+        an api error.
+        """
+        fake_file = self.setup_mock_socket_file(mock_create_connection)
+
+        client = sl4a_client.Sl4aClient()
+        client.open()
+
+        fake_file.resp = MOCK_RESP_WITH_ERROR
+
+        with self.assertRaises(sl4a_client.Sl4aApiError, msg=1):
+            client.some_rpc(1, 2, 3)
+
+    @mock.patch('socket.create_connection')
+    def test_rpc_id_mismatch(self, mock_create_connection):
+        """Test rpc that returns a different id than expected
+
+        Test that if an rpc returns with an id that is different than what
+        is expected will give a protocl error.
+        """
+        fake_file = self.setup_mock_socket_file(mock_create_connection)
+
+        client = sl4a_client.Sl4aClient()
+        client.open()
+
+        fake_file.resp = (MOCK_RESP_TEMPLATE % 52).encode('utf8')
+
+        with self.assertRaises(
+                sl4a_client.Sl4aProtocolError,
+                msg=sl4a_client.Sl4aProtocolError.MISMATCHED_API_ID):
+            client.some_rpc(1, 2, 3)
+
+    @mock.patch('socket.create_connection')
+    def test_rpc_no_response(self, mock_create_connection):
+        """Test rpc that does not get a reponse
+
+        Test that when an rpc does not get a response it throws a protocol
+        error.
+        """
+        fake_file = self.setup_mock_socket_file(mock_create_connection)
+
+        client = sl4a_client.Sl4aClient()
+        client.open()
+
+        fake_file.resp = None
+
+        with self.assertRaises(
+                sl4a_client.Sl4aProtocolError,
+                msg=sl4a_client.Sl4aProtocolError.NO_RESPONSE_FROM_SERVER):
+            client.some_rpc(1, 2, 3)
+
+    @mock.patch('socket.create_connection')
+    def test_rpc_send_to_socket(self, mock_create_connection):
+        """Test rpc sending and recieving
+
+        Tests that when an rpc is sent and received the corrent data
+        is used.
+        """
+        fake_file = self.setup_mock_socket_file(mock_create_connection)
+
+        client = sl4a_client.Sl4aClient()
+        client.open()
+
+        result = client.some_rpc(1, 2, 3)
+        self.assertEqual(result, 123)
+
+        expected = {'id': 0, 'method': 'some_rpc', 'params': [1, 2, 3]}
+        actual = json.loads(fake_file.last_write.decode('utf-8'))
+
+        self.assertEqual(expected, actual)
+
+    @mock.patch('socket.create_connection')
+    def test_rpc_call_increment_counter(self, mock_create_connection):
+        """Test rpc counter
+
+        Test that with each rpc call the counter is incremented by 1.
+        """
+        fake_file = self.setup_mock_socket_file(mock_create_connection)
+
+        client = sl4a_client.Sl4aClient()
+        client.open()
+
+        for i in range(0, 10):
+            fake_file.resp = (MOCK_RESP_TEMPLATE % i).encode('utf-8')
+            client.some_rpc()
+
+        self.assertEqual(next(client._counter), 10)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/acts/framework/tests/acts_test_runner_test.py b/acts/framework/tests/acts_test_runner_test.py
index 2126ca0..8ed2724 100755
--- a/acts/framework/tests/acts_test_runner_test.py
+++ b/acts/framework/tests/acts_test_runner_test.py
@@ -59,8 +59,9 @@
     def test_register_optional_controller_no_config(self):
         tr = test_runner.TestRunner(self.base_mock_test_config,
                                     self.mock_run_list)
-        self.assertIsNone(tr.register_controller(mock_controller,
-                                                 required=False))
+        self.assertIsNone(
+            tr.register_controller(
+                mock_controller, required=False))
 
     def test_register_controller_third_party_dup_register(self):
         """Verifies correctness of registration, internal tally of controllers
@@ -127,6 +128,22 @@
         finally:
             delattr(mock_controller, "ACTS_CONTROLLER_REFERENCE_NAME")
 
+    def test_register_controller_no_get_info(self):
+        mock_test_config = dict(self.base_mock_test_config)
+        tb_key = keys.Config.key_testbed.value
+        mock_ctrlr_config_name = mock_controller.ACTS_CONTROLLER_CONFIG_NAME
+        mock_ref_name = "haha"
+        get_info = getattr(mock_controller, "get_info")
+        delattr(mock_controller, "get_info")
+        try:
+            mock_test_config[tb_key][mock_ctrlr_config_name] = ["magic1",
+                                                                "magic2"]
+            tr = test_runner.TestRunner(mock_test_config, self.mock_run_list)
+            tr.register_controller(mock_controller)
+            self.assertEqual(tr.results.controller_info, {})
+        finally:
+            setattr(mock_controller, "get_info", get_info)
+
     def test_register_controller_return_value(self):
         mock_test_config = dict(self.base_mock_test_config)
         tb_key = keys.Config.key_testbed.value
@@ -164,14 +181,26 @@
         self.assertEqual(results["Requested"], 2)
         self.assertEqual(results["Executed"], 2)
         self.assertEqual(results["Passed"], 2)
+        expected_info = {'MagicDevice': [{'MyMagic': {'magic': 'Magic1'}},
+                                         {'MyMagic': {'magic': 'Magic2'}}]}
+        self.assertEqual(tr.results.controller_info, expected_info)
 
-    @mock.patch('acts.controllers.adb.AdbProxy',
-                return_value=acts_android_device_test.MockAdbProxy(1))
-    @mock.patch('acts.controllers.android_device.list_adb_devices',
-                return_value=["1"])
-    @mock.patch('acts.controllers.android_device.get_all_instances',
-                return_value=acts_android_device_test.get_mock_ads(1))
-    def test_run_two_test_classes(self, mock_adb, mock_list_adb, mock_get_all):
+    @mock.patch(
+        'acts.controllers.adb.AdbProxy',
+        return_value=acts_android_device_test.MockAdbProxy(1))
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=acts_android_device_test.MockFastbootProxy(1))
+    @mock.patch(
+        'acts.controllers.android_device.list_adb_devices', return_value=["1"])
+    @mock.patch(
+        'acts.controllers.android_device.get_all_instances',
+        return_value=acts_android_device_test.get_mock_ads(1))
+    def test_run_two_test_classes(self,
+                                  mock_get_all,
+                                  mock_list_adb,
+                                  mock_fastboot,
+                                  mock_adb, ):
         """Verifies that runing more than one test class in one test run works
         proerly.
 
@@ -181,8 +210,9 @@
         mock_test_config = dict(self.base_mock_test_config)
         tb_key = keys.Config.key_testbed.value
         mock_ctrlr_config_name = mock_controller.ACTS_CONTROLLER_CONFIG_NAME
-        my_config = [{"serial": "xxxx", "magic": "Magic1"},
-                     {"serial": "xxxx", "magic": "Magic2"}]
+        my_config = [{"serial": "xxxx",
+                      "magic": "Magic1"}, {"serial": "xxxx",
+                                           "magic": "Magic2"}]
         mock_test_config[tb_key][mock_ctrlr_config_name] = my_config
         mock_test_config[tb_key]["AndroidDevice"] = [
             {"serial": "1", "skip_sl4a": True}]
diff --git a/acts/framework/tests/acts_test_ssh.py b/acts/framework/tests/acts_test_ssh.py
new file mode 100755
index 0000000..eb4efe7
--- /dev/null
+++ b/acts/framework/tests/acts_test_ssh.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3.4
+
+# Copyright 2016 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import inspect
+import io
+import os
+import sys
+import time
+import unittest
+import re
+
+from acts.controllers.utils_lib.ssh import connection
+from acts.controllers.utils_lib.ssh import settings
+
+SSH_HOST = None
+SSH_USER = None
+
+
+class SshTestCases(unittest.TestCase):
+    def setUp(self):
+        self.settings = settings.SshSettings(SSH_HOST, SSH_USER)
+
+    def test_ssh_run(self):
+        """Test running an ssh command.
+
+        Test that running an ssh command works.
+        """
+        conn = connection.SshConnection(self.settings)
+
+        result = conn.run('echo "Hello World"')
+        self.assertTrue(result.stdout.find('Hello World') > -1)
+
+    def test_ssh_env(self):
+        """Test that special envirment variables get sent over ssh.
+
+        Test that given a dictonary of enviroment variables they will be sent
+        to the remote host.
+        """
+        conn = connection.SshConnection(self.settings)
+
+        result = conn.run('printenv', env={'MYSPECIALVAR': 20})
+        self.assertTrue(result.stdout.find('MYSPECIALVAR=20'))
+
+    def test_ssh_permission_denied(self):
+        """Test that permission denied works.
+
+        Connect to a remote host using an invalid username and see if we are
+        rejected.
+        """
+        with self.assertRaises(connection.Error):
+            bad_settings = settings.SshSettings(SSH_HOST, SSH_USER + 'BAD')
+            conn = connection.SshConnection(bad_settings)
+            result = conn.run('echo "Hello World"')
+
+    def test_ssh_unknown_host(self):
+        """Test that permission denied works.
+
+        Connect to a remote host using an invalid username and see if we are
+        rejected.
+        """
+        with self.assertRaises(connection.Error):
+            bad_settings = settings.SshSettings('BADHOSTNAME', SSH_USER)
+            conn = connection.SshConnection(bad_settings)
+            result = conn.run('echo "Hello World"')
+
+
+if __name__ == '__main__':
+    # Get host info from command line arguments.
+    if len(sys.argv) < 3:
+        raise ValueError("usage: %s <username> <hostname to ssh to>" %
+                         sys.argv[0])
+    SSH_HOST = sys.argv[2]
+    SSH_USER = sys.argv[1]
+    unittest.main(argv=sys.argv[0:1] + sys.argv[3:])
diff --git a/acts/framework/tests/acts_unittest_suite.py b/acts/framework/tests/acts_unittest_suite.py
index 108bc8d..02a9c6e 100755
--- a/acts/framework/tests/acts_unittest_suite.py
+++ b/acts/framework/tests/acts_unittest_suite.py
@@ -17,19 +17,27 @@
 import sys
 import unittest
 
-import acts_adb_test
 import acts_android_device_test
+import acts_asserts_test
 import acts_base_class_test
+import acts_host_utils_test
+import acts_logger_test
 import acts_records_test
+import acts_sl4a_client_test
 import acts_test_runner_test
+import acts_utils_test
+
 
 def compile_suite():
     test_classes_to_run = [
-        acts_adb_test.ActsAdbTest,
+        acts_asserts_test.ActsAssertsTest,
         acts_base_class_test.ActsBaseClassTest,
         acts_test_runner_test.ActsTestRunnerTest,
         acts_android_device_test.ActsAndroidDeviceTest,
-        acts_records_test.ActsRecordsTest
+        acts_records_test.ActsRecordsTest,
+        acts_sl4a_client_test.ActsSl4aClientTest,
+        acts_utils_test.ActsUtilsTest, acts_logger_test.ActsLoggerTest,
+        acts_host_utils_test.ActsHostUtilsTest
     ]
 
     loader = unittest.TestLoader()
@@ -42,6 +50,7 @@
     big_suite = unittest.TestSuite(suites_list)
     return big_suite
 
+
 if __name__ == "__main__":
     # This is the entry point for running all ACTS unit tests.
     runner = unittest.TextTestRunner()
diff --git a/acts/framework/tests/acts_utils_test.py b/acts/framework/tests/acts_utils_test.py
new file mode 100755
index 0000000..7ee2c9d
--- /dev/null
+++ b/acts/framework/tests/acts_utils_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import time
+import unittest
+
+from acts import utils
+
+
+class ActsUtilsTest(unittest.TestCase):
+    """This test class has unit tests for the implementation of everything
+    under acts.utils.
+    """
+
+    def test_start_standing_subproc(self):
+        with self.assertRaisesRegexp(utils.ActsUtilsError,
+                                     "Process .* has terminated"):
+            utils.start_standing_subprocess("sleep 0", check_health_delay=0.1)
+
+    def test_stop_standing_subproc(self):
+        p = utils.start_standing_subprocess("sleep 0")
+        time.sleep(0.1)
+        with self.assertRaisesRegexp(utils.ActsUtilsError,
+                                     "Process .* has terminated"):
+            utils.stop_standing_subprocess(p)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/acts/framework/tests/mock_controller.py b/acts/framework/tests/mock_controller.py
index 3ad3d5c..a3893fd 100644
--- a/acts/framework/tests/mock_controller.py
+++ b/acts/framework/tests/mock_controller.py
@@ -16,24 +16,38 @@
 
 # This is a mock third-party controller module used for unit testing ACTS.
 
+import logging
+
 ACTS_CONTROLLER_CONFIG_NAME = "MagicDevice"
 
-def create(configs, logger):
+
+def create(configs):
     objs = []
     for c in configs:
         if isinstance(c, dict):
             c.pop("serial")
-        objs.append(MagicDevice(c, logger))
+        objs.append(MagicDevice(c))
     return objs
 
+
 def destroy(objs):
     print("Destroying magic")
 
+
+def get_info(objs):
+    infos = []
+    for obj in objs:
+        infos.append(obj.who_am_i())
+    return infos
+
+
 class MagicDevice(object):
-    def __init__(self, config, log):
+    def __init__(self, config):
         self.magic = config
-        self.log = log
 
     def get_magic(self):
-        self.log.info("My magic is %s." % self.magic)
-        return self.magic
\ No newline at end of file
+        logging.info("My magic is %s.", self.magic)
+        return self.magic
+
+    def who_am_i(self):
+        return {"MyMagic": self.magic}
diff --git a/acts/framework/tests/test_all b/acts/framework/tests/test_all
deleted file mode 100755
index afc962c..0000000
--- a/acts/framework/tests/test_all
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-import unittest
-
-import acts_integration_test
-import acts_unittest_suite
-
-if __name__ == "__main__":
-    # This files executes both the unit tests and the integration test.
-    suite = acts_unittest_suite.compile_suite()
-    suite.addTest(acts_integration_test.ActsIntegrationTest("test_acts"))
-    runner = unittest.TextTestRunner()
-    results = runner.run(suite)
-    sys.exit(not results.wasSuccessful())
diff --git a/acts/tests/google/ble/api/BleAdvertiseApiTest.py b/acts/tests/google/ble/api/BleAdvertiseApiTest.py
index 0e1fee8..4e29ee4 100644
--- a/acts/tests/google/ble/api/BleAdvertiseApiTest.py
+++ b/acts/tests/google/ble/api/BleAdvertiseApiTest.py
@@ -20,7 +20,8 @@
 then other test suites utilising Ble Advertisements will also fail.
 """
 
-from acts.controllers.android import SL4AAPIError
+from acts.controllers import sl4a_client
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import adv_fail
 from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
@@ -39,6 +40,7 @@
         self.ad_dut = self.android_devices[0]
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d6d8d0a6-7b3e-4e4b-a5d0-bcfd6e207474')
     def test_adv_settings_defaults(self):
         """Tests the default advertisement settings.
 
@@ -90,6 +92,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='f2a276ae-1436-43e4-aba7-1ede787200ee')
     def test_adv_data_defaults(self):
         """Tests the default advertisement data.
 
@@ -148,6 +151,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='8d462e60-6b4e-49f3-9ef4-5a8b612d285d')
     def test_adv_settings_set_adv_mode_balanced(self):
         """Tests advertise settings balanced mode.
 
@@ -179,6 +183,7 @@
         return self.verify_adv_settings_adv_mode(droid, exp_adv_mode)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='334fefeb-365f-4ee3-9be0-42b1fabe3178')
     def test_adv_settings_set_adv_mode_low_power(self):
         """Tests advertise settings low power mode.
 
@@ -210,6 +215,7 @@
         return self.verify_adv_settings_adv_mode(droid, exp_adv_mode)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='ce087782-1535-4694-944a-e962c22638ed')
     def test_adv_settings_set_adv_mode_low_latency(self):
         """Tests advertise settings low latency mode.
 
@@ -241,6 +247,7 @@
         return self.verify_adv_settings_adv_mode(droid, exp_adv_mode)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='59b52be9-d38b-4814-af08-c68aa8910a16')
     def test_adv_settings_set_invalid_adv_mode(self):
         """Tests advertise settings invalid advertising mode.
 
@@ -268,6 +275,7 @@
         return self.verify_invalid_adv_settings_adv_mode(droid, exp_adv_mode)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d8292633-831f-41c4-974a-ad267e9795e9')
     def test_adv_settings_set_adv_tx_power_level_high(self):
         """Tests advertise settings tx power level high.
 
@@ -300,6 +308,7 @@
         return self.verify_adv_settings_tx_power_level(droid, exp_adv_tx_power)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d577de1f-4fd9-43d5-beff-c0696c5e0ea1')
     def test_adv_settings_set_adv_tx_power_level_medium(self):
         """Tests advertise settings tx power level medium.
 
@@ -333,6 +342,7 @@
         return self.verify_adv_settings_tx_power_level(droid, exp_adv_tx_power)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='e972f8b5-a3cf-4b3f-9223-a8a74c0fd855')
     def test_adv_settings_set_adv_tx_power_level_low(self):
         """Tests advertise settings tx power level low.
 
@@ -359,12 +369,12 @@
         droid = self.ad_dut.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))
+        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
+    @test_tracker_info(uuid='e972f8b5-a3cf-4b3f-9223-a8a74c0fd855')
     def test_adv_settings_set_adv_tx_power_level_ultra_low(self):
         """Tests advertise settings tx power level ultra low.
 
@@ -389,15 +399,14 @@
         """
         self.log.debug("Step 1: Setup environment.")
         droid = self.ad_dut.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))
+        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
+    @test_tracker_info(uuid='e972f8b5-a3cf-4b3f-9223-a8a74c0fd855')
     def test_adv_settings_set_invalid_adv_tx_power_level(self):
         """Tests advertise settings invalid advertising tx power level.
 
@@ -426,6 +435,7 @@
             droid, exp_adv_tx_power)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='1be71f77-64af-42b7-84cb-e06df0836966')
     def test_adv_settings_set_is_connectable_true(self):
         """Tests advertise settings is connectable true.
 
@@ -458,6 +468,7 @@
                                                        exp_is_connectable)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='f9865333-9198-4385-938d-5fc641ee9968')
     def test_adv_settings_set_is_connectable_false(self):
         """Tests advertise settings is connectable false.
 
@@ -489,6 +500,7 @@
                                                        exp_is_connectable)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='a770ed7e-c6cd-4533-8876-e42e68f8b4fb')
     def test_adv_data_set_service_uuids_empty(self):
         """Tests advertisement data's service uuids to empty.
 
@@ -519,6 +531,7 @@
         return self.verify_adv_data_service_uuids(droid, exp_service_uuids)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='3da511db-d024-45c8-be3c-fe8e123129fa')
     def test_adv_data_set_service_uuids_single(self):
         """Tests advertisement data's service uuids to empty.
 
@@ -549,6 +562,7 @@
         return self.verify_adv_data_service_uuids(droid, exp_service_uuids)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='15073359-d607-4a76-b60a-00a4b34f9a85')
     def test_adv_data_set_service_uuids_multiple(self):
         """Tests advertisement data's service uuids to multiple uuids.
 
@@ -573,13 +587,16 @@
         """
         self.log.debug("Step 1: Setup environment.")
         droid = self.ad_dut.droid
-        exp_service_uuids = ["00000000-0000-1000-8000-00805f9b34fb",
-                             "00000000-0000-1000-8000-00805f9b34fb"]
+        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
+    @test_tracker_info(uuid='af783a71-ef80-4974-a077-16a4ed8f0114')
     def test_adv_data_set_service_uuids_invalid_uuid(self):
         """Tests advertisement data's service uuids to an invalid uuid.
 
@@ -609,6 +626,7 @@
                                                           exp_service_uuids)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='51d634e7-6271-4cc0-a57b-3c1b632a7db6')
     def test_adv_data_set_service_data(self):
         """Tests advertisement data's service data.
 
@@ -634,7 +652,7 @@
         self.log.debug("Step 1: Setup environment.")
         droid = self.ad_dut.droid
         exp_service_data_uuid = "00000000-0000-1000-8000-00805f9b34fb"
-        exp_service_data = [1,2,3]
+        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))
@@ -642,6 +660,7 @@
                                                  exp_service_data)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='aa18b0af-2a41-4b2a-af64-ea639961d561')
     def test_adv_data_set_service_data_invalid_service_data(self):
         """Tests advertisement data's invalid service data.
 
@@ -675,6 +694,7 @@
             droid, exp_service_data_uuid, exp_service_data)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='13a75a47-eff4-429f-a436-d244bbfe4496')
     def test_adv_data_set_service_data_invalid_service_data_uuid(self):
         """Tests advertisement data's invalid service data and uuid.
 
@@ -708,6 +728,7 @@
             droid, exp_service_data_uuid, exp_service_data)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='386024e2-212e-4eed-8ef3-43d0c0239ea5')
     def test_adv_data_set_manu_id(self):
         """Tests advertisement data's manufacturers data and id.
 
@@ -733,7 +754,7 @@
         self.log.debug("Step 1: Setup environment.")
         droid = self.ad_dut.droid
         exp_manu_id = 0
-        exp_manu_specific_data = [1,2,3]
+        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,
@@ -742,6 +763,7 @@
                                             exp_manu_specific_data)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='386024e2-212e-4eed-8ef3-43d0c0239ea5')
     def test_adv_data_set_manu_id_invalid_manu_id(self):
         """Tests advertisement data's manufacturers invalid id.
 
@@ -766,7 +788,7 @@
         self.log.debug("Step 1: Setup environment.")
         droid = self.ad_dut.droid
         exp_manu_id = -1
-        exp_manu_specific_data = [1,2,3]
+        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,
@@ -775,6 +797,7 @@
                                                     exp_manu_specific_data)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='27ccd7d7-9cd2-4e95-8198-ef6ca746d1cc')
     def test_adv_data_set_manu_id_invalid_manu_specific_data(self):
         """Tests advertisement data's manufacturers invalid specific data.
 
@@ -808,6 +831,7 @@
                                                     exp_manu_specific_data)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='8e78d444-cd25-4c17-9532-53972a6f0ffe')
     def test_adv_data_set_manu_id_max(self):
         """Tests advertisement data's manufacturers id to the max size.
 
@@ -833,7 +857,7 @@
         self.log.debug("Step 1: Setup environment.")
         droid = self.ad_dut.droid
         exp_manu_id = JavaInteger.MAX.value
-        exp_manu_specific_data = [1,2,3]
+        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,
@@ -842,6 +866,7 @@
                                             exp_manu_specific_data)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='6c4866b7-ddf5-44ef-a231-0af683c6db80')
     def test_adv_data_set_include_tx_power_level_true(self):
         """Tests advertisement data's include tx power level to True.
 
@@ -874,6 +899,7 @@
             droid, exp_include_tx_power_level)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='db06cc5f-60cf-4f04-b0fe-0c354f987541')
     def test_adv_data_set_include_tx_power_level_false(self):
         """Tests advertisement data's include tx power level to False.
 
@@ -906,6 +932,7 @@
             droid, exp_include_tx_power_level)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='e99480c4-fd37-4791-a8d0-7eb8f8f72d62')
     def test_adv_data_set_include_device_name_true(self):
         """Tests advertisement data's include device name to True.
 
@@ -938,6 +965,7 @@
             droid, exp_include_device_name)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='b89ed642-c426-4777-8217-7bb8c2058592')
     def test_adv_data_set_include_device_name_false(self):
         """Tests advertisement data's include device name to False.
 
@@ -965,12 +993,13 @@
         droid = self.ad_dut.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))
+            "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
+    @test_tracker_info(uuid='5033bcf5-a841-4b8b-af35-92c7237c7b36')
     def test_advertisement_greater_than_31_bytes(self):
         """Tests advertisement data's size to be greater than 31 bytes.
 
@@ -999,16 +1028,15 @@
         for i in range(25):
             service_data.append(i)
         droid.bleAddAdvertiseDataServiceData(
-            "0000110D-0000-1000-8000-00805F9B34FB",
-            service_data)
+            "0000110D-0000-1000-8000-00805F9B34FB", 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)))
+        except sl4a_client.Sl4aApiError:
+            self.log.info("{} event was not found.".format(
+                adv_fail.format(advcallback)))
             return False
         return test_result
 
@@ -1083,9 +1111,8 @@
             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))
+        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,
@@ -1181,7 +1208,7 @@
             self.log.debug("Set Advertise settings invalid filtering mode "
                            "passed with input as {}".format(exp_adv_mode))
             return False
-        except SL4AAPIError:
+        except sl4a_client.Sl4aApiError:
             self.log.debug("Set Advertise settings invalid filtering mode "
                            "failed successfully with input as {}".format(
                                exp_adv_mode))
@@ -1195,7 +1222,7 @@
             self.log.debug("Set Advertise settings invalid tx power level " +
                            " with input as {}".format(exp_adv_tx_power))
             return False
-        except SL4AAPIError:
+        except sl4a_client.Sl4aApiError:
             self.log.debug("Set Advertise settings invalid tx power level "
                            "failed successfullywith input as {}".format(
                                exp_adv_tx_power))
@@ -1208,7 +1235,7 @@
             self.log.debug("Set Advertise Data service uuids " +
                            " with input as {}".format(exp_service_uuids))
             return False
-        except SL4AAPIError:
+        except sl4a_client.Sl4aApiError:
             self.log.debug("Set Advertise Data invalid service uuids failed "
                            "successfully with input as {}".format(
                                exp_service_uuids))
@@ -1224,7 +1251,7 @@
                            ", service data: {}".format(exp_service_data_uuid,
                                                        exp_service_data))
             return False
-        except SL4AAPIError:
+        except sl4a_client.Sl4aApiError:
             self.log.debug("Set Advertise Data service data uuid: " + str(
                 exp_service_data_uuid) + ", service data: " + str(
                     exp_service_data) + " failed successfully.")
@@ -1240,7 +1267,7 @@
                            ", manu specific data: " + str(
                                exp_manu_specific_data))
             return False
-        except SL4AAPIError:
+        except sl4a_client.Sl4aApiError:
             self.log.debug("Set Advertise Data manu id: {},"
                            " manu specific data: {},".format(
                                exp_manu_id, exp_manu_specific_data))
diff --git a/acts/tests/google/ble/api/BleScanApiTest.py b/acts/tests/google/ble/api/BleScanApiTest.py
index 6dad9bd..4c43a76 100644
--- a/acts/tests/google/ble/api/BleScanApiTest.py
+++ b/acts/tests/google/ble/api/BleScanApiTest.py
@@ -20,7 +20,8 @@
 then other test suites utilising Ble Scanner will also fail.
 """
 
-from acts.controllers.android import SL4AAPIError
+from acts.controllers import sl4a_client
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.BleEnum import ScanSettingsCallbackType
 from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
@@ -85,13 +86,13 @@
                                                           [1])
                 droid.bleSetScanSettingsScanMode(input['ScanSettings'][2])
                 droid.bleSetScanSettingsResultType(input['ScanSettings'][3])
-            except SL4AAPIError as error:
+            except sl4a_client.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:
+            except sl4a_client.Sl4aApiError as error:
                 self.log.debug("Set Scan Filter Device Name failed with: {}"
                                .format(error))
                 return False
@@ -99,7 +100,7 @@
             try:
                 droid.bleSetScanFilterDeviceAddress(input[
                     'ScanFilterDeviceAddress'])
-            except SL4AAPIError as error:
+            except sl4a_client.Sl4aApiError as error:
                 self.log.debug("Set Scan Filter Device Address failed with: {}"
                                .format(error))
                 return False
@@ -110,7 +111,7 @@
                     input['ScanFilterManufacturerDataId'],
                     input['ScanFilterManufacturerData'],
                     input['ScanFilterManufacturerDataMask'])
-            except SL4AAPIError as error:
+            except sl4a_client.Sl4aApiError as error:
                 self.log.debug("Set Scan Filter Manufacturer info with data "
                                "mask failed with: {}".format(error))
                 return False
@@ -121,7 +122,7 @@
                 droid.bleSetScanFilterManufacturerData(
                     input['ScanFilterManufacturerDataId'],
                     input['ScanFilterManufacturerData'])
-            except SL4AAPIError as error:
+            except sl4a_client.Sl4aApiError as error:
                 self.log.debug(
                     "Set Scan Filter Manufacturer info failed with: "
                     "{}".format(error))
@@ -214,6 +215,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5ffc9f7b-c261-4bf0-9a6b-7fda182b6c97')
     def test_start_ble_scan_with_default_settings(self):
         """Test LE scan with default settings.
 
@@ -237,6 +239,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='88c3b0cb-b4d4-45d1-be25-9855290a6d03')
     def test_stop_ble_scan_default_settings(self):
         """Test stopping an LE scan.
 
@@ -274,6 +277,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5aa7a4c2-0b7d-4000-a980-f00c9329a7b9')
     def test_scan_settings_callback_type_all_matches(self):
         """Test LE scan settings callback type all matches.
 
@@ -301,6 +305,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='fd764861-aa76-480e-b2d2-5d55a888d123')
     def test_scan_settings_set_callback_type_first_match(self):
         """Test LE scan settings callback type first match
 
@@ -330,6 +335,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='52e4626e-199c-4755-b9f1-8b38ecb30896')
     def test_scan_settings_set_callback_type_match_lost(self):
         """Test LE scan settings callback type match lost.
 
@@ -359,6 +365,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='57476b3c-ba7a-4342-86f6-1b56b2c00181')
     def test_scan_settings_set_invalid_callback_type(self):
         """Test LE scan settings invalid callback type.
 
@@ -386,6 +393,7 @@
         return not test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='52c80f0c-4f26-4cda-8a6b-291ac52f673a')
     def test_scan_settings_set_scan_mode_low_power(self):
         """Test LE scan settings scan mode low power mode.
 
@@ -415,6 +423,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='20f4513c-44a7-435d-be4e-03420093297a')
     def test_scan_settings_set_scan_mode_balanced(self):
         """Test LE scan settings scan mode balanced.
 
@@ -442,6 +451,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='bf14e7fd-853b-4833-8fef-8c4bd629374b')
     def test_scan_settings_set_scan_mode_low_latency(self):
         """Test LE scan settings scan mode low latency.
 
@@ -469,6 +479,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='9f3b2e10-98f8-4d6a-b6b6-e8dee87063f0')
     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.
@@ -492,6 +503,7 @@
         return not self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='cb246be7-4fef-4313-964d-5fb6dbe558c8')
     def test_scan_settings_set_report_delay_millis_min(self):
         """Test scan settings report delay millis as min value
 
@@ -520,6 +532,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='db1ea8f6-503d-4e9a-b61a-01210508c5a2')
     def test_scan_settings_set_report_delay_millis_min_plus_one(self):
         """Test scan settings report delay millis as min value plus one.
 
@@ -548,6 +561,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='54e83ff8-92b0-473e-839a-1ff1c7dcea83')
     def test_scan_settings_set_report_delay_millis_max(self):
         """Test scan settings report delay millis as max value.
 
@@ -576,6 +590,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='45d918ec-7e43-463b-8f07-f009f8808903')
     def test_scan_settings_set_report_delay_millis_max_minus_one(self):
         """Test scan settings report delay millis as max value minus one.
 
@@ -604,6 +619,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='eb94b5ee-f2e7-4322-b3df-7bdd3a250262')
     def test_scan_settings_set_invalid_report_delay_millis_min_minus_one(self):
         """Test scan settings report delay millis as an invalid value.
 
@@ -633,6 +649,7 @@
         return not self.validate_scan_settings_helper(input, droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='8f5a2bd0-6037-4ac6-a962-f11e7fc13920')
     def test_scan_settings_set_scan_result_type_full(self):
         """Test scan settings result type full.
 
@@ -660,6 +677,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='610fe301-600e-443e-a28b-cd722cc8a4c1')
     def test_scan_settings_set_scan_result_type_abbreviated(self):
         """Test scan settings result type abbreviated.
 
@@ -687,6 +705,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='ed58430b-8180-472f-a118-64f5fce5e84c')
     def test_scan_settings_set_invalid_scan_result_type(self):
         """Test scan settings result type as an invalid value.
 
@@ -713,6 +732,7 @@
         return not self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='6489665f-313d-4b1b-bd7f-f0fdeeaad335')
     def test_scan_filter_set_device_name(self):
         """Test scan filter set valid device name.
 
@@ -737,6 +757,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='76021a9a-14ca-4a2f-a908-96ab90db39ce')
     def test_scan_filter_set_device_name_blank(self):
         """Test scan filter set blank device name.
 
@@ -762,6 +783,7 @@
         return self.validate_scan_settings_helper(input, droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d77c3d81-43a9-4572-a99b-87969117ede5')
     def test_scan_filter_set_device_name_special_chars(self):
         """Test scan filter set device name as special chars.
 
@@ -786,6 +808,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='1697004e-76ab-444b-9419-0437e30444ad')
     def test_scan_filter_set_device_address(self):
         """Test scan filter set valid device address.
 
@@ -810,6 +833,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='eab0409c-7fc5-4d1f-8fbe-5ee2bb743f7e')
     def test_scan_filter_set_invalid_device_address_lower_case(self):
         """Test scan filter set invalid device address.
 
@@ -833,6 +857,7 @@
         return not self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='0ec491ac-d273-468e-bbfe-e36a290aeb2a')
     def test_scan_filter_set_invalid_device_address_blank(self):
         """Test scan filter set invalid device address.
 
@@ -858,6 +883,7 @@
         return not test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5cebc454-091c-4e46-b200-1e52c8dffbec')
     def test_scan_filter_set_invalid_device_address_bad_format(self):
         """Test scan filter set badly formatted device address.
 
@@ -881,6 +907,7 @@
         return not self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d5249d10-1486-4c38-a22d-1f1b077926db')
     def test_scan_filter_set_invalid_device_address_bad_address(self):
         """Test scan filter device address as an invalid value.
 
@@ -904,6 +931,7 @@
         return not self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='65c62d50-69f6-4a0b-bd74-2340e0ce32ca')
     def test_scan_filter_set_manufacturer_id_data(self):
         """Test scan filter manufacturer data.
 
@@ -931,6 +959,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='12807021-9f66-4784-b34a-80859cf4a32f')
     def test_scan_filter_set_manufacturer_id_data_mask(self):
         """Test scan filter manufacturer data mask.
 
@@ -961,6 +990,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='980e5ab6-5381-4471-8e5b-0b716665a9b8')
     def test_scan_filter_set_manufacturer_max_id(self):
         """Test scan filter manufacturer data id.
 
@@ -988,6 +1018,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='cf0efe38-8621-4288-be26-742719da2f6c')
     def test_scan_filter_set_manufacturer_data_empty(self):
         """Test scan filter empty manufacturer data.
 
@@ -1015,6 +1046,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='7ea0e82e-e92a-469c-8432-8f21978508cb')
     def test_scan_filter_set_manufacturer_data_mask_empty(self):
         """Test scan filter empty manufacturer data mask.
 
@@ -1045,6 +1077,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='88e4a9b8-afae-48cb-873a-fd6b4ef84116')
     def test_scan_filter_set_invalid_manufacturer_min_id_minus_one(self):
         """Test scan filter invalid manufacturer data.
 
@@ -1072,6 +1105,7 @@
         return not self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='2e8438dc-29cd-4f72-8747-4a161974d4d3')
     def test_scan_filter_set_service_uuid(self):
         """Test scan filter set valid service uuid.
 
@@ -1099,6 +1133,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='e07b9985-44b6-4dc4-b570-0833b5d2893c')
     def test_scan_filter_service_uuid_p_service(self):
         """Test scan filter service uuid.
 
@@ -1128,6 +1163,7 @@
         return self.validate_scan_settings_helper(input, self.ad_dut.droid)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='0467af19-6e9a-4cfe-9e10-878b0c224df2')
     def test_classic_ble_scan_with_service_uuids_p(self):
         """Test classic LE scan with valid service uuid.
 
@@ -1157,6 +1193,7 @@
             droid, scan_callback, service_uuid_list)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='516c295f-a2df-44f6-b2ad-54451af43ce8')
     def test_classic_ble_scan_with_service_uuids_hr(self):
         """Test classic LE scan with valid service uuid.
 
@@ -1185,6 +1222,7 @@
             droid, scan_callback, service_uuid_list)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='0458b5e0-bb0b-4d6e-ab79-e21169d3256b')
     def test_classic_ble_scan_with_service_uuids_empty_uuid_list(self):
         """Test classic LE scan with empty but valid uuid list.
 
@@ -1213,6 +1251,7 @@
             droid, scan_callback, service_uuid_list)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='c0d84a37-c86c-43c4-9dc7-d16959fdbc2a')
     def test_classic_ble_scan_with_service_uuids_hr_and_p(self):
         """Test classic LE scan with multiple service uuids.
 
diff --git a/acts/tests/google/ble/api/GattApiTest.py b/acts/tests/google/ble/api/GattApiTest.py
index 9955c67..06095de 100644
--- a/acts/tests/google/ble/api/GattApiTest.py
+++ b/acts/tests/google/ble/api/GattApiTest.py
@@ -17,13 +17,13 @@
 Test script to exercise Gatt Apis.
 """
 
-from acts.controllers.android import SL4AAPIError
+from acts.controllers import sl4a_client
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
 
 
 class GattApiTest(BluetoothBaseTest):
-
     def __init__(self, controllers):
         BluetoothBaseTest.__init__(self, controllers)
         self.ad = self.android_devices[0]
@@ -40,6 +40,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='fffe5c46-eb97-477b-ac3e-3f70700bb84e')
     def test_open_gatt_server(self):
         """Test a gatt server.
 
@@ -64,6 +65,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='12828c2c-b6ae-4670-a829-9867e75fb711')
     def test_open_gatt_server_on_same_callback(self):
         """Test repetitive opening of a gatt server.
 
@@ -90,6 +92,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='63fc684d-6c1d-455e-afdb-1887123b4d2f')
     def test_open_gatt_server_on_invalid_callback(self):
         """Test gatt server an an invalid callback.
 
@@ -111,7 +114,7 @@
         invalid_callback_index = -1
         try:
             self.ad.droid.gattServerOpenGattServer(invalid_callback_index)
-        except SL4AAPIError as e:
+        except sl4a_client.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
index 7055ed3..d8107ba 100644
--- a/acts/tests/google/ble/beacon_tests/BeaconSwarmTest.py
+++ b/acts/tests/google/ble/beacon_tests/BeaconSwarmTest.py
@@ -117,7 +117,7 @@
         threads = []
         for a in self.android_devices:
             d, e = a.droid, a.ed
-            serial_no = d.getBuildSerial()
+            serial_no = a.serial
             if serial_no not in beacon_serials:
                 continue
             thread = threading.Thread(target=self._start_advertisements_thread,
@@ -146,7 +146,7 @@
             self.log.info("Restarting advertisements.")
             for a in self.android_devices:
                 d, e = a.droid, a.ed
-                serial_no = d.getBuildSerial()
+                serial_no = a.serial
                 if serial_no not in beacon_serials:
                     continue
                 thread = threading.Thread(
diff --git a/acts/tests/google/ble/bt5/AdvertisingSetTest.py b/acts/tests/google/ble/bt5/AdvertisingSetTest.py
new file mode 100644
index 0000000..580520b
--- /dev/null
+++ b/acts/tests/google/ble/bt5/AdvertisingSetTest.py
@@ -0,0 +1,163 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+This test script exercises different Bluetooth 5 specific 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.asserts import assert_equal
+from acts.asserts import assert_true
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import ScanSettingsPhy
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.bt_test_utils import advertising_set_started
+from acts.test_utils.bt.bt_test_utils import advertising_set_enabled
+from acts.test_utils.bt.bt_test_utils import advertising_set_data_set
+from acts.test_utils.bt.bt_test_utils import advertising_set_scan_response_set
+from acts.test_utils.bt.bt_test_utils import advertising_set_parameters_update
+from acts.test_utils.bt.bt_test_utils import advertising_set_periodic_parameters_updated
+from acts.test_utils.bt.bt_test_utils import advertising_set_periodic_data_set
+from acts.test_utils.bt.bt_test_utils import advertising_set_periodic_enable
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts import signals
+
+
+class AdvertisingSetTest(BluetoothBaseTest):
+    default_timeout = 10
+    max_scan_instances = 28
+    report_delay = 2000
+    scan_callbacks = []
+    adv_callbacks = []
+    active_scan_callback_list = []
+    big_adv_data = {
+        "includeDeviceName": True,
+        "manufacturerData": [0x0123, "00112233445566778899AABBCCDDEE"],
+        "manufacturerData2": [0x2540, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
+                                       0x66, 0x77, 0x88, 0xFF]],
+        "serviceData": ["b19d42dc-58ba-4b20-b6c1-6628e7d21de4",
+                        "00112233445566778899AABBCCDDEE"],
+        "serviceData2":
+        ["000042dc-58ba-4b20-b6c1-6628e7d21de4",
+         [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xFF]]
+    }
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.adv_ad = self.android_devices[0]
+
+    def setup_class(self):
+        if not self.adv_ad.droid.bluetoothIsLeExtendedAdvertisingSupported():
+            raise signals.TestSkipClass(
+                "Advertiser does not support LE Extended Advertising")
+
+    def teardown_test(self):
+        self.active_scan_callback_list = []
+
+    def on_exception(self, test_name, begin_time):
+        reset_bluetooth(self.android_devices)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_reenabling(self):
+        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
+        self.adv_ad.droid.bleAdvSetStartAdvertisingSet(
+            {"connectable": False,
+             "legacyMode": False,
+             "primaryPhy": "PHY_LE_1M",
+             "secondaryPhy": "PHY_LE_1M",
+             "interval": 320}, self.big_adv_data, None, None, None,
+            adv_callback)
+
+        set_started_evt = self.adv_ad.ed.pop_event(
+            advertising_set_started.format(adv_callback), self.default_timeout)
+        set_id = set_started_evt['data']['setId']
+        assert_equal(0, set_started_evt['data']['status'])
+        assert_equal(0, set_started_evt['data']['status'])
+
+        self.log.info("Successfully started set " + str(set_id))
+
+        self.adv_ad.droid.bleAdvSetEnableAdvertising(set_id, False, 0)
+        enable_evt = self.adv_ad.ed.pop_event(
+            advertising_set_enabled.format(adv_callback), self.default_timeout)
+        assert_equal(set_id, enable_evt['data']['setId'])
+        assert_equal(False, enable_evt['data']['enable'])
+        assert_equal(0, enable_evt['data']['status'])
+        self.log.info("Successfully disabled advertising set " + str(set_id))
+
+        self.log.info("Enabling advertising for 2 seconds... ")
+        self.adv_ad.droid.bleAdvSetEnableAdvertising(set_id, True, 2000)
+        enable_evt = self.adv_ad.ed.pop_event(
+            advertising_set_enabled.format(adv_callback), self.default_timeout)
+        assert_equal(set_id, enable_evt['data']['setId'])
+        assert_equal(True, enable_evt['data']['enable'])
+        assert_equal(0, enable_evt['data']['status'])
+        self.log.info("Enabled. Waiting for disable event ~2s ...")
+
+        enable_evt = self.adv_ad.ed.pop_event(
+            advertising_set_enabled.format(adv_callback), self.default_timeout)
+        assert_equal(set_id, enable_evt['data']['setId'])
+        assert_equal(False, enable_evt['data']['enable'])
+        assert_equal(0, enable_evt['data']['status'])
+        self.log.info("Disable event received. Now trying to set data...")
+
+        self.adv_ad.droid.bleAdvSetSetAdvertisingData(
+            set_id,
+            {"manufacturerData": [0x0123, "00112233445566778899AABBCCDDEE"]})
+        data_set_evt = self.adv_ad.ed.pop_event(
+            advertising_set_data_set.format(adv_callback),
+            self.default_timeout)
+        assert_equal(set_id, data_set_evt['data']['setId'])
+        assert_equal(0, data_set_evt['data']['status'])
+        self.log.info("Data changed successfully.")
+
+        max_len = self.adv_ad.droid.bluetoothGetLeMaximumAdvertisingDataLength(
+        )
+
+        self.log.info("Will try to set data to maximum possible length")
+        data_len = max_len - 4
+        test_fill = '01' * data_len
+        self.adv_ad.droid.bleAdvSetSetAdvertisingData(
+            set_id, {"manufacturerData": [0x0123, test_fill]})
+        data_set_evt = self.adv_ad.ed.pop_event(
+            advertising_set_data_set.format(adv_callback),
+            self.default_timeout)
+        assert_equal(set_id, data_set_evt['data']['setId'])
+        assert_equal(0, data_set_evt['data']['status'])
+        self.log.info("Data changed successfully.")
+
+        if max_len < 1650:
+            self.log.info("Will try to set data to more than maximum length")
+            data_len = max_len - 4 + 1
+            test_fill = '01' * data_len
+            self.adv_ad.droid.bleAdvSetSetAdvertisingData(
+                set_id, {"manufacturerData": [0x0123, test_fill]})
+            data_set_evt = self.adv_ad.ed.pop_event(
+                advertising_set_data_set.format(adv_callback),
+                self.default_timeout)
+            assert_equal(set_id, data_set_evt['data']['setId'])
+            #TODO(jpawlowski): make nicer error fot this case
+            assert_true(data_set_evt['data']['status'] != 0,
+                        "Setting data should fail because data too long.")
+
+            self.log.info("Data change failed as expected.")
+
+        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+        return True
diff --git a/acts/tests/google/ble/bt5/Bt5ScanTest.py b/acts/tests/google/ble/bt5/Bt5ScanTest.py
new file mode 100644
index 0000000..3d71ebc
--- /dev/null
+++ b/acts/tests/google/ble/bt5/Bt5ScanTest.py
@@ -0,0 +1,433 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+This test script exercises different Bluetooth 5 specific 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 import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BleEnum import ScanSettingsPhy
+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 reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import scan_result
+from acts.test_utils.bt.bt_test_utils import advertising_set_on_own_address_read
+from acts.test_utils.bt.bt_test_utils import advertising_set_started
+from acts import signals
+
+
+class Bt5ScanTest(BluetoothBaseTest):
+    default_timeout = 10
+    max_scan_instances = 28
+    report_delay = 2000
+    scan_callbacks = []
+    adv_callbacks = []
+    active_scan_callback_list = []
+    big_adv_data = {
+        "includeDeviceName": True,
+        "manufacturerData": [0x0123, "00112233445566778899AABBCCDDEE"],
+        "manufacturerData2": [0x2540, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
+                                       0x66, 0x77, 0x88, 0xFF]],
+        "serviceData": ["b19d42dc-58ba-4b20-b6c1-6628e7d21de4",
+                        "00112233445566778899AABBCCDDEE"],
+        "serviceData2":
+        ["000042dc-58ba-4b20-b6c1-6628e7d21de4",
+         [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xFF]]
+    }
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.scn_ad = self.android_devices[0]
+        self.adv_ad = self.android_devices[1]
+
+    def setup_class(self):
+        if not self.scn_ad.droid.bluetoothIsLeExtendedAdvertisingSupported():
+            raise signals.TestSkipClass(
+                "Scanner does not support LE Extended Advertising")
+
+        if not self.adv_ad.droid.bluetoothIsLeExtendedAdvertisingSupported():
+            raise signals.TestSkipClass(
+                "Advertiser does not support LE Extended Advertising")
+
+    def teardown_test(self):
+        cleanup_scanners_and_advertisers(
+            self.scn_ad, self.active_scan_callback_list, self.adv_ad, [])
+        self.active_scan_callback_list = []
+
+    def on_exception(self, test_name, begin_time):
+        reset_bluetooth(self.android_devices)
+
+    # This one does not relly test anything, but display very helpful
+    # information that might help with debugging.
+    @BluetoothBaseTest.bt_test_wrap
+    def test_capabilities(self):
+        d = self.scn_ad.droid
+        sup2M = d.bluetoothIsLe2MPhySupported()
+        supCoded = d.bluetoothIsLeCodedPhySupported()
+        supExt = d.bluetoothIsLeExtendedAdvertisingSupported()
+        supPeriodic = d.bluetoothIsLePeriodicAdvertisingSupported()
+        maxDataLen = d.bluetoothGetLeMaximumAdvertisingDataLength()
+        self.log.info("Scanner capabilities:")
+        self.log.info("LE 2M: " + str(sup2M) + ", LE Coded: " + str(supCoded) +
+                      ", LE Extended Advertising: " + str(supExt) +
+                      ", LE Periodic Advertising: " + str(supPeriodic) +
+                      ", maximum advertising data length: " + str(maxDataLen))
+        d = self.adv_ad.droid
+        sup2M = d.bluetoothIsLe2MPhySupported()
+        supCoded = d.bluetoothIsLeCodedPhySupported()
+        supExt = d.bluetoothIsLeExtendedAdvertisingSupported()
+        supPeriodic = d.bluetoothIsLePeriodicAdvertisingSupported()
+        maxDataLen = d.bluetoothGetLeMaximumAdvertisingDataLength()
+        self.log.info("Advertiser capabilities:")
+        self.log.info("LE 2M: " + str(sup2M) + ", LE Coded: " + str(supCoded) +
+                      ", LE Extended Advertising: " + str(supExt) +
+                      ", LE Periodic Advertising: " + str(supPeriodic) +
+                      ", maximum advertising data length: " + str(maxDataLen))
+        return True
+
+    def test_1m_1m_extended_scan(self):
+        """Test scan on LE 1M PHY using LE 1M PHY as secondary.
+
+        Tests test verify that device is able to receive extended advertising
+        on 1M PHY when secondary is 1M PHY.
+
+        Steps:
+        1. Start advertising set on dut1
+        2. Start scanning on dut0, scan filter set to advertiser's device name
+        3. Try to find an event, expect found
+        4. Stop advertising
+
+        Expected Result:
+        Scan finds a advertisement.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE Advertising Extension, BT5, LE, Advertising, Scanning
+        Priority: 1
+        """
+        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
+        self.adv_ad.droid.bleAdvSetStartAdvertisingSet(
+            {"connectable": True,
+             "legacyMode": False,
+             "primaryPhy": "PHY_LE_1M",
+             "secondaryPhy": "PHY_LE_1M",
+             "interval": 320}, self.big_adv_data, None, None, None, 0, 0,
+            adv_callback)
+
+        self.scn_ad.droid.bleSetScanSettingsLegacy(False)
+        self.scn_ad.droid.bleSetScanSettingsPhy(
+            ScanSettingsPhy.PHY_LE_1M.value)
+
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_ad.droid)
+
+        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
+        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
+        self.scn_ad.droid.bleBuildScanFilter(filter_list)
+        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
+                                          scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+
+        try:
+            self.scn_ad.ed.pop_event(
+                scan_result.format(scan_callback), self.default_timeout)
+        except Empty:
+            self.log.error("Scan result not found")
+            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+            return False
+
+        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+        return True
+
+    def test_1m_2m_extended_scan(self):
+        """Test scan on LE 1M PHY using LE 2M PHY as secondary.
+
+        Tests test verify that device is able to receive extended advertising
+        on 1M PHY when secondary is 2M PHY.
+
+        Steps:
+        1. Start advertising set on dut1
+        2. Start scanning on dut0, scan filter set to advertiser's device name
+        3. Try to find an event, expect found
+        4. Stop advertising
+
+        Expected Result:
+        Scan finds a advertisement.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE Advertising Extension, BT5, LE, Advertising, Scanning
+        Priority: 1
+        """
+        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
+        self.adv_ad.droid.bleAdvSetStartAdvertisingSet(
+            {"connectable": True,
+             "legacyMode": False,
+             "primaryPhy": "PHY_LE_1M",
+             "secondaryPhy": "PHY_LE_2M",
+             "interval": 320}, self.big_adv_data, None, None, None, 0, 0,
+            adv_callback)
+
+        self.scn_ad.droid.bleSetScanSettingsLegacy(False)
+        self.scn_ad.droid.bleSetScanSettingsPhy(
+            ScanSettingsPhy.PHY_LE_1M.value)
+
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_ad.droid)
+
+        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
+        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
+        self.scn_ad.droid.bleBuildScanFilter(filter_list)
+        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
+                                          scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+
+        try:
+            self.scn_ad.ed.pop_event(
+                scan_result.format(scan_callback), self.default_timeout)
+        except Empty:
+            self.log.error("Scan result not found")
+            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+            return False
+
+        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+        return True
+
+
+    def test_legacy_scan_result_raw_length(self):
+        """Test that raw scan record data in legacy scan is 62 bytes long. This is required for compability with older apps that make this assumption.
+
+        Steps:
+        1. Start legacy advertising set on dut1
+        2. Start scanning on dut0, scan filter set to advertiser's device name
+        3. Try to find an event, expect found, verify scan recurd data length
+        4. Stop advertising
+
+        Expected Result:
+        Scan finds a legacy advertisement of proper size
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE Advertising Extension, BT5, LE, Advertising, Scanning
+        Priority: 1
+        """
+        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
+        self.adv_ad.droid.bleAdvSetStartAdvertisingSet(
+            {"connectable": True,
+             "scannable": True,
+             "legacyMode": True,
+             "interval": 320}, {"includeDeviceName": True}, None, None, None, 0, 0,
+            adv_callback)
+
+        self.scn_ad.droid.bleSetScanSettingsLegacy(True)
+        self.scn_ad.droid.bleSetScanSettingsPhy(
+            ScanSettingsPhy.PHY_LE_1M.value)
+
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_ad.droid)
+
+        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
+        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
+        self.scn_ad.droid.bleBuildScanFilter(filter_list)
+        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
+                                          scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+
+        try:
+            evt = self.scn_ad.ed.pop_event(
+                scan_result.format(scan_callback), self.default_timeout)
+            rawData = evt['data']['Result']['scanRecord']
+            asserts.assert_true(
+                62 == len(rawData.split(",")),
+                "Raw data should be 62 bytes long.")
+        except Empty:
+            self.log.error("Scan result not found")
+            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+            return False
+
+        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+        return True
+
+
+    def test_duration(self):
+        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
+        self.adv_ad.droid.bleAdvSetStartAdvertisingSet(
+            {"connectable": True,
+             "legacyMode": False,
+             "primaryPhy": "PHY_LE_1M",
+             "secondaryPhy": "PHY_LE_2M",
+             "interval": 320}, self.big_adv_data, None, None, None, 0, 0,
+            adv_callback)
+
+        self.scn_ad.droid.bleSetScanSettingsLegacy(False)
+        self.scn_ad.droid.bleSetScanSettingsPhy(
+            ScanSettingsPhy.PHY_LE_1M.value)
+
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_ad.droid)
+
+        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
+        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
+        self.scn_ad.droid.bleBuildScanFilter(filter_list)
+        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
+                                          scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+
+        try:
+            self.scn_ad.ed.pop_event(
+                scan_result.format(scan_callback), self.default_timeout)
+        except Empty:
+            self.log.error("Scan result not found")
+            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+            return False
+
+        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+        return True
+
+
+    def test_anonymous_advertising(self):
+        """Test anonymous advertising.
+
+        Tests test verify that device is able to receive anonymous advertising
+        on 1M PHY when secondary is 2M PHY.
+
+        Steps:
+        1. Start anonymous advertising set on dut1
+        2. Start scanning on dut0, scan filter set to advertiser's device name
+        3. Try to find an event, expect found
+        4. Stop advertising
+
+        Expected Result:
+        Scan finds a advertisement.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE Advertising Extension, BT5, LE, Advertising, Scanning
+        Priority: 1
+        """
+        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
+        self.adv_ad.droid.bleAdvSetStartAdvertisingSet(
+            {"connectable": False,
+             "anonymous": True,
+             "legacyMode": False,
+             "primaryPhy": "PHY_LE_1M",
+             "secondaryPhy": "PHY_LE_2M",
+             "interval": 320}, self.big_adv_data, None, None, None, 0, 0,
+            adv_callback)
+
+        self.scn_ad.droid.bleSetScanSettingsLegacy(False)
+        self.scn_ad.droid.bleSetScanSettingsPhy(
+            ScanSettingsPhy.PHY_LE_1M.value)
+
+        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
+            self.scn_ad.droid)
+
+        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
+        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
+        self.scn_ad.droid.bleBuildScanFilter(filter_list)
+        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
+                                          scan_callback)
+        self.active_scan_callback_list.append(scan_callback)
+
+        try:
+            evt = self.scn_ad.ed.pop_event(
+                scan_result.format(scan_callback), self.default_timeout)
+            address = evt['data']['Result']['deviceInfo']['address']
+            asserts.assert_true(
+                '00:00:00:00:00:00' == address,
+                "Anonymous address should be 00:00:00:00:00:00, but was " + str(address))
+        except Empty:
+            self.log.error("Scan result not found")
+            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+            return False
+
+        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_get_own_address(self):
+        """Test obtaining own address for PTS.
+
+        Steps:
+        1. Start advertising set dut1
+        2. Grab address
+        3. Stop advertising
+
+        Expected Result:
+        Callback with address is received.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE Advertising Extension, BT5, LE, Advertising
+        Priority: 1
+        """
+        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
+        self.adv_ad.droid.bleAdvSetStartAdvertisingSet(
+            {"connectable": False,
+             "anonymous": True,
+             "legacyMode": False,
+             "primaryPhy": "PHY_LE_1M",
+             "secondaryPhy": "PHY_LE_2M",
+             "interval": 320}, self.big_adv_data, None, None, None, 0, 0,
+            adv_callback)
+
+        set_id = -1
+
+        try:
+            evt = self.adv_ad.ed.pop_event(
+                advertising_set_started.format(adv_callback), self.default_timeout)
+            self.log.info("data: " + str(evt['data']))
+            set_id = evt['data']['setId']
+        except Empty:
+            self.log.error("did not receive the set started event!")
+            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+            return False
+
+        self.adv_ad.droid.bleAdvSetGetOwnAddress(set_id)
+
+        try:
+            evt = self.adv_ad.ed.pop_event(
+                advertising_set_on_own_address_read.format(set_id), self.default_timeout)
+            address = evt['data']['address']
+            self.log.info("Advertiser address is: " + str(address))
+        except Empty:
+            self.log.error("onOwnAddressRead not received.")
+            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+            return False
+
+
+        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
+        return True
diff --git a/acts/tests/google/ble/bt5/PhyTest.py b/acts/tests/google/ble/bt5/PhyTest.py
new file mode 100644
index 0000000..4046318
--- /dev/null
+++ b/acts/tests/google/ble/bt5/PhyTest.py
@@ -0,0 +1,246 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+This test script exercises set PHY and read PHY procedures.
+"""
+
+from queue import Empty
+
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
+from acts.test_utils.bt.GattEnum import GattCharacteristic
+from acts.test_utils.bt.GattEnum import GattConnectionPriority
+from acts.test_utils.bt.GattEnum import GattDescriptor
+from acts.test_utils.bt.GattEnum import MtuSize
+from acts.test_utils.bt.GattEnum import GattEvent
+from acts.test_utils.bt.GattEnum import GattCbStrings
+from acts.test_utils.bt.GattEnum import GattPhy
+from acts import signals
+
+CONNECTION_PRIORITY_HIGH = GattConnectionPriority.CONNECTION_PRIORITY_HIGH.value
+PHY_LE_1M = GattPhy.PHY_LE_1M.value
+PHY_LE_2M = GattPhy.PHY_LE_2M.value
+
+
+def lfmt(txPhy, rxPhy):
+    return '(' + GattPhy(txPhy).name + ', ' + GattPhy(rxPhy).name + ')'
+
+
+class PhyTest(GattConnectedBaseTest):
+    def setup_class(self):
+        if not self.cen_ad.droid.bluetoothIsLe2MPhySupported():
+            raise signals.TestSkipClass(
+                "Central device does not support LE 2M PHY")
+
+        if not self.per_ad.droid.bluetoothIsLe2MPhySupported():
+            raise signals.TestSkipClass(
+                "Peripheral device does not support LE 2M PHY")
+
+    # Some controllers auto-update PHY to 2M, and both client and server
+    # might receive PHY Update event right after connection, if the
+    # connection was established over 1M PHY. We will ignore this event, but
+    # must pop it from queue.
+    def pop_initial_phy_update(self):
+        try:
+            maybe_event = GattEvent.PHY_UPDATE.value["evt"].format(
+                self.gatt_callback)
+            self.cen_ad.ed.pop_event(maybe_event, 0)
+        except Empty:
+            pass
+
+        try:
+            maybe_event = GattEvent.SERV_PHY_UPDATE.value["evt"].format(
+                self.gatt_server_callback)
+            self.per_ad.ed.pop_event(maybe_event, 0)
+        except Empty:
+            pass
+
+    # this helper method checks wether both client and server received PHY
+    # update event with proper txPhy and rxPhy
+    def ensure_both_updated_phy(self, clientTxPhy, clientRxPhy):
+        event = self._client_wait(GattEvent.PHY_UPDATE)
+        txPhy = event['data']['TxPhy']
+        rxPhy = event['data']['RxPhy']
+        self.log.info("\tClient PHY updated: " + lfmt(txPhy, rxPhy))
+        self.assertEqual(0, event['data']['Status'], "Status should be 0")
+        self.assertEqual(clientTxPhy, event['data']['TxPhy'])
+        self.assertEqual(clientRxPhy, event['data']['RxPhy'])
+
+        bt_device_id = 0
+        event = self._server_wait(GattEvent.SERV_PHY_UPDATE)
+        txPhy = event['data']['TxPhy']
+        rxPhy = event['data']['RxPhy']
+        self.log.info("\tServer PHY updated: " + lfmt(txPhy, rxPhy))
+        self.assertEqual(0, event['data']['Status'], "Status should be 0")
+        self.assertEqual(clientRxPhy, event['data']['TxPhy'])
+        self.assertEqual(clientTxPhy, event['data']['RxPhy'])
+
+    # read the client phy, return (txPhy, rxPhy)
+    def read_client_phy(self):
+        self.cen_ad.droid.gattClientReadPhy(self.bluetooth_gatt)
+        event = self._client_wait(GattEvent.PHY_READ)
+        self.assertEqual(0, event['data']['Status'], "Status should be 0")
+        return (event['data']['TxPhy'], event['data']['RxPhy'])
+
+    # read the server phy, return (txPhy, rxPhy)
+    def read_server_phy(self):
+        bt_device_id = 0
+        self.per_ad.droid.gattServerReadPhy(self.gatt_server, bt_device_id)
+        event = self._server_wait(GattEvent.SERV_PHY_READ)
+        self.assertEqual(0, event['data']['Status'], "Status should be 0")
+        return (event['data']['TxPhy'], event['data']['RxPhy'])
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_phy_read(self):
+        """Test LE read PHY.
+
+        Test LE read PHY.
+
+        Steps:
+        1. Central, Peripheral : read PHY, make sure values are same.
+        2. Central: update PHY.
+        3. Ensure both Central and Peripheral received PHY update event.
+        4. Central, Peripheral: read PHY, make sure values are same.
+
+        Expected Result:
+        Verify that read PHY works properly.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, PHY
+        Priority: 0
+        """
+        self.cen_ad.droid.gattClientRequestConnectionPriority(
+            self.bluetooth_gatt, CONNECTION_PRIORITY_HIGH)
+        self.pop_initial_phy_update()
+
+        # read phy from client and server, make sure they're same
+        cTxPhy, cRxPhy = self.read_client_phy()
+        sTxPhy, sRxPhy = self.read_server_phy()
+        self.assertEqual(cTxPhy, sTxPhy)
+        self.assertEqual(cRxPhy, sRxPhy)
+
+        self.log.info("Initial connection PHY was: " + lfmt(cTxPhy, cRxPhy))
+
+        nextTxPhy = (cTxPhy == PHY_LE_1M) and PHY_LE_2M or PHY_LE_1M
+        nextRxPhy = (cRxPhy == PHY_LE_1M) and PHY_LE_2M or PHY_LE_1M
+
+        # try to update PHY from Client
+        self.log.info("Will try to set PHY to: " + lfmt(nextTxPhy, nextRxPhy))
+        self.cen_ad.droid.gattClientSetPreferredPhy(self.bluetooth_gatt,
+                                                    nextTxPhy, nextRxPhy, 0)
+        self.ensure_both_updated_phy(nextTxPhy, nextRxPhy)
+
+        # read phy on client and server, make sure values are same and equal
+        # the newly set value
+        cTxPhy, cRxPhy = self.read_client_phy()
+        sTxPhy, sRxPhy = self.read_server_phy()
+        self.assertEqual(cTxPhy, sTxPhy)
+        self.assertEqual(cRxPhy, sRxPhy)
+
+        self.assertEqual(nextTxPhy, cTxPhy)
+        self.assertEqual(nextRxPhy, cRxPhy)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_phy_change_20_times(self):
+        """Test PHY update.
+
+        Test LE PHY update.
+
+        Steps:
+        1. Central: read PHY.
+        2. Central: update PHY to 1M, 2M, 1M... 20 times, each time ensuring
+                    both client and server received PHY update event.
+
+        Expected Result:
+        Verify that read update PHY worked properly each time.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, PHY
+        Priority: 0
+        """
+        self.cen_ad.droid.gattClientRequestConnectionPriority(
+            self.bluetooth_gatt, CONNECTION_PRIORITY_HIGH)
+        self.pop_initial_phy_update()
+
+        txPhyB, rxPhyB = self.read_client_phy()
+        txPhyA = (txPhyB == PHY_LE_1M) and PHY_LE_2M or PHY_LE_1M
+        rxPhyA = (rxPhyB == PHY_LE_1M) and PHY_LE_2M or PHY_LE_1M
+
+        self.log.info("Initial connection PHY was: " + lfmt(txPhyB, rxPhyB))
+
+        for i in range(20):
+            #swap values between iterations
+            txPhy = (i & 1) and txPhyB or txPhyA
+            rxPhy = (i & 1) and rxPhyB or rxPhyA
+
+            self.log.info("Will try to set PHY to: " + lfmt(txPhy, rxPhy))
+            self.cen_ad.droid.gattClientSetPreferredPhy(
+                self.bluetooth_gatt, txPhy, rxPhy, 0)
+            self.ensure_both_updated_phy(txPhy, rxPhy)
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_phy_change_asym(self):
+        """Test PHY update with asymetric rx and tx PHY.
+
+        Test PHY update with asymetric rx and tx PHY.
+
+        Steps:
+        1. Central: read PHY.
+        2. Central: update PHY to tx: 1M, rx: 2M, ensure both devices received
+                    the asymetric update.
+        3. Central: update PHY to tx: 2M, rx: 1M, ensure both devices received
+                    the asymetric update.
+
+        Expected Result:
+        Verify that read update PHY worked properly each time.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, PHY
+        Priority: 0
+        """
+        self.cen_ad.droid.gattClientRequestConnectionPriority(
+            self.bluetooth_gatt, CONNECTION_PRIORITY_HIGH)
+        self.pop_initial_phy_update()
+
+        txPhy, rxPhy = self.read_client_phy()
+
+        self.log.info("Initial connection PHY was: " + lfmt(txPhy, rxPhy))
+        self.log.info("will try to set PHY to: PHY_LE_1M, PHY_LE_2M")
+
+        #try to update PHY to tx 1M, rx 2M from Client
+        self.cen_ad.droid.gattClientSetPreferredPhy(self.bluetooth_gatt,
+                                                    PHY_LE_1M, PHY_LE_2M, 0)
+        self.ensure_both_updated_phy(PHY_LE_1M, PHY_LE_2M)
+
+        #try to update PHY to TX 2M, RX 1M from Client
+        self.log.info("will try to set PHY to: PHY_LE_2M, PHY_LE_1M")
+        self.cen_ad.droid.gattClientSetPreferredPhy(self.bluetooth_gatt,
+                                                    PHY_LE_2M, PHY_LE_1M, 0)
+        self.ensure_both_updated_phy(PHY_LE_2M, PHY_LE_1M)
+
+        return True
diff --git a/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py b/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
index b66a5be..8fcfb50 100644
--- a/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
+++ b/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
@@ -23,6 +23,7 @@
 import time
 
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import BtTestUtilsError
 from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
@@ -65,11 +66,13 @@
             scan_and_verify_n_advertisements(self.scn_ad, num_advertisements)
         except BtTestUtilsError:
             return False
-        teardown_n_advertisements(self.adv_ad, len(advertise_callback_list),
+        teardown_n_advertisements(self.adv_ad,
+                                  len(advertise_callback_list),
                                   advertise_callback_list)
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='abc03874-6d7a-4b5d-9f29-18731a102793')
     def test_max_advertisements_defaults(self):
         """Testing max advertisements.
 
@@ -95,6 +98,7 @@
         return self._verify_n_advertisements(self.max_advertisements)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='50ee137e-eb71-40ef-b72f-a5fd646190d2')
     def test_max_advertisements_include_device_name_and_filter_device_name(
             self):
         """Testing max advertisement variant.
@@ -127,6 +131,7 @@
         return self._verify_n_advertisements(self.max_advertisements)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='f7e9ba2b-6286-4510-a8a0-f1df831056c0')
     def test_max_advertisements_exclude_device_name_and_filter_device_name(
             self):
         """Test max advertisement variant.
@@ -158,6 +163,7 @@
         return not self._verify_n_advertisements(self.max_advertisements)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='6ce102d7-61e1-4ca0-bcfb-767437b86c2b')
     def test_max_advertisements_with_manufacturer_data(self):
         """Test max advertisement variant.
 
@@ -188,6 +194,7 @@
         return self._verify_n_advertisements(self.max_advertisements)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='2fc7d5e8-1539-42a8-8681-ce0b8bfc0924')
     def test_max_advertisements_with_manufacturer_data_mask(self):
         """Test max advertisements variant.
 
@@ -218,6 +225,7 @@
         return self._verify_n_advertisements(self.max_advertisements)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='9ef615ed-1705-44ae-ab5b-f7e8fb4bb770')
     def test_max_advertisements_with_service_data(self):
         """Test max advertisement variant.
 
@@ -246,12 +254,13 @@
         test_result = True
         filter_list = self.scn_ad.droid.bleGenFilterList()
         self.scn_ad.droid.bleSetScanFilterServiceData(
-            "0000110A-0000-1000-8000-00805F9B34FB", [11,17,80])
+            "0000110A-0000-1000-8000-00805F9B34FB", [11, 17, 80])
         self.adv_ad.droid.bleAddAdvertiseDataServiceData(
-            "0000110A-0000-1000-8000-00805F9B34FB", [11,17,80])
+            "0000110A-0000-1000-8000-00805F9B34FB", [11, 17, 80])
         return self._verify_n_advertisements(self.max_advertisements)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='9ef615ed-1705-44ae-ab5b-f7e8fb4bb770')
     def test_max_advertisements_with_manufacturer_data_mask_and_include_device_name(
             self):
         """Test max advertisement variant.
@@ -286,6 +295,7 @@
         return self._verify_n_advertisements(self.max_advertisements)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='c2ca85fb-6663-431d-aa30-5286a85dbbe0')
     def test_max_advertisements_with_service_uuids(self):
         """Test max advertisement variant.
 
@@ -318,6 +328,7 @@
         return self._verify_n_advertisements(self.max_advertisements)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='756e026f-64d7-4a2f-935a-3790c0ac4503')
     def test_max_advertisements_with_service_uuid_and_service_mask(self):
         """Test max advertisements variant.
 
@@ -351,6 +362,7 @@
         return self._verify_n_advertisements(self.max_advertisements)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='79c4b6cd-9f07-49a9-829f-69b29ea8d322')
     def test_max_advertisements_plus_one(self):
         """Test max advertisements plus one.
 
@@ -376,6 +388,7 @@
         return not self._verify_n_advertisements(self.max_advertisements + 1)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='0bd6e490-a501-4fe1-88e5-9b77970c0b95')
     def test_start_two_advertisements_on_same_callback(self):
         """Test invalid advertisement scenario.
 
@@ -428,6 +441,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='12632b31-22b9-4121-80b6-1263b9d90909')
     def test_toggle_advertiser_bt_state(self):
         """Test forcing stopping advertisements.
 
@@ -466,8 +480,8 @@
             return False
         except concurrent.futures._base.TimeoutError as error:
             self.log.error(
-                "Test failed, filtering callback onSuccess never occurred: {}".format(
-                    error))
+                "Test failed, filtering callback onSuccess never occurred: {}".
+                format(error))
         self.scn_ad.droid.bleSetScanFilterDeviceName(
             self.adv_ad.droid.bluetoothGetLocalName())
         filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
@@ -501,6 +515,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='785c5c77-d5d4-4d0f-8b7b-3eb1f1646d2c')
     def test_restart_advertise_callback_after_bt_toggle(self):
         """Test starting an advertisement on a cleared out callback.
 
@@ -538,8 +553,8 @@
             test_result = False
         except concurrent.futures._base.TimeoutError as error:
             self.log.debug(
-                "Test failed, filtering callback onSuccess never occurred: {}".format(
-                    error))
+                "Test failed, filtering callback onSuccess never occurred: {}".
+                format(error))
         test_result = reset_bluetooth([self.android_devices[1]])
         if not test_result:
             return test_result
@@ -553,6 +568,60 @@
             test_result = False
         except concurrent.futures._base.TimeoutError as error:
             self.log.debug(
-                "Test failed, filtering callback onSuccess never occurred: {}".format(
-                    error))
+                "Test failed, filtering callback onSuccess never occurred: {}".
+                format(error))
+        return test_result
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_timeout(self):
+        """Test starting advertiser with timeout.
+
+        Test that when a timeout is used, the advertiser is cleaned properly,
+        and next one can be started.
+
+        Steps:
+        1. Setup the advertiser android device with 4 second timeout.
+        2. Call start ble advertising.
+        3. Wait 5 seconds, to make sure advertiser times out.
+        4. Repeat steps 1-4 four times.
+
+        Expected Result:
+        Starting the advertising should succeed each time.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Advertising, Concurrency
+        Priority: 1
+        """
+        advertise_timeout_s = 4
+        num_iterations = 4
+        test_result = True
+
+        for i in range(0, num_iterations):
+            advertise_callback, advertise_data, advertise_settings = (
+                generate_ble_advertise_objects(self.adv_ad.droid))
+
+            self.adv_ad.droid.bleSetAdvertiseSettingsTimeout(
+                advertise_timeout_s * 1000)
+
+            self.adv_ad.droid.bleStartBleAdvertising(
+                advertise_callback, advertise_data, advertise_settings)
+            try:
+                self.adv_ad.ed.pop_event(
+                    adv_succ.format(advertise_callback), self.default_timeout)
+            except Empty as error:
+                self.log.error("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))
+
+            if not test_result:
+                return test_result
+
+            time.sleep(advertise_timeout_s + 1)
+
         return test_result
diff --git a/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py b/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
index 5fa94e3..f3b6b9a 100644
--- a/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
+++ b/acts/tests/google/ble/concurrency/ConcurrentBleScanningTest.py
@@ -22,6 +22,7 @@
 import time
 
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
 from acts.test_utils.bt.BleEnum import ScanSettingsCallbackType
@@ -53,6 +54,7 @@
         return reset_bluetooth(self.android_devices)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='e7f68b9b-fb3f-48e9-a272-e41c2a32b4bd')
     def test_max_concurrent_ble_scans(self):
         """Test max LE scans.
 
@@ -139,6 +141,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='58b0c45e-1cbc-420a-9e89-901518ffe3d1')
     def test_max_concurrent_ble_scans_then_discover_advertisement(self):
         """Test max LE scans variant.
 
@@ -217,6 +220,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='7a45e45c-faf3-4e89-abb7-a52f63e53208')
     def test_max_concurrent_ble_scans_plus_one(self):
         """Test mac LE scans variant.
 
@@ -272,6 +276,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5a91f612-69e5-490f-b9d0-50d58a3db736')
     def test_max_concurrent_ble_scans_verify_scans_stop_independently(self):
         """Test max LE scans variant.
 
@@ -340,13 +345,12 @@
                 self.scn_ad.ed.pop_event(expected_scan_event_name,
                                          self.default_timeout)
                 self.log.info(
-                    "Found scan event successfully. Iteration {} successful.".format(
-                        i))
+                    "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))
+                self.log.info("Failed to find a scan result for callback {}".
+                              format(scan_callback))
                 return False
             self.scn_ad.droid.bleStopBleScan(callback)
         self.adv_ad.droid.bleStopBleAdvertising(advertise_callback)
diff --git a/acts/tests/google/ble/filtering/FilteringTest.py b/acts/tests/google/ble/filtering/FilteringTest.py
index 89bc9be..0dbb910 100644
--- a/acts/tests/google/ble/filtering/FilteringTest.py
+++ b/acts/tests/google/ble/filtering/FilteringTest.py
@@ -19,12 +19,14 @@
 import time
 
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.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 TIMEOUT_SMALL
 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
@@ -48,7 +50,7 @@
         },
         {
             'manufacturer_specific_data_id': 1,
-            'manufacturer_specific_data': [14,0,54,0,0,0,0,0]
+            'manufacturer_specific_data': [14, 0, 54, 0, 0, 0, 0, 0]
         },
         {
             'manufacturer_specific_data_id': 1,
@@ -57,8 +59,10 @@
         },
         {
             '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': [
+                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",
@@ -66,35 +70,35 @@
         },
         {
             'service_data_uuid': "0000110C-0000-1000-8000-00805F9B34FB",
-            'service_data': [11,14,50]
+            'service_data': [11, 14, 50]
         },
         {
             'service_data_uuid': "0000110D-0000-1000-8000-00805F9B34FB",
-            'service_data': [16,22,11]
+            'service_data': [16, 22, 11]
         },
         {
             'service_data_uuid': "0000110E-0000-1000-8000-00805F9B34FB",
-            'service_data': [2,9,54]
+            'service_data': [2, 9, 54]
         },
         {
             'service_data_uuid': "0000110F-0000-1000-8000-00805F9B34FB",
-            'service_data': [69,11,50]
+            'service_data': [69, 11, 50]
         },
         {
             'service_data_uuid': "00001101-0000-1000-8000-00805F9B34FB",
-            'service_data': [12,11,21]
+            'service_data': [12, 11, 21]
         },
         {
             'service_data_uuid': "00001102-0000-1000-8000-00805F9B34FB",
-            'service_data': [12,12,44]
+            'service_data': [12, 12, 44]
         },
         {
             'service_data_uuid': "00001103-0000-1000-8000-00805F9B34FB",
-            'service_data': [4,54,1]
+            'service_data': [4, 54, 1]
         },
         {
             'service_data_uuid': "00001104-0000-1000-8000-00805F9B34FB",
-            'service_data': [33,22,44]
+            'service_data': [33, 22, 44]
         },
         {
             'service_uuid': "00000000-0000-1000-8000-00805f9b34fb",
@@ -116,9 +120,9 @@
     valid_filter_variants = {
         'include_tx_power_level': [True, False],
         'manufacturer_specific_data_id': [1, 2, 65535],
-        'manufacturer_specific_data': [[1], [1,2], [127]],
+        'manufacturer_specific_data': [[1], [1, 2], [127]],
         'service_data_uuid': ["00000000-0000-1000-8000-00805f9b34fb"],
-        'service_data': [[1,2,3], [1], [127]],
+        'service_data': [[1, 2, 3], [1], [127]],
         'include_device_name': [False, True],
     }
 
@@ -140,10 +144,12 @@
             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, ]
+        "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
@@ -153,9 +159,10 @@
 
     def _get_combinations(self, t):
         varNames = sorted(t)
-        return ([dict(zip(varNames, prod))
-                 for prod in it.product(*(t[varName]
-                                          for varName in varNames))])
+        return ([
+            dict(zip(varNames, prod))
+            for prod in it.product(*(t[varName] for varName in varNames))
+        ])
 
     def __init__(self, controllers):
         BluetoothBaseTest.__init__(self, controllers)
@@ -166,7 +173,6 @@
         self.log.info("Advertiser device model: {}".format(
             self.adv_ad.droid.getBuildModel()))
 
-
     def _blescan_verify_onscanresult_event(self, event, filters):
         test_result = True
         self.log.debug("Verifying onScanResult event: {}".format(event))
@@ -195,8 +201,8 @@
             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 ('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.")
@@ -256,8 +262,8 @@
         (filters, settings_in_effect) = params
         test_result = True
 
-        self.log.debug("Settings in effect: {}".format(pprint.pformat(
-            settings_in_effect)))
+        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(
@@ -291,8 +297,8 @@
             self.log.debug(
                 "Setting advertisement include_device_name to False")
             self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(False)
-        if ('include_tx_power_level' in filters.keys() and filters[
-                'include_tx_power_level'] is not 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_ad.droid.bleSetAdvertiseDataIncludeTxPowerLevel(True)
@@ -325,13 +331,13 @@
         if 'service_mask' in filters.keys():
             self.scn_ad.droid.bleSetScanFilterServiceUuid(
                 filters['service_uuid'].upper(), filters['service_mask'])
-            self.adv_ad.droid.bleSetAdvertiseDataSetServiceUuids([filters[
-                'service_uuid'].upper()])
+            self.adv_ad.droid.bleSetAdvertiseDataSetServiceUuids(
+                [filters['service_uuid'].upper()])
         elif 'service_uuid' in filters.keys():
             self.scn_ad.droid.bleSetScanFilterServiceUuid(filters[
                 'service_uuid'])
-            self.adv_ad.droid.bleSetAdvertiseDataSetServiceUuids([filters[
-                'service_uuid']])
+            self.adv_ad.droid.bleSetAdvertiseDataSetServiceUuids(
+                [filters['service_uuid']])
         self.scn_ad.droid.bleBuildScanFilter(filter_list)
         advertise_callback, advertise_data, advertise_settings = (
             generate_ble_advertise_objects(self.adv_ad.droid))
@@ -361,29 +367,43 @@
                 ScanSettingsScanMode.SCAN_MODE_OPPORTUNISTIC.value)
         self.adv_ad.droid.bleStartBleAdvertising(
             advertise_callback, advertise_data, advertise_settings)
-        expected_advertise_event_name = adv_succ.format(advertise_callback)
-        self.log.debug(expected_advertise_event_name)
+        regex = "(" + adv_succ.format(
+            advertise_callback) + "|" + adv_fail.format(
+                advertise_callback) + ")"
+        self.log.debug(regex)
         try:
-            event = self.adv_ad.ed.pop_event(expected_advertise_event_name, self.default_timeout)
+            event = self.adv_ad.ed.pop_events(regex, self.default_timeout,
+                                              TIMEOUT_SMALL)
         except Empty:
-            self.log.error("Failed to start advertisement.")
+            self.adv_ad.log.error("Failed to get success or failed event.")
             return False
-        if not self._bleadvertise_verify_onsuccess(event, settings_in_effect):
-            return False
+        if event[0]["name"] == adv_succ.format(advertise_callback):
+            if not self._bleadvertise_verify_onsuccess(event[0],
+                                                       settings_in_effect):
+                return False
+            else:
+                self.adv_ad.log.info("Advertisement started successfully.")
+        else:
+            self.adv_ad.log.error("Failed to start advertisement: {}".format(
+                event[0]["data"]["Error"]))
         expected_scan_event_name = scan_result.format(scan_callback)
         try:
-            event = self.scn_ad.ed.pop_event(expected_scan_event_name, self.default_timeout)
+            event = self.scn_ad.ed.pop_event(expected_scan_event_name,
+                                             self.default_timeout)
         except Empty:
-            self.log.error("Scan event not found: {}".format(expected_scan_event_name))
+            self.log.error("Scan event not found: {}".format(
+                expected_scan_event_name))
             return False
         if not self._blescan_verify_onscanresult_event(event, filters):
             return False
         if opportunistic:
             expected_scan_event_name = scan_result.format(scan_callback2)
             try:
-                event = self.scn_ad.ed.pop_event(expected_scan_event_name, self.default_timeout)
+                event = self.scn_ad.ed.pop_event(expected_scan_event_name,
+                                                 self.default_timeout)
             except Empty:
-                self.log.error("Opportunistic scan event not found: {}".format(expected_scan_event_name))
+                self.log.error("Opportunistic scan event not found: {}".format(
+                    expected_scan_event_name))
                 return False
             if not self._blescan_verify_onscanresult_event(event, filters):
                 return False
@@ -393,6 +413,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='e758bed4-9da0-4c0a-a5c1-a758ccd3c47a')
     def test_default_advertisement(self):
         """Test a default advertisement.
 
@@ -422,6 +443,7 @@
         return self._magic(params)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='01743760-7b96-4736-824b-be168aab1f9a')
     def test_settings_in_effect_suite(self):
         """Test combinations of settings with scanning and advertising.
 
@@ -446,14 +468,14 @@
         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")
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="settings_in_effect_suite")
         if failed:
             return False
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='42eea443-2465-4858-b1bf-47f2ae8294db')
     def test_filters_suite(self):
         """Test combinations of settings with scanning and advertising.
 
@@ -475,19 +497,19 @@
         Priority: 1
         """
         valid_filter_suit = self._get_combinations(self.valid_filter_variants)
-        settings = [
-            {'mode':
-             AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value}
-        ]
+        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")
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="filters_suite")
         if failed:
             return False
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='79050573-24c4-4e24-b6b1-f4a774192036')
     def test_filters_suite_opportunistic_scan(self):
         """Test combinations of settings with opportunistic scanning.
 
@@ -511,20 +533,20 @@
         """
         reset_bluetooth(self.android_devices)
         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}
-        ]
+        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")
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="filters_suite")
         if failed:
             return False
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d72c3a12-b9db-4173-a1e6-48e4dde6651a')
     def test_valid_filters(self):
         """Test combinations of settings with scanning and advertising.
 
@@ -546,19 +568,19 @@
         Priority: 1
         """
         reset_bluetooth(self.android_devices)
-        settings = [
-            {'mode':
-             AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value}
-        ]
+        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")
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="valid_filters")
         if failed:
             return False
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='dcf32660-73e6-4736-8a6e-44419bf0ed35')
     def test_valid_filters_opportunistic_scan(self):
         """Test combinations of settings with opportunistic scanning.
 
@@ -580,20 +602,20 @@
         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}
-        ]
+        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")
+        failed = self.run_generated_testcases(
+            self._magic, params, tag="valid_filters")
         if failed:
             return False
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='dc2e2543-9e5b-460d-81c8-5203eabfb015')
     def test_non_connectable_advertise_data(self):
         """Test non connectable advertisement data.
 
@@ -622,8 +644,10 @@
         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],
+            '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
index f336052..44d2629 100644
--- a/acts/tests/google/ble/filtering/UniqueFilteringTest.py
+++ b/acts/tests/google/ble/filtering/UniqueFilteringTest.py
@@ -24,6 +24,7 @@
 import time
 
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
 from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
@@ -83,6 +84,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='c3764358-cd65-451c-9a2d-4bfb6cf98f48')
     def test_scan_flush_pending_scan_results(self):
         """Test LE scan api flush pending results.
 
@@ -135,6 +137,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='4b358654-db69-4b51-98ec-7599aee2db10')
     def test_scan_trigger_on_batch_scan_results(self):
         """Test triggering batch scan results.
 
@@ -191,6 +194,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='94dbe8b2-9e7f-4da4-973e-5162b84a7ce0')
     def test_scan_flush_results_without_on_batch_scan_results_triggered(self):
         """Test that doesn't expect a batch scan result.
 
@@ -239,6 +243,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5856882e-3d82-4a6f-b110-389086b0ac41')
     def test_scan_non_existent_name_filter(self):
         """Test non-existent name filter.
 
@@ -290,6 +295,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='0a54c50b-d4ef-4d8c-8328-775781aab2c7')
     def test_scan_advertisement_with_device_service_uuid_filter_expect_no_events(
             self):
         """Test scan filtering against an advertisement with no data.
@@ -349,6 +355,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='aefbc70c-ea46-4a63-a627-6fcceb72ac9e')
     def test_scan_filtering_multiple_advertisements_manufacturer_data(self):
         """Test scan filtering against multiple varying advertisements.
 
@@ -404,6 +411,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='cdbeeb8e-895e-4d11-9e42-7b89ff6917ce')
     def test_scan_filter_device_address(self):
         """Test scan filtering of a device address.
 
@@ -470,6 +478,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='a4921d7b-cdd5-4d52-834c-f4bb85a9e2e8')
     def test_filter_simulated_ibeacon(self):
         """Test scan filtering of a simulated ibeacon.
 
@@ -497,7 +506,8 @@
         Priority: 1
         """
         manufacturer_id = 0x4c
-        self.adv_ad.droid.bleAddAdvertiseDataManufacturerId(manufacturer_id, [0x01])
+        self.adv_ad.droid.bleAddAdvertiseDataManufacturerId(manufacturer_id,
+                                                            [0x01])
         self.adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
             AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value)
         advertise_callback, advertise_data, advertise_settings = (
@@ -513,7 +523,8 @@
 
         self.scn_ad.droid.bleSetScanSettingsScanMode(
             ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
-        self.scn_ad.droid.bleSetScanFilterManufacturerData(manufacturer_id, [0x01])
+        self.scn_ad.droid.bleSetScanFilterManufacturerData(manufacturer_id,
+                                                           [0x01])
         filter_list = self.scn_ad.droid.bleGenFilterList()
         scan_settings = self.scn_ad.droid.bleBuildScanSetting()
         scan_filter = self.scn_ad.droid.bleBuildScanFilter(filter_list)
@@ -544,6 +555,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='a70a276d-7990-4754-8899-792b586ecce6')
     def test_filter_manufacturer_id_bounds(self):
         """Test scan filtering of lower and upper bounds of allowed manu data
 
@@ -607,11 +619,11 @@
                 self.log.error("Unable to find beacon advertisement.")
                 return False
             found_manufacturer_id = json.loads(event['data']['Result'][
-                    'manufacturerIdList'])
+                'manufacturerIdList'])
             if found_manufacturer_id[0] != manufacturer_id:
                 self.log.error(
-                    "Manufacturer id mismatch. Found {}, Expected {}".
-                    format(found_manufacturer_id, manufacturer_id))
+                    "Manufacturer id mismatch. Found {}, Expected {}".format(
+                        found_manufacturer_id, manufacturer_id))
                 return False
             self.scn_ad.droid.bleStopBleScan(scan_callback)
             self.adv_ad.droid.bleStopBleAdvertising(advertise_callback)
diff --git a/acts/tests/google/ble/gatt/GattConnectTest.py b/acts/tests/google/ble/gatt/GattConnectTest.py
index bc5606b..9834ea9 100644
--- a/acts/tests/google/ble/gatt/GattConnectTest.py
+++ b/acts/tests/google/ble/gatt/GattConnectTest.py
@@ -21,6 +21,7 @@
 from queue import Empty
 import time
 
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.BtEnum import BluetoothProfile
 from acts.test_utils.bt.GattEnum import GattCharacteristic
@@ -29,6 +30,7 @@
 from acts.test_utils.bt.GattEnum import MtuSize
 from acts.test_utils.bt.GattEnum import GattCbErr
 from acts.test_utils.bt.GattEnum import GattCbStrings
+from acts.test_utils.bt.GattEnum import GattPhyMask
 from acts.test_utils.bt.GattEnum import GattTransport
 from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
 from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
@@ -128,6 +130,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='8a3530a3-c8bb-466b-9710-99e694c38618')
     def test_gatt_connect(self):
         """Test GATT connection over LE.
 
@@ -169,6 +172,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='a839b505-03ac-4783-be7e-1d43129a1948')
     def test_gatt_connect_stop_advertising(self):
         """Test GATT connection over LE then stop advertising
 
@@ -225,6 +229,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='b82f91a8-54bb-4779-a117-73dc7fdb28cc')
     def test_gatt_connect_autoconnect(self):
         """Test GATT connection over LE.
 
@@ -277,7 +282,8 @@
         autoconnect = True
         bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt(
             gatt_callback, mac_address, autoconnect,
-            GattTransport.TRANSPORT_AUTO.value)
+            GattTransport.TRANSPORT_AUTO.value,
+            GattPhyMask.PHY_LE_1M_MASK)
         self.bluetooth_gatt_list.append(bluetooth_gatt)
         expected_event = GattCbStrings.GATT_CONN_CHANGE.value.format(
             gatt_callback)
@@ -291,6 +297,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='1e01838e-c4de-4720-9adf-9e0419378226')
     def test_gatt_request_min_mtu(self):
         """Test GATT connection over LE and exercise MTU sizes.
 
@@ -340,6 +347,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='c1fa3a2d-fb47-47db-bdd1-458928cd6a5f')
     def test_gatt_request_max_mtu(self):
         """Test GATT connection over LE and exercise MTU sizes.
 
@@ -389,6 +397,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='4416d483-dec3-46cb-8038-4d82620f873a')
     def test_gatt_request_out_of_bounds_mtu(self):
         """Test GATT connection over LE and exercise an out of bound MTU size.
 
@@ -439,6 +448,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='31ffb9ca-cc75-43fb-9802-c19f1c5856b6')
     def test_gatt_connect_trigger_on_read_rssi(self):
         """Test GATT connection over LE read RSSI.
 
@@ -489,6 +499,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='dee9ef28-b872-428a-821b-cc62f27ba936')
     def test_gatt_connect_trigger_on_services_discovered(self):
         """Test GATT connection and discover services of peripheral.
 
@@ -541,6 +552,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='01883bdd-0cf8-48fb-bf15-467bbd4f065b')
     def test_gatt_connect_trigger_on_services_discovered_iterate_attributes(
             self):
         """Test GATT connection and iterate peripherals attributes.
@@ -601,6 +613,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d4277bee-da99-4f48-8a4d-f81b5389da18')
     def test_gatt_connect_with_service_uuid_variations(self):
         """Test GATT connection with multiple service uuids.
 
@@ -659,6 +672,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='7d3442c5-f71f-44ae-bd35-f2569f01b3b8')
     def test_gatt_connect_in_quick_succession(self):
         """Test GATT connections multiple times.
 
@@ -710,6 +724,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='148469d9-7ab0-4c08-b2e9-7e49e88da1fc')
     def test_gatt_connect_mitm_attack(self):
         """Test GATT connection with permission write encrypted mitm.
 
@@ -816,6 +831,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='cc3fc361-7bf1-4ee2-9e46-4a27c88ce6a8')
     def test_gatt_connect_get_connected_devices(self):
         """Test GATT connections show up in getConnectedDevices
 
@@ -875,4 +891,3 @@
         self.adv_instances.append(adv_callback)
         return self._orchestrate_gatt_disconnection(bluetooth_gatt,
                                                     gatt_callback)
-
diff --git a/acts/tests/google/ble/gatt/GattNotifyTest.py b/acts/tests/google/ble/gatt/GattNotifyTest.py
index 58b0180..cf7d069 100644
--- a/acts/tests/google/ble/gatt/GattNotifyTest.py
+++ b/acts/tests/google/ble/gatt/GattNotifyTest.py
@@ -17,6 +17,7 @@
 This test script exercises GATT notify/indicate procedures.
 """
 
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
 from acts.test_utils.bt.GattEnum import GattCharacteristic
@@ -29,6 +30,7 @@
 
 class GattNotifyTest(GattConnectedBaseTest):
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='e0ba60af-c1f2-4516-a5d5-89e2def0c757')
     def test_notify_char(self):
         """Test notify characteristic value.
 
@@ -79,14 +81,13 @@
         event = self._client_wait(GattEvent.DESC_WRITE)
 
         #set notified value
-        notified_value = [1,5,9,7,5,3,6,4,8,2]
+        notified_value = [1, 5, 9, 7, 5, 3, 6, 4, 8, 2]
         self.per_ad.droid.gattServerCharacteristicSetValue(
             self.notifiable_char_index, notified_value)
 
         #send notification
         self.per_ad.droid.gattServerNotifyCharacteristicChanged(
-            self.gatt_server, bt_device_id,
-            self.notifiable_char_index, False)
+            self.gatt_server, bt_device_id, self.notifiable_char_index, False)
 
         #wait for client to receive the notification
         event = self._client_wait(GattEvent.CHAR_CHANGE)
diff --git a/acts/tests/google/ble/gatt/GattReadTest.py b/acts/tests/google/ble/gatt/GattReadTest.py
index 70f6b6e..764c697 100644
--- a/acts/tests/google/ble/gatt/GattReadTest.py
+++ b/acts/tests/google/ble/gatt/GattReadTest.py
@@ -17,6 +17,7 @@
 This test script exercises different GATT read procedures.
 """
 
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
 from acts.test_utils.bt.GattEnum import GattCharacteristic
@@ -29,6 +30,7 @@
 
 class GattReadTest(GattConnectedBaseTest):
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='ed8523f4-0eb8-4d14-b558-d3c28902f8bb')
     def test_read_char(self):
         """Test read characteristic value.
 
@@ -78,6 +80,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5916a78d-3db8-4df2-9b96-b25e99096e0d')
     def test_read_long_char(self):
         """Test read long characteristic value.
 
@@ -145,3 +148,51 @@
                          "Read value shall be equal to value sent from server")
 
         return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_read_using_char_uuid(self):
+        """Test read using characteristic UUID.
+
+        Test GATT read value using characteristic UUID.
+
+        Steps:
+        1. Central: send read by UUID request.
+        2. Peripheral: receive read request .
+        3. Peripheral: send read response with status 0 (success), and
+           characteristic value.
+        4. Central: receive read response, verify it's conent matches what was
+           sent
+
+        Expected Result:
+        Verify that read request/response is properly delivered.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, GATT, Characteristic
+        Priority: 0
+        """
+        self.cen_ad.droid.gattClientReadUsingCharacteristicUuid(
+            self.bluetooth_gatt, self.READABLE_CHAR_UUID, 0x0001, 0xFFFF)
+
+        event = self._server_wait(GattEvent.CHAR_READ_REQ)
+
+        request_id = event['data']['requestId']
+        self.assertEqual(0, event['data']['offset'], "offset should be 0")
+
+        bt_device_id = 0
+        status = 0
+        char_value = [1, 2, 3, 4, 5, 6, 7, 20]
+        offset = 0
+        self.per_ad.droid.gattServerSendResponse(self.gatt_server,
+                                                 bt_device_id, request_id,
+                                                 status, offset, char_value)
+
+        event = self._client_wait(GattEvent.CHAR_READ)
+        self.assertEqual(status, event["data"]["Status"],
+                         "Write status should be 0")
+        self.assertEqual(char_value, event["data"]["CharacteristicValue"],
+                         "Read value shall be equal to value sent from server")
+
+        return True
diff --git a/acts/tests/google/ble/gatt/GattWriteTest.py b/acts/tests/google/ble/gatt/GattWriteTest.py
index b19fa6c..2f3c896 100644
--- a/acts/tests/google/ble/gatt/GattWriteTest.py
+++ b/acts/tests/google/ble/gatt/GattWriteTest.py
@@ -17,6 +17,7 @@
 This test script exercises different GATT write procedures.
 """
 
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.GattConnectedBaseTest import GattConnectedBaseTest
 from acts.test_utils.bt.GattEnum import GattCharacteristic
@@ -30,8 +31,8 @@
 
 
 class GattWriteTest(GattConnectedBaseTest):
-
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='513f4cef-489e-4bb6-96cc-c298c589225c')
     def test_write_char(self):
         """Test write characteristic value
 
@@ -90,6 +91,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='329dbef8-1b54-47e2-a388-b33ef9384464')
     def test_write_descr(self):
         """Test write descriptor value
 
@@ -144,6 +146,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='85757307-5bb1-43e5-9331-f1d7bdcbd6a0')
     def test_write_char_no_resp(self):
         """Test write characteristic value
 
@@ -194,6 +197,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='0bf0182a-c315-4160-81be-9ce09f93608b')
     def test_write_characteristic_long_no_resp(self):
         """Test write characteristic value
 
@@ -249,6 +253,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='b80f1b5a-a223-441e-a6ed-d3c284c83cc7')
     def test_write_characteristic_value_longer_than_mtu_request(self):
         """Test writing characteristic value longer than what mtu limts
 
@@ -312,8 +317,8 @@
             self.test_service_index, self.WRITABLE_CHAR_UUID)
 
         event = self._server_wait(GattEvent.CHAR_WRITE_REQ)
-        self.log.info("Received value with mtu = max MTU: {}".format(
-            event['data']['value']))
+        self.log.info("Received value with mtu = max MTU: {}".format(event[
+            'data']['value']))
 
         # check the data received by Peripheral shall be truncated to
         # (mtu - GattCharacteristicAttrLength.MTU_ATTR_2) bytes
@@ -337,8 +342,8 @@
             self.test_service_index, self.WRITABLE_CHAR_UUID)
 
         event = self._server_wait(GattEvent.CHAR_WRITE_REQ)
-        self.log.info("Data received when mtu = max MTU - 1: {}".format(
-            event['data']['value']))
+        self.log.info("Data received when mtu = max MTU - 1: {}".format(event[
+            'data']['value']))
 
         # check the data received by Peripheral shall be truncated to
         # (mtu - GattCharacteristicAttrLength.MTU_ATTR_2) bytes
@@ -350,6 +355,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='319eee6d-22d9-4498-bb15-21d0018e45e6')
     def test_write_characteristic_stress(self):
         """Stress test write characteristic value
 
@@ -425,6 +431,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='b19d42dc-58ba-4b20-b6c1-6628e7d21de4')
     def test_write_descriptor_stress(self):
         """Stress test write descriptor value
 
@@ -493,6 +500,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='74c147eb-2702-4cd8-be1f-efff3e9eaa6c')
     def test_write_characteristic_no_resp_stress(self):
         """Stress test write characteristic value
 
diff --git a/acts/tests/google/ble/power/BleScanPowerTest.py b/acts/tests/google/ble/power/BleScanPowerTest.py
index 2512fd2..1f430a2 100644
--- a/acts/tests/google/ble/power/BleScanPowerTest.py
+++ b/acts/tests/google/ble/power/BleScanPowerTest.py
@@ -22,108 +22,49 @@
 import json
 import os
 
-from acts import asserts
-from acts.controllers import monsoon
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
 from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts.test_utils.bt.bt_test_utils import disable_bluetooth
 from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
-from acts.test_utils.tel.tel_test_utils import set_phone_screen_on
-from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.utils import create_dir
-from acts.utils import force_airplane_mode
-from acts.utils import get_current_human_time
-from acts.utils import set_location_service
-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 sync_device_time
-
-# Monsoon output Voltage in V
-MONSOON_OUTPUT_VOLTAGE = 4.2
-# Monsoon output max current in A
-MONSOON_MAX_CURRENT = 7.8
-
-# Power mesaurement sampling rate in Hz
-BLE_SCAN_POWER_SAMPLING_RATE = 20
-# Power mesaurement sample duration in seconds
-BLE_SCAN_POWER_SAMPLE_TIME = 60
-# Power mesaurement start time in seconds
-BLE_SCAN_START_TIME = 30
-# BLE scanning time in seconds
-BLE_SCAN_DURATION = 5
-# BLE no scanning time in seconds
-BLE_NOSCAN_DURATION = 5
-
-start_pmc_cmd = ("am start -n com.android.pmc/com.android.pmc."
-                 "PMCMainActivity")
-pmc_base_cmd = ("am broadcast -a com.android.pmc.BLESCAN --es ScanMode ")
+from acts.test_utils.bt.PowerBaseTest import PowerBaseTest
 
 
-class BleScanPowerTest(BluetoothBaseTest):
-    SCREEN_TIME_OFF = 10
+class BleScanPowerTest(PowerBaseTest):
+    # Repetitions for scan and idle
+    REPETITIONS_40 = 40
+    REPETITIONS_360 = 360
+
+    # Power measurement start time in seconds
+    SCAN_START_TIME = 60
+    # BLE scanning time in seconds
+    SCAN_TIME_60 = 60
+    SCAN_TIME_5 = 5
+    # BLE no scanning time in seconds
+    IDLE_TIME_30 = 30
+    IDLE_TIME_5 = 5
+
+    PMC_BASE_CMD = ("am broadcast -a com.android.pmc.BLESCAN --es ScanMode ")
 
     def setup_class(self):
-        self.ad = self.android_devices[0]
-        self.mon = self.monsoons[0]
-        self.mon.set_voltage(MONSOON_OUTPUT_VOLTAGE)
-        self.mon.set_max_current(MONSOON_MAX_CURRENT)
-        # Monsoon phone
-        self.mon.attach_device(self.ad)
-        self.monsoon_log_path = os.path.join(self.log_path, "MonsoonLog")
-        create_dir(self.monsoon_log_path)
+        super(BleScanPowerTest, self).setup_class()
+        # Get power test device serial number
+        power_test_device_serial = self.user_params["PowerTestDevice"]
+        # If there are multiple devices in the shield box turn off
+        # all of them except the one for the power testing
+        if len(self.android_devices) > 1:
+            for ad in self.android_devices:
+                if ad.serial != power_test_device_serial[0]:
+                    self.ad.log.info("Disable BT for %s != %s", ad.serial,
+                                     power_test_device_serial[0])
+                    disable_bluetooth(ad.droid)
 
-        asserts.assert_true(
-            self.mon.usb("auto"),
-            "Failed to turn USB mode to auto on monsoon.")
-
-        sync_device_time(self.ad)
-
-        asserts.assert_true(
-            force_airplane_mode(self.ad, True),
-            "Can not turn on airplane mode on: %s" % self.ad.serial)
-        asserts.assert_true(
-            bluetooth_enabled_check(self.ad),
-            "Failed to set Bluetooth state to enabled")
-        set_location_service(self.ad, False)
-        set_adaptive_brightness(self.ad, False)
-        set_ambient_display(self.ad, False)
-        self.ad.adb.shell("settings put system screen_brightness 0")
-        set_auto_rotate(self.ad, False)
-        set_phone_screen_on(self.log, self.ad, self.SCREEN_TIME_OFF)
-
-        # Start pmc app.
-        self.ad.adb.shell(start_pmc_cmd)
-        self.ad.adb.shell("setprop log.tag.PMC VERBOSE")
-        wutils.wifi_toggle_state(self.ad, False)
-
-    def _save_logs_for_power_test(self, monsoon_result):
-        """utility function to save power data into log file.
-
-        "whether monsoon_log_for_power_test" needs to be set
-        in config file in order to save power data into a file
-        If bug_report_for_power_test is defined in test config file
-        it will also save a bug report.
-
-        Steps:
-        1. Save power data into a file if being configed.
-        2. Create a bug report if being configed
-
-        Args:
-            monsoon_result: power data object
-
-        Returns:
-            If success, return None.
-            if fail, exception will be thrown
-        """
-        current_time = get_current_human_time()
-        file_name = "%s_%s" % (self.current_test_name, current_time)
-        monsoon_result.save_to_text_file(
-            [monsoon_result], os.path.join(self.monsoon_log_path, file_name))
-
-        self.ad.take_bug_report(self.current_test_name, current_time)
-
-    def _test_power_for_scan(self, scan_mode):
+    def _measure_power_for_scan_n_log_data(self,
+                                           scan_mode,
+                                           scan_time,
+                                           idle_time,
+                                           repetitions,
+                                           remove_idle_data=True):
         """utility function for power test with BLE scan.
 
         Steps:
@@ -131,41 +72,54 @@
         2. Send the adb shell command to PMC
         3. PMC start first alarm to start scan
         4. After first alarm triggered it start the second alarm to stop scan
-        5. Save the power usage data into log file
+        5. Repeat the scan/idle cycle for the number of repetitions
+        6. Save the power usage data into log file
 
         Args:
-            scan_mode: scan mode
+            scan_mode: Scan mode
+            scan_time: Time duration for scanning
+            idle_time: Time duration for idle after scanning
+            repetitions:  The number of cycles of scanning/idle
+            remove_idle_data: Boolean to indicate whether to include idle data
+                              in average calculation
 
         Returns:
-            If success, return None.
-            if fail, error will be logged.
+            None
         """
 
-        msg = "%s%s --es StartTime %d --es ScanTime %d" % (
-            pmc_base_cmd, scan_mode, BLE_SCAN_START_TIME,
-            BLE_SCAN_POWER_SAMPLE_TIME)
+        first_part_msg = "%s%s --es StartTime %d --es ScanTime %d" % (
+            self.PMC_BASE_CMD, scan_mode, self.SCAN_START_TIME, scan_time)
+        msg = "%s --es NoScanTime %d --es Repetitions %d" % (
+            first_part_msg, idle_time, repetitions)
+
         self.ad.log.info("Send broadcast message: %s", msg)
         self.ad.adb.shell(msg)
         # Start the power measurement
-        result = self.mon.measure_power(
-            BLE_SCAN_POWER_SAMPLING_RATE, BLE_SCAN_POWER_SAMPLE_TIME,
-            self.current_test_name, BLE_SCAN_START_TIME)
+        sample_time = (scan_time + idle_time) * repetitions
+        result = self.mon.measure_power(self.POWER_SAMPLING_RATE, sample_time,
+                                        self.current_test_name,
+                                        self.SCAN_START_TIME)
 
-        self._save_logs_for_power_test(result)
+        if remove_idle_data:
+            self.save_logs_for_power_test(result, scan_time, idle_time)
+        else:
+            self.save_logs_for_power_test(result, scan_time, 0)
 
     @BluetoothBaseTest.bt_test_wrap
     def test_power_for_scan_w_low_latency(self):
         """Test power usage when scan with low latency.
 
         Tests power usage when the device scans with low latency mode
-        for 60 seconds where there are no advertisements.
+        for 60 seconds and then idle for 30 seconds, repeat for 60 minutes
+        where there are no advertisements.
 
         Steps:
         1. Prepare adb shell command
         2. Send the adb shell command to PMC
         3. PMC start first alarm to start scan
         4. After first alarm triggered it start the second alarm to stop scan
-        5. Save the power usage data into log file
+        5. Repeat the cycle for 60 minutes
+        6. Save the power usage data into log file
 
         Expected Result:
         power consumption results
@@ -173,22 +127,25 @@
         TAGS: LE, Scanning, Power
         Priority: 3
         """
-        self._test_power_for_scan(
-            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value)
+        self._measure_power_for_scan_n_log_data(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value,
+            self.SCAN_TIME_60, self.IDLE_TIME_30, self.REPETITIONS_40)
 
     @BluetoothBaseTest.bt_test_wrap
     def test_power_for_scan_w_balanced(self):
         """Test power usage when scan with balanced mode.
 
         Tests power usage when the device scans with balanced mode
-        for 60 seconds where there are no advertisements.
+        for 60 seconds and then idle for 30 seconds, repeat for 60 minutes
+        where there are no advertisements.
 
         Steps:
         1. Prepare adb shell command
         2. Send the adb shell command to PMC
         3. PMC start first alarm to start scan
         4. After first alarm triggered it start the second alarm to stop scan
-        5. Save the power usage data into log file
+        5. Repeat the cycle for 60 minutes
+        6. Save the power usage data into log file
 
         Expected Result:
         power consumption results
@@ -196,22 +153,25 @@
         TAGS: LE, Scanning, Power
         Priority: 3
         """
-        self._test_power_for_scan(
-            ScanSettingsScanMode.SCAN_MODE_BALANCED.value)
+        self._measure_power_for_scan_n_log_data(
+            ScanSettingsScanMode.SCAN_MODE_BALANCED.value, self.SCAN_TIME_60,
+            self.IDLE_TIME_30, self.REPETITIONS_40)
 
     @BluetoothBaseTest.bt_test_wrap
     def test_power_for_scan_w_low_power(self):
         """Test power usage when scan with low power.
 
         Tests power usage when the device scans with low power mode
-        for 60 seconds where there are no advertisements.
+        for 60 seconds and then idle for 30 seconds, repeat for 60 minutes
+        where there are no advertisements.
 
         Steps:
         1. Prepare adb shell command
         2. Send the adb shell command to PMC
         3. PMC start first alarm to start scan
         4. After first alarm triggered it start the second alarm to stop scan
-        5. Save the power usage data into log file
+        5. Repeat the cycle for 60 minutes
+        6. Save the power usage data into log file
 
         Expected Result:
         power consumption results
@@ -219,14 +179,15 @@
         TAGS: LE, Scanning, Power
         Priority: 3
         """
-        self._test_power_for_scan(
-            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value)
+        self._measure_power_for_scan_n_log_data(
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value, self.SCAN_TIME_60,
+            self.IDLE_TIME_30, self.REPETITIONS_40)
 
     @BluetoothBaseTest.bt_test_wrap
     def test_power_for_intervaled_scans_w_balanced(self):
-        """Test power usage when 12 intervaled scans with balanced mode
+        """Test power usage when intervaled scans with balanced mode
 
-        Tests power usage when the device perform 12 intervaled scans with
+        Tests power usage when the device perform multiple intervaled scans with
         balanced mode for 5 seconds each where there are no advertisements.
 
         Steps:
@@ -235,7 +196,8 @@
         3. PMC start first alarm to start scan
         4. After first alarm triggered it starts the second alarm to stop scan
         5. After second alarm triggered it starts the third alarm to start scan
-        6. Repeat the alarms until 12 scans are done
+        6. Repeat the alarms until 360 scans are done
+        7. Save the power usage data into log file
 
         Expected Result:
         power consumption results
@@ -243,18 +205,58 @@
         TAGS: LE, Scanning, Power
         Priority: 3
         """
-        msg1 = "%s%s --es StartTime %d --es ScanTime %d" % (
-            pmc_base_cmd, ScanSettingsScanMode.SCAN_MODE_BALANCED.value,
-            BLE_SCAN_START_TIME, BLE_SCAN_DURATION)
-        msg = "%s --es NoScanTime %d --es Repetitions %d" % (
-            msg1, BLE_NOSCAN_DURATION, 12)
+        self._measure_power_for_scan_n_log_data(
+            ScanSettingsScanMode.SCAN_MODE_BALANCED.value, self.SCAN_TIME_5,
+            self.IDLE_TIME_5, self.REPETITIONS_360)
 
-        self.ad.log.info("Send broadcast message: %s", msg)
-        self.ad.adb.shell(msg)
-        # Start the power measurement
-        result = self.mon.measure_power(
-            BLE_SCAN_POWER_SAMPLING_RATE,
-            (BLE_SCAN_DURATION + BLE_NOSCAN_DURATION) * 12,
-            self.current_test_name, BLE_SCAN_START_TIME)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_power_for_intervaled_scans_w_low_latency(self):
+        """Test power usage when intervaled scans with low latency mode
 
-        self._save_logs_for_power_test(result)
+        Tests power usage when the device perform multiple intervaled scans with
+        low latency mode for 5 seconds each where there are no advertisements.
+
+        Steps:
+        1. Prepare adb shell command
+        2. Send the adb shell command to PMC
+        3. PMC start first alarm to start scan
+        4. After first alarm triggered it starts the second alarm to stop scan
+        5. After second alarm triggered it starts the third alarm to start scan
+        6. Repeat the alarms until 360 scans are done
+        7. Save the power usage data into log file
+
+        Expected Result:
+        power consumption results
+
+        TAGS: LE, Scanning, Power
+        Priority: 3
+        """
+        self._measure_power_for_scan_n_log_data(
+            ScanSettingsScanMode.SCAN_MODE_LOW_LATENCY.value, self.SCAN_TIME_5,
+            self.IDLE_TIME_5, self.REPETITIONS_360)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_power_for_intervaled_scans_w_low_power(self):
+        """Test power usage when intervaled scans with low power mode
+
+        Tests power usage when the device perform multiple intervaled scans with
+        low power mode for 5 seconds each where there are no advertisements.
+
+        Steps:
+        1. Prepare adb shell command
+        2. Send the adb shell command to PMC
+        3. PMC start first alarm to start scan
+        4. After first alarm triggered it starts the second alarm to stop scan
+        5. After second alarm triggered it starts the third alarm to start scan
+        6. Repeat the alarms until 360 scans are done
+        7. Save the power usage data into log file
+
+        Expected Result:
+        power consumption results
+
+        TAGS: LE, Scanning, Power
+        Priority: 3
+        """
+        self._measure_power_for_scan_n_log_data(
+            ScanSettingsScanMode.SCAN_MODE_LOW_POWER.value, self.SCAN_TIME_5,
+            self.IDLE_TIME_5, self.REPETITIONS_360)
diff --git a/acts/tests/google/ble/power/GattPowerTest.py b/acts/tests/google/ble/power/GattPowerTest.py
new file mode 100644
index 0000000..31b07a9
--- /dev/null
+++ b/acts/tests/google/ble/power/GattPowerTest.py
@@ -0,0 +1,179 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+This test script exercises power test scenarios for GATT writing.
+This test script was designed with this setup in mind:
+Shield box one: Two Android Devices and Monsoon tool box
+"""
+
+import json
+import os
+
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.PowerBaseTest import PowerBaseTest
+from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
+from acts.utils import get_current_human_time
+from acts.utils import sync_device_time
+
+
+class GattPowerTest(PowerBaseTest):
+    """Class for Gatt Power Test"""
+    # Time to start GATT write
+    START_TIME = 30
+    # Repetitions
+    REPETITIONS_40 = 40
+    REPETITIONS_1 = 1
+    # Time for GATT writing
+    WRITE_TIME_60 = 60
+    WRITE_TIME_3600 = 3600
+    # Time for idle
+    IDLE_TIME_30 = 30
+    IDLE_TIME_0 = 0
+    # Base commands for PMC
+    PMC_GATT_CMD = ("am broadcast -a com.android.pmc.GATT ")
+    GATT_SERVER_MSG = "%s--es GattServer 1" % (PMC_GATT_CMD)
+
+    def __init__(self, controllers):
+        PowerBaseTest.__init__(self, controllers)
+
+        self.cen_ad = self.android_devices[0]
+        self.per_ad = self.android_devices[1]
+
+    def setup_class(self):
+        super(GattPowerTest, self).setup_class()
+        if not bluetooth_enabled_check(self.per_ad):
+            self.log.error("Failed to turn on Bluetooth on peripheral")
+
+        # Start PMC app for peripheral here
+        # since base class has started PMC for central device
+        self.per_ad.adb.shell(PowerBaseTest.START_PMC_CMD)
+        self.per_ad.adb.shell(self.PMC_VERBOSE_CMD)
+
+    def _measure_power_for_gatt_n_log_data(self, write_time, idle_time,
+                                           repetitions):
+        """Utility function for power test with GATT write.
+
+        Steps:
+        1. Prepare adb shell command for GATT server
+        2. Send the adb shell command to PMC to startup GATT Server
+        3. Prepare adb shell command for GATT Client
+        4. Send the adb shell command to PMC to startup GATT Client
+        5. PMC will start first alarm on GATT Client to start
+           GATT write continuousely for "write_time" seconds
+        6. After finishing writing for write_time it will stop for
+           for "idle_time" seconds
+        7  Repeat the write/idle cycle for "repetitions" times
+        8. Save the power usage data into log file
+
+        Args:
+            write_time: time(sec) duration for writing GATT characteristic
+            idle_time: time(sec) of idle (not write)
+            repetitions: number of repetitions of writing cycles
+
+        Returns:
+            None
+        """
+        # Send message to Gatt Server
+        self.per_ad.log.info("Send broadcast message to GATT Server: %s",
+                             self.GATT_SERVER_MSG)
+        self.per_ad.adb.shell(self.GATT_SERVER_MSG)
+
+        # Send message to Gatt Client
+        first_part_msg = "%s--es StartTime %d --es WriteTime %d" % (
+            self.PMC_GATT_CMD, self.START_TIME, write_time)
+        clientmsg = "%s --es IdleTime %d --es Repetitions %d" % (
+            first_part_msg, idle_time, repetitions)
+        self.cen_ad.log.info("Send broadcast message to GATT Client: %s",
+                             clientmsg)
+        self.cen_ad.adb.shell(clientmsg)
+
+        sample_time = (write_time + idle_time) * repetitions
+        # Start the power measurement
+        result = self.mon.measure_power(self.POWER_SAMPLING_RATE, sample_time,
+                                        self.current_test_name,
+                                        self.START_TIME)
+        # Calculate average and save power data into a file
+        self.save_logs_for_power_test(result, write_time, idle_time)
+        # Take bug report for peripheral device
+        current_time = get_current_human_time()
+        self.per_ad.take_bug_report(self.current_test_name, current_time)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_power_for_60_sec_n_30_sec_idle_gatt_write(self):
+        """Test power usage when do 60 sec GATT write & 30 sec idle
+
+        Tests power usage when the test device do 60 sec GATT write
+        and 30 sec idle with max MTU bytes after being connected.
+        After each write GATT server will send a response
+        back to GATT client so GATT client can do another write.
+
+        Steps:
+        1. Prepare adb shell command for GATT server
+        2. Send the adb shell command to PMC to startup GATT Server
+        3. Prepare adb shell command for GATT Client
+        4. Send the adb shell command to PMC to startup GATT Client
+        5. PMC will start first alarm on GATT Client
+        6. Start power measurement
+        7. Alarm will be triggered to start GATT write for 60 second
+        8. Then it will be idle for 30 seconds
+        9. Reconnect after idle time
+        10. Repeat the cycles for 60 minutes
+        11. End power measurement
+        12. Save the power usage data into log file
+
+        Expected Result:
+        power consumption results
+
+        TAGS: LE, GATT, Power
+        Priority: 3
+        """
+        self._measure_power_for_gatt_n_log_data(
+            self.WRITE_TIME_60, self.IDLE_TIME_30, self.REPETITIONS_40)
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_power_for_60_min_non_stop_gatt_write(self):
+        """Test power usage when do a single GATT write.
+
+        Tests power usage when the test device do 60 minutes of GATT write with
+        max MTU bytes after being connected. After each write GATT server will
+        send a response back to GATT client so GATT client can do another write.
+
+        Steps:
+        1. Prepare adb shell command for GATT server
+        2. Send the adb shell command to PMC to startup GATT Server
+        3. Prepare adb shell command for GATT Client
+        4. Send the adb shell command to PMC to startup GATT Client
+        5. PMC will start first alarm on GATT Client to start GATT write
+        6. Start power measurement
+        7. GATT server gets the write request after GATT Client sends a write
+        8. GATT server sends a response back to GATT Client
+        9. After GATT Client receive the response from GATT Server
+           it will check if time reaches 60 minutes.
+           if not it will write another characteristic
+           otherwise it will stop writing
+        10. Stop power measurement
+        11. Save the power usage data into log file
+
+        Expected Result:
+        power consumption results
+
+        TAGS: LE, GATT, Power
+        Priority: 3
+        """
+        self._measure_power_for_gatt_n_log_data(
+            self.WRITE_TIME_3600, self.IDLE_TIME_0, self.REPETITIONS_1)
diff --git a/acts/tests/google/ble/scan/BleBackgroundScanTest.py b/acts/tests/google/ble/scan/BleBackgroundScanTest.py
index 4114239..3636048 100644
--- a/acts/tests/google/ble/scan/BleBackgroundScanTest.py
+++ b/acts/tests/google/ble/scan/BleBackgroundScanTest.py
@@ -19,6 +19,7 @@
 
 from queue import Empty
 
+from acts.test_decorators import test_tracker_info
 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
@@ -38,6 +39,9 @@
     active_scan_callback_list = []
     active_adv_callback_list = []
 
+    bluetooth_le_on = "BleStateChangedOn"
+    bluetooth_le_off = "BleStateChangedOff"
+
     def __init__(self, controllers):
         BluetoothBaseTest.__init__(self, controllers)
         self.scn_ad = self.android_devices[0]
@@ -47,7 +51,7 @@
         if (self.scn_ad.droid.bluetoothGetLeState() ==
                 BluetoothAdapterState.STATE_OFF.value):
             self.scn_ad.droid.bluetoothEnableBLE()
-            self.scn_ad.ed.pop_event("BleStateChangedOn")
+            self.scn_ad.ed.pop_event(self.bluetooth_le_on)
         for a in self.android_devices:
             a.ed.clear_all_events()
         return True
@@ -76,6 +80,7 @@
             return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='4d13c3a8-1805-44ef-a92a-e385540767f1')
     def test_background_scan(self):
         """Test generic background scan.
 
@@ -117,7 +122,7 @@
             return False
         self.scn_ad.droid.bluetoothEnableBLE()
         try:
-            self.scn_ad.ed.pop_event(bluetooth_off, self.default_timeout*2)
+            self.scn_ad.ed.pop_event(bluetooth_off, self.default_timeout * 2)
         except Empty:
             self.log.error("Bluetooth On event not found. Expected {}".format(
                 bluetooth_on))
@@ -130,11 +135,13 @@
         try:
             self.scn_ad.ed.pop_event(expected_event, self.default_timeout)
         except Empty:
-            self.log.error("Scan Result event not found. Expected {}".format(expected_event))
+            self.log.error("Scan Result event not found. Expected {}".format(
+                expected_event))
             return False
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='9c4577f8-5e06-4034-b977-285956734974')
     def test_background_scan_ble_disabled(self):
         """Test background LE scanning with LE disabled.
 
@@ -176,7 +183,8 @@
             try:
                 self.scn_ad.ed.pop_event(expected_event, self.default_timeout)
             except Empty:
-                self.log.error("Scan Result event not found. Expected {}".format(expected_event))
+                self.log.error("Scan Result event not found. Expected {}".
+                               format(expected_event))
                 return False
             self.log.info("Was able to start background scan even though ble "
                           "was disabled.")
@@ -185,3 +193,113 @@
             self.log.info(
                 "Was not able to start a background scan as expected.")
         return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='0bdd1764-3dc6-4a82-b041-76e48ed0f424')
+    def test_airplane_mode_disables_ble(self):
+        """Try to start LE mode in Airplane Mode.
+
+        This test will enable airplane mode, then attempt to start LE scanning
+        mode.  This should result in bluetooth still being turned off, LE
+        not enabled.
+
+        Steps:
+        1. Start LE only mode.
+        2. Bluetooth should be in LE ONLY mode
+        2. Turn on airplane mode.
+        3. Bluetooth should be OFF
+        4. Try to start LE only mode.
+        5. Bluetooth should stay in OFF mode (LE only start should fail)
+        6. Turn off airplane mode.
+        7. Bluetooth should be OFF.
+
+        Expected Result:
+        No unexpected bluetooth state changes.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: LE, Airplane
+        Priority: 1
+        """
+        ble_state_error_msg = "Bluetooth LE State not OK {}. Expected {} got {}"
+        # Enable BLE always available (effectively enabling BT in location)
+        self.scn_ad.adb.shell(
+            "shell settings put global ble_scan_always_enabled 1")
+
+        self.scn_ad.droid.bluetoothToggleState(False)
+        try:
+            self.scn_ad.ed.pop_event(bluetooth_off, self.default_timeout)
+        except Empty:
+            self.log.error("Bluetooth Off event not found. Expected {}".format(
+                bluetooth_off))
+            return False
+
+        # Sleep because LE turns off after the bluetooth off event fires
+        time.sleep(self.default_timeout)
+        state = self.scn_ad.droid.bluetoothGetLeState()
+        if state != BluetoothAdapterState.STATE_OFF.value:
+            self.log.error(
+                ble_state_error_msg.format(
+                    "after BT Disable", BluetoothAdapterState.STATE_OFF.value,
+                    state))
+            return False
+
+        # TODO: BleStateChangedOn got generated as we shut off bluetooth above?
+        self.scn_ad.ed.clear_all_events()
+        result = self.scn_ad.droid.bluetoothEnableBLE()
+        try:
+            self.scn_ad.ed.pop_event(self.bluetooth_le_on,
+                                     self.default_timeout)
+        except Empty:
+            self.log.error("Bluetooth LE On event not found. Expected {}".
+                           format(self.bluetooth_le_on))
+            return False
+        state = self.scn_ad.droid.bluetoothGetLeState()
+        if state != BluetoothAdapterState.STATE_BLE_ON.value:
+            self.log.error(
+                ble_state_error_msg.format(
+                    "before Airplane Mode OFF",
+                    BluetoothAdapterState.STATE_BLE_ON.value, state))
+            return False
+
+        self.scn_ad.droid.bluetoothListenForBleStateChange()
+        self.scn_ad.droid.connectivityToggleAirplaneMode(True)
+        try:
+            self.scn_ad.ed.pop_event(self.bluetooth_le_off,
+                                     self.default_timeout)
+        except Empty:
+            self.log.error("Bluetooth LE Off event not found. Expected {}".
+                           format(self.bluetooth_le_off))
+            return False
+        state = self.scn_ad.droid.bluetoothGetLeState()
+        if state != BluetoothAdapterState.STATE_OFF.value:
+            self.log.error(
+                ble_state_error_msg.format(
+                    "after Airplane Mode ON",
+                    BluetoothAdapterState.STATE_OFF.value, state))
+            return False
+        result = self.scn_ad.droid.bluetoothEnableBLE()
+        if result:
+            self.log.error(
+                "Bluetooth Enable command succeded when it should have failed (in airplane mode)"
+            )
+            return False
+        state = self.scn_ad.droid.bluetoothGetLeState()
+        if state != BluetoothAdapterState.STATE_OFF.value:
+            self.log.error(
+                "Bluetooth LE State not OK after attempted enable. Expected {} got {}".
+                format(BluetoothAdapterState.STATE_OFF.value, state))
+            return False
+        self.scn_ad.droid.connectivityToggleAirplaneMode(False)
+        # Sleep to let Airplane Mode disable propogate through the system
+        time.sleep(self.default_timeout)
+        state = self.scn_ad.droid.bluetoothGetLeState()
+        if state != BluetoothAdapterState.STATE_OFF.value:
+            self.log.error(
+                ble_state_error_msg.format(
+                    "after Airplane Mode OFF",
+                    BluetoothAdapterState.STATE_OFF.value, state))
+            return False
+        return True
diff --git a/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py b/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py
index e65d266..bf59635 100644
--- a/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py
+++ b/acts/tests/google/ble/scan/BleOnLostOnFoundTest.py
@@ -18,6 +18,7 @@
 """
 
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode
 from acts.test_utils.bt.BleEnum import ScanSettingsCallbackType
@@ -75,6 +76,7 @@
             return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='9bd7fd09-71c9-4623-90f0-9a895eb37409')
     def test_onlost_onfound_defaults(self):
         """Test generic onlost/onfound defaults.
 
@@ -126,23 +128,23 @@
         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))
+                "Found Callbacreset_bluetoothkType:{}, Expected CallbackType:{}".
+                format(found_callback_type, ScanSettingsCallbackType.
+                       CALLBACK_TYPE_FIRST_MATCH.value))
             return False
         self.adv_ad.droid.bleStopBleAdvertising(adv_callback)
         event = self.scn_ad.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))
+            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
+    @test_tracker_info(uuid='10b48dc9-0c2a-46a3-8890-5cde3004a996')
     def test_onlost_onfound_match_mode_sticky(self):
         """Test generic onlost/onfound in sticky mode.
 
@@ -193,24 +195,23 @@
         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))
+            self.log.info("Found CallbackType:{}, Expected CallbackType:{}".
+                          format(found_callback_type, ScanSettingsCallbackType.
+                                 CALLBACK_TYPE_FIRST_MATCH.value))
             return False
         self.adv_ad.droid.bleStopBleAdvertising(adv_callback)
         event = self.scn_ad.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))
+            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
+    @test_tracker_info(uuid='4fefed82-7800-41be-8272-aac076640fed')
     def test_onlost_onfound_match_num_few(self):
         """Test generic onlost/onfound num few.
 
@@ -261,19 +262,17 @@
         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))
+            self.log.info("Found CallbackType:{}, Expected CallbackType:{}".
+                          format(found_callback_type, ScanSettingsCallbackType.
+                                 CALLBACK_TYPE_FIRST_MATCH.value))
             return False
         self.adv_ad.droid.bleStopBleAdvertising(adv_callback)
         event = self.scn_ad.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))
+            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
index 3408449..a26311e 100644
--- a/acts/tests/google/ble/scan/BleOpportunisticScanTest.py
+++ b/acts/tests/google/ble/scan/BleOpportunisticScanTest.py
@@ -23,6 +23,7 @@
 
 from queue import Empty
 
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
 from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
@@ -76,6 +77,7 @@
             return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='6bccfbea-3734-4504-8ea9-3511ad17a3e0')
     def test_scan_result_no_advertisement(self):
         """Test opportunistic scan with no advertisement.
 
@@ -110,6 +112,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='8430bc57-925c-4b70-a62e-cd34df264ca1')
     def test_batch_scan_result_no_advertisement(self):
         """Test batch opportunistic scan without an advertisement.
 
@@ -149,6 +152,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='4613cb67-0f54-494e-8a56-2e8ce56fad41')
     def test_scan_result(self):
         """Test opportunistic scan with an advertisement.
 
@@ -207,6 +211,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5b46fefc-70ef-48a0-acf4-35077cd72202')
     def test_batch_scan_result(self):
         """Test batch opportunistic scan with advertisement.
 
@@ -273,6 +278,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='fd85d95e-dc8c-48c1-8d8a-83c3475755ff')
     def test_batch_scan_result_not_expected(self):
         """Test opportunistic batch scan without expecting an event.
 
@@ -334,6 +340,7 @@
             batch_scan_result.format(scan_callback))
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='6138592e-8fd5-444f-9a7c-25cd9695644a')
     def test_scan_result_not_expected(self):
         """Test opportunistic scan without expecting an event.
 
@@ -389,6 +396,7 @@
         return self._verify_no_events_found(scan_result.format(scan_callback))
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='f7aba3d9-d3f7-4b2f-976e-441772705613')
     def test_max_opportunistic_scan_instances(self):
         """Test max number of opportunistic scan instances.
 
@@ -444,6 +452,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='cf971f08-4d92-4046-bba6-b86a75aa773c')
     def test_max_opportunistic_batch_scan_instances(self):
         """Test max opportunistic batch scan instances.
 
@@ -503,6 +512,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='965d84ef-11a7-418a-97e9-2a441c6de776')
     def test_discover_opportunistic_scan_result_off_secondary_scan_filter(
             self):
         """Test opportunistic scan result from secondary scan filter.
@@ -553,8 +563,8 @@
         self.scn_ad.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)):
+        if not self._verify_no_events_found(
+                scan_result.format(scan_callback2)):
             return False
         try:
             self.scn_ad.ed.pop_event(
@@ -565,6 +575,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='13b0a83f-e96e-4d64-84ef-66351ec5054c')
     def test_negative_opportunistic_scan_filter_result_off_secondary_scan_result(
             self):
         """Test opportunistic scan not found scenario.
@@ -623,6 +634,7 @@
         return self._verify_no_events_found(scan_result.format(scan_callback))
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='087f60b2-f6a1-4919-b4c5-cdf3debcfeff')
     def test_opportunistic_scan_filter_result_off_secondary_scan_result(self):
         """Test opportunistic scan from a secondary scan result.
 
diff --git a/acts/tests/google/ble/system_tests/BleStressTest.py b/acts/tests/google/ble/system_tests/BleStressTest.py
index 15827a6..c1c29ba 100644
--- a/acts/tests/google/ble/system_tests/BleStressTest.py
+++ b/acts/tests/google/ble/system_tests/BleStressTest.py
@@ -22,6 +22,7 @@
 import time
 
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import BtTestUtilsError
 from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
@@ -32,6 +33,7 @@
 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):
     default_timeout = 10
     PAIRING_TIMEOUT = 20
@@ -63,6 +65,7 @@
         return False
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='22f98085-6ed8-4ad8-b62d-b8d1eae20b89')
     def test_loop_scanning_1000(self):
         """Stress start/stop scan instances.
 
@@ -95,6 +98,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='6caa84c2-50ac-46f2-a5e5-f942fd2cd6f6')
     def test_loop_scanning_100_verify_no_hci_timeout(self):
         """Stress start/stop scan instances variant.
 
@@ -128,13 +132,14 @@
                 self.scn_ad.droid)
             self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
                                               scan_callback)
-            self.log.info(self.scn_ad.ed.pop_event(scan_result.format(
-                scan_callback)))
+            self.log.info(
+                self.scn_ad.ed.pop_event(scan_result.format(scan_callback)))
             self.scn_ad.droid.bleStopBleScan(scan_callback)
             time.sleep(1)
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5e9e4c8d-b72e-4767-81e5-f907c1834430')
     def test_loop_advertising_100(self):
         """Stress start/stop advertising instances.
 
@@ -162,16 +167,16 @@
                 self.adv_ad.droid)
             self.adv_ad.droid.bleStartBleAdvertising(
                 advertise_callback, advertise_data, advertise_settings)
-            expected_advertise_event_name = "".join(["BleAdvertise", str(
-                advertise_callback), "onSuccess"])
+            expected_advertise_event_name = "".join(
+                ["BleAdvertise", str(advertise_callback), "onSuccess"])
             worker = self.adv_ad.ed.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)]))
+                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([
@@ -183,6 +188,7 @@
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='11a2f51b-7178-4c32-bb5c-7eddd100a50f')
     def test_restart_advertise_callback_after_bt_toggle(self):
         """Test to reuse an advertise callback.
 
@@ -212,21 +218,22 @@
             self.adv_ad.droid)
         self.adv_ad.droid.bleStartBleAdvertising(
             advertise_callback, advertise_data, advertise_settings)
-        expected_advertise_event_name = "".join(["BleAdvertise", str(
-            advertise_callback), "onSuccess"])
+        expected_advertise_event_name = "".join(
+            ["BleAdvertise", str(advertise_callback), "onSuccess"])
         worker = self.adv_ad.ed.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)]))
+            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)]))
+            self.log.debug(" ".join([
+                "Test failed, filtering callback onSuccess never occurred:",
+                str(error)
+            ]))
         test_result = reset_bluetooth([self.scn_ad])
         if not test_result:
             return test_result
@@ -239,16 +246,18 @@
         try:
             self.log.debug(worker.result(self.default_timeout))
         except Empty as error:
-            self.log.debug(" ".join(["Test failed with Empty error:", str(
-                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)]))
+            self.log.debug(" ".join([
+                "Test failed, filtering callback onSuccess never occurred:",
+                str(error)
+            ]))
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='88f3c068-41be-41df-920c-c0ecaae1a619')
     def test_restart_scan_callback_after_bt_toggle(self):
         """Test to reuse an scan callback.
 
@@ -279,9 +288,11 @@
         reset_bluetooth([self.scn_ad])
         self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
                                           scan_callback)
+
         return test_result
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='ce8adfc0-384f-4751-9438-13a76cada8da')
     def test_le_pairing(self):
         """Test LE pairing transport stress
 
diff --git a/acts/tests/google/ble/system_tests/GattLongevityTest.py b/acts/tests/google/ble/system_tests/GattLongevityTest.py
index c3d6fb0..8784910 100644
--- a/acts/tests/google/ble/system_tests/GattLongevityTest.py
+++ b/acts/tests/google/ble/system_tests/GattLongevityTest.py
@@ -32,7 +32,6 @@
 class GattLongevityTest(GattConnectedBaseTest):
     longevity_iterations = 1100000
 
-    @BluetoothBaseTest.bt_test_wrap
     def test_write_characteristic_no_resp_longevity(self):
         """Longevity test write characteristic value
 
diff --git a/acts/tests/google/bt/BtAirplaneModeTest.py b/acts/tests/google/bt/BtAirplaneModeTest.py
index c7b051e..90334fe 100644
--- a/acts/tests/google/bt/BtAirplaneModeTest.py
+++ b/acts/tests/google/bt/BtAirplaneModeTest.py
@@ -17,6 +17,8 @@
 Test script to test various airplane mode scenarios and how it
 affects Bluetooth state.
 """
+
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
 from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
@@ -42,6 +44,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='11209b74-f27f-44cc-b336-8cf7f168f653')
     def test_bt_on_toggle_airplane_mode_on(self):
         """Test that toggles airplane mode on while BT on
 
@@ -72,6 +75,7 @@
         return not self.dut.droid.bluetoothCheckState()
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='823bb1e1-ce39-43a9-9f2c-0bd2a9b8793f')
     def test_bt_on_toggle_airplane_mode_on_bt_remains_off(self):
         """Test that verifies BT remains off after airplane mode toggles
 
@@ -102,13 +106,13 @@
             self.log.error("Failed to toggle airplane mode on")
             return False
         toggle_timeout = 60
-        self.log.info(
-            "Waiting {} seconds until verifying Bluetooth state.".format(
-                toggle_timeout))
+        self.log.info("Waiting {} seconds until verifying Bluetooth state.".
+                      format(toggle_timeout))
         time.sleep(toggle_timeout)
         return not self.dut.droid.bluetoothCheckState()
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d3977a15-c4b8-4dad-b4e4-98e7c3216688')
     def test_bt_on_toggle_airplane_mode_on_then_off(self):
         """Test that toggles airplane mode both on and off
 
diff --git a/acts/tests/google/bt/BtBasicFunctionalityTest.py b/acts/tests/google/bt/BtBasicFunctionalityTest.py
index c8af44e..7d89867 100644
--- a/acts/tests/google/bt/BtBasicFunctionalityTest.py
+++ b/acts/tests/google/bt/BtBasicFunctionalityTest.py
@@ -21,6 +21,7 @@
 import time
 
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.BtEnum import BluetoothScanModeType
 from acts.test_utils.bt.bt_test_utils import check_device_supported_profiles
@@ -52,10 +53,11 @@
         return True
 
     def on_fail(self, test_name, begin_time):
-        take_btsnoop_logs(self.android_devices, self, test_name)
+        take_btsnoop_logs(self.android_devices, self, test_name, begin_time)
         reset_bluetooth(self.android_devices)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5a5dcf94-8114-405c-a048-b80d73e80ecc')
     def test_bluetooth_reset(self):
         """Test resetting bluetooth.
 
@@ -78,6 +80,7 @@
         return reset_bluetooth([self.droid_ad])
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='fc205cb8-6878-4f97-b9c8-7ed532742a1b')
     def test_make_device_discoverable(self):
         """Test device discoverablity.
 
@@ -142,6 +145,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='c4d77bde-04ed-4805-9185-9bc46dc8af4b')
     def test_make_device_undiscoverable(self):
         """Test device un-discoverability.
 
@@ -166,8 +170,7 @@
         Priority: 1
         """
         self.droid_ad.droid.bluetoothMakeUndiscoverable()
-        set_bt_scan_mode(self.droid1_ad,
-                         BluetoothScanModeType.SCAN_MODE_NONE)
+        set_bt_scan_mode(self.droid1_ad, BluetoothScanModeType.SCAN_MODE_NONE)
         scan_mode = self.droid1_ad.droid.bluetoothGetScanMode()
         if scan_mode == BluetoothScanModeType.SCAN_MODE_NONE:
             self.log.debug("Android device1 scan mode is SCAN_MODE_NONE")
@@ -204,6 +207,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='2bcb6288-64c3-437e-bc89-bcd416310135')
     def test_set_device_name(self):
         """Test bluetooth device name.
 
@@ -227,6 +231,8 @@
         name = "SetDeviceName"
         return set_device_name(self.droid_ad.droid, name)
 
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='b38fb110-a707-47cf-b1c3-981266373786')
     def test_scan_mode_off(self):
         """Test disabling bluetooth scanning.
 
@@ -248,10 +254,11 @@
         Priority: 1
         """
         self.log.debug("Test scan mode STATE_OFF.")
-        return set_bt_scan_mode(self.droid_ad,
-                                BluetoothScanModeType.STATE_OFF)
+        return set_bt_scan_mode(self.droid_ad, BluetoothScanModeType.STATE_OFF)
 
+    #@BluetoothTest(UUID=27576aa8-d52f-45ad-986a-f44fb565167d)
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='702c3d58-94fd-47ee-9323-2421ce182ddb')
     def test_scan_mode_none(self):
         """Test bluetooth scan mode none.
 
@@ -277,6 +284,7 @@
                                 BluetoothScanModeType.SCAN_MODE_NONE)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='cb998a99-31a6-46b6-9de6-a9a17081a604')
     def test_scan_mode_connectable(self):
         """Test bluetooth scan mode connectable.
 
@@ -298,10 +306,11 @@
         Priority: 2
         """
         self.log.debug("Test scan mode SCAN_MODE_CONNECTABLE.")
-        return set_bt_scan_mode(
-            self.droid_ad, BluetoothScanModeType.SCAN_MODE_CONNECTABLE)
+        return set_bt_scan_mode(self.droid_ad,
+                                BluetoothScanModeType.SCAN_MODE_CONNECTABLE)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='59bec55c-c64e-43e4-9a9a-e44408a801d7')
     def test_scan_mode_connectable_discoverable(self):
         """Test bluetooth scan mode connectable.
 
@@ -328,6 +337,7 @@
             BluetoothScanModeType.SCAN_MODE_CONNECTABLE_DISCOVERABLE)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='cd20a09d-a68d-4f55-b016-ba283b0460df')
     def test_if_support_hid_profile(self):
         """ Test that a single device can support HID profile.
         Steps
@@ -352,6 +362,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='a110d330-7090-4784-a33b-33089dc5f67f')
     def test_if_support_hsp_profile(self):
         """ Test that a single device can support HSP profile.
         Steps
@@ -367,6 +378,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='9ccefdd9-62a9-4aed-b4d9-7b0a55c338b2')
     def test_if_support_a2dp_profile(self):
         """ Test that a single device can support A2DP profile.
         Steps
diff --git a/acts/tests/google/bt/BtFactoryResetTest.py b/acts/tests/google/bt/BtFactoryResetTest.py
index 2c07492..c2968e3 100644
--- a/acts/tests/google/bt/BtFactoryResetTest.py
+++ b/acts/tests/google/bt/BtFactoryResetTest.py
@@ -16,6 +16,7 @@
 """
 Test script to test BluetoothAdapter's resetFactory method.
 """
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
 
@@ -30,6 +31,7 @@
         self.sec_dut = self.android_devices[1]
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='800bce6a-87ef-4fd1-82a5-4c60c4133be1')
     def test_factory_reset_bluetooth(self):
         """Test that BluetoothAdapter.factoryReset removes bonded devices
 
@@ -52,7 +54,7 @@
         TAGS: Bluetooth, Factory Reset
         Priority: 2
         """
-        if not pair_pri_to_sec(self.pri_dut.droid, self.sec_dut.droid):
+        if not pair_pri_to_sec(self.pri_dut, self.sec_dut, attemps=1):
             self.log.error("Failed to bond devices.")
             return False
         if not self.pri_dut.droid.bluetoothFactoryReset():
diff --git a/acts/tests/google/bt/BtKillProcessTest.py b/acts/tests/google/bt/BtKillProcessTest.py
index 2096970..eedca38 100644
--- a/acts/tests/google/bt/BtKillProcessTest.py
+++ b/acts/tests/google/bt/BtKillProcessTest.py
@@ -19,6 +19,7 @@
 """
 
 import time
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 
 
@@ -39,6 +40,8 @@
         else:
             return False
 
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='c51186e9-4ba8-406c-b609-ea552868e4c9')
     def test_kill_process(self):
         """Test that a killed Bluetooth process restarts
 
diff --git a/acts/tests/google/bt/BtMetricsTest.py b/acts/tests/google/bt/BtMetricsTest.py
new file mode 100644
index 0000000..3dfde6e
--- /dev/null
+++ b/acts/tests/google/bt/BtMetricsTest.py
@@ -0,0 +1,137 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+import logging
+import time
+from google import protobuf
+
+from acts import asserts
+from acts.test_utils.bt.BtMetricsBaseTest import BtMetricsBaseTest
+from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
+from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts.test_utils.bt.bt_test_utils import reset_bluetooth
+from acts.utils import get_current_epoch_time, sync_device_time
+
+
+class BtMetricsTest(BtMetricsBaseTest):
+    def __init__(self, controllers):
+        BtMetricsBaseTest.__init__(self, controllers)
+        self.iterations = 1
+
+    def setup_class(self):
+        return super(BtMetricsTest, self).setup_class()
+
+    def setup_test(self):
+        # Reset bluetooth
+        reset_bluetooth(self.android_devices)
+        for ad in self.android_devices:
+            if not clear_bonded_devices(ad):
+                logging.error("Failed to unbound device")
+                return False
+            # Sync device time for timestamp comparison
+            sync_device_time(ad)
+        return super(BtMetricsTest, self).setup_test()
+
+    def test_pairing_metric(self):
+        """Test if a pairing event generates the correct metric entry
+
+        This test tries to pair two Bluetooth devices and dumps metrics after
+        pairing. A correctly implemented stack should record 8 pairing events.
+
+        Steps:
+        1. Start pairing between two Bluetooth devices
+        2. After pairing is done, dump and parse the metrics
+        3. Compare the number of pairing events and the time stamp of the
+        pairing event
+
+        Expected Result:
+        No errors, 8 pairing events should be generated
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 1
+        """
+        time_bonds = []
+        for n in range(self.iterations):
+            start_time = get_current_epoch_time()
+            self.log.info("Pair bluetooth iteration {}.".format(n + 1))
+            if (not pair_pri_to_sec(
+                    self.android_devices[0],
+                    self.android_devices[1],
+                    attempts=1,
+                    auto_confirm=False)):
+                self.log.error("Failed to bond devices.")
+                return False
+            end_time = get_current_epoch_time()
+            time_bonds.append((start_time, end_time))
+            # A device bond will trigger a number of system routines that need
+            # to settle before unbond
+            time.sleep(2)
+            for ad in self.android_devices:
+                if not clear_bonded_devices(ad):
+                    return False
+                # Necessary sleep time for entries to update unbonded state
+                time.sleep(2)
+                bonded_devices = ad.droid.bluetoothGetBondedDevices()
+                if len(bonded_devices) > 0:
+                    self.log.error(
+                        "Failed to unbond devices: {}".format(bonded_devices))
+                    return False
+        end_time = get_current_epoch_time()
+        bluetooth_logs, bluetooth_logs_ascii = \
+            self.collect_bluetooth_manager_metrics_logs(
+                [self.android_devices[0]])
+        bluetooth_log = bluetooth_logs[0]
+        bluetooth_log_ascii = bluetooth_logs_ascii[0]
+        asserts.assert_equal(
+            len(bluetooth_log.pair_event), 8, extras=bluetooth_log_ascii)
+        for pair_event in bluetooth_log.pair_event:
+            t = pair_event.event_time_millis
+            asserts.assert_true(start_time <= t <= end_time,
+                                "Event time %d not within limit [%d, %d]" %
+                                (t, start_time, end_time))
+            device_info = pair_event.device_paired_with
+            asserts.assert_true(device_info, "Device info is none")
+            asserts.assert_equal(
+                device_info.device_type,
+                self.bluetooth_proto_module.DeviceInfo.DEVICE_TYPE_BREDR,
+                "Device type does not match")
+
+    def test_bluetooth_metrics_parsing(self):
+        """Test if metrics could be dumped and parsed
+
+        This test simply dumps Bluetooth metrics and print out the ASCII
+        representation
+
+        Steps:
+        1. For the first Android device, dump metrics
+        2. Parse and print metrics in INFO log using ASCII format
+
+        Expected Result:
+        No errors, metrics should be printed to INFO log
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic
+        Priority: 1
+        """
+        bluetooth_logs, bluetooth_logs_ascii = \
+            self.collect_bluetooth_manager_metrics_logs(
+                [self.android_devices[0]])
+        bluetooth_log = bluetooth_logs[0]
+        self.log.info(protobuf.text_format.MessageToString(bluetooth_log))
+        return True
diff --git a/acts/tests/google/bt/RfcommTest.py b/acts/tests/google/bt/RfcommTest.py
index a61be5a..c1aee08 100644
--- a/acts/tests/google/bt/RfcommTest.py
+++ b/acts/tests/google/bt/RfcommTest.py
@@ -22,6 +22,7 @@
 import time
 
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
 from acts.test_utils.bt.bt_test_utils import kill_bluetooth_process
@@ -76,6 +77,7 @@
             self.server_ad.droid.bluetoothRfcommStop()
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='f0bd466f-9a59-4612-8b75-ae4f691eef77')
     def test_rfcomm_connection(self):
         """Test bluetooth RFCOMM connection
 
@@ -105,6 +107,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='240e106a-efd0-4795-8baa-9c0ea88b8b25')
     def test_rfcomm_connection_write_ascii(self):
         """Test bluetooth RFCOMM writing and reading ascii data
 
@@ -143,6 +146,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='c6ebf4aa-1ccb-415f-98c2-cbffb067d1ea')
     def test_rfcomm_write_binary(self):
         """Test bluetooth RFCOMM writing and reading binary data
 
@@ -183,6 +187,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='2b36d71e-102b-469e-b064-e0da8cefdbfe')
     def test_rfcomm_accept_timeout(self):
         """Test bluetooth RFCOMM accept socket timeout
 
diff --git a/acts/tests/google/bt/audio_lab/BtFunhausMetricsTest.py b/acts/tests/google/bt/audio_lab/BtFunhausMetricsTest.py
new file mode 100644
index 0000000..4de60ea
--- /dev/null
+++ b/acts/tests/google/bt/audio_lab/BtFunhausMetricsTest.py
@@ -0,0 +1,181 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+import time
+
+from acts import asserts
+from acts.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest
+
+
+class BtFunhausMetricsTest(BtFunhausBaseTest):
+    def __init__(self, controllers):
+        BtFunhausBaseTest.__init__(self, controllers)
+        self.bluetooth_proto_path = None
+        self.metrics_path = None
+        self.bluetooth_proto_module = None
+
+    def setup_class(self):
+        return super(BtFunhausMetricsTest, self).setup_class()
+
+    def setup_test(self):
+        return super(BtFunhausMetricsTest, self).setup_test()
+
+    def test_run_bt_audio(self):
+        """Test run Bluetooth A2DP audio for one iteration
+
+        This test runs Bluetooth A2DP Audio for 60 seconds and sleep for 20
+        seconds.
+
+        Steps:
+        1. For the first Android device, run audio for 60 seconds
+        2. Sleep while connected to A2DP sink for 20 seconds
+        3. Pull Bluetooth metrics
+        4. Verify metrics values
+
+        Expected Result:
+        The correct metrics should have one Bluetooth session with
+            audio_duration_millis = 60000 +/- 10000
+            session_duration_sec = 80 +/- 10
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic, A2DP
+        Priority: 1
+        """
+        play_duration_seconds = 60
+        start_time = time.time()
+        status, bluetooth_off_list, device_not_connected_list = \
+            self.play_music_for_duration(play_duration_seconds)
+        if not status:
+            return status
+        self.stop_playing_music_on_all_devices()
+        time.sleep(20)
+        bt_duration = time.time() - start_time
+        bluetooth_logs, bluetooth_logs_ascii = \
+            self.collect_bluetooth_manager_metrics_logs(
+                [self.android_devices[0]])
+        bluetooth_log = bluetooth_logs[0]
+        bluetooth_log_ascii = bluetooth_logs_ascii[0]
+        self.log.info(bluetooth_log_ascii)
+        asserts.assert_equal(len(bluetooth_log.session), 1)
+        a2dp_session_log = bluetooth_log.session[0]
+        asserts.assert_almost_equal(
+            a2dp_session_log.session_duration_sec, bt_duration, delta=10)
+        asserts.assert_almost_equal(
+            a2dp_session_log.a2dp_session.audio_duration_millis,
+            play_duration_seconds * 1000,
+            delta=10000)
+        return True
+
+    def test_run_multiple_bt_audio(self):
+        """Test metrics for multiple Bluetooth audio sessions
+
+        This test will run Bluetooth A2DP audio for five 30 seconds sessions
+        and collect metrics after that
+
+        Steps:
+        1. For the first Android device connected, run A2DP audio for 30
+        seconds for 5 times
+        2. Dump and compare metrics
+
+        Expected Result:
+        There should be a single Bluetooth session with
+            session_duration_sec = 250 +/- 10
+            audio_duration_millis = 150000 +/- 20000
+
+        Note: The discrepancies are mainly due to command delays
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic, A2DP
+        Priority: 1
+        """
+        num_play = 5
+        play_duration_seconds = 30
+        bt_duration = 0
+        a2dp_duration = 0
+        for i in range(num_play):
+            start_time = time.time()
+            status, bluetooth_off_list, device_not_connected_list = \
+                self.play_music_for_duration(play_duration_seconds)
+            if not status:
+                return status
+            a2dp_duration += (time.time() - start_time)
+            time.sleep(20)
+            bt_duration += (time.time() - start_time)
+        bluetooth_logs, bluetooth_logs_ascii = \
+            self.collect_bluetooth_manager_metrics_logs(
+                [self.android_devices[0]])
+        bluetooth_log = bluetooth_logs[0]
+        bluetooth_log_ascii = bluetooth_logs_ascii[0]
+        self.log.info(bluetooth_log_ascii)
+        asserts.assert_equal(len(bluetooth_log.session), 1)
+        a2dp_session_log = bluetooth_log.session[0]
+        asserts.assert_almost_equal(
+            a2dp_session_log.session_duration_sec, bt_duration, delta=10)
+        asserts.assert_almost_equal(
+            a2dp_session_log.a2dp_session.audio_duration_millis,
+            a2dp_duration * 1000,
+            delta=20000)
+        return True
+
+    def test_run_multiple_bt_audio_dump_each(self):
+        """Test run Bluetooth A2DP audio multiple times and dump metrics each time
+
+        Steps:
+        1. For the first Android device connected, run A2DP audio for 30 seconds
+        2. Sleep for 20 seconds
+        3. Dump metrics and compare
+        4. Repeate steps 1-3 five times
+
+        Expected Result:
+        Each time, we should observe the following:
+            session_duration_sec = 50 +/- 10
+            audio_duration_millis = 30 +/- 5
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        TAGS: Classic, A2DP
+        Priority: 1
+        """
+        num_play = 5
+        play_duration_seconds = 30
+        for i in range(num_play):
+            start_time = time.time()
+            status, bluetooth_off_list, device_not_connected_list = \
+                self.play_music_for_duration(play_duration_seconds)
+            if not status:
+                return status
+            time.sleep(20)
+            bt_duration = time.time() - start_time
+            bluetooth_logs, bluetooth_logs_ascii = \
+                self.collect_bluetooth_manager_metrics_logs(
+                    [self.android_devices[0]])
+            bluetooth_log = bluetooth_logs[0]
+            bluetooth_log_ascii = bluetooth_logs_ascii[0]
+            self.log.info(bluetooth_log_ascii)
+            asserts.assert_equal(len(bluetooth_log.session), 1)
+            a2dp_session_log = bluetooth_log.session[0]
+            asserts.assert_almost_equal(
+                a2dp_session_log.session_duration_sec, bt_duration, delta=10)
+            asserts.assert_almost_equal(
+                a2dp_session_log.a2dp_session.audio_duration_millis,
+                play_duration_seconds * 1000,
+                delta=5000)
+        return True
diff --git a/acts/tests/google/bt/audio_lab/BtFunhausTest.py b/acts/tests/google/bt/audio_lab/BtFunhausTest.py
index 31de4db..b5cc6c1 100644
--- a/acts/tests/google/bt/audio_lab/BtFunhausTest.py
+++ b/acts/tests/google/bt/audio_lab/BtFunhausTest.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+# /usr/bin/env python3.4
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
@@ -16,179 +16,18 @@
 """
 Test script to automate the Bluetooth Audio Funhaus.
 """
-from acts.keys import Config
-from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
-from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
-from acts.utils import bypass_setup_wizard
-from acts.utils import create_dir
-from acts.utils import exe_cmd
-from acts.utils import sync_device_time
-from queue import Empty
-import json
 import time
-import os
+
+from acts.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest
 
 
-class BtFunhausTest(BluetoothBaseTest):
+class BtFunhausTest(BtFunhausBaseTest):
     music_file_to_play = ""
     device_fails_to_connect_list = []
 
     def __init__(self, controllers):
-        BluetoothBaseTest.__init__(self, controllers)
+        BtFunhausBaseTest.__init__(self, controllers)
 
-    def on_fail(self, test_name, begin_time):
-        self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
-        super(BluetoothBaseTest, self).on_fail(test_name, begin_time)
-
-    def setup_class(self):
-        for ad in self.android_devices:
-            sync_device_time(ad)
-            # Disable Bluetooth HCI Snoop Logs for audio tests
-            ad.droid.bluetoothConfigHciSnoopLog(False)
-            if not bypass_setup_wizard(ad):
-                self.log.debug(
-                    "Failed to bypass setup wizard, continuing test.")
-        if not "bt_config" in self.user_params.keys():
-            self.log.error("Missing mandatory user config \"bt_config\"!")
-            return False
-        bt_config_map_file = self.user_params["bt_config"]
-        return self._setup_bt_config(bt_config_map_file)
-
-    def _setup_bt_config(self, bt_config_map_file):
-        bt_config_map = {}
-        bt_conf_path = "/data/misc/bluedroid/bt_config.conf"
-        if not os.path.isfile(bt_config_map_file):
-            bt_config_map_file = os.path.join(
-                self.user_params[Config.key_config_path], bt_config_map_file)
-            if not os.path.isfile(bt_config_map_file):
-                self.log.error("Unable to load bt_config file {}.".format(
-                    bt_config_map_file))
-                return False
-        try:
-            f = open(bt_config_map_file, 'r')
-            bt_config_map = json.load(f)
-            f.close()
-        except FileNotFoundError:
-            self.log.error("File not found: {}.".format(bt_config_map_file))
-            return False
-        # Connected devices return all upper case mac addresses.
-        # Make the peripheral_info address attribute upper case
-        # in order to ensure the BT mac addresses match.
-        for serial in bt_config_map.keys():
-            mac_address = bt_config_map[serial]["peripheral_info"][
-                "address"].upper()
-            bt_config_map[serial]["peripheral_info"]["address"] = mac_address
-        for ad in self.android_devices:
-            serial = ad.serial
-
-            # Verify serial number in bt_config_map
-            self.log.info("Verify serial number of Android device in config.")
-            if serial not in bt_config_map.keys():
-                self.log.error(
-                    "Missing android device serial {} in bt_config.".format(
-                        serial))
-                return False
-            # Push the bt_config.conf file to Android device
-            self.log.info("Pushing bt_config.conf file to Android device.")
-            config_path = bt_config_map[serial]["config_path"]
-            if not os.path.isfile(config_path):
-                config_path = os.path.join(
-                    self.user_params[Config.key_config_path], config_path)
-                if not os.path.isfile(config_path):
-                    self.log.error("Unable to load bt_config file {}.".format(
-                        config_path))
-                    return False
-            ad.adb.push("{} {}".format(config_path, bt_conf_path))
-
-            # Add music to the Android device
-            self.log.info("Pushing music to the Android device.")
-            music_path_str = "music_path"
-            android_music_path = "/sdcard/Music/"
-            if music_path_str not in self.user_params:
-                self.log.error("Need music for audio testcases...")
-                return False
-
-            music_path = self.user_params[music_path_str]
-            if not os.path.isdir(music_path):
-                music_path = os.path.join(
-                    self.user_params[Config.key_config_path], music_path)
-                if not os.path.isdir(music_path):
-                    self.log.error("Unable to find music directory {}.".format(
-                        music_path))
-                    return False
-            self._add_music_to_primary_android_device(ad, music_path,
-                                                      android_music_path)
-            ad.reboot()
-
-            # Verify Bluetooth is enabled
-            self.log.info("Verifying Bluetooth is enabled on Android Device.")
-            if not bluetooth_enabled_check(ad):
-                self.log.error("Failed to toggle on Bluetooth on device {}".
-                               format(serial))
-                return False
-
-            # Verify Bluetooth device is connected
-            self.log.info(
-                "Waiting up to 10 seconds for device to reconnect...")
-            connected_devices = ad.droid.bluetoothGetConnectedDevices()
-            start_time = time.time()
-            wait_time = 10
-            result = False
-            while time.time() < start_time + wait_time:
-                connected_devices = ad.droid.bluetoothGetConnectedDevices()
-                if len(connected_devices) > 0:
-                    if bt_config_map[serial]["peripheral_info"]["address"] in {
-                            d['address']
-                            for d in connected_devices
-                    }:
-                        result = True
-                        break
-                else:
-                    try:
-                        ad.droid.bluetoothConnectBonded(bt_config_map[serial][
-                            "peripheral_info"]["address"])
-                    except Exception as err:
-                        self.log.error(
-                            "Failed to connect bonded. Err: {}".format(err))
-            if not result:
-                self.log.info("Connected Devices: {}".format(
-                    connected_devices))
-                self.log.info("Bonded Devices: {}".format(
-                    ad.droid.bluetoothGetBondedDevices()))
-                self.log.error(
-                    "Failed to connect to peripheral name: {}, address: {}".
-                    format(bt_config_map[serial]["peripheral_info"]["name"],
-                           bt_config_map[serial]["peripheral_info"][
-                               "address"]))
-                self.device_fails_to_connect_list.append("{}:{}".format(
-                    serial, bt_config_map[serial]["peripheral_info"]["name"]))
-        if len(self.device_fails_to_connect_list) == len(self.android_devices):
-            self.log.error("All devices failed to reconnect.")
-            return False
-        return True
-
-    def _add_music_to_primary_android_device(self, ad, music_path,
-                                             android_music_path):
-        for dirname, dirnames, filenames in os.walk(music_path):
-            for filename in filenames:
-                self.music_file_to_play = filename
-                file = os.path.join(dirname, filename)
-                #TODO: Handle file paths with spaces
-                ad.adb.push("{} {}".format(file, android_music_path))
-        return True
-
-    def _collect_bluetooth_manager_dumpsys_logs(self, ads):
-        for ad in ads:
-            serial = ad.serial
-            out_name = "{}_{}".format(serial, "bluetooth_dumpsys.txt")
-            dumpsys_path = ''.join((ad.log_path, "/BluetoothDumpsys"))
-            create_dir(dumpsys_path)
-            cmd = ''.join(
-                ("adb -s ", serial, " shell dumpsys bluetooth_manager > ",
-                 dumpsys_path, "/", out_name))
-            exe_cmd(cmd)
-
-    @BluetoothBaseTest.bt_test_wrap
     def test_run_bt_audio_12_hours(self):
         """Test audio quality over 12 hours.
 
@@ -214,46 +53,18 @@
         TAGS: Classic, A2DP
         Priority: 1
         """
-        for ad in self.android_devices:
-            ad.droid.mediaPlayOpen("file:///sdcard/Music/{}".format(
-                self.music_file_to_play))
-            ad.droid.mediaPlaySetLooping(True)
-            self.log.info("Music is now playing on device {}".format(
-                ad.serial))
+        self.start_playing_music_on_all_devices()
 
         sleep_interval = 120
         twelve_hours_in_seconds = 43200
         end_time = time.time() + twelve_hours_in_seconds
-        test_result = True
-        bluetooth_off_list = []
-        device_not_connected_list = []
-        while time.time() < end_time:
-            for ad in self.android_devices:
-                serial = ad.serial
-                if (not ad.droid.bluetoothCheckState() and
-                        serial not in bluetooth_off_list):
-                    self.log.error(
-                        "Device {}'s Bluetooth state is off.".format(serial))
-                    bluetooth_off_list.append(serial)
-                if (ad.droid.bluetoothGetConnectedDevices() == 0 and
-                        serial not in device_not_connected_list):
-                    self.log.error(
-                        "Device {} not connected to any Bluetooth devices.".
-                        format(serial))
-                    device_not_connected_list.append(serial)
-                if len(bluetooth_off_list) == len(self.android_devices):
-                    self.log.error(
-                        "Bluetooth off on all Android devices. Ending Test")
-                    return False
-                if len(device_not_connected_list) == len(self.android_devices):
-                    self.log.error(
-                        "Every Android device has no device connected.")
-                    return False
-            time.sleep(sleep_interval)
-
+        status, bluetooth_off_list, device_not_connected_list = \
+            self.monitor_music_play_util_deadline(end_time, sleep_interval)
+        if not status:
+            return status
         self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
-        for ad in self.android_devices:
-            ad.droid.mediaPlayStopAll()
+        self.stop_playing_music_on_all_devices()
+        self.collect_bluetooth_manager_metrics_logs(self.android_devices)
         if len(device_not_connected_list) > 0 or len(bluetooth_off_list) > 0:
             self.log.info("Devices reported as not connected: {}".format(
                 device_not_connected_list))
diff --git a/acts/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py b/acts/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py
new file mode 100644
index 0000000..49031af
--- /dev/null
+++ b/acts/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py
@@ -0,0 +1,94 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Test script to execute Bluetooth basic functionality test cases relevant to car.
+"""
+
+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 BtCarBasicFunctionalityTest(BluetoothBaseTest):
+    default_timeout = 10
+    scan_discovery_time = 5
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.car_ad = self.android_devices[0]
+
+    def setup_class(self):
+        return setup_multiple_devices_for_bt_test(self.android_devices)
+
+    #@BluetoothTest(UUID=b52a032a-3438-4b84-863f-c46a969882a4)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_if_support_a2dp_sink_profile(self):
+        """ Test that a single device can support A2DP SNK profile.
+        Steps
+        1. Initialize one android devices
+        2. Check devices support profiles and return a dictionary
+        3. Check the value of key 'a2dp_sink'
+        :return: test_result: bool
+        """
+        profiles = check_device_supported_profiles(self.car_ad.droid)
+        if not profiles['a2dp_sink']:
+            self.car_ad.log.debug(
+                "Android device do not support A2DP SNK profile.")
+            return False
+        return True
+
+    #@BluetoothTest(UUID=3c2cb613-6c8a-4ed7-8783-37fb47bff5f2)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_if_support_hfp_client_profile(self):
+        """ Test that a single device can support HFP HF profile.
+        Steps
+        1. Initialize one android devices
+        2. Check devices support profiles and return a dictionary
+        3. Check the value of key 'hfp_client'
+        :return: test_result: bool
+        """
+        profiles = check_device_supported_profiles(self.car_ad.droid)
+        if not profiles['hfp_client']:
+            self.car_ad.log.debug(
+                "Android device do not support HFP Client profile.")
+            return False
+        return True
+
+    #@BluetoothTest(UUID=c3854e74-33da-4e4d-a9cb-4f5170ef7d10)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_if_support_pbap_client_profile(self):
+        """ Test that a single device can support PBAP PCE profile.
+        Steps
+        1. Initialize one android devices
+        2. Check devices support profiles and return a dictionary
+        3. Check the value of key 'pbap_client'
+        :return: test_result: bool
+        """
+        profiles = check_device_supported_profiles(self.car_ad.droid)
+        if not profiles['pbap_client']:
+            self.car_ad.log.debug(
+                "Android device do not support PBAP Client profile.")
+            return False
+        return True
diff --git a/acts/tests/google/bt/car_bt/BtCarHfpConferenceTest.py b/acts/tests/google/bt/car_bt/BtCarHfpConferenceTest.py
new file mode 100644
index 0000000..43f3dfc
--- /dev/null
+++ b/acts/tests/google/bt/car_bt/BtCarHfpConferenceTest.py
@@ -0,0 +1,161 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Test the HFP profile for conference calling functionality.
+"""
+
+import time
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.car import car_telecom_utils
+from acts.test_utils.tel import tel_defines
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts.test_utils.tel.tel_test_utils import wait_for_ringing_call
+from acts.test_utils.tel.tel_voice_utils import get_audio_route
+from acts.test_utils.tel.tel_voice_utils import set_audio_route
+from acts.test_utils.tel.tel_voice_utils import swap_calls
+
+BLUETOOTH_PKG_NAME = "com.android.bluetooth"
+CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING"
+CALL_TYPE_INCOMING = "CALL_TYPE_INCOMING"
+SHORT_TIMEOUT = 3
+
+
+class BtCarHfpConferenceTest(BluetoothCarHfpBaseTest):
+    def setup_class(self):
+        if not super(BtCarHfpConferenceTest, self).setup_class():
+            return False
+
+        # Connect the devices now, try twice.
+        attempts = 2
+        connected = False
+        while attempts > 0 and not connected:
+            connected = bt_test_utils.connect_pri_to_sec(
+                self.hf, self.ag,
+                set([BtEnum.BluetoothProfile.HEADSET_CLIENT.value]))
+            self.log.info("Connected {}".format(connected))
+            attempts -= 1
+        return connected
+
+    #@BluetoothTest(UUID=a9657693-b534-4625-bf91-69a1d1b9a943)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_multi_way_call_accept(self):
+        """
+        Tests if we can have a 3-way calling between re, RE2 and AG/HF.
+
+        Precondition:
+        1. Devices are connected over HFP.
+
+        Steps:
+        1. Make a call from re to AG
+        2. Wait for dialing on re and ringing on HF/AG.
+        3. Accept the call on HF
+        4. Make a call on RE2 to AG
+        5. Wait for dialing on re and ringing on HF/AG.
+        6. Accept the call on HF.
+        7. See that HF/AG have one active and one held call.
+        8. Merge the call on HF.
+        9. Verify that we have a conference call on HF/AG.
+        10. Hangup the call on HF.
+        11. Wait for all devices to go back into stable state.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 0
+        """
+        timeout_for_state_updates = 3
+        # Dial AG from re
+        if not initiate_call(self.log, self.re, self.ag_phone_number):
+            self.log.error("Failed to initiate call from re.")
+            return False
+
+        # Wait for dialing/ringing
+        ret = True
+        ret &= wait_for_ringing_call(self.log, self.ag)
+        ret &= car_telecom_utils.wait_for_ringing(self.log, self.hf)
+
+        if not ret:
+            self.log.error("Failed to dial incoming number from")
+            return False
+
+        # Give time for state to update due to carrier limitations
+        time.sleep(SHORT_TIMEOUT)
+        # Extract the call.
+        call_1 = car_telecom_utils.get_calls_in_states(
+            self.log, self.hf, [tel_defines.CALL_STATE_RINGING])
+        if len(call_1) != 1:
+            self.hf.log.error("Call State in ringing failed {}".format(call_1))
+            return False
+
+        # Accept the call on HF
+        if not car_telecom_utils.accept_call(self.log, self.hf, call_1[0]):
+            self.hf.log.error("Accepting call failed {}".format(
+                self.hf.serial))
+            return False
+
+        # Dial another call from RE2
+        if not initiate_call(self.log, self.re2, self.ag_phone_number):
+            self.re2.log.error("Failed to initiate call from re.")
+            return False
+
+        # Wait for dialing/ringing
+        ret &= wait_for_ringing_call(self.log, self.ag)
+        ret &= car_telecom_utils.wait_for_ringing(self.log, self.hf)
+
+        if not ret:
+            self.log.error("AG and HF not in ringing state.")
+            return False
+
+        # Give time for state to update due to carrier limitations
+        time.sleep(SHORT_TIMEOUT)
+        # Extract the call.
+        #input("Continue?")
+        call_2 = car_telecom_utils.get_calls_in_states(
+            self.log, self.hf, [tel_defines.CALL_STATE_RINGING])
+        if len(call_2) != 1:
+            self.hf.log.info("Call State in ringing failed {}".format(call_2))
+            return False
+
+        # Accept the call on HF
+        if not car_telecom_utils.accept_call(self.log, self.hf, call_2[0]):
+            self.hf.log.info("Accepting call failed {}".format(
+                calls_in_ringing))
+            return False
+
+        # Merge the calls now.
+        self.hf.droid.telecomCallJoinCallsInConf(call_1[0], call_2[0])
+
+        # Check if we are in conference with call_1 and call_2
+        conf_call_id = car_telecom_utils.wait_for_conference(
+            self.log, self.hf, [call_1[0], call_2[0]])
+        if conf_call_id == None:
+            self.hf.log.error("Did not get the conference setup correctly")
+            return False
+
+        # Now hangup the conference call.
+        if not car_telecom_utils.hangup_conf(self.log, self.hf, conf_call_id):
+            self.hf.log.error("Could not hangup conference call {}!".format(
+                conf_call_id))
+            return False
+
+        return True
diff --git a/acts/tests/google/bt/car_bt/BtCarHfpConnectionTest.py b/acts/tests/google/bt/car_bt/BtCarHfpConnectionTest.py
new file mode 100644
index 0000000..98aa8f3
--- /dev/null
+++ b/acts/tests/google/bt/car_bt/BtCarHfpConnectionTest.py
@@ -0,0 +1,293 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Test the HFP profile for calling and connection management.
+"""
+
+import time
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.car import car_bt_utils
+from acts.test_utils.car import car_telecom_utils
+from acts.test_utils.tel import tel_defines
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+
+BLUETOOTH_PKG_NAME = "com.android.bluetooth"
+CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING"
+CALL_TYPE_INCOMING = "CALL_TYPE_INCOMING"
+default_timeout = 20
+
+
+class BtCarHfpConnectionTest(BluetoothCarHfpBaseTest):
+    def setup_class(self):
+        if not super(BtCarHfpConnectionTest, self).setup_class():
+            return False
+
+        # Disable all
+        car_bt_utils.set_car_profile_priorities_off(self.hf, self.ag)
+
+        # Enable A2DP
+        bt_test_utils.set_profile_priority(
+            self.hf, self.ag, [BtEnum.BluetoothProfile.HEADSET_CLIENT],
+            BtEnum.BluetoothPriorityLevel.PRIORITY_ON)
+
+        return True
+
+    def setup_test(self):
+        if not super(BtCarHfpConnectionTest, self).setup_test():
+            return False
+        self.hf.droid.bluetoothDisconnectConnected(
+            self.ag.droid.bluetoothGetLocalAddress())
+
+    #@BluetoothTest(UUID=a6669f9b-fb49-4bd8-aa9c-9d6369e34442)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_call_transfer_disconnect_connect(self):
+        """
+        Tests that after we connect when an active call is in progress,
+        we show the call.
+
+        Precondition:
+        1. AG & HF are disconnected but paired.
+
+        Steps:
+        1. Make a call from AG role (since disconnected)
+        2. Accept from RE role and transition the call to Active
+        3. Connect AG & HF
+        4. HF should transition into Active call state.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 1
+        """
+        # make a call on AG
+        if not initiate_call(self.log, self.ag, self.re_phone_number):
+            self.ag.log.error("Failed to initiate call from ag.")
+            return False
+        if not wait_and_answer_call(self.log, self.re):
+            self.re.log.error("Failed to accept call on re.")
+            return False
+
+        # Wait for AG, RE to go into an Active state.
+        if not car_telecom_utils.wait_for_active(self.log, self.ag):
+            self.ag.log.error("AG not in Active state.")
+            return False
+        if not car_telecom_utils.wait_for_active(self.log, self.re):
+            self.re.log.error("RE not in Active state.")
+            return False
+
+        # Now connect the devices.
+        if not bt_test_utils.connect_pri_to_sec(self.hf, self.ag, set(
+            [BtEnum.BluetoothProfile.HEADSET_CLIENT.value])):
+            self.log.error("Could not connect HF and AG {} {}".format(
+                self.hf.serial, self.ag.serial))
+            return False
+
+        # Check that HF is in active state
+        if not car_telecom_utils.wait_for_active(self.log, self.hf):
+            self.hf.log.error("HF not in Active state.")
+            return False
+
+        # Hangup the call and check all devices are clean
+        self.hf.droid.telecomEndCall()
+        ret = True
+        ret &= car_telecom_utils.wait_for_not_in_call(self.log, self.hf)
+        ret &= car_telecom_utils.wait_for_not_in_call(self.log, self.ag)
+        ret &= car_telecom_utils.wait_for_not_in_call(self.log, self.re)
+
+        return ret
+
+    #@BluetoothTest(UUID=97727b64-a590-4d84-a257-1facd8aafd16)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_call_transfer_off_on(self):
+        """
+        Tests that after we turn adapter on when an active call is in
+        progress, we show the call.
+
+        Precondition:
+        1. AG & HF are disconnected but paired.
+        2. HF's adapter is OFF
+
+        Steps:
+        1. Make a call from AG role (since disconnected)
+        2. Accept from RE role and transition the call to Active
+        3. Turn HF's adapter ON
+        4. HF should transition into Active call state.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 1
+        """
+        # Connect HF & AG
+        if not bt_test_utils.connect_pri_to_sec(self.hf, self.ag, set(
+            [BtEnum.BluetoothProfile.HEADSET_CLIENT.value])):
+            self.log.error("Could not connect HF and AG {} {}".format(
+                self.hf.serial, self.ag.serial))
+            return False
+
+        # make a call on AG
+        if not initiate_call(self.log, self.ag, self.re_phone_number):
+            self.ag.log.error("Failed to initiate call from ag.")
+            return False
+
+        # Wait for all HF
+        if not car_telecom_utils.wait_for_dialing(self.log, self.hf):
+            self.hf.log.error("HF not in ringing state.")
+            return False
+
+        # Accept the call on RE
+        if not wait_and_answer_call(self.log, self.re):
+            self.re.log.error("Failed to accept call on re.")
+            return False
+        # Wait for all HF, AG, RE to go into an Active state.
+        if not car_telecom_utils.wait_for_active(self.log, self.hf):
+            self.hf.log.error("HF not in Active state.")
+            return False
+        if not car_telecom_utils.wait_for_active(self.log, self.ag):
+            self.ag.log.error("AG not in Active state.")
+            return False
+        if not car_telecom_utils.wait_for_active(self.log, self.re):
+            self.re.log.error("RE not in Active state.")
+            return False
+
+        # Turn the adapter OFF on HF
+        if not bt_test_utils.disable_bluetooth(self.hf.droid):
+            self.hf.log.error("Failed to turn BT off on HF.")
+            return False
+
+        # Turn adapter ON on HF
+        if not bt_test_utils.enable_bluetooth(self.hf.droid, self.hf.ed):
+            self.hf.log.error("Failed to turn BT ON after call on HF.")
+            return False
+
+        # Check that HF is in active state
+        if not car_telecom_utils.wait_for_active(self.log, self.hf):
+            self.hf.log.error("HF not in Active state.")
+            return False
+
+        # Hangup the call and check all devices are clean
+        self.hf.droid.telecomEndCall()
+        ret = True
+        ret &= car_telecom_utils.wait_for_not_in_call(self.log, self.hf)
+        ret &= car_telecom_utils.wait_for_not_in_call(self.log, self.ag)
+        ret &= car_telecom_utils.wait_for_not_in_call(self.log, self.re)
+
+        return ret
+
+    #@BluetoothTest(UUID=95f76e2c-1cdd-4a7c-8e26-863b4c4242be)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_call_transfer_connect_disconnect_connect(self):
+        """
+        Test that when we go from connect -> disconnect -> connect on an active
+        call then the call is restored on HF.
+
+        Precondition:
+        1. AG & HF are paired
+
+        Steps:
+        0. Connect AG & HF
+        1. Make a call from HF role
+        2. Accept from RE role and transition the call to Active
+        3. Disconnect AG & HF
+        4. Verify that we don't have any calls on HF
+        5. Connect AG & HF
+        6. Verify that HF gets the call back.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 1
+        """
+        # Now connect the devices.
+        if not bt_test_utils.connect_pri_to_sec(self.hf, self.ag, set(
+            [BtEnum.BluetoothProfile.HEADSET_CLIENT.value])):
+            self.log.error("Could not connect HF and AG {} {}".format(
+                self.hf.serial, self.ag.serial))
+            return False
+
+        # make a call on HF
+        if not car_telecom_utils.dial_number(self.log, self.hf,
+                                             self.re_phone_number):
+            self.hf.log.error("HF not in dialing state.")
+            return False
+
+        # Wait for HF, AG to be dialing and RE to be ringing
+        ret = True
+        ret &= car_telecom_utils.wait_for_dialing(self.log, self.hf)
+        #uncomment once sl4a code has been merged.
+        ret &= car_telecom_utils.wait_for_dialing(self.log, self.ag)
+        ret &= car_telecom_utils.wait_for_ringing(self.log, self.re)
+
+        if not ret:
+            self.log.error("Outgoing call did not get established")
+            return False
+
+        # Accept call on RE.
+        if not wait_and_answer_call(self.log, self.re):
+            self.re.log.error("Failed to accept call on re.")
+            return False
+
+        ret &= car_telecom_utils.wait_for_active(self.log, self.hf)
+        ret &= car_telecom_utils.wait_for_active(self.log, self.ag)
+        ret &= car_telecom_utils.wait_for_active(self.log, self.re)
+
+        if not ret:
+            self.log.error("Outgoing call did not transition to active")
+            return False
+
+        # Disconnect HF & AG
+        self.hf.droid.bluetoothDisconnectConnected(
+            self.ag.droid.bluetoothGetLocalAddress())
+
+        # We use the proxy of the Call going away as HF disconnected
+        if not car_telecom_utils.wait_for_not_in_call(self.log, self.hf):
+            self.hf.log.error("HF still in call after disconnection.")
+            return False
+
+        # Now connect the devices.
+        if not bt_test_utils.connect_pri_to_sec(self.hf, self.ag, set(
+            [BtEnum.BluetoothProfile.HEADSET_CLIENT.value])):
+            self.log.error("Could not connect HF and AG {} {}".format(
+                self.hf.serial, self.ag.serial))
+            # Additional profile connection check for b/
+            if not bt_test_utils.is_hfp_client_device_connected(
+                    self.hf, self.ag.droid.bluetoothGetLocalAddress()):
+                self.hf.log.info(
+                    "HFP Client connected even though connection state changed "
+                    + " event not found")
+                return False
+
+        # Check that HF is in active state
+        if not car_telecom_utils.wait_for_active(self.log, self.hf):
+            self.hf.log.error("HF not in Active state.")
+            return False
+
+        # Hangup the call and check all devices are clean
+        self.hf.droid.telecomEndCall()
+        ret &= car_telecom_utils.wait_for_not_in_call(self.log, self.hf)
+        ret &= car_telecom_utils.wait_for_not_in_call(self.log, self.ag)
+        ret &= car_telecom_utils.wait_for_not_in_call(self.log, self.re)
+
+        return ret
diff --git a/acts/tests/google/bt/car_bt/BtCarHfpFuzzTest.py b/acts/tests/google/bt/car_bt/BtCarHfpFuzzTest.py
new file mode 100644
index 0000000..f6166b1
--- /dev/null
+++ b/acts/tests/google/bt/car_bt/BtCarHfpFuzzTest.py
@@ -0,0 +1,285 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Test the HFP profile for advanced functionality and try to create race
+conditions by executing actions quickly.
+"""
+
+import time
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.car import car_telecom_utils
+from acts.test_utils.tel import tel_defines
+
+STABILIZATION_DELAY_SEC = 5
+
+
+class BtCarHfpFuzzTest(BluetoothCarHfpBaseTest):
+    def setup_class(self):
+        if not super(BtCarHfpFuzzTest, self).setup_class():
+            return False
+
+        # Connect the devices now, try twice.
+        attempts = 2
+        connected = False
+        while attempts > 0 and not connected:
+            connected = bt_test_utils.connect_pri_to_sec(
+                self.hf, self.ag,
+                set([BtEnum.BluetoothProfile.HEADSET_CLIENT.value]))
+            self.log.info("Connected {}".format(connected))
+            attempts -= 1
+
+        if not connected:
+            self.log.error("Failed to connect")
+            return False
+
+        # Delay set contains the delay between dial and hangup for a call.
+        # We keep very small delays to significantly large ones to stress test
+        # various kind of timing issues.
+        self.delay_set = [0.1,
+                          0.2,
+                          0.3,
+                          0.4,
+                          0.5,  # Very short delays
+                          1.0,
+                          2.0,
+                          3.0,
+                          4.0,
+                          5.0,  # Med delays
+                          10.0]  # Large delays
+
+    def dial_a_hangup_b_quick(self, a, b, delay=0, ph=""):
+        """
+        This test does a quick succession of dial and hangup. We make the test
+        case sleep for delay amount between each dial and hangup. This is
+        different from other test cases where we give enough time for devices
+        to get into a calling state before trying to tear down connection.
+        """
+        if ph == "": ph = self.re_phone_number
+
+        # Dial from A now.
+        self.log.info("Dialing at droid {}".format(a.droid.getBuildDisplay()))
+        a.droid.telecomCallTelUri(ph)
+
+        # Wait for delay millis.
+        time.sleep(delay)
+
+        # Cancel the call at B. Use end call in this scenario so that we do not
+        # wait for looking up the call!
+        self.log.info("Hanging up at droid {}".format(b.droid.getBuildDisplay(
+        )))
+        b.droid.telecomEndCall()
+
+        # Check/Wait that we are clear before executing the test.
+        for d in self.android_devices:
+            if not car_telecom_utils.wait_for_not_in_call(self.log, d):
+                self.log.warn(
+                    "dial_a_hangup_quick wait_for_not_in_call failed {}".
+                    format(d.serial))
+                return False
+
+        return True
+
+    def stabilize_and_check_sanity(self):
+        # Since we dial and hangup very very quickly we may end up in a state
+        # where we need to wait to see the results. For instance if the delay is
+        # 0.1 sec it may take upto 2 seconds for the platform to respond to a
+        # dial() and hence even if we hangup 0.1 sec later we will not see its
+        # result immidiately (this may be a false positive on test).
+        time.sleep(STABILIZATION_DELAY_SEC)
+
+        # First check if HF is in dialing state, we can send an actual hangup if
+        # that is the case and then wait for devices to come back to normal.
+        if self.hf.droid.telecomIsInCall():
+            self.log.info("HF still in call, send hangup")
+            self.hf.droid.telecomEndCall()
+
+        # Wait for devices to go back to normal.
+        for d in self.android_devices:
+            if not car_telecom_utils.wait_for_not_in_call(self.log, d):
+                self.log.warning(
+                    "stabilize_and_check_sanity wait_for_not_in_call failed {}".
+                    format(d.serial))
+                return False
+
+        return True
+
+    #@BluetoothTest(UUID=32022c74-fdf3-44c4-9e82-e518bdcce667)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_fuzz_outgoing_hf(self):
+        """
+        Test calling and hangup from HF with varied delays as defined in
+        self.delay_set
+
+        Precondition:
+        1. Devices are paired and connected
+
+        Steps:
+        For each delay do the following:
+        a) Call HF
+        b) Wait for delay seconds
+        c) Hangup HF
+        d) Check if all devices are in stable state, if not wait for stabilizing
+        e) If (d) fails then we fail otherwise we go back to (a)
+        f) Once all delays are done we do a final check for sanity as pass
+        scenario
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 1
+        """
+
+        for delay in self.delay_set:
+            self.log.info("test_fuzz outgoing_hf: {}".format(delay))
+            # Make the call and hangup, we do a light check inside to see if the
+            # phones are in a clean state -- if not, we let them stabilize
+            # before continuing.
+            if not self.dial_a_hangup_b_quick(self.hf, self.hf, delay):
+                if not self.stabilize_and_check_sanity():
+                    self.log.info("Devices not able to stabilize!")
+                    return False
+
+        # Final sanity check (if we never called stabilize_and_check_sanity
+        # above).
+        return self.stabilize_and_check_sanity()
+
+    #@BluetoothTest(UUID=bc6d52b2-4acc-461e-ad55-fad5a5ecb091)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_fuzz_outgoing_ag(self):
+        """
+        Test calling and hangup from AG with varied delays as defined in
+        self.delay_set
+
+        Precondition:
+        1. Devices are paired and connected
+
+        Steps:
+        For each delay do the following:
+        a) Call AG
+        b) Wait for delay seconds
+        c) Hangup AG
+        d) Check if all devices are in stable state, if not wait for stabilizing
+        e) If (d) fails then we fail otherwise we go back to (a)
+        f) Once all delays are done we do a final check for sanity as pass
+        scenario
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 1
+        """
+
+        for delay in self.delay_set:
+            self.log.info("test_fuzz outgoing_ag: {}".format(delay))
+            # Make the call and hangup, we do a light check inside to see if the
+            # phones are in a clean state -- if not, we let them stabilize
+            # before continuing.
+            if not self.dial_a_hangup_b_quick(self.ag, self.ag, delay):
+                if not self.stabilize_and_check_sanity():
+                    self.log.error("Devices not able to stabilize!")
+                    return False
+
+        # Final sanity check (if we never called stabilize_and_check_sanity
+        # above).
+        return self.stabilize_and_check_sanity()
+
+    #@BluetoothTest(UUID=d834384a-38d5-4260-bfd5-98f8207c04f5)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_fuzz_dial_hf_hangup_ag(self):
+        """
+        Test calling and hangup from HF and AG resp. with varied delays as defined in
+        self.delay_set
+
+        Precondition:
+        1. Devices are paired and connected
+
+        Steps:
+        For each delay do the following:
+        a) Call HF
+        b) Wait for delay seconds
+        c) Hangup AG
+        d) Check if all devices are in stable state, if not wait for stabilizing
+        e) If (d) fails then we fail otherwise we go back to (a)
+        f) Once all delays are done we do a final check for sanity as pass
+        scenario
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 1
+        """
+
+        for delay in self.delay_set:
+            self.log.info("test_fuzz dial_hf hangup_ag: {}".format(delay))
+            # Make the call and hangup, we do a light check inside to see if the
+            # phones are in a clean state -- if not, we let them stabilize
+            # before continuing.
+            if not self.dial_a_hangup_b_quick(self.hf, self.ag, delay):
+                if not self.stabilize_and_check_sanity():
+                    self.log.info("Devices not able to stabilize!")
+                    return False
+
+        # Final sanity check (if we never called stabilize_and_check_sanity
+        # above).
+        return self.stabilize_and_check_sanity()
+
+    #@BluetoothTest(UUID=6de1a8ab-3cb0-4594-a9bb-d882a3414836)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_fuzz_dial_ag_hangup_hf(self):
+        """
+        Test calling and hangup from HF and AG resp. with varied delays as defined in
+        self.delay_set
+
+        Precondition:
+        1. Devices are paired and connected
+
+        Steps:
+        For each delay do the following:
+        a) Call AG
+        b) Wait for delay seconds
+        c) Hangup HF
+        d) Check if all devices are in stable state, if not wait for stabilizing
+        e) If (d) fails then we fail otherwise we go back to (a)
+        f) Once all delays are done we do a final check for sanity as pass
+        scenario
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 1
+        """
+
+        for delay in self.delay_set:
+            self.log.info("test_fuzz dial_ag hangup_hf: {}".format(delay))
+            # Make the call and hangup, we do a light check inside to see if the
+            # phones are in a clean state -- if not, we let them stabilize
+            # before continuing.
+            if not self.dial_a_hangup_b_quick(self.ag, self.hf, delay):
+                if not self.stabilize_and_check_sanity():
+                    self.log.info("Devices not able to stabilize!")
+                    return False
+
+        # Final sanity check (if we never called stabilize_and_check_sanity
+        # above).
+        return self.stabilize_and_check_sanity()
diff --git a/acts/tests/google/bt/car_bt/BtCarHfpTest.py b/acts/tests/google/bt/car_bt/BtCarHfpTest.py
index 32726ad..63a092c 100644
--- a/acts/tests/google/bt/car_bt/BtCarHfpTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarHfpTest.py
@@ -13,64 +13,45 @@
 # 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 the HFP profile for basic calling functionality.
 """
 
 import time
-
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
 from acts.test_utils.bt import BtEnum
 from acts.test_utils.bt import bt_test_utils
 from acts.test_utils.car import car_telecom_utils
+from acts.test_utils.car import tel_telecom_utils
 from acts.test_utils.tel import tel_defines
 
 BLUETOOTH_PKG_NAME = "com.android.bluetooth"
 CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING"
 CALL_TYPE_INCOMING = "CALL_TYPE_INCOMING"
-default_timeout = 20
+SHORT_TIMEOUT = 5
 
-class BtCarHfpTest(BluetoothBaseTest):
+
+class BtCarHfpTest(BluetoothCarHfpBaseTest):
     def setup_class(self):
-        self.hf = self.android_devices[0]
-        self.ag = self.android_devices[1]
-        self.re = self.android_devices[2]
-        self.ag_phone_number = "tel:{}".format(
-            self.ag.droid.telephonyGetLine1Number())
-        self.re_phone_number = "tel:{}".format(
-            self.re.droid.telephonyGetLine1Number())
-        self.log.info("ag tel: {} re tel: {}".format(
-            self.ag_phone_number, self.re_phone_number))
-
-        # Setup includes pairing and connecting the devices.
-        bt_test_utils.setup_multiple_devices_for_bt_test([self.hf, self.ag])
-        bt_test_utils.reset_bluetooth([self.hf, self.ag])
-
-        # Pair and connect the devices.
-        if not bt_test_utils.pair_pri_to_sec(self.hf.droid, self.ag.droid):
-            self.log.error("Failed to pair")
+        if not super(BtCarHfpTest, self).setup_class():
             return False
+        # Disable the A2DP profile.
+        bt_test_utils.set_profile_priority(
+            self.hf, self.ag, [BtEnum.BluetoothProfile.PBAP_CLIENT.value,
+                               BtEnum.BluetoothProfile.A2DP_SINK.value],
+            BtEnum.BluetoothPriorityLevel.PRIORITY_OFF)
+        bt_test_utils.set_profile_priority(
+            self.hf, self.ag, [BtEnum.BluetoothProfile.HEADSET_CLIENT.value],
+            BtEnum.BluetoothPriorityLevel.PRIORITY_ON)
 
-        # Connect the devices now, try twice.
-        attempts = 2
-        connected = False
-        while attempts > 0 and not connected:
-            connected = bt_test_utils.connect_pri_to_sec(
-                self.log, self.hf, self.ag.droid,
-                set([BtEnum.BluetoothProfile.HEADSET_CLIENT.value]))
-            self.log.info("Connected {}".format(connected))
-            attempts -= 1
-        return connected
+        if not bt_test_utils.connect_pri_to_sec(self.hf, self.ag, set(
+            [BtEnum.BluetoothProfile.HEADSET_CLIENT.value])):
+            self.log.error("Failed to connect.")
+            return False
+        return True
 
-    def setup_test(self):
-        # Reset the devices.
-        for d in self.android_devices:
-            d.ed.clear_all_events()
-
-    def on_fail(self, test_name, begin_time):
-        self.log.debug("Test {} failed.".format(test_name))
-
+    #@BluetoothTest(UUID=4ce2195a-b70a-4584-912e-cbd20d20e19d)
     @BluetoothBaseTest.bt_test_wrap
     def test_default_calling_account(self):
         """
@@ -92,7 +73,7 @@
         selected_acc = \
             self.hf.droid.telecomGetUserSelectedOutgoingPhoneAccount()
         if not selected_acc:
-            self.log.info("No default account found.")
+            self.hf.log.error("No default account found.")
             return False
 
         # Check if the default account is from the Bluetooth package. This is a
@@ -100,15 +81,16 @@
         try:
             acc_component_id = selected_acc['ComponentName']
         except KeyError:
-            self.log.info(
-                "No component name for account {}".format(selected_acc))
+            self.hf.log.error("No component name for account {}".format(
+                selected_acc))
             return False
         if not acc_component_id.startswith(BLUETOOTH_PKG_NAME):
-            self.log.info(
-                "Component name does not start with pkg name {}".format(
-                    selected_acc))
+            self.hf.log.error("Component name does not start with pkg name {}".
+                          format(selected_acc))
             return False
+        return True
 
+    #@BluetoothTest(UUID=e579009d-05f3-4236-a698-5de8c11d73a9)
     @BluetoothBaseTest.bt_test_wrap
     def test_outgoing_call_hf(self):
         """
@@ -132,7 +114,7 @@
         """
         return self.dial_a_hangup_b(self.hf, self.hf)
 
-
+    #@BluetoothTest(UUID=c9d5f9cd-f275-4adf-b212-c2e9a70d4cac)
     @BluetoothBaseTest.bt_test_wrap
     def test_outgoing_call_ag(self):
         """
@@ -156,6 +138,7 @@
         """
         return self.dial_a_hangup_b(self.ag, self.ag)
 
+    #@BluetoothTest(UUID=908c199b-ca65-4694-821d-1b864ee3fe69)
     @BluetoothBaseTest.bt_test_wrap
     def test_outgoing_dial_ag_hangup_hf(self):
         """
@@ -179,6 +162,7 @@
         """
         return self.dial_a_hangup_b(self.ag, self.hf)
 
+    #@BluetoothTest(UUID=5d1d52c7-51d8-4c82-b437-2e91a6220db3)
     @BluetoothBaseTest.bt_test_wrap
     def test_outgoing_dial_hf_hangup_ag(self):
         """
@@ -202,6 +186,7 @@
         """
         return self.dial_a_hangup_b(self.hf, self.ag)
 
+    #@BluetoothTest(UUID=a718e238-7e31-40c9-a45b-72081210cc73)
     @BluetoothBaseTest.bt_test_wrap
     def test_incoming_dial_re_hangup_re(self):
         """
@@ -225,7 +210,7 @@
         """
         return self.dial_a_hangup_b(self.re, self.re, self.ag_phone_number)
 
-    def dial_a_hangup_b(self, a, b, ph=""):
+    def dial_a_hangup_b(self, caller, callee, ph=""):
         """
         a, b and c can be either of AG, HF or Remote.
         1. Make a call from 'a' on a fixed number.
@@ -240,36 +225,40 @@
 
         # Determine if this is outgoing or incoming call.
         call_type = None
-        if a == self.ag or a == self.hf:
+        if caller == self.ag or caller == self.hf:
             call_type = CALL_TYPE_OUTGOING
-            if b != self.ag and b != self.hf:
+            if callee != self.ag and callee != self.hf:
                 self.log.info("outgoing call should terminate at AG or HF")
                 return False
-        elif a == self.re:
+        elif caller == self.re:
             call_type = CALL_TYPE_INCOMING
-            if b != self.re:
+            if callee != self.re:
                 self.log.info("Incoming call should terminate at Re")
                 return False
 
         self.log.info("Call type is {}".format(call_type))
 
-        # make a call on 'a'
-        if not car_telecom_utils.dial_number(self.log, a, ph):
+        # make a call on 'caller'
+        if not tel_telecom_utils.dial_number(self.log, caller, ph):
             return False
 
+        # Give time for state to update due to carrier limitations
+        time.sleep(SHORT_TIMEOUT)
         # Check that everyone is in dialing/ringing state.
         ret = True
         if call_type == CALL_TYPE_OUTGOING:
-            ret &= car_telecom_utils.wait_for_dialing(self.log, self.hf)
-            ret &= car_telecom_utils.wait_for_dialing(self.log, self.ag)
-            ret &= car_telecom_utils.wait_for_ringing(self.log, self.re)
+            ret &= tel_telecom_utils.wait_for_dialing(self.log, self.hf)
+            ret &= tel_telecom_utils.wait_for_dialing(self.log, self.ag)
+            ret &= tel_telecom_utils.wait_for_ringing(self.log, self.re)
         else:
-            ret &= car_telecom_utils.wait_for_ringing(self.log, self.hf)
-            ret &= car_telecom_utils.wait_for_ringing(self.log, self.ag)
-            ret &= car_telecom_utils.wait_for_dialing(self.log, self.re)
+            ret &= tel_telecom_utils.wait_for_ringing(self.log, self.hf)
+            ret &= tel_telecom_utils.wait_for_ringing(self.log, self.ag)
+            ret &= tel_telecom_utils.wait_for_dialing(self.log, self.re)
         if not ret:
             return False
 
+        # Give time for state to update due to carrier limitations
+        time.sleep(SHORT_TIMEOUT)
         # Check if we have any calls with dialing or active state on 'b'.
         # We assume we never disconnect from 'ringing' state since it will lead
         # to voicemail.
@@ -278,8 +267,8 @@
              tel_defines.CALL_STATE_DIALING,
              tel_defines.CALL_STATE_ACTIVE]
 
-        calls_in_dialing_or_active = car_telecom_utils.get_calls_in_states(
-            self.log, b, call_state_dialing_or_active)
+        calls_in_dialing_or_active = tel_telecom_utils.get_calls_in_states(
+            self.log, callee, call_state_dialing_or_active)
 
         # Make sure there is only one!
         if len(calls_in_dialing_or_active) != 1:
@@ -287,12 +276,13 @@
                 calls_in_dialing_or_active))
             return False
 
-        # Hangup the *only* call on 'b'
-        if not car_telecom_utils.hangup_call(
-            self.log, b, calls_in_dialing_or_active[0]):
+        # Hangup the *only* call on 'callee'
+        if not car_telecom_utils.hangup_call(self.log, callee,
+                                             calls_in_dialing_or_active[0]):
             return False
 
+        time.sleep(SHORT_TIMEOUT)
         # Make sure everyone got out of in call state.
         for d in self.android_devices:
-            ret &= car_telecom_utils.wait_for_not_in_call(self.log, d)
+            ret &= tel_telecom_utils.wait_for_not_in_call(self.log, d)
         return ret
diff --git a/acts/tests/google/bt/car_bt/BtCarMapMceTest.py b/acts/tests/google/bt/car_bt/BtCarMapMceTest.py
new file mode 100644
index 0000000..ca44ea3
--- /dev/null
+++ b/acts/tests/google/bt/car_bt/BtCarMapMceTest.py
@@ -0,0 +1,177 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Automated tests for the testing send and receive SMS commands in MAP profile.
+"""
+
+import time
+import queue
+
+import acts
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.tel.tel_defines import EventSmsReceived
+from acts.test_utils.tel.tel_defines import EventSmsSentSuccess
+from acts.test_utils.tel.tel_defines import EventSmsDeliverSuccess
+from acts.test_utils.tel.tel_test_utils import get_phone_number
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+
+EVENT_MAP_MESSAGE_RECEIVED = "MapMessageReceived"
+TIMEOUT = 2000
+MESSAGE_TO_SEND = "Don't text and Drive!"
+
+SEND_FAILED_NO_MCE = 1
+SEND_FAILED_NO_NETWORK = 2
+
+
+class BtCarMapMceTest(BluetoothCarHfpBaseTest):
+    def setup_class(self):
+        if not super(BtCarMapMceTest, self).setup_class():
+            return False
+        # MAP roles
+        # Carkit device
+        self.MCE = self.hf
+        # Phone device
+        self.MSE = self.ag
+        # Remote device
+        self.REMOTE = self.re
+        time.sleep(4)
+        return True
+
+    def message_delivered(self, device):
+        try:
+            self.MCE.ed.pop_event(EventSmsDeliverSuccess, 15)
+        except queue.Empty:
+            self.log.error("Message failed to be delivered.")
+            return False
+        return True
+
+    def send_message(self, remotes):
+        self.REMOTE.droid.smsStartTrackingIncomingSmsMessage()
+        destinations = []
+        for phone in remotes:
+            destinations.append("tel:{}".format(
+                get_phone_number(self.log, phone)))
+        self.log.info(destinations)
+        self.MCE.droid.mapSendMessage(
+            self.MSE.droid.bluetoothGetLocalAddress(), destinations,
+            MESSAGE_TO_SEND)
+        try:
+            self.MCE.ed.pop_event(EventSmsSentSuccess, 15)
+        except queue.Empty:
+            self.MCE.log.error("Message failed to send.")
+            return False
+
+        try:
+            receivedMessage = self.REMOTE.ed.pop_event(EventSmsReceived, 15)
+            self.REMOTE.log.info("Received a message: {}".format(
+                receivedMessage['data']['Text']))
+        except queue.Empty:
+            self.REMOTE.log.error("Remote did not receive message.")
+            return False
+
+        if MESSAGE_TO_SEND != receivedMessage['data']['Text']:
+            self.log.error("Messages don't match.")
+            self.log.error("Sent     {}".format(MESSAGE_TO_SEND))
+            self.log.error("Received {}".format(receivedMessage['data'][
+                'Text']))
+            return False
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_send_message(self):
+        bt_test_utils.connect_pri_to_sec(
+            self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
+        return self.send_message([self.REMOTE])
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_receive_message(self):
+        bt_test_utils.connect_pri_to_sec(
+            self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
+        self.MSE.log.info("Start Tracking SMS.")
+        self.MSE.droid.smsStartTrackingIncomingSmsMessage()
+        self.REMOTE.log.info("Ready to send")
+        self.REMOTE.droid.smsSendTextMessage(
+            get_phone_number(self.log, self.MSE), "test_receive_message",
+            False)
+        self.MCE.log.info("Check inbound Messages.")
+        receivedMessage = self.MCE.ed.pop_event(EVENT_MAP_MESSAGE_RECEIVED, 15)
+        self.MCE.log.info(receivedMessage['data'])
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_send_message_failure_no_cellular(self):
+        if not toggle_airplane_mode(self.log, self.MSE, True):
+            return False
+        bt_test_utils.reset_bluetooth([self.MSE])
+        bt_test_utils.connect_pri_to_sec(
+            self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
+        return not self.send_message([self.REMOTE])
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_send_message_failure_no_map_connection(self):
+        return not self.send_message([self.REMOTE])
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_send_message_failure_no_bluetooth(self):
+        if not toggle_airplane_mode(self.log, self.MSE, True):
+            return False
+        try:
+            bt_test_utils.connect_pri_to_sec(
+                self.MCE, self.MSE,
+                set([BtEnum.BluetoothProfile.MAP_MCE.value]))
+        except acts.controllers.android.SL4AAPIError:
+            self.MCE.log.info("Failed to connect as expected")
+        return not self.send_message([self.REMOTE])
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_disconnect_failure_send_message(self):
+        connected = bt_test_utils.connect_pri_to_sec(
+            self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
+        addr = self.MSE.droid.bluetoothGetLocalAddress()
+        if bt_test_utils.is_map_mce_device_connected(self.MCE, addr):
+            connected = True
+        disconnected = bt_test_utils.disconnect_pri_from_sec(
+            self.MCE, self.MSE, [BtEnum.BluetoothProfile.MAP_MCE.value])
+        # Grace time for the disconnection to complete.
+        time.sleep(3)
+        if not bt_test_utils.is_map_mce_device_connected(self.MCE, addr):
+            disconnected = True
+        self.MCE.log.info("Connected = {}, Disconnected = {}".format(
+            connected, disconnected))
+        return connected and disconnected and not self.send_message(
+            [self.REMOTE])
+
+    @BluetoothBaseTest.bt_test_wrap
+    def manual_test_send_message_to_contact(self):
+        bt_test_utils.connect_pri_to_sec(
+            self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
+        contacts = self.MCE.droid.contactsGetContactIds()
+        self.log.info(contacts)
+        selected_contact = self.MCE.droid.contactsDisplayContactPickList()
+        if selected_contact:
+            return self.MCE.droid.mapSendMessage(
+                self.MSE.droid.bluetoothGetLocalAddress(),
+                selected_contact['data'], "Don't Text and Drive!")
+        return False
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_send_message_to_multiple_phones(self):
+        bt_test_utils.connect_pri_to_sec(
+            self.MCE, self.MSE, set([BtEnum.BluetoothProfile.MAP_MCE.value]))
+        return self.send_message([self.REMOTE, self.REMOTE])
diff --git a/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py b/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
new file mode 100644
index 0000000..b99a7c3
--- /dev/null
+++ b/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
@@ -0,0 +1,168 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Automated tests for the testing Connectivity of Avrcp/A2dp profile.
+"""
+
+import time
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.car import car_bt_utils
+from acts.test_utils.car import car_media_utils
+from acts.test_utils.bt import BtEnum
+
+
+class BtCarMediaConnectionTest(BluetoothBaseTest):
+    def setup_class(self):
+        # AVRCP roles
+        self.CT = self.android_devices[0]
+        self.TG = self.android_devices[1]
+        # A2DP roles for the same devices
+        self.SNK = self.CT
+        self.SRC = self.TG
+
+        # Setup devices
+        bt_test_utils.setup_multiple_devices_for_bt_test([self.CT, self.TG])
+
+        self.btAddrCT = self.CT.droid.bluetoothGetLocalAddress()
+        self.btAddrTG = self.TG.droid.bluetoothGetLocalAddress()
+
+        # Additional time from the stack reset in setup.
+        time.sleep(4)
+        # Pair the devices.
+        if not bt_test_utils.pair_pri_to_sec(
+                self.CT, self.TG, attempts=4, auto_confirm=False):
+            self.log.error("Failed to pair")
+            return False
+
+        # Disable all
+        car_bt_utils.set_car_profile_priorities_off(self.SNK, self.SRC)
+
+        # Enable A2DP
+        bt_test_utils.set_profile_priority(
+            self.SNK, self.SRC, [BtEnum.BluetoothProfile.A2DP_SINK],
+            BtEnum.BluetoothPriorityLevel.PRIORITY_ON)
+
+    def is_a2dp_connected(self, device1, device2):
+        """
+        Convenience Function to see if the 2 devices are connected on
+        A2dp.
+        ToDo: Move to bt_test_utils if used in more places.
+        Args:
+            device1:    Device 1
+            device2:    Device 2
+        Returns:
+            True if Connected
+            False if Not connected
+        """
+        devices = device1.droid.bluetoothA2dpSinkGetConnectedDevices()
+        for device in devices:
+            self.device1.log.info("A2dp Connected device {}".format(device[
+                "name"]))
+            if (device["address"] == device2.droid.bluetoothGetLocalAddress()):
+                return True
+        return False
+
+    #@BluetoothTest(UUID=1934c0d5-3fa3-43e5-a91f-2c8a4424f5cd)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_a2dp_connect_disconnect_from_src(self):
+        """
+        Test Connect/Disconnect on A2DP profile.
+
+        Pre-Condition:
+        1. Devices previously bonded and NOT connected on A2dp
+
+        Steps:
+        1. Initiate a connection on A2DP profile from SRC
+        2. Check if they connected.
+        3. Initiate a disconnect on A2DP profile from SRC
+        4. Ensure they disconnected on A2dp alone
+
+        Returns:
+        True    if we connected/disconnected successfully
+        False   if we did not connect/disconnect successfully
+
+        Priority: 0
+        """
+        if (car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC)):
+            self.log.info("Already Connected")
+        else:
+            if (not bt_test_utils.connect_pri_to_sec(self.SRC, self.SNK, set(
+                [BtEnum.BluetoothProfile.A2DP.value]))):
+                return False
+
+        result = bt_test_utils.disconnect_pri_from_sec(
+            self.SRC, self.SNK, [BtEnum.BluetoothProfile.A2DP.value])
+        # Grace timeout to allow a2dp time to disconnect
+        time.sleep(3)
+        if not result:
+            # Additional profile connection check for b/
+            if bt_test_utils.is_a2dp_src_device_connected(
+                    self.SRC, self.SNK.droid.bluetoothGetLocalAddress()):
+                self.SRC.log.error("Failed to disconnect on A2dp")
+                return False
+        # Logging if we connected right back, since that happens sometimes
+        # Not failing the test if it did though
+        if (car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC)):
+            self.SNK.log.error("Still connected after a disconnect")
+
+        return True
+
+    #@BluetoothTest(UUID=70d30007-540a-4e86-bd75-ab218774350e)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_a2dp_connect_disconnect_from_snk(self):
+        """
+        Test Connect/Disconnect on A2DP Sink profile.
+
+        Pre-Condition:
+        1. Devices previously bonded and NOT connected on A2dp
+
+        Steps:
+        1. Initiate a connection on A2DP Sink profile from SNK
+        2. Check if they connected.
+        3. Initiate a disconnect on A2DP Sink profile from SNK
+        4. Ensure they disconnected on A2dp alone
+
+        Returns:
+        True    if we connected/disconnected successfully
+        False   if we did not connect/disconnect successfully
+
+        Priority: 0
+        """
+        # Connect
+        if car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC):
+            self.log.info("Already Connected")
+        else:
+            if (not bt_test_utils.connect_pri_to_sec(self.SNK, self.SRC, set(
+                [BtEnum.BluetoothProfile.A2DP_SINK.value]))):
+                return False
+        # Disconnect
+        result = bt_test_utils.disconnect_pri_from_sec(
+            self.SNK, self.SRC, [BtEnum.BluetoothProfile.A2DP_SINK.value])
+        # Grace timeout to allow a2dp time to disconnect
+        time.sleep(3)
+        if not result:
+            # Additional profile connection check for b/
+            if bt_test_utils.is_a2dp_snk_device_connected(
+                    self.SNK, self.SRC.droid.bluetoothGetLocalAddress()):
+                self.SNK.log.error("Failed to disconnect on A2dp Sink")
+                return False
+        # Logging if we connected right back, since that happens sometimes
+        # Not failing the test if it did though
+        if car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC):
+            self.SNK.log.error("Still connected after a disconnect")
+        return True
diff --git a/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py b/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
new file mode 100644
index 0000000..9585449
--- /dev/null
+++ b/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
@@ -0,0 +1,428 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Automated tests for the testing passthrough commands in Avrcp/A2dp profile.
+"""
+
+import os
+import time
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.bt import BtEnum
+from acts.test_utils.car import car_media_utils
+from acts.utils import exe_cmd
+from acts.controllers import adb
+
+DEFAULT_WAIT_TIME = 1.0
+DEFAULT_EVENT_TIMEOUT = 1.0
+PHONE_MEDIA_BROWSER_SERVICE_NAME = 'BluetoothSL4AAudioSrcMBS'
+CAR_MEDIA_BROWSER_SERVICE_NAME = 'A2dpMediaBrowserService'
+# This test requires some media files to play, skip and compare metadata.
+# The setup part of BtCarMediaPassthroughTest pushes media files from
+# the local_media_path in user params defined below to ANDROID_MEDIA_PATH
+# via adb. Before running these tests, place some media files in your host
+# location.
+ANDROID_MEDIA_PATH = '/sdcard/Music/test'
+
+
+class BtCarMediaPassthroughTest(BluetoothBaseTest):
+    local_media_path = ""
+
+    def setup_class(self):
+        if not super(BtCarMediaPassthroughTest, self).setup_class():
+            return False
+        # AVRCP roles
+        self.CT = self.android_devices[0]
+        self.TG = self.android_devices[1]
+        # A2DP roles for the same devices
+        self.SNK = self.CT
+        self.SRC = self.TG
+        # To keep track of the state of the MediaBrowserService
+        self.mediaBrowserServiceRunning = False
+        self.btAddrCT = self.CT.droid.bluetoothGetLocalAddress()
+        self.btAddrTG = self.TG.droid.bluetoothGetLocalAddress()
+        self.android_music_path = ANDROID_MEDIA_PATH
+
+        if not "local_media_path" in self.user_params.keys():
+            self.log.error(
+                "Missing mandatory user config \"local_media_path\"!")
+            return False
+        self.local_media_path = self.user_params["local_media_path"]
+        if not os.path.isdir(self.local_media_path):
+            self.local_media_path = os.path.join(
+                self.user_params[Config.key_config_path],
+                self.local_media_path)
+            if not os.path.isdir(self.local_media_path):
+                self.log.error("Unable to load user config " + self.
+                               local_media_path + " from test config file.")
+                return False
+
+        # Additional time from the stack reset in setup.
+        time.sleep(4)
+        # Pair and connect the devices.
+        if not bt_test_utils.pair_pri_to_sec(
+                self.CT, self.TG, attempts=4, auto_confirm=False):
+            self.log.error("Failed to pair")
+            return False
+
+        # TODO - check for Avrcp Connection state as well.
+        # For now, the passthrough tests will catch Avrcp Connection failures
+        # But add an explicit test for it.
+        bt_test_utils.connect_pri_to_sec(
+            self.SNK, self.SRC, set([BtEnum.BluetoothProfile.A2DP_SINK.value]))
+
+        # Push media files from self.local_media_path to ANDROID_MEDIA_PATH
+        # Refer to note in the beginning of file
+        self.TG.adb.push("{} {}".format(self.local_media_path,
+                                        self.android_music_path))
+
+        return True
+
+    def _init_mbs(self):
+        """
+        This is required to be done before running any of the passthrough
+        commands.
+        1. Starts up the AvrcpMediaBrowserService on the TG.
+           This MediaBrowserService is part of the SL4A app
+        2. Connects a MediaBrowser to the Carkitt's A2dpMediaBrowserService
+        """
+        if (not self.mediaBrowserServiceRunning):
+            self.TG.log.info("Starting AvrcpMediaBrowserService")
+            self.TG.droid.bluetoothMediaPhoneSL4AMBSStart()
+            time.sleep(DEFAULT_WAIT_TIME)
+            self.mediaBrowserServiceRunning = True
+
+        self.CT.droid.bluetoothMediaConnectToCarMBS()
+        #TODO - Wait for an event back instead of sleep
+        time.sleep(DEFAULT_WAIT_TIME)
+
+    def teardown_test(self):
+        # Stop the browser service if it is running to clean up the slate.
+        if self.mediaBrowserServiceRunning:
+            self.TG.log.info("Stopping AvrcpMediaBrowserService")
+            self.TG.droid.bluetoothMediaPhoneSL4AMBSStop()
+            self.mediaBrowserServiceRunning = False
+        if not super(BtCarMediaPassthroughTest, self).teardown_test():
+            return False
+        # If A2dp connection was disconnected as part of the test, connect it back
+        if not (car_media_utils.is_a2dp_connected(self.log, self.SNK,
+                                                  self.SRC)):
+            result = bt_test_utils.connect_pri_to_sec(
+                self.SRC, self.SNK, set([BtEnum.BluetoothProfile.A2DP.value]))
+            if not result:
+                if not bt_test_utils.is_a2dp_src_device_connected(
+                        self.SRC, self.SNK.droid.bluetoothGetLocalAddress()):
+                    self.SRC.log.error("Failed to connect on A2dp")
+                    return False
+        return True
+
+    #@BluetoothTest(UUID=cf4fae08-f4f6-4e0d-b00a-4f6c41d69ff9)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_play_pause(self):
+        """
+        Test the Play and Pause passthrough commands
+
+        Pre-Condition:
+        1. Devices previously bonded & Connected
+
+        Steps:
+        1. Invoke Play, Pause from CT
+        2. Wait to receive the corresponding received event from TG
+
+        Returns:
+        True    if the event was received
+        False   if the event was not received
+
+        Priority: 0
+        """
+        # Set up the MediaBrowserService
+        self._init_mbs()
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PLAY,
+                car_media_utils.EVENT_PLAY_RECEIVED, DEFAULT_EVENT_TIMEOUT):
+            return False
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PAUSE,
+                car_media_utils.EVENT_PAUSE_RECEIVED, DEFAULT_EVENT_TIMEOUT):
+            return False
+        return True
+
+    #@BluetoothTest(UUID=15615b26-3a49-4fa0-b369-41962e8de192)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_passthrough(self):
+        """
+        Test the Skip Next & Skip Previous passthrough commands
+
+        Pre-Condition:
+        1. Devices previously bonded & Connected
+
+        Steps:
+        1. Invoke other passthrough commands (skip >> & <<) from CT
+        2. Wait to receive the corresponding received event from TG
+
+        Returns:
+        True    if the event was received
+        False   if the event was not received
+
+        Priority: 0
+        """
+        # Set up the MediaBrowserService
+        self._init_mbs()
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG,
+                car_media_utils.CMD_MEDIA_SKIP_NEXT,
+                car_media_utils.EVENT_SKIP_NEXT_RECEIVED,
+                DEFAULT_EVENT_TIMEOUT):
+            return False
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG,
+                car_media_utils.CMD_MEDIA_SKIP_PREV,
+                car_media_utils.EVENT_SKIP_PREV_RECEIVED,
+                DEFAULT_EVENT_TIMEOUT):
+            return False
+
+        # Just pause media before test ends
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PAUSE,
+                car_media_utils.EVENT_PAUSE_RECEIVED):
+            return False
+
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_media_metadata(self):
+        """
+        Test if the metadata matches between the two ends.
+        Send some random sequence of passthrough commands and compare metadata.
+        TODO: truely randomize of the seq of passthrough commands.
+        Pre-Condition:
+        1. Devices previously bonded & Connected
+
+        Steps:
+        1. Invoke Play from CT
+        2. Compare the metadata between CT and TG. Fail if they don't match
+        3. Send Skip Next from CT
+        4. Compare the metadata between CT and TG. Fail if they don't match
+        5. Repeat steps 3 & 4
+        6. Send Skip Prev from CT
+        7. Compare the metadata between CT and TG. Fail if they don't match
+
+        Returns:
+        True    if the metadata matched all the way
+        False   if there was a metadata mismatch at any point
+
+        Priority: 0
+        """
+        if not (car_media_utils.is_a2dp_connected(self.log, self.SNK,
+                                                  self.SRC)):
+            self.SNK.log.error('No A2dp Connection')
+            return False
+
+        self._init_mbs()
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PLAY,
+                car_media_utils.EVENT_PLAY_RECEIVED, DEFAULT_EVENT_TIMEOUT):
+            return False
+        time.sleep(DEFAULT_WAIT_TIME)
+        if not car_media_utils.check_metadata(self.log, self.TG, self.CT):
+            return False
+
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG,
+                car_media_utils.CMD_MEDIA_SKIP_NEXT,
+                car_media_utils.EVENT_SKIP_NEXT_RECEIVED,
+                DEFAULT_EVENT_TIMEOUT):
+            return False
+        time.sleep(DEFAULT_WAIT_TIME)
+        if not car_media_utils.check_metadata(self.log, self.TG, self.CT):
+            return False
+
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG,
+                car_media_utils.CMD_MEDIA_SKIP_NEXT,
+                car_media_utils.EVENT_SKIP_NEXT_RECEIVED,
+                DEFAULT_EVENT_TIMEOUT):
+            return False
+        time.sleep(DEFAULT_WAIT_TIME)
+        if not car_media_utils.check_metadata(self.log, self.TG, self.CT):
+            return False
+
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG,
+                car_media_utils.CMD_MEDIA_SKIP_PREV,
+                car_media_utils.EVENT_SKIP_PREV_RECEIVED,
+                DEFAULT_EVENT_TIMEOUT):
+            return False
+        time.sleep(DEFAULT_WAIT_TIME)
+        if not car_media_utils.check_metadata(self.log, self.TG, self.CT):
+            return False
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_disconnect_while_media_playing(self):
+        """
+        Disconnect BT between CT and TG in the middle of a audio streaming session and check
+        1) If TG continues to still play music
+        2) If CT stops playing
+
+        Pre-Condition:
+        1. Devices previously bonded & Connected
+
+        Steps:
+        1. Invoke Play from CT
+        2. Check if both CT and TG are playing music by checking if the respective
+           MediaSessions are active
+        3. Fail if either the CT or TG is not playing
+        4. Disconnect Bluetooth connection between CT and TG
+        5. Check if the CT MediaSession stopped being active.
+           Fail if its mediasession is still active.
+        6. Check if the TG MediaSession is still active.
+        7. Fail if TG stopped playing music.
+
+        Returns:
+        True    if the CT stopped playing audio and the TG continued after BT disconnect
+        False   if the CT still was playing audio or TG stopped after BT disconnect.
+
+        Priority: 0
+        """
+        self._init_mbs()
+        self.log.info("Sending Play command from Car")
+        if not car_media_utils.send_media_passthrough_cmd(
+                self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PLAY,
+                car_media_utils.EVENT_PLAY_RECEIVED, DEFAULT_EVENT_TIMEOUT):
+            return False
+
+        time.sleep(DEFAULT_WAIT_TIME)
+
+        self.TG.log.info("Phone Media Sessions:")
+        if not car_media_utils.isMediaSessionActive(
+                self.log, self.TG, PHONE_MEDIA_BROWSER_SERVICE_NAME):
+            self.TG.log.error("Media not playing in connected Phone")
+            return False
+
+        self.CT.log.info("Car Media Sessions:")
+        if not car_media_utils.isMediaSessionActive(
+                self.log, self.CT, CAR_MEDIA_BROWSER_SERVICE_NAME):
+            self.CT.log.error("Media not playing in connected Car")
+            return False
+
+        self.log.info("Bluetooth Disconnect the car and phone")
+        result = bt_test_utils.disconnect_pri_from_sec(
+            self.SRC, self.SNK, [BtEnum.BluetoothProfile.A2DP.value])
+        if not result:
+            if bt_test_utils.is_a2dp_src_device_connected(
+                    self.SRC, self.SNK.droid.bluetoothGetLocalAddress()):
+                self.SRC.log.error("Failed to disconnect on A2dp")
+                return False
+
+        self.TG.log.info("Phone Media Sessions:")
+        if not car_media_utils.isMediaSessionActive(
+                self.log, self.TG, PHONE_MEDIA_BROWSER_SERVICE_NAME):
+            self.TG.log.error(
+                "Media stopped playing in phone after BT disconnect")
+            return False
+
+        self.CT.log.info("Car Media Sessions:")
+        if car_media_utils.isMediaSessionActive(
+                self.log, self.CT, CAR_MEDIA_BROWSER_SERVICE_NAME):
+            self.CT.log.error(
+                "Media still playing in a Car after BT disconnect")
+            return False
+
+        return True
+
+    @BluetoothBaseTest.bt_test_wrap
+    def test_connect_while_media_playing(self):
+        """
+        BT connect SRC and SNK when the SRC is already playing music and verify SNK strarts streaming
+        after connection.
+        Connect to another device (Audio Sink) via BT while it is playing audio.
+        Check if the audio starts streaming on the Sink.
+
+        Pre-Condition:
+        1. Devices previously bonded & Connected
+
+        Steps:
+        1. Disconnect TG from CT (since they are connected as a precondition)
+        2. Play Music on TG (Audio SRC)
+        3. Get the metadata of the playing music
+        4. Connect TG and CT
+        5. Check if the music is streaming on CT (Audio SNK) by checking if its MediaSession became active.
+        6. Fail if CT is not streaming.
+        7. Get the metdata from the CT (Audio SNK) and compare it with the metadata from Step 3
+        8. Fail if the metadata did not match.
+
+        Returns:
+        True    if the event was received
+        False   if the event was not received
+
+        Priority: 0
+        """
+        self.log.info("Bluetooth Disconnect the car and phone")
+        result = bt_test_utils.disconnect_pri_from_sec(
+            self.SRC, self.SNK, [BtEnum.BluetoothProfile.A2DP.value])
+        if not result:
+            # Temporary timeout
+            time.sleep(3)
+            if bt_test_utils.is_a2dp_src_device_connected(
+                    self.SRC, self.SNK.droid.bluetoothGetLocalAddress()):
+                self.SRC.log.error("Failed to disconnect on A2dp")
+                return False
+
+        self._init_mbs()
+
+        # Play Media on Phone
+        self.TG.droid.bluetoothMediaHandleMediaCommandOnPhone(
+            car_media_utils.CMD_MEDIA_PLAY)
+        # At this point, media should be playing only on phone, not on Car, since they are disconnected
+        if not car_media_utils.isMediaSessionActive(
+                self.log, self.TG,
+                PHONE_MEDIA_BROWSER_SERVICE_NAME) or car_media_utils.isMediaSessionActive(
+                    self.log, self.CT, CAR_MEDIA_BROWSER_SERVICE_NAME):
+            self.log.error("Media playing in wrong end")
+            return False
+
+        # Get the metadata of the song that the phone is playing
+        metadata_TG = self.TG.droid.bluetoothMediaGetCurrentMediaMetaData()
+        if metadata_TG is None:
+            self.TG.log.error("No Media Metadata available from Phone")
+            return False
+
+        # Now connect to Car on Bluetooth
+        if (not bt_test_utils.connect_pri_to_sec(self.SRC, self.SNK, set(
+            [BtEnum.BluetoothProfile.A2DP.value]))):
+            return False
+
+        # Wait for a bit for the information to show up in the car side
+        time.sleep(2)
+
+        # At this point, since we have connected while the Phone was playing media, the car
+        # should automatically play.  Both devices should have their respective MediaSessions active
+        if not car_media_utils.isMediaSessionActive(
+                self.log, self.TG, PHONE_MEDIA_BROWSER_SERVICE_NAME):
+            self.TG.log.error("Media not playing in Phone")
+            return False
+        if not car_media_utils.isMediaSessionActive(
+                self.log, self.CT, CAR_MEDIA_BROWSER_SERVICE_NAME):
+            self.CT.log.error("Media not playing in Car")
+            return False
+
+        # Get the metadata from Car and compare it with the Phone's media metadata before the connection happened.
+        metadata_CT = self.CT.droid.bluetoothMediaGetCurrentMediaMetaData()
+        if metadata_CT is None:
+            self.CT.log.info("No Media Metadata available from car")
+        return car_media_utils.compare_metadata(self.log, metadata_TG,
+                                                metadata_CT)
diff --git a/acts/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py b/acts/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py
index 00ec028..635ba86 100644
--- a/acts/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py
@@ -13,7 +13,6 @@
 # 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 test connect and disconnect sequence between two devices which can run
 SL4A. The script does the following:
@@ -28,38 +27,69 @@
 
 import time
 
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.base_test import BaseTestClass
 from acts.test_utils.bt import bt_test_utils
 from acts.test_utils.bt import BtEnum
 from acts import asserts
 
-class BtCarPairedConnectDisconnectTest(BaseTestClass):
+
+class BtCarPairedConnectDisconnectTest(BluetoothBaseTest):
     def setup_class(self):
         self.car = self.android_devices[0]
         self.ph = self.android_devices[1]
         self.car_bt_addr = self.car.droid.bluetoothGetLocalAddress()
         self.ph_bt_addr = self.ph.droid.bluetoothGetLocalAddress()
 
-    def setup_test(self):
-        # Reset the devices in a clean state.
-        bt_test_utils.setup_multiple_devices_for_bt_test(self.android_devices)
-        bt_test_utils.reset_bluetooth(self.android_devices)
-        for a in self.android_devices:
-            a.ed.clear_all_events()
+        bt_test_utils.setup_multiple_devices_for_bt_test([self.car, self.ph])
 
         # Pair the devices.
         # This call may block until some specified timeout in bt_test_utils.py.
-        result = bt_test_utils.pair_pri_to_sec(self.car.droid, self.ph.droid)
+        result = bt_test_utils.pair_pri_to_sec(
+            self.car, self.ph, auto_confirm=False)
 
-        asserts.assert_true(result, "pair_pri_to_sec returned false.");
+        asserts.assert_true(result, "pair_pri_to_sec returned false.")
 
         # Check for successful setup of test.
         devices = self.car.droid.bluetoothGetBondedDevices()
-        asserts.assert_equal(len(devices), 1, "pair_pri_to_sec succeeded but no bonded devices.")
+        asserts.assert_equal(
+            len(devices), 1,
+            "pair_pri_to_sec succeeded but no bonded devices.")
 
-    def on_fail(self, test_name, begin_time):
-        bt_test_utils.take_btsnoop_logs(self.android_devices, self, test_name)
+    #@BluetoothTest(UUID=b0babf3b-8049-4b64-9125-408efb1bbcd2)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_pairing(self):
+        """
+        Tests if we can connect two devices over A2dp and then disconnect
 
+        Precondition:
+        1. Devices are paired.
+
+        Steps:
+        1. Set the priority to OFF for all profiles.
+        2. Initiate connection over A2dp Sink client profile.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        """
+        # Set the priority to OFF for all profiles.
+        self.car.droid.bluetoothHfpClientSetPriority(
+            self.ph.droid.bluetoothGetLocalAddress(),
+            BtEnum.BluetoothPriorityLevel.PRIORITY_OFF.value)
+        self.ph.droid.bluetoothHspSetPriority(
+            self.car.droid.bluetoothGetLocalAddress(),
+            BtEnum.BluetoothPriorityLevel.PRIORITY_OFF.value)
+        addr = self.ph.droid.bluetoothGetLocalAddress()
+        if not bt_test_utils.connect_pri_to_sec(self.car, self.ph, set(
+            [BtEnum.BluetoothProfile.A2DP_SINK.value])):
+            if not bt_test_utils.is_a2dp_snk_device_connected(self.car, addr):
+                return False
+        return True
+
+    #@BluetoothTest(UUID=a44f13e2-c012-4292-8dd5-9f32a023e297)
+    @BluetoothBaseTest.bt_test_wrap
     def test_connect_disconnect_paired(self):
         """
         Tests if we can connect two devices over Headset, A2dp and then disconnect them with success
@@ -80,45 +110,47 @@
 
         NUM_TEST_RUNS = 2
         failure = 0
+        addr = self.ph.droid.bluetoothGetLocalAddress()
         for i in range(NUM_TEST_RUNS):
-            self.log.info("Running test [" + str(i) + "/" + str(NUM_TEST_RUNS) + "]")
-            success = bt_test_utils.connect_pri_to_sec(
-                self.log, self.car, self.ph.droid,
-                set([BtEnum.BluetoothProfile.HEADSET_CLIENT.value,
-                     BtEnum.BluetoothProfile.A2DP_SINK.value]))
+            self.log.info("Running test [" + str(i) + "/" + str(NUM_TEST_RUNS)
+                          + "]")
+            success = bt_test_utils.connect_pri_to_sec(self.car, self.ph, set(
+                [BtEnum.BluetoothProfile.HEADSET_CLIENT.value,
+                 BtEnum.BluetoothProfile.A2DP_SINK.value]))
 
             # Check if we got connected.
             if not success:
-                self.log.info("Not all profiles connected.")
-                failure = failure + 1
+                self.car.log.info("Not all profiles connected.")
+                if (bt_test_utils.is_hfp_client_device_connected(self.car,
+                                                                 addr) and
+                        bt_test_utils.is_a2dp_snk_device_connected(self.car,
+                                                                   addr)):
+                    self.car.log.info(
+                        "HFP Client or A2DP SRC connected successfully.")
+                else:
+                    failure = failure + 1
                 continue
 
             # Disconnect the devices.
-            self.log.info("Attempt to disconnect.")
-            self.car.droid.bluetoothDisconnectConnected(self.ph_bt_addr)
+            success = bt_test_utils.disconnect_pri_from_sec(
+                self.car, self.ph,
+                [BtEnum.BluetoothProfile.HEADSET_CLIENT.value,
+                 BtEnum.BluetoothProfile.A2DP_SINK.value])
 
-            end_time = time.time() + 10
-            disconnected = False
-            # Busy loop to check if we have successfully disconnected from the
-            # device
-            while time.time() < end_time:
-                connectedDevices = self.car.droid.bluetoothGetConnectedDevices()
-                exists = False
-                connected_devices = \
-                    self.car.droid.bluetoothGetConnectedDevices()
-                for d in connected_devices:
-                  if d['address'] == self.ph_bt_addr:
-                      exists = True
-                      break
-                if exists is False:
-                    disconnected = True
-                    break
-                time.sleep(1)
-
-            if disconnected is False:
-                self.log.info("Still connected devices.")
-                failure = failure + 1
+            if success is False:
+                self.car.log.info("Disconnect failed.")
+                if (bt_test_utils.is_hfp_client_device_connected(self.car,
+                                                                 addr) or
+                        bt_test_utils.is_a2dp_snk_device_connected(self.car,
+                                                                   addr)):
+                    self.car.log.info(
+                        "HFP Client or A2DP SRC failed to disconnect.")
+                    failure = failure + 1
                 continue
-        self.log.info("Failure {} total tests {}".format(failure, NUM_TEST_RUNS))
-        asserts.assert_equal(failure, 0, "")
+
+        self.log.info("Failure {} total tests {}".format(failure,
+                                                         NUM_TEST_RUNS))
+        if failure > 0:
+            return False
+        return True
 
diff --git a/acts/tests/google/bt/car_bt/BtCarPairingTest.py b/acts/tests/google/bt/car_bt/BtCarPairingTest.py
new file mode 100644
index 0000000..12a58c9
--- /dev/null
+++ b/acts/tests/google/bt/car_bt/BtCarPairingTest.py
@@ -0,0 +1,157 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Test script to test the pairing scenarios and setting priorities.
+"""
+
+import time
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.base_test import BaseTestClass
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.car import car_bt_utils
+from acts.test_utils.bt import BtEnum
+
+# Timed wait between Bonding happens and Android actually gets the list of
+# supported services (and subsequently updates the priorities)
+BOND_TO_SDP_WAIT = 3
+UNBOND_TIMEOUT = 3
+
+
+class BtCarPairingTest(BluetoothBaseTest):
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        self.car = self.android_devices[0]
+        self.ph = self.android_devices[1]
+
+    #@BluetoothTest(UUID=bf56e915-eef7-45cd-b5a6-771f6ef72602)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_simple_pairing(self):
+        """
+        Tests if after first pairing the remote device has the default
+        priorities for A2DP and HFP.
+
+        Steps:
+        1. Pair the devices (do not connect)
+        2. Check the priorities.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 0
+        """
+        # Pair the devices.
+        if not bt_test_utils.pair_pri_to_sec(
+                self.car, self.ph, attempts=1, auto_confirm=False):
+            self.log.error("cannot pair")
+            return False
+
+        # Sleep because priorities are not event driven.
+        time.sleep(BOND_TO_SDP_WAIT)
+
+        # Check that the default priority for HFP and A2DP is ON.
+        ph_hfp_p = self.car.droid.bluetoothHfpClientGetPriority(
+            self.ph.droid.bluetoothGetLocalAddress())
+        if ph_hfp_p != BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value:
+            self.log.error("hfp {} priority {} expected {}".format(
+                self.ph.serial, ph_hfp_p,
+                BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value))
+            return False
+
+        ph_a2dp_p = self.car.droid.bluetoothA2dpSinkGetPriority(
+            self.ph.droid.bluetoothGetLocalAddress())
+        if ph_a2dp_p != BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value:
+            self.log.error("a2dp {} priority {} expected {}".format(
+                self.ph.serial, ph_a2dp_p,
+                BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value))
+            return False
+
+        ph_pbap_p = self.car.droid.bluetoothPbapClientGetPriority(
+            self.ph.droid.bluetoothGetLocalAddress())
+        if ph_pbap_p != BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value:
+            self.log.error("pbap {} priority {} expected {}".format(
+                self.ph.serial, ph_pbap_p,
+                BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value))
+            return False
+        return True
+
+    #@BluetoothTest(UUID=be4db211-10a0-479a-8958-dff0ccadca1a)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_repairing(self):
+        """
+        Tests that even if we modify the priorities, on unpair and pair
+        we will reset the priorities.
+
+        Steps:
+        1. Pair the devices (do not connect)
+        2. Unset the priorities for HFP and A2DP
+        3. Pair again
+        4. Check the priorities, they should be set to default.
+
+        Returns:
+          Pass if True
+          Fail if False
+
+        Priority: 0
+        """
+        # Pair the devices.
+        self.log.info("Pairing the devices ...")
+        if not bt_test_utils.pair_pri_to_sec(
+                self.car, self.ph, attempts=1, auto_confirm=False):
+            self.log.error("Failed to pair devices.")
+            return False
+
+        # Timed wait for the profile priorities to propagate.
+        time.sleep(BOND_TO_SDP_WAIT)
+
+        # Set the priority to OFF for ALL car profiles.
+        self.car.log.info("Set priorities off ...")
+        car_bt_utils.set_car_profile_priorities_off(self.car, self.ph)
+
+        # Now unpair the devices.
+        self.log.info("Resetting the devices ...")
+        for ad in self.android_devices:
+            bt_test_utils.clear_bonded_devices(ad)
+        # Give the stack time to unbond.
+        time.sleep(UNBOND_TIMEOUT)
+
+        # Pair them again!
+        self.log.info("Pairing them again ...")
+        if not bt_test_utils.pair_pri_to_sec(
+                self.car, self.ph, attempts=1, auto_confirm=False):
+            self.log.error("Faild to pair devices.")
+            return False
+
+        # Timed wait for the profile priorities to propagate.
+        time.sleep(BOND_TO_SDP_WAIT)
+
+        # Check the default priorities.
+        ph_hfp_p = self.car.droid.bluetoothHfpClientGetPriority(
+            self.ph.droid.bluetoothGetLocalAddress())
+        if ph_hfp_p != BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value:
+            self.hf.log.error("HFP priority found: {}, expected: {}.".format(
+                ph_hfp_p, BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value))
+            return False
+
+        ph_a2dp_p = self.car.droid.bluetoothA2dpSinkGetPriority(
+            self.ph.droid.bluetoothGetLocalAddress())
+        if ph_a2dp_p != BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value:
+            self.ph.log.error("A2DP priority found: {}, expected {}.".format(
+                ph_a2dp_p, BtEnum.BluetoothPriorityLevel.PRIORITY_ON.value))
+            return False
+
+        return True
diff --git a/acts/tests/google/bt/car_bt/BtCarPbapTest.py b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
new file mode 100644
index 0000000..bacf4c4
--- /dev/null
+++ b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
@@ -0,0 +1,487 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Test script to test PBAP contact download between two devices which can run SL4A.
+"""
+
+import os
+import time
+
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts.base_test import BaseTestClass
+from acts.test_utils.bt import bt_contacts_utils
+from acts.test_utils.bt import bt_test_utils
+from acts.test_utils.car import car_bt_utils
+from acts.utils import exe_cmd
+import acts.test_utils.bt.BtEnum as BtEnum
+
+# Offset call logs by 1 minute
+CALL_LOG_TIME_OFFSET_IN_MSEC = 60000
+PSE_CONTACTS_FILE = "psecontacts.vcf"
+PCE_CONTACTS_FILE = "pcecontacts.vcf"
+MERGED_CONTACTS_FILE = "psecombined.vcf"
+STANDART_CONTACT_COUNT = 100
+
+
+class BtCarPbapTest(BluetoothBaseTest):
+    contacts_destination_path = ""
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.pce = self.android_devices[0]
+        self.pse = self.android_devices[1]
+        self.pse2 = self.android_devices[2]
+        self.contacts_destination_path = self.log_path + "/"
+
+    def setup_class(self):
+        if not super(BtCarPbapTest, self).setup_class():
+            return False
+        permissions_list = [
+            "android.permission.READ_CONTACTS",
+            "android.permission.WRITE_CONTACTS",
+            "android.permission.READ_EXTERNAL_STORAGE"
+        ]
+        for permission in permissions_list:
+            self.pse.adb.shell(
+                "pm grant com.google.android.contacts {}".format(permission))
+        for permission in permissions_list:
+            self.pce.adb.shell("pm grant com.android.contacts {}".format(
+                permission))
+
+        # Pair the devices.
+        # This call may block until some specified timeout in bt_test_utils.py.
+        # Grace time inbetween stack state changes
+
+        setup_multiple_devices_for_bt_test(self.android_devices)
+        if not bt_test_utils.pair_pri_to_sec(self.pce, self.pse):
+            self.log.error("Failed to pair.")
+            return False
+        time.sleep(3)
+        if not bt_test_utils.pair_pri_to_sec(self.pce, self.pse2):
+            self.log.error("Failed to pair.")
+            return False
+
+        # Disable the HFP and A2DP profiles. This will ensure only PBAP
+        # gets connected. Also, this will eliminate the auto-connect loop.
+        car_bt_utils.set_car_profile_priorities_off(self.pce, self.pse)
+        car_bt_utils.set_car_profile_priorities_off(self.pce, self.pse2)
+
+        # Enable PBAP on PSE & PCE.
+
+        self.pse.droid.bluetoothChangeProfileAccessPermission(
+            self.pce.droid.bluetoothGetLocalAddress(),
+            BtEnum.BluetoothProfile.PBAP_SERVER.value,
+            BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value)
+
+        self.pse2.droid.bluetoothChangeProfileAccessPermission(
+            self.pce.droid.bluetoothGetLocalAddress(),
+            BtEnum.BluetoothProfile.PBAP_SERVER.value,
+            BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value)
+
+        bt_test_utils.set_profile_priority(
+            self.pce, self.pse, [BtEnum.BluetoothProfile.PBAP_CLIENT],
+            BtEnum.BluetoothPriorityLevel.PRIORITY_ON)
+        bt_test_utils.set_profile_priority(
+            self.pce, self.pse2, [BtEnum.BluetoothProfile.PBAP_CLIENT],
+            BtEnum.BluetoothPriorityLevel.PRIORITY_ON)
+
+        return True
+
+    def setup_test(self):
+        if not super(BtCarPbapTest, self).setup_test():
+            return False
+        self.pse.droid.callLogsEraseAll()
+        if not (bt_contacts_utils.erase_contacts(self.pse) and
+                bt_contacts_utils.erase_contacts(self.pce)):
+            return False
+        # Allow all content providers to synchronize.
+        time.sleep(1)
+        return True
+
+    def teardown_test(self):
+        if not super(BtCarPbapTest, self).teardown_test():
+            return False
+        self.pce.droid.bluetoothPbapClientDisconnect(
+            self.pse.droid.bluetoothGetLocalAddress())
+        bt_contacts_utils.erase_contacts(self.pse)
+        return True
+
+    def verify_contacts_match(self):
+        bt_contacts_utils.export_device_contacts_to_vcf(
+            self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE)
+        return bt_contacts_utils.count_contacts_with_differences(
+            self.contacts_destination_path, PCE_CONTACTS_FILE,
+            PSE_CONTACTS_FILE) == 0
+
+    def connect_and_verify(self, count):
+        bt_test_utils.connect_pri_to_sec(
+            self.pce, self.pse,
+            set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
+        bt_contacts_utils.wait_for_phone_number_update_complete(self.pce,
+                                                                count)
+        contacts_added = self.verify_contacts_match()
+        self.pce.droid.bluetoothPbapClientDisconnect(
+            self.pse.droid.bluetoothGetLocalAddress())
+        contacts_removed = bt_contacts_utils.wait_for_phone_number_update_complete(
+            self.pce, 0)
+        return contacts_added and contacts_removed
+
+    #@BluetoothTest(UUID=7dcdecfc-42d1-4f41-b66e-823c8f161356)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_pbap_connect_and_disconnect(self):
+        """Test Connectivity
+
+        Test connecting with the server enabled and disabled
+
+        Precondition:
+        1. Devices are paired.
+
+        Steps:
+        1. Disable permission on PSE to prevent PCE from connecting
+        2. Attempt to connect PCE to PSE
+        3. Verify connection failed
+        4. Enable permission on PSE to allow PCE to connect
+        5. Attempt to connect PCE to PSE
+        6. Verify connection succeeded
+
+        Returns:
+            Pass if True
+            Fail if False
+        """
+        self.pse.droid.bluetoothChangeProfileAccessPermission(
+            self.pce.droid.bluetoothGetLocalAddress(),
+            BtEnum.BluetoothProfile.PBAP_SERVER.value,
+            BtEnum.BluetoothAccessLevel.ACCESS_DENIED.value)
+        if bt_test_utils.connect_pri_to_sec(
+                self.pce, self.pse,
+                set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])):
+            self.log.error("Client connected and shouldn't be.")
+            return False
+
+        self.pce.droid.bluetoothPbapClientDisconnect(
+            self.pse.droid.bluetoothGetLocalAddress())
+
+        self.pse.droid.bluetoothChangeProfileAccessPermission(
+            self.pce.droid.bluetoothGetLocalAddress(),
+            BtEnum.BluetoothProfile.PBAP_SERVER.value,
+            BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value)
+
+        if not bt_test_utils.connect_pri_to_sec(
+                self.pce, self.pse,
+                set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])):
+            self.log.error("No client connected and should be.")
+            return False
+
+        return True
+
+    #@BluetoothTest(UUID=1733efb9-71af-4956-bd3a-0d3167d94d0c)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_contact_download(self):
+        """Test Contact Download
+
+        Test download of contacts from a clean state.
+
+        Precondition:
+        1. Devices are paired.
+
+        Steps:
+        1. Erase contacts from PSE and PCE.
+        2. Add a predefined list of contacts to PSE.
+        3. Connect PCE to PSE to perform transfer.
+        4. Compare transfered contacts.
+        5. Disconnect.
+        6. Verify PCE cleaned up contact list.
+
+        Returns:
+            Pass if True
+            Fail if False
+        """
+        bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
+                                                PSE_CONTACTS_FILE, 100)
+        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+            self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE)
+        bt_test_utils.connect_pri_to_sec(
+            self.pce, self.pse,
+            set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
+        bt_contacts_utils.wait_for_phone_number_update_complete(
+            self.pce, phone_numbers_added)
+        if not self.verify_contacts_match():
+            return False
+        return bt_contacts_utils.erase_contacts(self.pce)
+
+    #@BluetoothTest(UUID=99dc6ac6-b7cf-45ce-927b-8c4ebf8ab664)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_modify_phonebook(self):
+        """Test Modify Phonebook
+
+        Test changing contacts and reconnecting PBAP.
+
+        Precondition:
+        1. Devices are paired.
+
+        Steps:
+        1. Add a predefined list of contacts to PSE.
+        2. Connect PCE to PSE to perform transfer.
+        3. Verify that contacts match.
+        4. Change some contacts on the PSE.
+        5. Reconnect PCE to PSE to perform transfer.
+        6. Verify that new contacts match.
+
+        Returns:
+            Pass if True
+            Fail if False
+        """
+        bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
+                                                PSE_CONTACTS_FILE, 100)
+        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+            self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE)
+        if not self.connect_and_verify(phone_numbers_added):
+            return False
+
+        bt_contacts_utils.erase_contacts(self.pse)
+        bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
+                                                PSE_CONTACTS_FILE, 110, 2)
+        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+            self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE)
+        return self.connect_and_verify(phone_numbers_added)
+
+    #@BluetoothTest(UUID=bbe31bf5-51e8-4175-b266-1c7750e44f5b)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_special_contacts(self):
+        """Test Special Contacts
+
+        Test numerous special cases of contacts that could cause errors.
+
+        Precondition:
+        1. Devices are paired.
+
+        Steps:
+        1. Add a predefined list of contacts to PSE that includes special cases:
+        2. Connect PCE to PSE to perform transfer.
+        3. Verify that contacts match.
+
+        Returns:
+            Pass if True
+            Fail if False
+        """
+
+        vcards = []
+
+        # Generate a contact with no email address
+        current_contact = bt_contacts_utils.VCard()
+        current_contact.first_name = "Mr."
+        current_contact.last_name = "Smiley"
+        current_contact.add_phone_number(
+            bt_contacts_utils.generate_random_phone_number())
+        vcards.append(current_contact)
+
+        # Generate a 2nd contact with the same name but different phone number
+        current_contact = bt_contacts_utils.VCard()
+        current_contact.first_name = "Mr."
+        current_contact.last_name = "Smiley"
+        current_contact.add_phone_number(
+            bt_contacts_utils.generate_random_phone_number())
+        vcards.append(current_contact)
+
+        # Generate a contact with no name
+        current_contact = bt_contacts_utils.VCard()
+        current_contact.email = "{}@gmail.com".format(
+            bt_contacts_utils.generate_random_string())
+        current_contact.add_phone_number(
+            bt_contacts_utils.generate_random_phone_number())
+        vcards.append(current_contact)
+
+        # Generate a contact with random characters in its name
+        current_contact = bt_contacts_utils.VCard()
+        current_contact.first_name = bt_contacts_utils.generate_random_string()
+        current_contact.last_name = bt_contacts_utils.generate_random_string()
+        current_contact.add_phone_number(
+            bt_contacts_utils.generate_random_phone_number())
+        vcards.append(current_contact)
+
+        # Generate a contact with only a phone number
+        current_contact = bt_contacts_utils.VCard()
+        current_contact.add_phone_number(
+            bt_contacts_utils.generate_random_phone_number())
+        vcards.append(current_contact)
+
+        # Generate a 2nd contact with only a phone number
+        current_contact = bt_contacts_utils.VCard()
+        current_contact.add_phone_number(
+            bt_contacts_utils.generate_random_phone_number())
+        vcards.append(current_contact)
+
+        bt_contacts_utils.create_new_contacts_vcf_from_vcards(
+            self.contacts_destination_path, PSE_CONTACTS_FILE, vcards)
+
+        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+            self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE)
+
+        return self.connect_and_verify(phone_numbers_added)
+
+    #@BluetoothTest(UUID=2aa2bd00-86cc-4f39-a06a-90b17ea5b320)
+    @BluetoothBaseTest.bt_test_wrap
+    def test_call_log(self):
+        """Test Call Log
+
+        Test that Call Logs are transfered
+
+        Precondition:
+        1. Devices are paired.
+
+        Steps:
+        1. Add a predefined list of calls to the PSE call log.
+        2. Connect PCE to PSE to allow call log transfer
+        3. Verify the Missed, Incoming, and Outgoing Call History
+
+        Returns:
+            Pass if True
+            Fail if False
+        """
+
+        bt_contacts_utils.add_call_log(
+            self.pse, bt_contacts_utils.INCOMMING_CALL_TYPE,
+            bt_contacts_utils.generate_random_phone_number().phone_number,
+            int(time.time() * 1000))
+        bt_contacts_utils.add_call_log(
+            self.pse, bt_contacts_utils.INCOMMING_CALL_TYPE,
+            bt_contacts_utils.generate_random_phone_number().phone_number,
+            int(time.time()) * 1000 - 4 * CALL_LOG_TIME_OFFSET_IN_MSEC)
+        bt_contacts_utils.add_call_log(
+            self.pse, bt_contacts_utils.OUTGOING_CALL_TYPE,
+            bt_contacts_utils.generate_random_phone_number().phone_number,
+            int(time.time()) * 1000 - CALL_LOG_TIME_OFFSET_IN_MSEC)
+        bt_contacts_utils.add_call_log(
+            self.pse, bt_contacts_utils.MISSED_CALL_TYPE,
+            bt_contacts_utils.generate_random_phone_number().phone_number,
+            int(time.time()) * 1000 - 2 * CALL_LOG_TIME_OFFSET_IN_MSEC)
+        bt_contacts_utils.add_call_log(
+            self.pse, bt_contacts_utils.MISSED_CALL_TYPE,
+            bt_contacts_utils.generate_random_phone_number().phone_number,
+            int(time.time()) * 1000 - 2 * CALL_LOG_TIME_OFFSET_IN_MSEC)
+
+        self.pce.droid.bluetoothPbapClientDisconnect(
+            self.pse.droid.bluetoothGetLocalAddress())
+        self.pce.droid.bluetoothPbapClientDisconnect(
+            self.pse2.droid.bluetoothGetLocalAddress())
+
+        bt_test_utils.connect_pri_to_sec(
+            self.pce, self.pse,
+            set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
+        pse_call_log_count = self.pse.droid.callLogGetCount()
+        self.log.info("Waiting for {} call logs to be transfered".format(
+            pse_call_log_count))
+        bt_contacts_utils.wait_for_call_log_update_complete(self.pce,
+                                                            pse_call_log_count)
+
+        if not bt_contacts_utils.get_and_compare_call_logs(
+                self.pse, self.pce, bt_contacts_utils.INCOMMING_CALL_TYPE):
+            return False
+        if not bt_contacts_utils.get_and_compare_call_logs(
+                self.pse, self.pce, bt_contacts_utils.OUTGOING_CALL_TYPE):
+            return False
+        if not bt_contacts_utils.get_and_compare_call_logs(
+                self.pse, self.pce, bt_contacts_utils.MISSED_CALL_TYPE):
+            return False
+
+        return True
+
+    def test_multiple_phones(self):
+        """Test Multiple Phones
+
+        Test that connects two phones and confirms contacts are transfered
+        and merged while still being associated with their original phone.
+
+        Precondition:
+        1. Devices are paired.
+
+        Steps:
+        1. Add a unique list of contacts to PSE on each phone.
+        2. Connect PCE to PSE 1 to perform transfer.
+        3. Verify contacts match.
+        4. Connect PCE to PSE 2 to perform transfer.
+        5. Verify that the PCE has a union set of contacts from
+           PSE 1 and PSE 2.
+        6. Disconnect PCE from PSE 1 to clean up contacts.
+        7. Verify that only PSE 2 contacts remain on PCE and they match.
+        8. Disconnect PCE from PSE 2 to clean up contacts.
+
+        Returns:
+           Pass if True
+           Fail if False
+        """
+
+        PSE1_CONTACTS_FILE = "{}{}".format(PSE_CONTACTS_FILE, "1")
+        PSE2_CONTACTS_FILE = "{}{}".format(PSE_CONTACTS_FILE, "2")
+
+        bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
+                                                PSE1_CONTACTS_FILE, 100)
+        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+            self.pse, self.contacts_destination_path, PSE1_CONTACTS_FILE)
+        bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
+                                                PSE2_CONTACTS_FILE, 100)
+        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+            self.pse2, self.contacts_destination_path, PSE2_CONTACTS_FILE)
+
+        self.pce.droid.bluetoothPbapClientDisconnect(
+            self.pse.droid.bluetoothGetLocalAddress())
+        self.pce.droid.bluetoothPbapClientDisconnect(
+            self.pse2.droid.bluetoothGetLocalAddress())
+
+        bt_test_utils.connect_pri_to_sec(
+            self.pce, self.pse,
+            set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
+        bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 100)
+        bt_contacts_utils.export_device_contacts_to_vcf(
+            self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE)
+        pse1_matches = bt_contacts_utils.count_contacts_with_differences(
+            self.contacts_destination_path, PCE_CONTACTS_FILE,
+            PSE1_CONTACTS_FILE) == 0
+
+        bt_test_utils.connect_pri_to_sec(
+            self.pce, self.pse2,
+            set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
+
+        bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 200)
+
+        bt_contacts_utils.export_device_contacts_to_vcf(
+            self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE)
+
+        merged_file = open('{}{}'.format(self.contacts_destination_path,
+                                         MERGED_CONTACTS_FILE), 'w')
+        for contacts_file in [PSE1_CONTACTS_FILE, PSE2_CONTACTS_FILE]:
+            infile = open(self.contacts_destination_path + contacts_file)
+            merged_file.write(infile.read())
+
+        self.log.info("Checking combined phonebook.")
+        pse1andpse2_matches = bt_contacts_utils.count_contacts_with_differences(
+            self.contacts_destination_path, PCE_CONTACTS_FILE,
+            MERGED_CONTACTS_FILE) == 0
+
+        self.pce.droid.bluetoothPbapClientDisconnect(
+            self.pse.droid.bluetoothGetLocalAddress())
+        bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 100)
+
+        self.log.info("Checking phonebook after disconnecting first device.")
+        bt_contacts_utils.export_device_contacts_to_vcf(
+            self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE)
+        pse2_matches = bt_contacts_utils.count_contacts_with_differences(
+            self.contacts_destination_path, PCE_CONTACTS_FILE,
+            PSE2_CONTACTS_FILE) == 0
+
+        bt_contacts_utils.erase_contacts(self.pse)
+        bt_contacts_utils.erase_contacts(self.pse2)
+        return pse1_matches and pse2_matches and pse1andpse2_matches
diff --git a/acts/tests/google/bt/car_bt/BtCarToggleTest.py b/acts/tests/google/bt/car_bt/BtCarToggleTest.py
index e284916..fa2bf23 100644
--- a/acts/tests/google/bt/car_bt/BtCarToggleTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarToggleTest.py
@@ -13,7 +13,6 @@
 # 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 is used to test basic functionality of bluetooth adapter by turning it ON/OFF.
 """
@@ -21,6 +20,7 @@
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt import bt_test_utils
 
+
 class BtCarToggleTest(BluetoothBaseTest):
     def setup_class(self):
         self.droid_ad = self.android_devices[0]
@@ -45,4 +45,4 @@
           Pass if True
           Fail if False
         """
-        asserts.assert_true(bt_test_utils.reset_bluetooth([self.droid_ad]), "")
+        return bt_test_utils.reset_bluetooth([self.droid_ad])
diff --git a/acts/tests/google/bt/gatt/GattOverBrEdrTest.py b/acts/tests/google/bt/gatt/GattOverBrEdrTest.py
index da558cc..71b79d8 100644
--- a/acts/tests/google/bt/gatt/GattOverBrEdrTest.py
+++ b/acts/tests/google/bt/gatt/GattOverBrEdrTest.py
@@ -19,6 +19,7 @@
 
 import time
 
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import reset_bluetooth
 from acts.test_utils.bt.GattEnum import GattCharacteristic
@@ -99,6 +100,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='32d32c87-911e-4f14-9654-29fe1431e995')
     def test_gatt_bredr_connect(self):
         """Test GATT connection over BR/EDR.
 
@@ -130,9 +132,10 @@
         self.gatt_server_list.append(gatt_server)
         try:
             bluetooth_gatt, gatt_callback, adv_callback = (
-                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
-                                            GattTransport.TRANSPORT_BREDR.value,
-                                            self.per_droid_mac_address))
+                orchestrate_gatt_connection(
+                    self.cen_ad, self.per_ad,
+                    GattTransport.TRANSPORT_BREDR.value,
+                    self.per_droid_mac_address))
             self.bluetooth_gatt_list.append(bluetooth_gatt)
         except GattTestUtilsError as err:
             self.log.error(err)
@@ -141,6 +144,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='357b697b-a52c-4c2a-997c-00876a018f37')
     def test_gatt_bredr_connect_trigger_on_read_rssi(self):
         """Test GATT connection over BR/EDR read RSSI.
 
@@ -173,9 +177,10 @@
         self.gatt_server_list.append(gatt_server)
         try:
             bluetooth_gatt, gatt_callback, adv_callback = (
-                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
-                                            GattTransport.TRANSPORT_BREDR.value,
-                                            self.per_droid_mac_address))
+                orchestrate_gatt_connection(
+                    self.cen_ad, self.per_ad,
+                    GattTransport.TRANSPORT_BREDR.value,
+                    self.per_droid_mac_address))
             self.bluetooth_gatt_list.append(bluetooth_gatt)
         except GattTestUtilsError as err:
             self.log.error(err)
@@ -188,6 +193,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='dee9ef28-b872-428a-821b-cc62f27ba936')
     def test_gatt_bredr_connect_trigger_on_services_discovered(self):
         """Test GATT connection and discover services of peripheral.
 
@@ -220,9 +226,10 @@
         self.gatt_server_list.append(gatt_server)
         try:
             bluetooth_gatt, gatt_callback, adv_callback = (
-                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
-                                            GattTransport.TRANSPORT_BREDR.value,
-                                            self.per_droid_mac_address))
+                orchestrate_gatt_connection(
+                    self.cen_ad, self.per_ad,
+                    GattTransport.TRANSPORT_BREDR.value,
+                    self.per_droid_mac_address))
             self.bluetooth_gatt_list.append(bluetooth_gatt)
         except GattTestUtilsError as err:
             self.log.error(err)
@@ -237,6 +244,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='01883bdd-0cf8-48fb-bf15-467bbd4f065b')
     def test_gatt_bredr_connect_trigger_on_services_discovered_iterate_attributes(
             self):
         """Test GATT connection and iterate peripherals attributes.
@@ -275,9 +283,10 @@
         self.gatt_server_list.append(gatt_server)
         try:
             bluetooth_gatt, gatt_callback, adv_callback = (
-                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
-                                            GattTransport.TRANSPORT_BREDR.value,
-                                            self.per_droid_mac_address))
+                orchestrate_gatt_connection(
+                    self.cen_ad, self.per_ad,
+                    GattTransport.TRANSPORT_BREDR.value,
+                    self.per_droid_mac_address))
             self.bluetooth_gatt_list.append(bluetooth_gatt)
         except GattTestUtilsError as err:
             self.log.error(err)
@@ -293,6 +302,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d4277bee-da99-4f48-8a4d-f81b5389da18')
     def test_gatt_bredr_connect_with_service_uuid_variations(self):
         """Test GATT connection with multiple service uuids.
 
@@ -333,9 +343,10 @@
             return False
         try:
             bluetooth_gatt, gatt_callback, adv_callback = (
-                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
-                                            GattTransport.TRANSPORT_BREDR.value,
-                                            self.per_droid_mac_address))
+                orchestrate_gatt_connection(
+                    self.cen_ad, self.per_ad,
+                    GattTransport.TRANSPORT_BREDR.value,
+                    self.per_droid_mac_address))
             self.bluetooth_gatt_list.append(bluetooth_gatt)
         except GattTestUtilsError as err:
             self.log.error(err)
@@ -351,6 +362,7 @@
                                                     gatt_callback)
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='15c726dc-788a-4400-9a90-8c6866b24a3a')
     def test_gatt_bredr_connect_multiple_iterations(self):
         """Test GATT connections multiple times.
 
@@ -386,9 +398,10 @@
         for i in range(20):
             try:
                 bluetooth_gatt, gatt_callback, adv_callback = (
-                    orchestrate_gatt_connection(self.cen_ad, self.per_ad,
-                                                GattTransport.TRANSPORT_BREDR.value,
-                                                self.per_droid_mac_address))
+                    orchestrate_gatt_connection(
+                        self.cen_ad, self.per_ad,
+                        GattTransport.TRANSPORT_BREDR.value,
+                        self.per_droid_mac_address))
                 self.bluetooth_gatt_list.append(bluetooth_gatt)
             except GattTestUtilsError as err:
                 self.log.error(err)
@@ -402,6 +415,7 @@
         return True
 
     @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='6ec766ca-6358-48ff-9d85-ede4d2756546')
     def test_bredr_write_descriptor_stress(self):
         """Test GATT connection writing and reading descriptors.
 
@@ -430,16 +444,18 @@
         Priority: 1
         """
         try:
-            gatt_server_callback, gatt_server = setup_multiple_services(self.per_ad)
+            gatt_server_callback, gatt_server = setup_multiple_services(
+                self.per_ad)
             self.gatt_server_list.append(gatt_server)
         except GattTestUtilsError as err:
             self.log.error(err)
             return False
         try:
             bluetooth_gatt, gatt_callback, adv_callback = (
-                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
-                                            GattTransport.TRANSPORT_BREDR.value,
-                                            self.per_droid_mac_address))
+                orchestrate_gatt_connection(
+                    self.cen_ad, self.per_ad,
+                    GattTransport.TRANSPORT_BREDR.value,
+                    self.per_droid_mac_address))
             self.bluetooth_gatt_list.append(bluetooth_gatt)
         except GattTestUtilsError as err:
             self.log.error(err)
@@ -507,4 +523,3 @@
                                     GattCbStrings.DESC_WRITE.value.format(
                                         gatt_callback), self.default_timeout)))
         return True
-
diff --git a/acts/tests/google/bt/power/A2dpPowerTest.py b/acts/tests/google/bt/power/A2dpPowerTest.py
new file mode 100644
index 0000000..e761023
--- /dev/null
+++ b/acts/tests/google/bt/power/A2dpPowerTest.py
@@ -0,0 +1,1235 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+This test script exercises power test scenarios for A2DP streaming with
+   5 codec types, 4 sample rate values, 3 bits per sample values,
+   2 music file types and 3 LDAC playback quality values.
+
+This test script was designed with this setup in mind:
+Shield box one: Android Device, headset and Monsoon tool box
+"""
+
+import json
+import os
+import time
+
+from acts import logger
+from acts.controllers import monsoon
+from acts.keys import Config
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.BleEnum import ScanSettingsScanMode
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts.test_utils.bt.PowerBaseTest import PowerBaseTest
+from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts.test_utils.bt.bt_test_utils import disable_bluetooth
+
+
+def push_file_to_device(ad, file_path, device_path, config_path):
+    """Utility functions to push a file to android device
+
+    Args:
+        ad: Device for file push
+        file_path: File path for the file to be pushed to the device
+        device_path: File path on the device as destination
+
+    Returns:
+        True if successful, False if unsuccessful.
+    """
+
+    if not os.path.isfile(file_path):
+        file_path = os.path.join(config_path, file_path)
+        if not os.path.isfile(file_path):
+            return False
+    ad.adb.push("{} {}".format(file_path, device_path))
+    return True
+
+
+class A2dpPowerTest(PowerBaseTest):
+    # Time for measuring the power when playing music in seconds
+    MEASURE_TIME = 3600
+    # Time for Music to start playing in seconds
+    START_PLAY_TIME = 60
+    # Delay time in seconds between starting playing and measuring power
+    DELAY_TIME = 60
+    # Wait time in seconds to check if BT device is connected
+    WAIT_TIME = 10
+    # Base command for PMC
+    PMC_BASE_CMD = ("am broadcast -a com.android.pmc.A2DP --es MusicURL")
+    # Codec Types
+    CODEC_SBC = 0
+    CODEC_AAC = 1
+    CODEC_APTX = 2
+    CODEC_APTX_HD = 3
+    CODEC_LDAC = 4
+
+    # Music sample rates
+    SAMPLE_RATE_44100 = 0x1 << 0
+    SAMPLE_RATE_48000 = 0x1 << 1
+    SAMPLE_RATE_88200 = 0x1 << 2
+    SAMPLE_RATE_96000 = 0x1 << 3
+
+    # Bits per sample
+    BITS_PER_SAMPLE_16 = 0x1 << 0
+    BITS_PER_SAMPLE_24 = 0x1 << 1
+    BITS_PER_SAMPLE_32 = 0x1 << 2
+
+    # LDAC playback quality:
+    # For High Quality
+    LDACBT_EQMID_HQ = 1000
+    # For Standard Quality
+    LDACBT_EQMID_SQ = 1001
+    # For Mobile Use Quality
+    LDACBT_EQMID_MQ = 1002
+    # LDAC playback quality constant for the codecs other than LDAC
+    LDACBT_NONE = 0
+
+    def setup_class(self):
+
+        self.ad = self.android_devices[0]
+
+        bt_device_address = self.user_params["bt_device_address"]
+
+        # Push the bt_config.conf file to Android device
+        # if it is specified in config file
+        # so it can pair and connect automatically.
+        # Because of bug 34933072 AK XB10 and Sony XB2 may not be able to
+        # reconnect after flashing a new build and pushing bt_config
+        # so just manually pair/connect before running the tests
+        bt_conf_path_dut = "/data/misc/bluedroid/bt_config.conf"
+        bt_config_path = None
+        try:
+            bt_config_path = self.user_params["bt_config"]
+        except KeyError:
+            self.log.info("No bt_config is specified in config file")
+
+        if bt_config_path != None:
+            self.log.info(
+                "Push bt_config file so it will connect automatically")
+            if not push_file_to_device(
+                    self.ad, bt_config_path, bt_conf_path_dut,
+                    self.user_params[Config.key_config_path]):
+                self.log.error("Unable to push file {} to DUT.".format(
+                    bt_config_path))
+
+            self.log.info("Reboot")
+            self.ad.reboot()
+
+        # Add music files to the Android device
+        music_path_dut = "/sdcard/Music/"
+        self.cd_quality_music_file = self.user_params["cd_quality_music_file"]
+        self.log.info("Push CD quality music file {}".format(
+            self.cd_quality_music_file))
+        if not push_file_to_device(self.ad, self.cd_quality_music_file,
+                                   music_path_dut,
+                                   self.user_params[Config.key_config_path]):
+            self.log.error("Unable to push file {} to DUT.".format(
+                self.cd_quality_music_file))
+
+        self.hi_res_music_file = self.user_params["hi_res_music_file"]
+        self.log.info("Push Hi Res quality music file {}".format(
+            self.hi_res_music_file))
+        if not push_file_to_device(self.ad, self.hi_res_music_file,
+                                   music_path_dut,
+                                   self.user_params[Config.key_config_path]):
+            self.log.error("Unable to find file {}.".format(
+                self.hi_res_music_file))
+
+        if not bluetooth_enabled_check(self.ad):
+            self.log.error("Failed to enable Bluetooth on DUT")
+            return False
+
+        # Verify Bluetooth device is connected
+        self.log.info(
+            "Waiting up to {} seconds for device to reconnect.".format(
+                self.WAIT_TIME))
+        start_time = time.time()
+        result = False
+        while time.time() < start_time + self.WAIT_TIME:
+            connected_devices = self.ad.droid.bluetoothGetConnectedDevices()
+            if len(connected_devices) > 0:
+                result = True
+                break
+
+            try:
+                self.ad.droid.bluetoothConnectBonded(self.user_params[
+                    "bt_device_address"])
+            except Exception as err:
+                self.log.error(
+                    "Failed to connect bonded device. Err: {}".format(err))
+
+        if not result:
+            self.log.error("No headset is connected")
+            return False
+
+        # Then do other power testing setup in the base class
+        # including start PMC etc
+        super(A2dpPowerTest, self).setup_class()
+        return True
+
+    def _main_power_test_function_for_codec(self,
+                                            codec_type,
+                                            sample_rate,
+                                            bits_per_sample,
+                                            music_file,
+                                            ldac_playback_quality=LDACBT_NONE,
+                                            not_play=False,
+                                            mute=False):
+        """Main util function for power test of A2dp with different codec.
+
+        Steps:
+        1. Prepare adb shell command
+        2. Send the adb shell command to PMC
+        3. PMC will create Media Player and set the codec info
+        4. PMC start first alarm to start music
+        5. After first alarm triggered it start the second alarm to stop music
+        6. Save the power usage data into log file
+
+        Args:
+            codec_type: Codec Type - SBC, ACC, AptX, AptX-HD or LDAC
+            sample_rate: Sample Rate - 44.1, 48.0, 88.2, 96.0 Khz
+            bits_per_sample: Bits per Sample - 16, 24, 32 bits
+            music_file: a music file with CD quality (44.1KHz/16bits) or
+                        Hi Res quality (96KHz/24bits)
+            ldac_playback_quality: LDACBT_EQMID_HQ, LDACBT_EQMID_SQ,
+                                   LDACBT_EQMID_MQ
+            not_play: for baseline case when MediaPlayer is not play
+            mute: for baseline case when BT is off
+
+        Returns:
+            None
+        """
+        # Get the base name of music file
+        music_name = os.path.basename(music_file)
+        self.music_url = "file:///sdcard/Music/{}".format(music_name)
+        play_time = self.MEASURE_TIME + self.DELAY_TIME * 2
+        first_part_msg = "%s %s --es StartTime %d --es PlayTime %d --es" % (
+            self.PMC_BASE_CMD, self.music_url, self.START_PLAY_TIME, play_time)
+
+        sec_part_msg = "%s  CodecType %d --es SampleRate %d" % (
+            first_part_msg, codec_type, sample_rate)
+
+        third_part_msg = "%s --es BitsPerSample %d" % (sec_part_msg,
+                                                       bits_per_sample)
+
+        if codec_type == self.CODEC_LDAC:
+            fourth_part_msg = "%s --es LdacPlaybackQuality %d" % (
+                third_part_msg, ldac_playback_quality)
+        else:
+            fourth_part_msg = third_part_msg
+
+        if not_play == True:
+            fifth_part_msg = "%s --es NotPlay %d" % (fourth_part_msg, 1)
+        else:
+            fifth_part_msg = fourth_part_msg
+
+        if mute == True:
+            msg = "%s --es Mute %d" % (fifth_part_msg, 1)
+        else:
+            msg = fifth_part_msg
+
+        self.ad.log.info("Send broadcast message: %s", msg)
+        self.ad.adb.shell(msg)
+        start_measure_time = self.START_PLAY_TIME + self.DELAY_TIME
+        # Start the power measurement
+        result = self.mon.measure_power(
+            self.POWER_SAMPLING_RATE, self.MEASURE_TIME,
+            self.current_test_name, start_measure_time)
+        # Save data into log file
+        self.save_logs_for_power_test(result, self.MEASURE_TIME, 0)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='6dc78cf4-7cae-4b03-8a31-0d23f41d1baa')
+    def test_power_baseline_not_play_music(self):
+        """Test power usage baseline without playing music.
+
+        Test power usage baseline without playing music.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+           except telling PMC not to play music
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_SBC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file, self.LDACBT_NONE, True)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d96080e3-1944-48b8-9655-4a77664a463b')
+    def test_power_baseline_play_music_but_disable_bluetooth(self):
+        """Test power usage baseline of playing music but BT is off.
+
+        Test power usage baseline of playing music but BT is off
+           speaker volume is set to 0.
+
+        Steps:
+        1. Disable Bluetooth
+        2. The same steps described in _main_power_test_function_for_codec()
+        3. Enable Bluetooth
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+        self.ad.log.info("Disable BT")
+        if not disable_bluetooth(self.ad.droid):
+            self.log.error("Failed to disable Bluetooth on DUT")
+            return False
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_SBC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file, self.LDACBT_NONE, False, True)
+
+        self.ad.log.info("Enable BT")
+        if not bluetooth_enabled_check(self.ad):
+            self.log.error("Failed to turn Bluetooth on DUT")
+
+        # Because of bug 34933072 BT device may not be able to reconnect
+        #  after running this test case
+        # We need manually to turn on BT devices to get it reconnect
+        # for the next test case
+        # The workaround is to run this test case in the end of test suite
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='f62238cf-35df-4e42-a160-16944f76fb86')
+    def test_power_for_sbc_with_cd_quality_music(self):
+        """Test power usage for SBC codec with CD quality music file.
+
+        Test power usage for SBC codec with CD quality music file.
+        SBC only supports 44.1 Khz and 16 bits
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_SBC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d9421c37-c2db-4dc5-841b-f837c0b2ea48')
+    def test_power_for_sbc_with_hi_res_music(self):
+        """Test power usage for SBC codec with Hi Resolution music file.
+
+        Test power usage for SBC codec with Hi Resolution music file.
+        SBC only supports 44.1 Khz and 16 bits
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_SBC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.hi_res_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='f746c038-9a00-43d0-b6b1-b9a36fae5a5a')
+    def test_power_for_aac_44100_16_with_cd_quality_music(self):
+        """Test power usage for AAC codec with CD quality music file.
+
+        Test power usage for AAC codec using 44.1KHz/16bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_AAC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='510448e1-f4fb-4048-adad-67f8f16f96c4')
+    def test_power_for_aac_44100_16_with_hi_res_music(self):
+        """Test power usage for AAC codec with Hi Resolution music file.
+
+        Test power usage for AAC codec using 44.1KHz/16bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_AAC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.hi_res_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='9df971d1-91b6-4fad-86ff-aa91d14aa895')
+    def test_power_for_aac_48000_16_with_cd_quality_music(self):
+        """Test power usage for AAC codec with CD quality music file.
+
+        Test power usage for AAC codec using 48KHz/16bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_AAC, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='b020518e-027b-4716-8abb-cd6d83551869')
+    def test_power_for_aac_48000_16_with_hi_res_music(self):
+        """Test power usage for AAC codec with Hi Resolution music file.
+
+        Test power usage for AAC codec using 48KHz/16bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_AAC, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_16,
+            self.hi_res_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='2006dffb-b47b-4986-b11d-de151d5f4794')
+    def test_power_for_aptx_44100_16_with_cd_quality_music(self):
+        """Test power usage for APTX codec with CD quality music file.
+
+        Test power usage for APTX codec using 44.1KHz/16bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_APTX, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='8844356b-7756-4da6-89fe-96161e715cab')
+    def test_power_for_aptx_44100_16_with_hi_res_music(self):
+        """Test power usage for APTX codec with Hi Resolution music file.
+
+        Test power usage for APTX codec using 44.1KHz/16bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_APTX, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.hi_res_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d037ae2e-c5e8-4f84-9908-88335803a3d9')
+    def test_power_for_aptx_48000_16_with_cd_quality_music(self):
+        """Test power usage for APTX codec with CD quality music file.
+
+        Test power usage for APTX codec using 48KHz/16bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_APTX, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='4741b8cc-b038-4b38-8326-6a98de3f5ac6')
+    def test_power_for_aptx_48000_16_with_hi_res_music(self):
+        """Test power usage for APTX codec with Hi Resolution music file.
+
+        Test power usage for APTX codec using 48KHz/16bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_APTX, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_16,
+            self.hi_res_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='8dff8f63-bbdb-4a2d-afca-ff1aabf7b5f2')
+    def test_power_for_aptx_hd_44100_24_with_cd_quality_music(self):
+        """Test power usage for APTX-HD codec with CD quality music file.
+
+        Test power usage for APTX-HD codec using 44.1KHz/24bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_APTX_HD, self.SAMPLE_RATE_44100,
+            self.BITS_PER_SAMPLE_24, self.cd_quality_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='ab364fdd-04dd-42b7-af0d-fe1d7d7b809b')
+    def test_power_for_aptx_hd_44100_24_with_hi_res_music(self):
+        """Test power usage for APTX-HD codec with Hi Resolution music file.
+
+        Test power usage for APTX-HD codec using 44.1KHz/24bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_APTX_HD, self.SAMPLE_RATE_44100,
+            self.BITS_PER_SAMPLE_24, self.hi_res_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='dd838989-9440-4833-91f6-6fca6e219796')
+    def test_power_for_aptx_hd_48000_24_with_cd_quality_music(self):
+        """Test power usage for APTX-HD codec with CD quality music file.
+
+        Test power usage for APTX-HD codec using 48KHz/24bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_APTX_HD, self.SAMPLE_RATE_48000,
+            self.BITS_PER_SAMPLE_24, self.cd_quality_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='814122eb-7068-470b-b04c-d64883258b0c')
+    def test_power_for_aptx_hd_48000_24_with_hi_res_music(self):
+        """Test power usage for APTX-HD codec with Hi Resolution music file.
+
+        Test power usage for APTX-HD codec using 48KHz/24bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_APTX_HD, self.SAMPLE_RATE_48000,
+            self.BITS_PER_SAMPLE_24, self.hi_res_music_file)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='682c055f-4883-4559-828a-67956e110475')
+    def test_power_for_ldac_44100_16_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 44.1KHz/16bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='21a2478c-9b66-49ae-a8ec-d2393142ed6c')
+    def test_power_for_ldac_44100_16_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 44.1KHz/16bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_16,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='f081cb13-ec02-4814-ba00-3ed33630e7c0')
+    def test_power_for_ldac_48000_16_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 48KHz/16bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='1c6b3a1b-59b8-479f-8247-e8cbbef6d82f')
+    def test_power_for_ldac_48000_16_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 48KHz/16bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_16,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='e6806e10-0f1e-4c44-8f70-66e0267ebf95')
+    def test_power_for_ldac_88200_16_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 88.2KHz/16bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_88200, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='2458880d-c662-4313-9c3a-b14ad04dddfa')
+    def test_power_for_ldac_88200_16_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 88.2KHz/16bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_88200, self.BITS_PER_SAMPLE_16,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='e492f173-8a5b-4a92-b3de-c792e8db32fb')
+    def test_power_for_ldac_96000_16_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 96KHz/16bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_96000, self.BITS_PER_SAMPLE_16,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='65f78a14-5a1f-443e-9a23-8ae8a206bd6f')
+    def test_power_for_ldac_96000_16_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 96KHz/16bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_96000, self.BITS_PER_SAMPLE_16,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='3f026ae2-e34e-4a0f-aac2-1684d22a3796')
+    def test_power_for_ldac_44100_24_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 44.1KHz/24bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_24,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='6d94bd0c-039e-47a7-8fc1-b87abcf9b27d')
+    def test_power_for_ldac_44100_24_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 44.1KHz/24bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_24,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='2d5cbad5-5293-434d-b996-850ef32792a0')
+    def test_power_for_ldac_48000_24_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 48KHz/24bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_24,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='fab004d1-c67e-4b9b-af33-e4469ce9f44a')
+    def test_power_for_ldac_48000_24_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 48KHz/24bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_24,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='9527f997-61c6-4e88-90f9-6791cbe00883')
+    def test_power_for_ldac_88200_24_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 88.2KHz/24bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_88200, self.BITS_PER_SAMPLE_24,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='4a14a499-8b62-43d9-923e-f0c46e15121e')
+    def test_power_for_ldac_88200_24_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 88.2KHz/24bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_88200, self.BITS_PER_SAMPLE_24,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d6254318-7a9a-4c19-800b-03686642e846')
+    def test_power_for_ldac_96000_24_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 96KHz/24bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_96000, self.BITS_PER_SAMPLE_24,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='1eb26676-19ec-43af-ab20-bfb7b055114f')
+    def test_power_for_ldac_96000_24_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 96KHz/24bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_96000, self.BITS_PER_SAMPLE_24,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='efb75158-ff90-4a95-8bd0-0189d719e647')
+    def test_power_for_ldac_44100_32_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 44.1KHz/32bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_32,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='7d5ee1a0-b903-4cf4-8bcc-8db653f04e3b')
+    def test_power_for_ldac_44100_32_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 44.1KHz/32bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_44100, self.BITS_PER_SAMPLE_32,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='2981e30a-9f5a-4d35-9387-96dd2ab3421a')
+    def test_power_for_ldac_48000_32_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 48KHz/32bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_32,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='297f0ab3-be6b-4367-9750-48f3ba12bb4b')
+    def test_power_for_ldac_48000_32_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 48KHz/32bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_48000, self.BITS_PER_SAMPLE_32,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='71a23350-03bf-4690-a692-eb944f7d4782')
+    def test_power_for_ldac_88200_32_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 88.2KHz/32bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_88200, self.BITS_PER_SAMPLE_32,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='7452e2dd-cbdd-4f50-a482-99d038ba0ee0')
+    def test_power_for_ldac_88200_32_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 88.2KHz/32bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_88200, self.BITS_PER_SAMPLE_32,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='042493c1-00d9-46d8-b2e9-844c9ac849f8')
+    def test_power_for_ldac_96000_32_with_cd_quality_music(self):
+        """Test power usage for LDAC codec with CD quality music file.
+
+        Test power usage for LDAC codec using 96KHz/32bits with
+             CD quality music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_96000, self.BITS_PER_SAMPLE_32,
+            self.cd_quality_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='7d90b5ee-c32b-4ef8-b27f-587f3550aed9')
+    def test_power_for_ldac_96000_32_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Hi Resolution music file.
+
+        Test power usage for LDAC codec using 96KHz/32bits with
+             Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_96000, self.BITS_PER_SAMPLE_32,
+            self.hi_res_music_file, self.LDACBT_EQMID_HQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='d3da605f-acd4-49a6-ae0f-d1ef216ac5b4')
+    def test_power_for_ldac_96000_24_sq_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Standard Quality(SQ)
+           for Hi Resolution music file.
+
+        Test power usage for LDAC codec using 96KHz/24bits with
+        Standard Quality(SQ) for Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_96000, self.BITS_PER_SAMPLE_24,
+            self.hi_res_music_file, self.LDACBT_EQMID_SQ)
+
+    @BluetoothBaseTest.bt_test_wrap
+    @test_tracker_info(uuid='5d6494a3-ab00-48f3-9e68-50600708c176')
+    def test_power_for_ldac_96000_24_mq_with_hi_res_music(self):
+        """Test power usage for LDAC codec with Mobile Quality(SQ)
+           for Hi Resolution music file.
+
+        Test power usage for LDAC codec using 96KHz/24bits with
+        Mobile Quality(SQ) for Hi Resolution music file.
+
+        Steps:
+        The same steps described in _main_power_test_function_for_codec()
+
+        Expected Result:
+        Power consumption results
+
+        TAGS: Bluetooth, A2DP, Power, Codec
+        Priority: 3
+
+        """
+
+        self._main_power_test_function_for_codec(
+            self.CODEC_LDAC, self.SAMPLE_RATE_96000, self.BITS_PER_SAMPLE_24,
+            self.hi_res_music_file, self.LDACBT_EQMID_MQ)
diff --git a/acts/tests/google/bt/pts/BtCmdLineTest.py b/acts/tests/google/bt/pts/BtCmdLineTest.py
new file mode 100644
index 0000000..842172d
--- /dev/null
+++ b/acts/tests/google/bt/pts/BtCmdLineTest.py
@@ -0,0 +1,89 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Script for initializing a cmd line tool for PTS and other purposes.
+Required custom config parameters:
+'target_mac_address': '00:00:00:00:00:00'
+
+Optional config parameters:
+'sim_conf_file' : '/path_to_config/'
+"""
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from cmd_input import CmdInput
+from queue import Empty
+import os
+import uuid
+
+
+class BtCmdLineTest(BluetoothBaseTest):
+    target_mac_address = ""
+
+    def __init__(self, controllers):
+        BluetoothBaseTest.__init__(self, controllers)
+        if not "target_mac_address" in self.user_params.keys():
+            self.log.error(
+                "Missing mandatory user config \"target_mac_address\"!")
+            return False
+        self.target_mac_address = self.user_params["target_mac_address"].upper(
+        )
+
+        self.android_devices[0].droid.bluetoothSetLocalName("CMD LINE Test")
+        if len(self.android_devices) > 1:
+            #Setup for more complex testcases
+            if not "sim_conf_file" in self.user_params.keys():
+                self.log.error(
+                    "Missing mandatory user config \"sim_conf_file\"!")
+                return False
+            sim_conf_file = self.user_params["sim_conf_file"]
+            # If the sim_conf_file is not a full path, attempt to find it
+            # relative to the config file.
+            if not os.path.isfile(sim_conf_file):
+                sim_conf_file = os.path.join(
+                    self.user_params[Config.key_config_path], sim_conf_file)
+                if not os.path.isfile(sim_conf_file):
+                    log.error("Unable to load user config " + sim_conf_file +
+                              " from test config file.")
+                    return False
+            for ad in self.android_devices:
+                setup_droid_properties(self.log, ad, sim_conf_file)
+            music_path_str = "music_path"
+            android_music_path = "/sdcard/Music/"
+            if music_path_str not in self.user_params:
+                log.error("Need music for A2DP testcases")
+                return False
+            music_path = self.user_params[music_path_str]
+            self._add_music_to_primary_android_device(music_path,
+                                                      android_music_path)
+
+    def _add_music_to_primary_android_device(self, music_path,
+                                             android_music_path):
+        music_list = []
+        for dirname, dirnames, filenames in os.walk(music_path):
+            for filename in filenames:
+                file = os.path.join(dirname, filename)
+                #TODO: Handle file paths with spaces
+                self.android_devices[0].adb.push("{} {}".format(
+                    file, android_music_path))
+
+    def setup_class(self):
+        return True
+
+    def test_pts_cmd_line_helper(self):
+        cmd_line = CmdInput()
+        cmd_line.setup_vars(self.android_devices, self.target_mac_address,
+                            self.log)
+        cmd_line.cmdloop()
+        return True
diff --git a/acts/tests/google/bt/pts/cmd_input.py b/acts/tests/google/bt/pts/cmd_input.py
new file mode 100644
index 0000000..278c108
--- /dev/null
+++ b/acts/tests/google/bt/pts/cmd_input.py
@@ -0,0 +1,42 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""
+Python script for wrappers to various libraries.
+"""
+
+import cmd
+"""Various Global Strings"""
+CMD_LOG = "CMD {} result: {}"
+FAILURE = "CMD {} threw exception: {}"
+
+
+class CmdInput(cmd.Cmd):
+    """Simple command processor for Bluetooth PTS Testing"""
+
+    def setup_vars(self, android_devices, mac_addr, log):
+        self.pri_dut = android_devices[0]
+        if len(android_devices) > 1:
+            self.sec_dut = android_devices[1]
+            self.ter_dut = android_devices[2]
+        self.mac_addr = mac_addr
+        self.log = log
+
+    def emptyline(self):
+        pass
+
+    def do_EOF(self, line):
+        "End Script"
+        return True
diff --git a/acts/tests/google/bt/setup/BtPreFlightTest.py b/acts/tests/google/bt/setup/BtPreFlightTest.py
index 6a05157..970cd2b 100644
--- a/acts/tests/google/bt/setup/BtPreFlightTest.py
+++ b/acts/tests/google/bt/setup/BtPreFlightTest.py
@@ -17,6 +17,7 @@
 Bluetooth Pre-Flight Test.
 """
 
+from acts.test_decorators import test_tracker_info
 from acts.base_test import BaseTestClass
 import os
 import pprint
@@ -30,7 +31,7 @@
     def setup_class(self):
         for a in self.android_devices:
             d = a.droid
-            serial = d.getBuildSerial()
+            serial = a.serial
             self.log.info("****START: {} DEVICE INFO****".format(serial))
             self.log.info("BOOTLOADER VERSION {}".format(d.getBuildBootloader(
             )))
@@ -41,9 +42,10 @@
             self.log.info("****END: {} DEVICE INFO****".format(serial))
         return True
 
+    @test_tracker_info(uuid='7b6ac700-9e63-4871-bf7b-527c3da1e462')
     def test_setup_logging(self):
-        conf_path = "{}/bt_stack.conf".format(os.path.dirname(os.path.realpath(
-            __file__)))
+        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:
diff --git a/acts/tests/google/bt/setup/bt_stack.conf b/acts/tests/google/bt/setup/bt_stack.conf
index 1b4aa91..9eec1b4 100644
--- a/acts/tests/google/bt/setup/bt_stack.conf
+++ b/acts/tests/google/bt/setup/bt_stack.conf
@@ -3,7 +3,7 @@
 BtSnoopLogOutput=true
 
 # BtSnoop log output file
-BtSnoopFileName=/sdcard/btsnoop_hci.log
+BtSnoopFileName=/data/misc/bluetooth/logs/btsnoop_hci.log
 
 # Preserve existing BtSnoop log before overwriting
 BtSnoopSaveLog=true
@@ -35,4 +35,4 @@
 TRC_GATT=5
 TRC_SMP=5
 TRC_BTAPP=5
-TRC_BTIF=5
\ No newline at end of file
+TRC_BTIF=5
diff --git a/acts/tests/google/bt/system_tests/BtStressTest.py b/acts/tests/google/bt/system_tests/BtStressTest.py
index 0dd42dc..e756c9c 100644
--- a/acts/tests/google/bt/system_tests/BtStressTest.py
+++ b/acts/tests/google/bt/system_tests/BtStressTest.py
@@ -19,6 +19,7 @@
 
 import time
 from acts.base_test import BaseTestClass
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
 from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
@@ -33,12 +34,12 @@
     def __init__(self, controllers):
         BluetoothBaseTest.__init__(self, controllers)
 
-
     def teardown_test(self):
         super(BluetoothBaseTest, self).teardown_test()
         self.log_stats()
         return True
 
+    @test_tracker_info(uuid='bbe050f8-7970-42b3-9104-a2cd8f09579c')
     def test_toggle_bluetooth(self):
         """Stress test toggling bluetooth on and off.
 
@@ -67,17 +68,20 @@
                 return False
         return True
 
+    @test_tracker_info(uuid='a6fac426-d068-4a86-9d55-00dbe51b2ff0')
     def test_pair_bluetooth_stress(self):
         """Stress test for pairing BT devices.
 
         Test the integrity of Bluetooth pairing.
 
-        Steps:
-        1. Pair two Android devices
-        2. Verify both devices are paired
-        3. Unpair devices.
-        4. Verify devices unpaired.
-        5. Repeat steps 1-4 100 times.
+        1. Primary device discover the secondary device
+        2. Primary device tries to pair to secondary device
+        3. Pair two devices after verifying pin number on both devices are equal
+        4. Verify both devices are paired
+        5. Unpair devices.
+        6. Verify devices unpaired.
+        7. Repeat steps 1-6 100 times.
+
 
         Expected Result:
         Each iteration of toggling Bluetooth pairing and unpairing
@@ -93,16 +97,22 @@
         for n in range(self.iterations):
             self.log.info("Pair bluetooth iteration {}.".format(n + 1))
             self.start_timer()
-            if (not pair_pri_to_sec(self.android_devices[0].droid,
-                                self.android_devices[1].droid)):
+            if (not pair_pri_to_sec(
+                    self.android_devices[0],
+                    self.android_devices[1],
+                    attempts=1,
+                    auto_confirm=False)):
                 self.log.error("Failed to bond devices.")
                 return False
             self.log.info("Total time (ms): {}".format(self.end_timer()))
+            # A device bond will trigger a number of system routines that need
+            # to settle before unbond
+            time.sleep(2)
             for ad in self.android_devices:
                 if not clear_bonded_devices(ad):
                     return False
                 # Necessary sleep time for entries to update unbonded state
-                time.sleep(1)
+                time.sleep(2)
                 bonded_devices = ad.droid.bluetoothGetBondedDevices()
                 if len(bonded_devices) > 0:
                     self.log.error("Failed to unbond devices: {}".format(
diff --git a/acts/tests/google/bt/system_tests/RfcommStressTest.py b/acts/tests/google/bt/system_tests/RfcommStressTest.py
index bf24d9c..ce4ee68 100644
--- a/acts/tests/google/bt/system_tests/RfcommStressTest.py
+++ b/acts/tests/google/bt/system_tests/RfcommStressTest.py
@@ -22,6 +22,7 @@
 import time
 
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
@@ -41,11 +42,8 @@
         BluetoothBaseTest.__init__(self, controllers)
         self.client_ad = self.android_devices[0]
         self.server_ad = self.android_devices[1]
-        self.tests = (
-            "test_rfcomm_connection_stress",
-            "test_rfcomm_read_write_stress",
-        )
 
+    @test_tracker_info(uuid='10452f63-e1fd-4345-a4da-e00fc4609a69')
     def test_rfcomm_connection_stress(self):
         """Stress test an RFCOMM connection
 
@@ -80,6 +78,7 @@
             self.log.info("Iteration {} completed".format(n))
         return True
 
+    @test_tracker_info(uuid='2dba96ec-4e5d-4394-85ef-b57b66399fbc')
     def test_rfcomm_connection_write_msg_stress(self):
         """Stress test an RFCOMM connection
 
@@ -117,6 +116,7 @@
             self.log.info("Iteration {} completed".format(n))
         return True
 
+    @test_tracker_info(uuid='78dca597-89c0-431c-a553-531f230fc3c0')
     def test_rfcomm_read_write_stress(self):
         """Stress test an RFCOMM connection's read and write capabilities
 
diff --git a/acts/tests/google/native/NativeTest.py b/acts/tests/google/native/NativeTest.py
new file mode 100644
index 0000000..7faa9fa
--- /dev/null
+++ b/acts/tests/google/native/NativeTest.py
@@ -0,0 +1,57 @@
+#/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import time
+from acts.base_test import BaseTestClass
+from acts.test_utils.bt.native_bt_test_utils import setup_native_bluetooth
+from acts.test_utils.bt.bt_test_utils import generate_id_by_size
+
+class NativeTest(BaseTestClass):
+    tests = None
+
+    def __init__(self, controllers):
+        BaseTestClass.__init__(self, controllers)
+        self.droid = self.native_android_devices[0].droid
+        self.tests = (
+                "test_bool_return_true",
+                "test_bool_return_false",
+                "test_null_return",
+                "test_string_empty_return",
+                "test_max_param_size",
+        )
+
+    def test_bool_return_true(self):
+        return self.droid.TestBoolTrueReturn()
+
+    def test_bool_return_false(self):
+        return not self.droid.TestBoolFalseReturn()
+
+    def test_null_return(self):
+        return not self.droid.TestNullReturn()
+
+    def test_string_empty_return(self):
+        return self.droid.TestStringEmptyReturn() == ""
+
+    def test_max_param_size(self):
+        json_buffer_size = 64
+        max_sl4n_buffer_size = 4096
+        test_string = "x" * (max_sl4n_buffer_size - json_buffer_size)
+        return test_string == self.droid.TestStringMaxReturn(test_string)
+
+    def test_specific_param_naming(self):
+        a = [{"string_test":"test", "int_test":1}]
+        return self.droid.TestSpecificParamNaming(a)
+
diff --git a/acts/tests/google/bt/native/BtNativeTest.py b/acts/tests/google/native/bt/BtNativeTest.py
similarity index 85%
rename from acts/tests/google/bt/native/BtNativeTest.py
rename to acts/tests/google/native/bt/BtNativeTest.py
index 2b5d21b..9660a8e 100644
--- a/acts/tests/google/bt/native/BtNativeTest.py
+++ b/acts/tests/google/native/bt/BtNativeTest.py
@@ -37,29 +37,29 @@
             self.tests = self.tests + ("test_two_devices_set_get_name", )
 
     def test_binder_get_name(self):
-        result = self.droid.BluetoothBinderGetName()
+        result = self.droid.BtBinderGetName()
         self.log.info("Bluetooth device name: {}".format(result))
         return True
 
     def test_binder_get_name_invalid_parameter(self):
         try:
-            self.droid.BluetoothBinderGetName("unexpected_parameter")
+            self.droid.BtBinderGetName("unexpected_parameter")
             return False
         except Exception:
             return True
 
     def test_binder_set_name_get_name(self):
         test_name = generate_id_by_size(4)
-        result = self.droid.BluetoothBinderSetName(test_name)
+        result = self.droid.BtBinderSetName(test_name)
         if not result:
             return False
-        name = self.droid.BluetoothBinderGetName()
+        name = self.droid.BtBinderGetName()
         if test_name != name:
             return False
         return True
 
     def test_binder_get_address(self):
-        result = self.droid.BluetoothBinderGetAddress()
+        result = self.droid.BtBinderGetAddress()
         self.log.info("Found BT address: {}".format(result))
         if not result:
             return False
@@ -69,9 +69,8 @@
         test_name = generate_id_by_size(4)
         for n in self.native_devices:
             d = n.droid
-            d.BluetoothBinderSetName(test_name)
-            name = d.BluetoothBinderGetName()
+            d.BtBinderSetName(test_name)
+            name = d.BtBinderGetName()
             if name != test_name:
                 return False
         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..8ab8e93
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabCmasTest.py
@@ -0,0 +1,515 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Sanity tests for voice tests in telephony
+"""
+import time
+
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib.md8475a import CBCHSetup
+from acts.controllers.anritsu_lib.md8475a import CTCHSetup
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CATEGORY_AMBER
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CATEGORY_EXTREME
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CATEGORY_PRESIDENTIAL
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CERTIANTY_LIKELY
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_RESPONSETYPE_EVACUATE
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_RESPONSETYPE_MONITOR
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_RESPONSETYPE_SHELTER
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_SEVERITY_EXTREME
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_URGENCY_IMMEDIATE
+from acts.test_utils.tel.anritsu_utils import CMAS_C2K_CERTIANTY_OBSERVED
+from acts.test_utils.tel.anritsu_utils import CMAS_MESSAGE_CHILD_ABDUCTION_EMERGENCY
+from acts.test_utils.tel.anritsu_utils import CMAS_MESSAGE_EXTREME_IMMEDIATE_LIKELY
+from acts.test_utils.tel.anritsu_utils import CMAS_MESSAGE_PRESIDENTIAL_ALERT
+from acts.test_utils.tel.anritsu_utils import cb_serial_number
+from acts.test_utils.tel.anritsu_utils import cmas_receive_verify_message_cdma1x
+from acts.test_utils.tel.anritsu_utils import cmas_receive_verify_message_lte_wcdma
+from acts.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts.test_utils.tel.anritsu_utils import set_system_model_1x_evdo
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_1XRTT
+from acts.test_utils.tel.tel_defines import RAT_LTE
+from acts.test_utils.tel.tel_defines import RAT_GSM
+from acts.test_utils.tel.tel_defines import RAT_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_decorators import test_tracker_info
+
+WAIT_TIME_BETWEEN_REG_AND_MSG = 15  # default 15 sec
+
+
+class TelLabCmasTest(TelephonyBaseTest):
+    SERIAL_NO = cb_serial_number()
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.ad = self.android_devices[0]
+        self.ad.sim_card = getattr(self.ad, "sim_card", None)
+        self.md8475a_ip_address = self.user_params[
+            "anritsu_md8475a_ip_address"]
+        self.wlan_option = self.user_params.get("anritsu_wlan_option", False)
+        self.wait_time_between_reg_and_msg = self.user_params.get(
+            "wait_time_between_reg_and_msg", WAIT_TIME_BETWEEN_REG_AND_MSG)
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log,
+                                   self.wlan_option)
+        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.bts1] = set_simulation_func(self.anritsu, self.user_params,
+                                              self.ad.sim_card)
+            set_usim_parameters(self.anritsu, self.ad.sim_card)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                preferred_network_setting = NETWORK_MODE_LTE_GSM_WCDMA
+                rat_family = RAT_FAMILY_LTE
+            elif rat == RAT_WCDMA:
+                self.bts1.wcdma_ctch = CTCHSetup.CTCH_ENABLE
+                self.ad.droid.telephonyToggleDataConnection(False)
+                preferred_network_setting = NETWORK_MODE_GSM_UMTS
+                rat_family = RAT_FAMILY_UMTS
+            elif rat == RAT_GSM:
+                self.bts1.gsm_cbch = CBCHSetup.CBCH_ENABLE
+                self.ad.droid.telephonyToggleDataConnection(False)
+                preferred_network_setting = NETWORK_MODE_GSM_ONLY
+                rat_family = RAT_FAMILY_GSM
+            elif rat == RAT_1XRTT:
+                preferred_network_setting = NETWORK_MODE_CDMA
+                rat_family = RAT_FAMILY_CDMA2000
+            else:
+                self.log.error("No valid RAT provided for CMAS test.")
+                return False
+
+            if not ensure_network_rat(
+                    self.log,
+                    self.ad,
+                    preferred_network_setting,
+                    rat_family,
+                    toggle_apm_after_setting=True):
+                self.log.error(
+                    "Failed to set rat family {}, preferred network:{}".format(
+                        rat_family, preferred_network_setting))
+                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 """
+
+    @test_tracker_info(uuid="e5ddf562-e94b-4b58-bc7d-6635c01f290e")
+    @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")
+
+    @test_tracker_info(uuid="33be2aaa-e8a6-4832-afea-8bd7e5555cc7")
+    @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")
+
+    @test_tracker_info(uuid="111d3b0b-020a-4818-9681-e46d1d4d61fd")
+    @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")
+
+    @test_tracker_info(uuid="df5676b2-cfab-4d64-8c51-ed2b95642dce")
+    @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")
+
+    @test_tracker_info(uuid="954544cc-75e8-408b-a5a5-4d820d0e0b3d")
+    @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")
+
+    @test_tracker_info(uuid="8c681524-b217-422b-9b45-0dea9b30deed")
+    @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")
+
+    @test_tracker_info(uuid="44b0c8c5-b5f4-4fe0-b62f-6586bd37c728")
+    @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")
+
+    @test_tracker_info(uuid="67cccd46-4cce-47b2-9cba-a24abe7aedc5")
+    @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)
+
+    @test_tracker_info(uuid="4053c54b-cda8-456a-8bce-5483732c9aee")
+    @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)
+
+    @test_tracker_info(uuid="95e4643b-3387-41ce-ac24-8db3a8f96557")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_presidential_alert_1x_evdo(self):
+        """CMAS Presidential alert message reception on 1x with EVDO
+
+        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 with EVDO
+        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_evdo, RAT_1XRTT,
+            CMAS_C2K_CATEGORY_PRESIDENTIAL, "1X CMAS Presidential Alert")
+
+    @test_tracker_info(uuid="d1283544-81d0-4852-9387-c94826794896")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_extreme_immediate_likely_1x_evdo(self):
+        """CMAS Extreme immediate likely message reception on 1x with EVDO
+
+        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 with EVDO
+        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_evdo, 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)
+
+    @test_tracker_info(uuid="8ae7027e-77ec-4c9b-88e5-c20caef007a5")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_cmas_child_abduction_emergency_1x_evdo(self):
+        """CMAS Child abduction emergency message reception on 1x with EVDO
+
+        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_evdo, 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)
+
+    @test_tracker_info(uuid="7e4ab36b-4e9b-4bdf-bdb8-8356103a3e79")
+    @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")
+
+    @test_tracker_info(uuid="c6d6b57b-c915-46e3-acbe-4d7f8cd6e52e")
+    @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")
+
+    @test_tracker_info(uuid="eb0a51de-f5fa-4b66-b0c2-21320fca44ca")
+    @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/TelLabDataTest.py b/acts/tests/google/tel/lab/TelLabDataTest.py
new file mode 100644
index 0000000..ce1536f
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabDataTest.py
@@ -0,0 +1,219 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Sanity tests for connectivity tests in telephony
+"""
+
+import time
+import json
+import logging
+import os
+
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import BtsBandwidth
+from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
+from acts.test_utils.tel.anritsu_utils import cb_serial_number
+from acts.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts.test_utils.tel.anritsu_utils import sms_mo_send
+from acts.test_utils.tel.anritsu_utils import sms_mt_receive_verify
+from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_1XRTT
+from acts.test_utils.tel.tel_defines import RAT_GSM
+from acts.test_utils.tel.tel_defines import RAT_LTE
+from acts.test_utils.tel.tel_defines import RAT_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts.test_utils.tel.tel_defines import GEN_4G
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.tel_test_utils import iperf_test_by_adb
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.utils import adb_shell_ping
+from acts.utils import rand_ascii_str
+from acts.controllers import iperf_server
+from acts.utils import exe_cmd
+
+DEFAULT_PING_DURATION = 30
+MAX_ITERATIONS = 10
+
+class TelLabDataTest(TelephonyBaseTest):
+    SETTLING_TIME = 30
+    SERIAL_NO = cb_serial_number()
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.ad = self.android_devices[0]
+        self.ip_server = self.iperf_servers[0]
+        self.port_num = self.ip_server.port
+        self.log.info("Iperf Port is %s", self.port_num)
+        self.ad.sim_card = getattr(self.ad, "sim_card", None)
+        self.log.info("SIM Card is %s", self.ad.sim_card)
+        self.md8475a_ip_address = self.user_params[
+            "anritsu_md8475a_ip_address"]
+        self.wlan_option = self.user_params.get("anritsu_wlan_option", False)
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log,
+                                   self.wlan_option)
+        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 _setup_data(self,
+                   set_simulation_func,
+                   rat):
+        try:
+            [self.bts1] = set_simulation_func(self.anritsu, self.user_params,
+                                self.ad.sim_card)
+            set_usim_parameters(self.anritsu, self.ad.sim_card)
+            self.bts1.bandwidth = BtsBandwidth.LTE_BANDWIDTH_20MHz
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                preferred_network_setting = NETWORK_MODE_LTE_GSM_WCDMA
+                rat_family = RAT_FAMILY_LTE
+            elif rat == RAT_WCDMA:
+                preferred_network_setting = NETWORK_MODE_GSM_UMTS
+                rat_family = RAT_FAMILY_UMTS
+            elif rat == RAT_GSM:
+                preferred_network_setting = NETWORK_MODE_GSM_ONLY
+                rat_family = RAT_FAMILY_GSM
+            elif rat == RAT_1XRTT:
+                preferred_network_setting = NETWORK_MODE_CDMA
+                rat_family = RAT_FAMILY_CDMA2000
+            else:
+                self.log.error("No valid RAT provided for SMS test.")
+                return False
+
+            if not ensure_network_rat(self.log,
+                                      self.ad,
+                                      preferred_network_setting,
+                                      rat_family,
+                                      toggle_apm_after_setting=True):
+                self.log.error(
+                    "Failed to set rat family {}, preferred network:{}".format(
+                        rat_family, preferred_network_setting))
+                return False
+
+            self.anritsu.wait_for_registration_state()
+            time.sleep(self.SETTLING_TIME)
+            if not ensure_network_generation(self.log, self.ad,
+                                             GEN_4G, NETWORK_SERVICE_DATA):
+                self.log.error("Device not in 4G Connected Mode.")
+                return False
+
+            # Fetch IP address of the host machine
+            cmd = "|".join(("ifconfig", "grep eth0 -A1", "grep inet",
+                           "cut -d ':' -f2", "cut -d ' ' -f 1"))
+            destination_ip = exe_cmd(cmd)
+            destination_ip = (destination_ip.decode("utf-8")).split("\n")[0]
+            self.log.info("Dest IP is %s", destination_ip)
+
+            if not adb_shell_ping(self.ad, DEFAULT_PING_DURATION,
+                                  destination_ip):
+                self.log.error("Pings failed to Destination.")
+                return False
+
+            # Power, iperf, file output, power change
+            for iteration in range (1, MAX_ITERATIONS+1):
+                self.log.info("------- Current Iteration: %d / %d -------",
+                               iteration, MAX_ITERATIONS)
+                current_power = self.bts1.output_level
+                self.log.info("Current Power Level is %s", current_power)
+
+                self.ip_server.start()
+                tput_dict = {"Uplink" : 0, "Downlink" : 0}
+                if iperf_test_by_adb(self.log, self.ad, destination_ip,
+                                 self.port_num, True, 10, rate_dict=tput_dict):
+                    uplink = tput_dict["Uplink"]
+                    downlink = tput_dict["Downlink"]
+                else:
+                    self.log.error("iperf failed to Destination.")
+                    self.log.info("Iteration %d Failed", iteration)
+                    return False
+                self.ip_server.stop()
+
+                self.log.info("Iteration %d Passed", iteration)
+                self.logpath = os.path.join(logging.log_path, "power_tput.txt")
+                line = "Power " + current_power + " DL TPUT " + str(downlink)
+                with open (self.logpath, "a") as tput_file:
+                    tput_file.write(line)
+                    tput_file.write("\n")
+                current_power = float(current_power)
+                new_power = current_power - 5.0
+                self.log.info("Setting Power Level to %f", new_power)
+                self.bts1.output_level = new_power
+
+        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 Data procedure: " + str(e))
+            return False
+        return True
+
+    """ Tests Begin """
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_lte_pings_iperf(self):
+        """ Test Pings functionality on LTE
+
+        Make Sure Phone is in LTE mode
+        Ping to destination server IP
+        iperf server on host machine
+        iperf client in on adb
+        iperf DL
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_data(set_system_model_lte, RAT_LTE)
+
+    """ 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..e451a06
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabEmergencyCallTest.py
@@ -0,0 +1,931 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Sanity tests for voice tests in telephony
+"""
+import time
+
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib.md8475a import CsfbType
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import VirtualPhoneAutoAnswer
+from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
+from acts.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_CALL
+from acts.test_utils.tel.anritsu_utils import call_mo_setup_teardown
+from acts.test_utils.tel.anritsu_utils import ims_call_cs_teardown
+from acts.test_utils.tel.anritsu_utils import call_mt_setup_teardown
+from acts.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts.test_utils.tel.anritsu_utils import set_system_model_1x_evdo
+from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte_1x
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
+from acts.test_utils.tel.tel_defines import DEFAULT_EMERGENCY_CALL_NUMBER
+from acts.test_utils.tel.tel_defines import EMERGENCY_CALL_NUMBERS
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts.test_utils.tel.tel_defines import RAT_1XRTT
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+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 toggle_airplane_mode_by_adb
+from acts.test_utils.tel.tel_test_utils import toggle_volte
+from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_decorators import test_tracker_info
+
+class TelLabEmergencyCallTest(TelephonyBaseTest):
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        try:
+            self.stress_test_number = int(self.user_params[
+                "stress_test_number"])
+            self.log.info("Executing {} calls per test in stress test mode".
+                          format(self.stress_test_number))
+        except KeyError:
+            self.stress_test_number = 0
+            self.log.info(
+                "No 'stress_test_number' defined: running single iteration tests"
+            )
+
+        self.ad = self.android_devices[0]
+        self.ad.sim_card = getattr(self.ad, "sim_card", None)
+        self.md8475a_ip_address = self.user_params[
+            "anritsu_md8475a_ip_address"]
+        self.wlan_option = self.user_params.get("anritsu_wlan_option", False)
+
+        setattr(self, 'emergency_call_number', DEFAULT_EMERGENCY_CALL_NUMBER)
+        if 'emergency_call_number' in self.user_params:
+            self.emergency_call_number = self.user_params[
+                'emergency_call_number']
+            self.log.info("Using provided emergency call number: {}".format(
+                self.emergency_call_number))
+        if not self.emergency_call_number in EMERGENCY_CALL_NUMBERS:
+            self.log.warning("Unknown Emergency Number {}".format(
+                self.emergency_call_number))
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log,
+                                   self.wlan_option)
+        except AnritsuError:
+            self.log.error("Error in connecting to Anritsu Simulator")
+            return False
+        return True
+
+    def setup_test(self):
+        ensure_phone_default_state(self.log, self.ad, check_subscription=False)
+        toggle_airplane_mode_by_adb(self.log, self.ad, True)
+        # get a handle to virtual phone
+        self.virtualPhoneHandle = self.anritsu.get_VirtualPhone()
+        return True
+
+    def teardown_test(self):
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        toggle_airplane_mode_by_adb(self.log, self.ad, True)
+        return True
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+        return True
+
+    def _setup_emergency_call(self,
+                              set_simulation_func,
+                              phone_setup_func,
+                              phone_idle_func_after_registration=None,
+                              is_ims_call=False,
+                              is_wait_for_registration=True,
+                              csfb_type=None,
+                              srlte_csfb=None,
+                              srvcc=None,
+                              emergency_number=DEFAULT_EMERGENCY_CALL_NUMBER,
+                              teardown_side=CALL_TEARDOWN_PHONE,
+                              wait_time_in_call=WAIT_TIME_IN_CALL):
+        try:
+            set_simulation_func(self.anritsu, self.user_params,
+                                self.ad.sim_card)
+            set_usim_parameters(self.anritsu, self.ad.sim_card)
+            self.virtualPhoneHandle.auto_answer = (VirtualPhoneAutoAnswer.ON,
+                                                   2)
+            if csfb_type:
+                self.anritsu.csfb_type = csfb_type
+            if srlte_csfb == "lte_call_failure":
+                self.anritsu.send_command("IMSPSAPAUTOANSWER 1,DISABLE")
+                self.anritsu.start_simulation()
+                self.anritsu.send_command("IMSSTARTVN 1")
+                check_ims_reg = True
+                check_ims_calling = True
+            elif srlte_csfb == "ims_unregistered":
+                self.anritsu.start_simulation()
+                self.anritsu.send_command("IMSSTARTVN 1")
+                self.anritsu.send_command(
+                    "IMSCSCFBEHAVIOR 1,SENDERRORRESPONSE603")
+                check_ims_reg = False
+                check_ims_calling = False
+            elif srlte_csfb == "ps911_unsupported":
+                self.anritsu.send_command("EMCBS NOTSUPPORT,BTS1")
+                self.anritsu.start_simulation()
+                self.anritsu.send_command("IMSSTARTVN 1")
+                check_ims_reg = True
+                check_ims_calling = False
+            elif srlte_csfb == "emc_barred":
+                self.anritsu.send_command("ACBARRED USERSPECIFIC,BTS1")
+                self.anritsu.send_command("LTEEMERGENCYACBARRED BARRED,BTS1")
+                self.anritsu.start_simulation()
+                self.anritsu.send_command("IMSSTARTVN 1")
+                check_ims_reg = True
+                check_ims_calling = False
+            elif srvcc == "InCall":
+                self.anritsu.start_simulation()
+                self.anritsu.send_command("IMSSTARTVN 1")
+                check_ims_reg = True
+                check_ims_calling = True
+            else:
+                self.anritsu.start_simulation()
+            if is_ims_call:
+                self.anritsu.send_command("IMSSTARTVN 1")
+
+            iterations = 1
+            if self.stress_test_number > 0:
+                iterations = self.stress_test_number
+            successes = 0
+            for i in range(1, iterations + 1):
+                if self.stress_test_number:
+                    self.log.info("Running iteration {} of {}".format(
+                        i, iterations))
+                # FIXME: There's no good reason why this must be true;
+                # I can only assume this was done to work around a problem
+                self.ad.droid.telephonyToggleDataConnection(False)
+
+                # turn off all other BTS to ensure UE registers on BTS1
+                sim_model = (self.anritsu.get_simulation_model()).split(",")
+                no_of_bts = len(sim_model)
+                for i in range(2, no_of_bts + 1):
+                    self.anritsu.send_command("OUTOFSERVICE OUT,BTS{}".format(
+                        i))
+
+                if phone_setup_func is not None:
+                    if not phone_setup_func(self.ad):
+                        self.log.error("phone_setup_func failed.")
+                        continue
+                if is_wait_for_registration:
+                    self.anritsu.wait_for_registration_state()
+
+                if phone_idle_func_after_registration:
+                    if not phone_idle_func_after_registration(self.log,
+                                                              self.ad):
+                        continue
+
+                for i in range(2, no_of_bts + 1):
+                    self.anritsu.send_command("OUTOFSERVICE IN,BTS{}".format(
+                        i))
+
+                time.sleep(WAIT_TIME_ANRITSU_REG_AND_CALL)
+                if srlte_csfb or srvcc:
+                    if not ims_call_cs_teardown(
+                            self.log, self.ad, self.anritsu, emergency_number,
+                            CALL_TEARDOWN_PHONE, True, check_ims_reg,
+                            check_ims_calling, srvcc,
+                            WAIT_TIME_IN_CALL_FOR_IMS, WAIT_TIME_IN_CALL):
+                        self.log.error(
+                            "Phone {} Failed to make emergency call to {}"
+                            .format(self.ad.serial, emergency_number))
+                        continue
+                else:
+                    if not call_mo_setup_teardown(
+                            self.log, self.ad, self.anritsu, emergency_number,
+                            CALL_TEARDOWN_PHONE, True, WAIT_TIME_IN_CALL,
+                            is_ims_call):
+                        self.log.error(
+                            "Phone {} Failed to make emergency call to {}"
+                            .format(self.ad.serial, emergency_number))
+                        continue
+                successes += 1
+                if self.stress_test_number:
+                    self.log.info("Passed iteration {}".format(i))
+            if self.stress_test_number:
+                self.log.info("Total of {} successes out of {} attempts".
+                              format(successes, iterations))
+            return True if successes == iterations else 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 _phone_setup_lte_wcdma(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_LTE_GSM_WCDMA,
+            RAT_FAMILY_LTE,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_lte_1x(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_LTE_CDMA_EVDO,
+            RAT_FAMILY_LTE,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_wcdma(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_GSM_UMTS,
+            RAT_FAMILY_UMTS,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_gsm(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_GSM_ONLY,
+            RAT_FAMILY_GSM,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_1x(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_CDMA,
+            RAT_FAMILY_CDMA2000,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_airplane_mode(self, ad):
+        return toggle_airplane_mode_by_adb(self.log, ad, True)
+
+    def _phone_disable_airplane_mode(self, ad):
+        return toggle_airplane_mode_by_adb(self.log, ad, False)
+
+    def _phone_setup_volte_airplane_mode(self, ad):
+        toggle_volte(self.log, ad, True)
+        return toggle_airplane_mode_by_adb(self.log, ad, True)
+
+    def _phone_setup_volte(self, ad):
+        ad.droid.telephonyToggleDataConnection(True)
+        toggle_volte(self.log, ad, True)
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+            RAT_FAMILY_LTE,
+            toggle_apm_after_setting=True)
+
+    """ Tests Begin """
+
+    @test_tracker_info(uuid="f5c93228-3b43-48a3-b509-796d41625171")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_lte_wcdma_csfb_redirection(self):
+        """ Test Emergency call functionality on LTE (CSFB to WCDMA).
+            CSFB type is REDIRECTION
+
+        Steps:
+        1. Setup CallBox on LTE and WCDMA network, make sure DUT register on LTE network.
+        2. Make an emergency call to 911. Make sure DUT CSFB to WCDMA.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed. DUT CSFB to WCDMA.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_wcdma,
+            self._phone_setup_lte_wcdma,
+            emergency_number=self.emergency_call_number,
+            csfb_type=CsfbType.CSFB_TYPE_REDIRECTION)
+
+    @test_tracker_info(uuid="8deb6b21-2cb0-4241-bcad-6cd62a340b07")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_lte_wcdma_csfb_handover(self):
+        """ Test Emergency call functionality on LTE (CSFB to WCDMA).
+            CSFB type is HANDOVER
+
+        Steps:
+        1. Setup CallBox on LTE and WCDMA network, make sure DUT register on LTE network.
+        2. Make an emergency call to 911. Make sure DUT CSFB to WCDMA.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed. DUT CSFB to WCDMA.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_wcdma,
+            self._phone_setup_lte_wcdma,
+            emergency_number=self.emergency_call_number,
+            csfb_type=CsfbType.CSFB_TYPE_HANDOVER)
+
+    @test_tracker_info(uuid="52b6b783-de77-497d-87e0-63c930e6c9bb")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_lte_1x_csfb(self):
+        """ Test Emergency call functionality on LTE (CSFB to 1x).
+
+        Steps:
+        1. Setup CallBox on LTE and CDMA 1X network, make sure DUT register on LTE network.
+        2. Make an emergency call to 911. Make sure DUT CSFB to 1x.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed. DUT CSFB to 1x.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_1x,
+            self._phone_setup_lte_1x,
+            emergency_number=self.emergency_call_number)
+
+    @test_tracker_info(uuid="fcdd5a4f-fdf2-44dc-b1b2-44ab175791ce")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_wcdma(self):
+        """ Test Emergency call functionality on WCDMA
+
+        Steps:
+        1. Setup CallBox on WCDMA network, make sure DUT register on WCDMA network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_wcdma,
+            self._phone_setup_wcdma,
+            emergency_number=self.emergency_call_number)
+
+    @test_tracker_info(uuid="295c3188-24e2-4c53-80c6-3d3001e2ff16")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_gsm(self):
+        """ Test Emergency call functionality on GSM
+
+        Steps:
+        1. Setup CallBox on GSM network, make sure DUT register on GSM network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_gsm,
+            self._phone_setup_gsm,
+            emergency_number=self.emergency_call_number)
+
+    @test_tracker_info(uuid="4f21bfc0-a0a9-43b8-9285-dcfb131a3a04")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_1x(self):
+        """ Test Emergency call functionality on CDMA 1X
+
+        Steps:
+        1. Setup CallBox on 1x network, make sure DUT register on 1x network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_1x,
+            self._phone_setup_1x,
+            emergency_number=self.emergency_call_number)
+
+    @test_tracker_info(uuid="6dfd3e1d-7faa-426f-811a-ebd5e54f9c9a")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_1x_evdo(self):
+        """ Test Emergency call functionality on CDMA 1X with EVDO
+
+        Steps:
+        1. Setup CallBox on 1x and EVDO network, make sure DUT register on 1x network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_1x_evdo,
+            self._phone_setup_1x,
+            emergency_number=self.emergency_call_number)
+
+    @test_tracker_info(uuid="40f9897e-4924-4896-9f2c-0b4f45331251")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_1x_apm(self):
+        """ Test Emergency call functionality on Airplane mode
+
+        Steps:
+        1. Setup CallBox on 1x network.
+        2. Turn on Airplane mode on DUT. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_1x,
+            self._phone_setup_airplane_mode,
+            is_wait_for_registration=False,
+            emergency_number=self.emergency_call_number)
+
+    @test_tracker_info(uuid="5997c004-449b-4a5e-816e-2ff0c0eb928d")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_wcdma_apm(self):
+        """ Test Emergency call functionality on Airplane mode
+
+        Steps:
+        1. Setup CallBox on WCDMA network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_wcdma,
+            self._phone_setup_airplane_mode,
+            is_wait_for_registration=False,
+            emergency_number=self.emergency_call_number)
+
+    @test_tracker_info(uuid="9afde8d3-bdf0-41d9-af8c-2a8f36aae169")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_volte_wcdma_srvcc(self):
+        """ Test Emergency call functionality,
+        VoLTE to WCDMA SRVCC
+        Steps:
+        1. Setup CallBox on VoLTE network with WCDMA.
+        2. Turn on DUT and enable VoLTE. Make an emergency call to 911.
+        3. Check if VoLTE emergency call connected successfully.
+        4. Handover the call to WCDMA and check if the call is still up.
+        5. Tear down the call.
+
+        Expected Results:
+        1. VoLTE Emergency call is made successfully.
+        2. After SRVCC, the 911 call is not dropped.
+        3. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_wcdma,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srvcc="InCall",
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="2e454f11-e77d-452a-bcac-7271378953ed")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_volte_gsm_srvcc(self):
+        """ Test Emergency call functionality,
+        VoLTE to GSM SRVCC
+        Steps:
+        1. Setup CallBox on VoLTE network with GSM.
+        2. Turn on DUT and enable VoLTE. Make an emergency call to 911.
+        3. Check if VoLTE emergency call connected successfully.
+        4. Handover the call to GSM and check if the call is still up.
+        5. Tear down the call.
+
+        Expected Results:
+        1. VoLTE Emergency call is made successfully.
+        2. After SRVCC, the 911 call is not dropped.
+        3. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_gsm,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srvcc="InCall",
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="4dae7e62-b73e-4ba1-92ee-ecc121b898b3")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_csfb_1x_lte_call_failure(self):
+        """ Test Emergency call functionality,
+        CSFB to CDMA1x after VoLTE call failure
+        Ref: VzW LTE E911 test plan, 2.23, VZ_TC_LTEE911_7481
+        Steps:
+        1. Setup CallBox on VoLTE network with CDMA1x.
+        2. Turn on DUT and enable VoLTE. Make an emergency call to 911.
+        3. Make sure Anritsu IMS server does not answer the call
+        4. The DUT requests CSFB to 1XCDMA and Anritsu accepts the call.
+        5. Tear down the call.
+
+        Expected Results:
+        1. VoLTE Emergency call is made.
+        2. Anritsu receive the call but does not answer.
+        3. The 911 call CSFB to CDMA1x and answered successfully.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_1x,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srlte_csfb="lte_call_failure",
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="e98c9101-1ab6-497e-9d67-a4ff62d28fea")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_csfb_1x_ims_unregistered(self):
+        """ Test Emergency call functionality,
+        CSFB to CDMA1x because ims registration delcined
+        Ref: VzW LTE E911 test plan, 2.25, VZ_TC_LTEE911_7483
+        Steps:
+        1. Setup CallBox on VoLTE network with CDMA1x.
+        2. Setup Anritsu IMS server to decline IMS registration
+        3. Turn on DUT and enable VoLTE. Make an emergency call to 911.
+        4. The DUT requests CSFB to 1XCDMA and Anritsu accepts the call.
+        5. Tear down the call.
+
+        Expected Results:
+        1. Phone registers on LTE network with VoLTE enabled.
+        2. When Emergency call is made, phone request CSFB to CDMA1x.
+        3. The 911 call on CDMA1x is answered successfully.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_1x,
+            self._phone_setup_volte,
+            None,
+            srlte_csfb="ims_unregistered",
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="d96e1b9b-2f6d-49f3-a203-f4277056869e")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_csfb_1x_ps911_unsupported(self):
+        """ Test Emergency call functionality,
+        CSFB to CDMA1x because MME does not support PS911, by setting
+        Emergency Bearer Service not supported in EPS network feature
+        Ref: VzW LTE E911 test plan, 2.26, VZ_TC_LTEE911_8357
+        Steps:
+        1. Setup CallBox on VoLTE network with CDMA1x.
+        2. Setup Emergency Bearer Service not supported in EPS network feature
+        3. Turn on DUT and enable VoLTE. Make an emergency call to 911.
+        4. The DUT requests CSFB to 1XCDMA and Anritsu accepts the call.
+        5. Tear down the call.
+
+        Expected Results:
+        1. Phone registers on LTE network with VoLTE enabled.
+        2. When Emergency call is made, phone request CSFB to CDMA1x.
+        3. The 911 call on CDMA1x is answered successfully.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_1x,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srlte_csfb="ps911_unsupported",
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="f7d48841-b8ef-4031-99de-28534aaf4c44")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_csfb_1x_emc_barred(self):
+        """ Test Emergency call functionality,
+        CSFB to CDMA1x because SIB2 Emergency Barred,
+        by setting Access Class Barred for Emergency
+        Ref: VzW LTE E911 test plan, 2.27, VZ_TC_LTEE911_8358
+        Steps:
+        1. Setup CallBox on VoLTE network with CDMA1x.
+        2. Set Access Class Barred for Emergency in SIB2
+        3. Turn on DUT and enable VoLTE. Make an emergency call to 911.
+        4. The DUT requests CSFB to 1XCDMA and Anritsu accepts the call.
+        5. Tear down the call.
+
+        Expected Results:
+        1. Phone registers on LTE network with VoLTE enabled.
+        2. When Emergency call is made, phone request CSFB to CDMA1x.
+        3. The 911 call on CDMA1x is answered successfully.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_1x,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srlte_csfb="emc_barred",
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="5bbbecec-0fef-430b-acbd-01ef7c7055c0")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_volte_1x(self):
+        """ Test Emergency call functionality on VoLTE with CDMA1x
+        Ref: VzW LTE E911 test plan, 2.24, VZ_TC_LTEE911_7482
+        Steps:
+        1. Setup CallBox on VoLTE network with CDMA1x.
+        2. Turn on DUT and enable VoLTE. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte_1x,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            is_ims_call=True,
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="e32862e0-ec11-4de8-8b9a-851bab9feb29")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_volte(self):
+        """ Test Emergency call functionality on VoLTE
+
+        Steps:
+        1. Setup CallBox on VoLTE network.
+        2. Turn on DUT and enable VoLTE. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            is_ims_call=True,
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="fa5b4e52-c249-42ec-a9d3-9523336f88f7")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_volte_apm(self):
+        """ Test Emergency call functionality on VoLTE
+
+        Steps:
+        1. Setup CallBox on VoLTE network.
+        2. Turn on Airplane mode on DUT. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte,
+            self._phone_setup_volte_airplane_mode,
+            is_ims_call=True,
+            is_wait_for_registration=False,
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="260e4892-fdae-4d49-bfc6-04fe72a5a715")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_no_sim_wcdma(self):
+        """ Test Emergency call functionality with no SIM.
+
+        Steps:
+        1. Setup CallBox on WCDMA network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_wcdma,
+            self._phone_disable_airplane_mode,
+            emergency_number=self.emergency_call_number,
+            is_wait_for_registration=False)
+
+    @test_tracker_info(uuid="2dbbbde5-c298-4a2d-ae65-b739d1dd1445")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_no_sim_1x(self):
+        """ Test Emergency call functionality with no SIM.
+
+        Steps:
+        1. Setup CallBox on 1x network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_1x,
+            self._phone_disable_airplane_mode,
+            emergency_number=self.emergency_call_number,
+            is_wait_for_registration=False)
+
+    @test_tracker_info(uuid="fa77dee2-3235-42e0-b24d-c8fdb3fbef2f")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_no_sim_gsm(self):
+        """ Test Emergency call functionality with no SIM.
+
+        Steps:
+        1. Setup CallBox on GSM network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_gsm,
+            self._phone_disable_airplane_mode,
+            emergency_number=self.emergency_call_number,
+            is_wait_for_registration=False)
+
+    @test_tracker_info(uuid="9ead5c6a-b4cb-40d5-80b0-54d3c7ad31ff")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_no_sim_volte(self):
+        """ Test Emergency call functionality with no SIM.
+
+        Steps:
+        1. Setup CallBox on VoLTE network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_emergency_call(
+            set_system_model_lte,
+            self._phone_disable_airplane_mode,
+            is_wait_for_registration=False,
+            is_ims_call=True,
+            emergency_number=self.emergency_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @test_tracker_info(uuid="d6bdc1d7-0a08-4e6e-9de8-4084abb48bad")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_emergency_call_no_sim_1x_ecbm(self):
+        """ Test Emergency call functionality with no SIM.
+
+        Steps:
+        1. Setup CallBox on 1x network.
+        2. Make an emergency call to 911.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+        5. Make a call from Callbox to DUT.
+        6. Verify DUT receive the incoming call.
+        7. Answer on DUT, verify DUT can answer the call correctly.
+        8. Hangup the call on DUT.
+
+        Expected Results:
+        2. Emergency call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+        6. DUT receive incoming call.
+        7. DUT answer the call correctly.
+        8. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        if not self._setup_emergency_call(
+                set_system_model_1x,
+                self._phone_disable_airplane_mode,
+                emergency_number=self.emergency_call_number,
+                is_wait_for_registration=False):
+            self.log.error("Failed to make 911 call.")
+            return False
+        return call_mt_setup_teardown(self.log, self.ad,
+                                      self.anritsu.get_VirtualPhone(), None,
+                                      CALL_TEARDOWN_PHONE, 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..f5bce08
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabEtwsTest.py
@@ -0,0 +1,288 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Sanity tests for voice tests in telephony
+"""
+import time
+
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import CBCHSetup
+from acts.controllers.anritsu_lib.md8475a import CTCHSetup
+from acts.test_utils.tel.anritsu_utils import ETWS_WARNING_EARTHQUAKETSUNAMI
+from acts.test_utils.tel.anritsu_utils import ETWS_WARNING_OTHER_EMERGENCY
+from acts.test_utils.tel.anritsu_utils import cb_serial_number
+from acts.test_utils.tel.anritsu_utils import etws_receive_verify_message_lte_wcdma
+from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_1XRTT
+from acts.test_utils.tel.tel_defines import RAT_GSM
+from acts.test_utils.tel.tel_defines import RAT_LTE
+from acts.test_utils.tel.tel_defines import RAT_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_decorators import test_tracker_info
+
+WAIT_TIME_BETWEEN_REG_AND_MSG = 15  # default 15 sec
+
+
+class TelLabEtwsTest(TelephonyBaseTest):
+    SERIAL_NO = cb_serial_number()
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.ad = self.android_devices[0]
+        self.ad.sim_card = getattr(self.ad, "sim_card", None)
+        self.md8475a_ip_address = self.user_params[
+            "anritsu_md8475a_ip_address"]
+        self.wlan_option = self.user_params.get("anritsu_wlan_option", False)
+        self.ad.adb.shell("settings put secure cmas_additional_broadcast_pkg "
+                          "com.googlecode.android_scripting")
+        self.wait_time_between_reg_and_msg = self.user_params.get(
+            "wait_time_between_reg_and_msg", WAIT_TIME_BETWEEN_REG_AND_MSG)
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log,
+                                   self.wlan_option)
+        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.bts1] = set_simulation_func(self.anritsu, self.user_params,
+                                              self.ad.sim_card)
+            set_usim_parameters(self.anritsu, self.ad.sim_card)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                preferred_network_setting = NETWORK_MODE_LTE_GSM_WCDMA
+                rat_family = RAT_FAMILY_LTE
+            elif rat == RAT_WCDMA:
+                self.bts1.wcdma_ctch = CTCHSetup.CTCH_ENABLE
+                self.ad.droid.telephonyToggleDataConnection(False)
+                preferred_network_setting = NETWORK_MODE_GSM_UMTS
+                rat_family = RAT_FAMILY_UMTS
+            elif rat == RAT_GSM:
+                self.bts1.gsm_cbch = CBCHSetup.CBCH_ENABLE
+                self.ad.droid.telephonyToggleDataConnection(False)
+                preferred_network_setting = NETWORK_MODE_GSM_ONLY
+                rat_family = RAT_FAMILY_GSM
+            elif rat == RAT_1XRTT:
+                preferred_network_setting = NETWORK_MODE_CDMA
+                rat_family = RAT_FAMILY_CDMA2000
+            else:
+                self.log.error("No valid RAT provided for ETWS test.")
+                return False
+
+            if not ensure_network_rat(
+                    self.log,
+                    self.ad,
+                    preferred_network_setting,
+                    rat_family,
+                    toggle_apm_after_setting=True):
+                self.log.error(
+                    "Failed to set rat family {}, preferred network:{}".format(
+                        rat_family, preferred_network_setting))
+                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 """
+
+    @test_tracker_info(uuid="af4a00d0-9a91-45d5-9f65-9541e64a57f2")
+    @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")
+
+    @test_tracker_info(uuid="03785878-0319-413c-9190-d4e08f0edc33")
+    @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")
+
+    @test_tracker_info(uuid="1ef4a5d7-9ceb-49eb-8ec7-5538625c8bd4")
+    @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")
+
+    @test_tracker_info(uuid="71dc9650-d00a-4533-99f5-5cc301c21334")
+    @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")
+
+    @test_tracker_info(uuid="a9fd9c0e-21bf-41d1-81d2-c34679052fe0")
+    @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")
+
+    @test_tracker_info(uuid="0ae42f8d-1720-449c-9200-e88f7f1d2cbe")
+    @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..5f76ad2
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabNeighborCellTest.py
@@ -0,0 +1,3071 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+    Test Script for Telephony Pre Check In Sanity
+"""
+
+import math
+import time
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib.md8475a import CTCHSetup
+from acts.controllers.anritsu_lib.md8475a import BtsBandwidth
+from acts.controllers.anritsu_lib.md8475a import BtsPacketRate
+from acts.controllers.anritsu_lib.md8475a import BtsServiceState
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.mg3710a import MG3710A
+from acts.test_utils.tel.anritsu_utils import LTE_BAND_2
+from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte_lte
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma_gsm
+from acts.test_utils.tel.anritsu_utils import  set_system_model_wcdma_wcdma
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    gsm_band850_ch128_fr869_cid58_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    gsm_band850_ch251_fr893_cid59_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    gsm_band1900_ch512_fr1930_cid51_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    gsm_band1900_ch512_fr1930_cid52_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    gsm_band1900_ch512_fr1930_cid53_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    gsm_band1900_ch512_fr1930_cid54_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    gsm_band1900_ch640_fr1955_cid56_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    gsm_band1900_ch750_fr1977_cid57_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    lte_band2_ch900_fr1960_pcid9_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    lte_band4_ch2000_fr2115_pcid1_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    lte_band4_ch2000_fr2115_pcid2_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    lte_band4_ch2000_fr2115_pcid3_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    lte_band4_ch2000_fr2115_pcid4_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    lte_band4_ch2050_fr2120_pcid7_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    lte_band4_ch2050_fr2120_pcid7_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    lte_band4_ch2250_fr2140_pcid8_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    lte_band12_ch5095_fr737_pcid10_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    wcdma_band1_ch10700_fr2140_cid31_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    wcdma_band1_ch10700_fr2140_cid32_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    wcdma_band1_ch10700_fr2140_cid33_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    wcdma_band1_ch10700_fr2140_cid34_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    wcdma_band1_ch10575_fr2115_cid36_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    wcdma_band1_ch10800_fr2160_cid37_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    wcdma_band2_ch9800_fr1960_cid38_cell
+from acts.framework.acts.controllers.anritsu_lib.cell_configurations import \
+    wcdma_band2_ch9900_fr1980_cid39_cell
+
+
+class TelLabNeighborCellTest(TelephonyBaseTest):
+
+    # This is the default offset between CallBox Power Level and Phone measure
+    # Power Level.
+    # TODO: Probably need to further tune those values.
+    _LTE_RSSI_OFFSET = -39
+    _WCDMA_RSSI_OFFSET = -31
+    _GSM_RSSI_OFFSET = -30
+
+    _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'
+
+    # Below are keys should be included in expected cell info.
+    # TARGET_RSSI: This is expected RSSI.
+    TARGET_RSSI = 'target_rssi'
+    # MAX_ERROR_RSSI: This is max error between 'each sample of reported RSSI'
+    # and 'expected RSSI'
+    MAX_ERROR_RSSI = 'max_error_rssi'
+    # MAX_ERROR_AVERAGE_RSSI: This is max error between
+    # 'average value of reported RSSI' and 'expected RSSI'
+    MAX_ERROR_AVERAGE_RSSI = 'max_error_average_rssi'
+    # REPORT_RATE: expected report rate for neighbor cell.
+    REPORT_RATE = 'report_rate'
+    # RAT: expected network rat.
+    RAT = 'rat'
+    # IS_REGISTERED: is the cell registered.
+    # For serving cell, this value should be True; for neighbor cell, should be
+    # False
+    IS_REGISTERED = 'registered'
+    # CID: cell CID info.
+    CID = 'cid'
+    # PCID: cell PCID info.
+    PCID = 'pcid'
+    # PSC: cell PSC info.
+    PSC = 'psc'
+
+    # Keys for calculate average RSSI. Only used in _verify_cell_info
+    RSSI = 'rssi'
+    COUNT = 'count'
+
+    # Pre-defined invalid value. If this value is reported in cell info, just
+    # discard this value. E.g. if in reported cell info, cid is reported as
+    # 0x7fffffff, need to discard this value when calculating unique_id
+    INVALID_VALUE = 0x7fffffff
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        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"]
+
+        if "lte_rssi_offset" in self.user_params:
+            self._LTE_RSSI_OFFSET = int(self.user_param["lte_rssi_offset"])
+        if "wcdma_rssi_offset" in self.user_params:
+            self._WCDMA_RSSI_OFFSET = int(self.user_param["wcdma_rssi_offset"])
+        if "gsm_rssi_offset" in self.user_params:
+            self._GSM_RSSI_OFFSET = int(self.user_param["gsm_rssi_offset"])
+
+
+    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.telephonySetPreferredNetworkTypes(
+            NETWORK_MODE_LTE_GSM_WCDMA)
+        ensure_phones_idle(self.log, self.android_devices)
+        self.ad.droid.connectivityToggleAirplaneMode(True)
+        self.ad.droid.telephonyToggleDataConnection(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 _is_matching_cell(self, expected_cell_info, input_cell_info):
+        """Return if 'input_cell_info' matches 'expected_cell_info'.
+
+        Args:
+            expected_cell_info: expected cell info. (dictionary)
+            input_cell_info: input cell info to test. (dictionary)
+
+        Returns:
+            True if:
+                for each key in key_list, if key exist in expected_cell_info,
+                it should also exist in input_cell_info, and the values should
+                equal in expected_cell_info and input_cell_info
+            False otherwise.
+        """
+        for key in [self.CID, self.PCID, self.RAT, self.PSC,
+                    self.IS_REGISTERED]:
+            if key in expected_cell_info:
+                if key not in input_cell_info:
+                    return False
+                if input_cell_info[key] != expected_cell_info[key]:
+                    return False
+        return True
+
+    def _unique_cell_id(self, input_cell_info):
+        """Get the unique id for cell_info, based on cid, pcid, rat, psc and
+        is_registered.
+
+        Args:
+            input_cell_info: cell info to get unique id.
+
+        Returns:
+            unique id (string)
+        """
+        unique_id = ""
+        for key in [self.CID, self.PCID, self.RAT, self.PSC,
+                    self.IS_REGISTERED]:
+            if key in input_cell_info:
+                if input_cell_info[key] != self.INVALID_VALUE:
+                    unique_id += key + ":" + str(input_cell_info[key]) + ":"
+        return unique_id
+
+    def _get_rssi_from_cell_info(self, cell_info):
+        """Return the RSSI value reported in cell_info.
+
+        Args:
+            cell_info: cell info to get RSSI.
+
+        Returns:
+            RSSI reported in this cell info.
+        """
+        rat_to_rssi_tbl = {
+            'lte': 'rsrp',
+            'wcdma': 'signal_strength',
+            'gsm':  'signal_strength'
+        }
+        try:
+            return cell_info[rat_to_rssi_tbl[cell_info[self.RAT]]]
+        except KeyError:
+            return None
+
+    def _is_rssi_in_expected_range(self, actual_rssi, expected_rssi, max_error):
+        """Return if actual_rssi is within expected range.
+
+        Args:
+            actual_rssi: the rssi value to test.
+            expected_rssi: expected rssi value.
+            max_error: max error.
+
+        Returns:
+            if the difference between actual_rssi and expected_rssi is within
+            max_error, return True. Otherwise False.
+        """
+        if abs(actual_rssi - expected_rssi) > max_error:
+            self.log.error(
+                "Expected RSSI: {}, max error: {}, reported RSSI: {}".
+                format(expected_rssi, max_error, actual_rssi))
+            return False
+        else:
+            return True
+
+    def _verify_cell_info(self, ad, expected_cell_info_stats,
+                          number_of_sample=10, delay_each_sample=5):
+        """Return if reported cell info from ad matches expected_cell_info_stats
+        or not.
+
+        Args:
+            ad: android device object.
+            expected_cell_info_stats: expected cell info list.
+            number_of_sample: number of sample to take from DUT.
+            delay_each_sample: time delay between each sample.
+
+        Returns:
+            True if reported cell info matches with expected_cell_info_stats.
+            False otherwise.
+
+        """
+
+        cell_info_stats = {}
+
+        for index in range(number_of_sample):
+            info_list = ad.droid.telephonyGetAllCellInfo()
+
+            self.log.info("Received Cell Info List: {}".format(info_list))
+
+            for sample in info_list:
+                rssi = self._get_rssi_from_cell_info(sample)
+                unique_id = self._unique_cell_id(sample)
+
+                # check cell logic
+                if not unique_id in expected_cell_info_stats:
+                    self.log.error("Found unexpected cell!")
+                    return False
+                elif not self._is_matching_cell(
+                        expected_cell_info_stats[unique_id],
+                        sample):
+                    self.log.error("Mismatched Cell Info")
+                    return False
+
+                # Check RSSI within expected range
+                if not self._is_rssi_in_expected_range(
+                        self._get_rssi_from_cell_info(sample),
+                        expected_cell_info_stats[unique_id][self.TARGET_RSSI],
+                        expected_cell_info_stats[unique_id][
+                            self.MAX_ERROR_RSSI]):
+                    self.log.error("Cell Info: {}. Cell RSSI not" +
+                                   " in expected range".format(sample))
+                    return False
+                if unique_id not in cell_info_stats:
+                    cell_info_stats[unique_id] = {
+                        self.RSSI: 0,
+                        self.COUNT: 0
+                    }
+                cell_info_stats[unique_id][self.RSSI] += rssi
+                cell_info_stats[unique_id][self.COUNT] += 1
+
+            time.sleep(delay_each_sample)
+
+        try:
+            for unique_id in expected_cell_info_stats.keys():
+                expected_cell_info = expected_cell_info_stats[unique_id]
+
+                expected_number_of_sample_reported = math.floor(
+                        expected_cell_info[self.REPORT_RATE] * number_of_sample)
+
+                if cell_info_stats[unique_id][
+                    self.COUNT] < expected_number_of_sample_reported:
+                    self.log.error(
+                        "Insufficient reports {}/{} for {}, expected: {}",
+                        expected_cell_info[unique_id][self.COUNT],
+                        number_of_sample,
+                        expected_cell_info,
+                        expected_number_of_sample_reported)
+                    return False
+
+                average_rssi = cell_info_stats[unique_id][self.RSSI] / \
+                               cell_info_stats[unique_id][self.COUNT]
+
+                # Check Average RSSI within expected range
+                if not self._is_rssi_in_expected_range(
+                        average_rssi,
+                        expected_cell_info[self.TARGET_RSSI],
+                        expected_cell_info[self.MAX_ERROR_AVERAGE_RSSI]):
+                    self.log.error("Cell Average RSSI not in expected range.")
+                    return False
+        except KeyError as unique_id:
+            self.log.error("Failed to find key {}".format(unique_id))
+            self.log.error("Expected cell info not reported {}.".format(
+                    expected_cell_info))
+            return False
+
+        return True
+
+    """ 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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell_cid,
+                self.PCID: serving_cell_pcid,
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell_dlpower,
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self._SETTLING_TIME)
+        return self._verify_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell_cid,
+                self.PCID: serving_cell_pcid,
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell_dlpower,
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neigh_cell_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self._SETTLING_TIME)
+        return self._verify_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell_cid,
+                self.PCID: serving_cell_pcid,
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell_dlpower,
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neigh_cell_1_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_1_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neigh_cell_2_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_2_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell_cid,
+                self.PCID: serving_cell_pcid,
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell_dlpower,
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neigh_cell_1_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_1_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neigh_cell_2_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_2_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neigh_cell_3_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_3_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell_cid,
+                self.PCID: serving_cell_pcid,
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell_dlpower,
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neigh_cell_1_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_1_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neigh_cell_2_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_2_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neigh_cell_3_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_3_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neigh_cell_4_pcid,
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neigh_cell_4_dlpower,
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                    "Failed to set rat family {}, preferred network:{}".format(
+                            RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PCID: serving_cell[self.PCID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self._SETTLING_TIME)
+        return self._verify_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PCID: serving_cell[self.PCID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neighbor_cell[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PCID: serving_cell[self.PCID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neighbor_cell_1[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neighbor_cell_2[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PCID: serving_cell[self.PCID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neighbor_cell_1[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neighbor_cell_2[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neighbor_cell_3[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_3['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PCID: serving_cell[self.PCID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neighbor_cell_1[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PCID: serving_cell[self.PCID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neighbor_cell_1[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neighbor_cell_2[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PCID: serving_cell[self.PCID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neighbor_cell_1[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neighbor_cell_2[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PCID: serving_cell[self.PCID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PSC: neighbor_cell_1[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PCID: serving_cell[self.PCID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PSC: neighbor_cell_1[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.CID: neighbor_cell_2[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_LTE_GSM_WCDMA,
+                                  RAT_FAMILY_LTE,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_LTE, NETWORK_MODE_LTE_GSM_WCDMA))
+            return False
+        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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PSC: serving_cell[self.PSC],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_UMTS,
+                                  RAT_FAMILY_UMTS,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_UMTS, NETWORK_MODE_GSM_UMTS))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self._SETTLING_TIME)
+        return self._verify_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PSC: serving_cell[self.PSC],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PSC: neighbor_cell[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_UMTS,
+                                  RAT_FAMILY_UMTS,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_UMTS, NETWORK_MODE_GSM_UMTS))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PSC: serving_cell[self.PSC],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PSC: neighbor_cell_1[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PSC: neighbor_cell_2[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_UMTS,
+                                  RAT_FAMILY_UMTS,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_UMTS, NETWORK_MODE_GSM_UMTS))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PSC: serving_cell[self.PSC],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PSC: neighbor_cell_1[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PSC: neighbor_cell_2[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PSC: neighbor_cell_3[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_3['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_UMTS,
+                                  RAT_FAMILY_UMTS,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                    "Failed to set rat family {}, preferred network:{}".format(
+                            RAT_FAMILY_UMTS, NETWORK_MODE_GSM_UMTS))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PSC: serving_cell[self.PSC],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PSC: neighbor_cell_1[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_UMTS,
+                                  RAT_FAMILY_UMTS,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_UMTS, NETWORK_MODE_GSM_UMTS))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PSC: serving_cell[self.PSC],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PSC: neighbor_cell_1[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PSC: neighbor_cell_2[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_UMTS,
+                                  RAT_FAMILY_UMTS,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_UMTS, NETWORK_MODE_GSM_UMTS))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PSC: serving_cell[self.PSC],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PSC: neighbor_cell_1[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PSC: neighbor_cell_2[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_UMTS,
+                                  RAT_FAMILY_UMTS,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_UMTS, NETWORK_MODE_GSM_UMTS))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PSC: serving_cell[self.PSC],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.CID: neighbor_cell_1[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neighbor_cell_2[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_UMTS,
+                                  RAT_FAMILY_UMTS,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_UMTS, NETWORK_MODE_GSM_UMTS))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.PSC: serving_cell[self.PSC],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.CID: neighbor_cell_1[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PCID: neighbor_cell_2[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_UMTS,
+                                  RAT_FAMILY_UMTS,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_UMTS, NETWORK_MODE_GSM_UMTS))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_ONLY,
+                                  RAT_FAMILY_GSM,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_GSM, NETWORK_MODE_GSM_ONLY))
+            return False
+        self.md8475a.wait_for_registration_state()
+        time.sleep(self._SETTLING_TIME)
+        return self._verify_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.CID: neighbor_cell_1[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_ONLY,
+                                  RAT_FAMILY_GSM,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_GSM, NETWORK_MODE_GSM_ONLY))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.CID: neighbor_cell_1[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.CID: neighbor_cell_2[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_ONLY,
+                                  RAT_FAMILY_GSM,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_GSM, NETWORK_MODE_GSM_ONLY))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.CID: neighbor_cell_1[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.CID: neighbor_cell_2[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.CID: neighbor_cell_3[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_3['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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 ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_ONLY,
+                                  RAT_FAMILY_GSM,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_GSM, NETWORK_MODE_GSM_ONLY))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.CID: neighbor_cell_1[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_ONLY,
+                                  RAT_FAMILY_GSM,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_GSM, NETWORK_MODE_GSM_ONLY))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.CID: neighbor_cell_1[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.CID: neighbor_cell_2[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_ONLY,
+                                  RAT_FAMILY_GSM,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_GSM, NETWORK_MODE_GSM_ONLY))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.CID: neighbor_cell_1[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.CID: neighbor_cell_2[self.CID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_ONLY,
+                                  RAT_FAMILY_GSM,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_GSM, NETWORK_MODE_GSM_ONLY))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    @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_cell_info_list = [
+            # Serving Cell
+            {
+                self.CID: serving_cell[self.CID],
+                self.REPORT_RATE: 1,
+                self.IS_REGISTERED: True,
+                self.RAT: 'gsm',
+                self.TARGET_RSSI: self._GSM_RSSI_OFFSET + serving_cell['power'],
+                self.MAX_ERROR_RSSI: 3,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            # Neighbor Cells
+            {
+                self.PCID: neighbor_cell_1[self.PCID],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'lte',
+                self.TARGET_RSSI: self._LTE_RSSI_OFFSET + neighbor_cell_1['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            },
+            {
+                self.PSC: neighbor_cell_2[self.PSC],
+                self.REPORT_RATE: 0.1,
+                self.IS_REGISTERED: False,
+                self.RAT: 'wcdma',
+                self.TARGET_RSSI: self._WCDMA_RSSI_OFFSET + neighbor_cell_2['power'],
+                self.MAX_ERROR_RSSI: 4,
+                self.MAX_ERROR_AVERAGE_RSSI: 3
+            }
+        ]
+        expected_cell_info_stats = {}
+        for sample in expected_cell_info_list:
+            expected_cell_info_stats[self._unique_cell_id(sample)] = sample
+
+        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.telephonyToggleDataConnection(False)
+        if not ensure_network_rat(self.log,
+                                  self.ad,
+                                  NETWORK_MODE_GSM_ONLY,
+                                  RAT_FAMILY_GSM,
+                                  toggle_apm_after_setting=True):
+            self.log.error(
+                "Failed to set rat family {}, preferred network:{}".format(
+                    RAT_FAMILY_GSM, NETWORK_MODE_GSM_ONLY))
+            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_cell_info(self.ad, expected_cell_info_stats)
+
+    """ 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..bfe990a
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabSmsTest.py
@@ -0,0 +1,934 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Sanity tests for connectivity tests in telephony
+"""
+
+import time
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
+from acts.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts.test_utils.tel.anritsu_utils import sms_mo_send
+from acts.test_utils.tel.anritsu_utils import sms_mt_receive_verify
+from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_1XRTT
+from acts.test_utils.tel.tel_defines import RAT_GSM
+from acts.test_utils.tel.tel_defines import RAT_LTE
+from acts.test_utils.tel.tel_defines import RAT_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.utils import rand_ascii_str
+
+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\\ACTS\\2cell_param.wnscp'
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.ad = self.android_devices[0]
+        self.md8475a_ip_address = self.user_params[
+            "anritsu_md8475a_ip_address"]
+        self.ad.sim_card = getattr(self.ad, "sim_card", None)
+
+    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=DIRECTION_MOBILE_ORIGINATED):
+        try:
+            self.anritsu.reset()
+            self.anritsu.load_cell_paramfile(self.CELL_PARAM_FILE)
+            set_simulation_func(self.anritsu, self.user_params,
+                                self.ad.sim_card)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                preferred_network_setting = NETWORK_MODE_LTE_GSM_WCDMA
+                rat_family = RAT_FAMILY_LTE
+            elif rat == RAT_WCDMA:
+                preferred_network_setting = NETWORK_MODE_GSM_UMTS
+                rat_family = RAT_FAMILY_UMTS
+            elif rat == RAT_GSM:
+                preferred_network_setting = NETWORK_MODE_GSM_ONLY
+                rat_family = RAT_FAMILY_GSM
+            elif rat == RAT_1XRTT:
+                preferred_network_setting = NETWORK_MODE_CDMA
+                rat_family = RAT_FAMILY_CDMA2000
+            else:
+                self.log.error("No valid RAT provided for SMS test.")
+                return False
+
+            if not ensure_network_rat(
+                    self.log,
+                    self.ad,
+                    preferred_network_setting,
+                    rat_family,
+                    toggle_apm_after_setting=True):
+                self.log.error(
+                    "Failed to set rat family {}, preferred network:{}".format(
+                        rat_family, preferred_network_setting))
+                return False
+
+            self.anritsu.wait_for_registration_state()
+            time.sleep(self.SETTLING_TIME)
+            if mo_mt == DIRECTION_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),
+                               DIRECTION_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),
+                               DIRECTION_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),
+                               DIRECTION_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),
+                               DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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), DIRECTION_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), DIRECTION_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), DIRECTION_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), DIRECTION_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,
+                               DIRECTION_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,
+                               DIRECTION_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,
+                               DIRECTION_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,
+                               DIRECTION_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,
+                               DIRECTION_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,
+                               DIRECTION_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,
+                               DIRECTION_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,
+                               DIRECTION_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,
+                               DIRECTION_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,
+                               DIRECTION_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),
+                               DIRECTION_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),
+                               DIRECTION_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),
+                               DIRECTION_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),
+                               DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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, DIRECTION_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), DIRECTION_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), DIRECTION_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), DIRECTION_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: b/26346258 Anritsu is not sending message.
+        return self._setup_sms(
+            set_system_model_1x, RAT_1XRTT, self.phoneNumber,
+            rand_ascii_str(MULTI_PART_LEN), DIRECTION_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..9ca701c
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabUeIdentityTest.py
@@ -0,0 +1,316 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Tests for reading UE Identity
+"""
+import time
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import UEIdentityType
+from acts.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_OPER
+from acts.test_utils.tel.anritsu_utils import read_ue_identity
+from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_1XRTT
+from acts.test_utils.tel.tel_defines import RAT_GSM
+from acts.test_utils.tel.tel_defines import RAT_LTE
+from acts.test_utils.tel.tel_defines import RAT_WCDMA
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+
+class TelLabUeIdentityTest(TelephonyBaseTest):
+
+    CELL_PARAM_FILE = 'C:\\MX847570\\CellParam\\ACTS\\2cell_param.wnscp'
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.ad = self.android_devices[0]
+        self.md8475a_ip_address = self.user_params[
+            "anritsu_md8475a_ip_address"]
+        self.ad.sim_card = getattr(self.ad, "sim_card", None)
+
+    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.ad.sim_card)
+            self.anritsu.start_simulation()
+
+            if rat == RAT_LTE:
+                preferred_network_setting = NETWORK_MODE_LTE_GSM_WCDMA
+                rat_family = RAT_FAMILY_LTE
+            elif rat == RAT_WCDMA:
+                preferred_network_setting = NETWORK_MODE_GSM_UMTS
+                rat_family = RAT_FAMILY_UMTS
+            elif rat == RAT_GSM:
+                preferred_network_setting = NETWORK_MODE_GSM_ONLY
+                rat_family = RAT_FAMILY_GSM
+            elif rat == RAT_1XRTT:
+                preferred_network_setting = NETWORK_MODE_CDMA
+                rat_family = RAT_FAMILY_CDMA2000
+            else:
+                self.log.error("Invalid RAT - Please specify a valid RAT")
+                return False
+
+            self.ad.droid.telephonyToggleDataConnection(True)
+            if not ensure_network_rat(
+                    self.log,
+                    self.ad,
+                    preferred_network_setting,
+                    rat_family,
+                    toggle_apm_after_setting=True):
+                self.log.error(
+                    "Failed to set rat family {}, preferred network:{}".format(
+                        rat_family, preferred_network_setting))
+                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..e7e812a
--- /dev/null
+++ b/acts/tests/google/tel/lab/TelLabVoiceTest.py
@@ -0,0 +1,637 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Sanity tests for voice tests in telephony
+"""
+import time
+
+from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
+from acts.controllers.anritsu_lib.md8475a import CsfbType
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import VirtualPhoneAutoAnswer
+from acts.controllers.anritsu_lib.md8475a import VirtualPhoneStatus
+from acts.test_utils.tel.anritsu_utils import WAIT_TIME_ANRITSU_REG_AND_CALL
+from acts.test_utils.tel.anritsu_utils import call_mo_setup_teardown
+from acts.test_utils.tel.anritsu_utils import ims_call_cs_teardown
+from acts.test_utils.tel.anritsu_utils import call_mt_setup_teardown
+from acts.test_utils.tel.anritsu_utils import set_system_model_1x
+from acts.test_utils.tel.anritsu_utils import set_system_model_1x_evdo
+from acts.test_utils.tel.anritsu_utils import set_system_model_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte_1x
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte_wcdma
+from acts.test_utils.tel.anritsu_utils import set_system_model_lte_gsm
+from acts.test_utils.tel.anritsu_utils import set_system_model_wcdma
+from acts.test_utils.tel.anritsu_utils import set_usim_parameters
+from acts.test_utils.tel.tel_defines import CALL_TEARDOWN_PHONE
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts.test_utils.tel.tel_defines import RAT_FAMILY_UMTS
+from acts.test_utils.tel.tel_defines import RAT_1XRTT
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
+from acts.test_utils.tel.tel_test_utils import ensure_network_rat
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts.test_utils.tel.tel_test_utils import toggle_volte
+from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+DEFAULT_CALL_NUMBER = "0123456789"
+
+
+class TelLabVoiceTest(TelephonyBaseTest):
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        try:
+            self.stress_test_number = int(self.user_params[
+                "stress_test_number"])
+            self.log.info("Executing {} calls per test in stress test mode".
+                          format(self.stress_test_number))
+        except KeyError:
+            self.stress_test_number = 0
+            self.log.info(
+                "No 'stress_test_number' defined: running single iteration tests"
+            )
+
+        self.ad = self.android_devices[0]
+        self.ad.sim_card = getattr(self.ad, "sim_card", None)
+        self.md8475a_ip_address = self.user_params[
+            "anritsu_md8475a_ip_address"]
+        self.wlan_option = self.user_params.get("anritsu_wlan_option", False)
+
+        setattr(self, 'voice_call_number', DEFAULT_CALL_NUMBER)
+        if 'voice_call_number' in self.user_params:
+            self.voice_call_number = self.user_params['voice_call_number']
+            self.log.info("Using provided voice call number: {}".format(
+                self.voice_call_number))
+
+    def setup_class(self):
+        try:
+            self.anritsu = MD8475A(self.md8475a_ip_address, self.log,
+                                   self.wlan_option)
+        except AnritsuError:
+            self.log.error("Error in connecting to Anritsu Simulator")
+            return False
+        return True
+
+    def setup_test(self):
+        try:
+            self.ad.droid.telephonyFactoryReset()
+        except Exception as e:
+            self.ad.log.error(e)
+        toggle_airplane_mode_by_adb(self.log, self.ad, True)
+        # get a handle to virtual phone
+        self.virtualPhoneHandle = self.anritsu.get_VirtualPhone()
+        return True
+
+    def teardown_test(self):
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        toggle_airplane_mode_by_adb(self.log, self.ad, True)
+        return True
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+        return True
+
+    def _setup_voice_call(self,
+                          set_simulation_func,
+                          phone_setup_func,
+                          phone_idle_func_after_registration=None,
+                          is_ims_call=False,
+                          is_wait_for_registration=True,
+                          csfb_type=None,
+                          srvcc=None,
+                          mo=True,
+                          voice_number=DEFAULT_CALL_NUMBER,
+                          teardown_side=CALL_TEARDOWN_PHONE,
+                          wait_time_in_call=WAIT_TIME_IN_CALL):
+        try:
+            set_simulation_func(self.anritsu, self.user_params,
+                                self.ad.sim_card)
+            set_usim_parameters(self.anritsu, self.ad.sim_card)
+            self.virtualPhoneHandle.auto_answer = (VirtualPhoneAutoAnswer.ON,
+                                                   2)
+            if srvcc != None:
+                if srvcc == "Alert":
+                    self.anritsu.send_command("IMSCSCFAUTOANSWER 1,DISABLE")
+                self.anritsu.start_simulation()
+                self.anritsu.send_command("IMSSTARTVN 1")
+                check_ims_reg = True
+                check_ims_calling = True
+            else:
+                self.anritsu.start_simulation()
+            if is_ims_call:
+                self.anritsu.send_command("IMSSTARTVN 1")
+
+            iterations = 1
+            if self.stress_test_number > 0:
+                iterations = self.stress_test_number
+            successes = 0
+            for i in range(1, iterations + 1):
+                if self.stress_test_number:
+                    self.log.info("Running iteration {} of {}".format(
+                        i, iterations))
+                # FIXME: There's no good reason why this must be true;
+                # I can only assume this was done to work around a problem
+                self.ad.droid.telephonyToggleDataConnection(False)
+
+                # turn off all other BTS to ensure UE registers on BTS1
+                sim_model = (self.anritsu.get_simulation_model()).split(",")
+                no_of_bts = len(sim_model)
+                for i in range(2, no_of_bts + 1):
+                    self.anritsu.send_command("OUTOFSERVICE OUT,BTS{}".format(
+                        i))
+
+                if phone_setup_func is not None:
+                    if not phone_setup_func(self.ad):
+                        self.log.error("phone_setup_func failed.")
+                        continue
+                if is_wait_for_registration:
+                    self.anritsu.wait_for_registration_state()
+
+                if phone_idle_func_after_registration:
+                    if not phone_idle_func_after_registration(self.log,
+                                                              self.ad):
+                        continue
+
+                for i in range(2, no_of_bts + 1):
+                    self.anritsu.send_command("OUTOFSERVICE IN,BTS{}".format(
+                        i))
+
+                time.sleep(WAIT_TIME_ANRITSU_REG_AND_CALL)
+                if srvcc:
+                    if not ims_call_cs_teardown(
+                            self.log, self.ad, self.anritsu, voice_number,
+                            CALL_TEARDOWN_PHONE, False, check_ims_reg,
+                            check_ims_calling, srvcc, mo,
+                            WAIT_TIME_IN_CALL_FOR_IMS, WAIT_TIME_IN_CALL):
+                        if mo:
+                            self.log.error(
+                                "Phone {} Failed to make voice call to {}"
+                                .format(self.ad.serial, voice_number))
+                        else:
+                            self.log.error(
+                                "Phone {} failed to answer voice call."
+                                .format(self.ad.serial))
+                        continue
+                else:
+                    if not call_mo_setup_teardown(
+                            self.log, self.ad, self.anritsu, voice_number,
+                            CALL_TEARDOWN_PHONE, False, WAIT_TIME_IN_CALL,
+                            is_ims_call):
+                        self.log.error(
+                            "Phone {} Failed to make voice call to {}"
+                            .format(self.ad.serial, voice_number))
+                        continue
+                successes += 1
+                if self.stress_test_number:
+                    self.log.info("Passed iteration {}".format(i))
+            if self.stress_test_number:
+                self.log.info("Total of {} successes out of {} attempts".
+                              format(successes, iterations))
+            return True if successes == iterations else 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 voice call procedure: " + str(e))
+            return False
+        return True
+
+    def _phone_setup_lte_wcdma(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_LTE_GSM_WCDMA,
+            RAT_FAMILY_LTE,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_lte_1x(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_LTE_CDMA_EVDO,
+            RAT_FAMILY_LTE,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_wcdma(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_GSM_UMTS,
+            RAT_FAMILY_UMTS,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_gsm(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_GSM_ONLY,
+            RAT_FAMILY_GSM,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_1x(self, ad):
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_CDMA,
+            RAT_FAMILY_CDMA2000,
+            toggle_apm_after_setting=True)
+
+    def _phone_setup_airplane_mode(self, ad):
+        return toggle_airplane_mode_by_adb(self.log, ad, True)
+
+    def _phone_setup_volte_airplane_mode(self, ad):
+        toggle_volte(self.log, ad, True)
+        return toggle_airplane_mode_by_adb(self.log, ad, True)
+
+    def _phone_setup_volte(self, ad):
+        ad.droid.telephonyToggleDataConnection(True)
+        toggle_volte(self.log, ad, True)
+        return ensure_network_rat(
+            self.log,
+            ad,
+            NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+            RAT_FAMILY_LTE,
+            toggle_apm_after_setting=True)
+
+    """ Tests Begin """
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_lte_wcdma_csfb_redirection(self):
+        """ Test Voice call functionality on LTE (CSFB to WCDMA).
+            CSFB type is REDIRECTION
+
+        Steps:
+        1. Setup CallBox on LTE and WCDMA network, make sure DUT register on LTE network.
+        2. Make an voice call to DEFAULT_CALL_NUMBER. Make sure DUT CSFB to WCDMA.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Voice call succeed. DUT CSFB to WCDMA.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte_wcdma,
+            self._phone_setup_lte_wcdma,
+            voice_number=self.voice_call_number,
+            csfb_type=CsfbType.CSFB_TYPE_REDIRECTION)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_lte_wcdma_csfb_handover(self):
+        """ Test Voice call functionality on LTE (CSFB to WCDMA).
+            CSFB type is HANDOVER
+
+        Steps:
+        1. Setup CallBox on LTE and WCDMA network, make sure DUT register on LTE network.
+        2. Make an voice call to DEFAULT_CALL_NUMBER. Make sure DUT CSFB to WCDMA.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Voice call succeed. DUT CSFB to WCDMA.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte_wcdma,
+            self._phone_setup_lte_wcdma,
+            voice_number=self.voice_call_number,
+            csfb_type=CsfbType.CSFB_TYPE_HANDOVER)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_lte_1x_csfb(self):
+        """ Test Voice call functionality on LTE (CSFB to 1x).
+
+        Steps:
+        1. Setup CallBox on LTE and CDMA 1X network, make sure DUT register on LTE network.
+        2. Make an voice call to DEFAULT_CALL_NUMBER. Make sure DUT CSFB to 1x.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Voice call succeed. DUT CSFB to 1x.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte_1x,
+            self._phone_setup_lte_1x,
+            voice_number=self.voice_call_number)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_wcdma(self):
+        """ Test Voice call functionality on WCDMA
+
+        Steps:
+        1. Setup CallBox on WCDMA network, make sure DUT register on WCDMA network.
+        2. Make an voice call to DEFAULT_CALL_NUMBER.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Voice call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_wcdma,
+            self._phone_setup_wcdma,
+            voice_number=self.voice_call_number)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_gsm(self):
+        """ Test Voice call functionality on GSM
+
+        Steps:
+        1. Setup CallBox on GSM network, make sure DUT register on GSM network.
+        2. Make an voice call to DEFAULT_CALL_NUMBER.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Voice call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_gsm,
+            self._phone_setup_gsm,
+            voice_number=self.voice_call_number)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_1x(self):
+        """ Test Voice call functionality on CDMA 1X
+
+        Steps:
+        1. Setup CallBox on 1x network, make sure DUT register on 1x network.
+        2. Make an voice call to DEFAULT_CALL_NUMBER.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Voice call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_1x,
+            self._phone_setup_1x,
+            voice_number=self.voice_call_number)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_1x_evdo(self):
+        """ Test Voice call functionality on CDMA 1X with EVDO
+
+        Steps:
+        1. Setup CallBox on 1x and EVDO network, make sure DUT register on 1x network.
+        2. Make an voice call to DEFAULT_CALL_NUMBER.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Voice call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_1x_evdo,
+            self._phone_setup_1x,
+            voice_number=self.voice_call_number)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_volte_wcdma_srvcc(self):
+        """ Test Voice call functionality,
+        VoLTE to WCDMA SRVCC
+        Steps:
+        1. Setup CallBox on VoLTE network with WCDMA.
+        2. Turn on DUT and enable VoLTE. Make an voice call to DEFAULT_CALL_NUMBER.
+        3. Check if VoLTE voice call connected successfully.
+        4. Handover the call to WCDMA and check if the call is still up.
+        5. Tear down the call.
+
+        Expected Results:
+        1. VoLTE Voice call is made successfully.
+        2. After SRVCC, the DEFAULT_CALL_NUMBER call is not dropped.
+        3. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte_wcdma,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srvcc="InCall",
+            voice_number=self.voice_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_volte_gsm_srvcc(self):
+        """ Test Voice call functionality,
+        VoLTE to GSM SRVCC
+        Steps:
+        1. Setup CallBox on VoLTE network with GSM.
+        2. Turn on DUT and enable VoLTE. Make an voice call to DEFAULT_CALL_NUMBER.
+        3. Check if VoLTE voice call connected successfully.
+        4. Handover the call to GSM and check if the call is still up.
+        5. Tear down the call.
+
+        Expected Results:
+        1. VoLTE Voice call is made successfully.
+        2. After SRVCC, the DEFAULT_CALL_NUMBER call is not dropped.
+        3. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte_gsm,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srvcc="InCall",
+            voice_number=self.voice_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_volte_wcdma_asrvcc(self):
+        """ Test Voice call functionality,
+        VoLTE to WCDMA aSRVCC
+        Steps:
+        1. Setup CallBox on VoLTE network with WCDMA.
+        2. Turn on DUT and enable VoLTE. Make an voice call to DEFAULT_CALL_NUMBER.
+        3. Check if Virtual UA in CSCF server rings.
+        4. Handover the call to WCDMA and check if the call is connected.
+        5. Tear down the call.
+
+        Expected Results:
+        1. Virtual UA is rining.
+        2. After aSRVCC, the DEFAULT_CALL_NUMBER call is connected.
+        3. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte_wcdma,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srvcc="Alert",
+            voice_number=self.voice_call_number)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_volte_gsm_asrvcc(self):
+        """ Test Voice call functionality,
+        VoLTE to GSM aSRVCC
+        Steps:
+        1. Setup CallBox on VoLTE network with GSM.
+        2. Turn on DUT and enable VoLTE. Make an voice call to DEFAULT_CALL_NUMBER.
+        3. Check if Virtual UA in CSCF server rings.
+        4. Handover the call to GSM and check if the call is connected.
+        5. Tear down the call.
+
+        Expected Results:
+        1. Virtual UA is rining.
+        2. After aSRVCC, the DEFAULT_CALL_NUMBER call is connected.
+        3. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte_gsm,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srvcc="Alert",
+            voice_number=self.voice_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_volte_wcdma_asrvcc_mt(self):
+        """ Test Voice call functionality,
+        MT VoLTE to WCDMA aSRVCC
+        Steps:
+        1. Setup CallBox on VoLTE network with WCDMA.
+        2. Turn on DUT and enable VoLTE. Make a VoLTE call from MD8475A to UE.
+        3. Check if Virtual UA in CSCF server calling.
+        4. Handover the call to WCDMA and check if the call is connected.
+        5. Tear down the call.
+
+        Expected Results:
+        1. Virtual UA is rining.
+        2. After aSRVCC, the DEFAULT_CALL_NUMBER call is connected.
+        3. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte_wcdma,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srvcc="Alert",
+            mo=False,
+            voice_number=self.voice_call_number)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_volte_gsm_asrvcc_mt(self):
+        """ Test Voice call functionality,
+        MT VoLTE to GSM aSRVCC
+        Steps:
+        1. Setup CallBox on VoLTE network with GSM.
+        2. Turn on DUT and enable VoLTE. Make a VoLTE call from MD8475A to UE.
+        3. Check if Virtual UA in CSCF server calling.
+        4. Handover the call to GSM and check if the call is connected.
+        5. Tear down the call.
+
+        Expected Results:
+        1. Virtual UA is rining.
+        2. After aSRVCC, the DEFAULT_CALL_NUMBER call is connected.
+        3. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte_gsm,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            srvcc="Alert",
+            mo=False,
+            voice_number=self.voice_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_call_volte(self):
+        """ Test Voice call functionality on VoLTE
+
+        Steps:
+        1. Setup CallBox on VoLTE network.
+        2. Turn on DUT and enable VoLTE. Make an voice call to DEFAULT_CALL_NUMBER.
+        3. Make sure Anritsu receives the call and accept.
+        4. Tear down the call.
+
+        Expected Results:
+        2. Voice call succeed.
+        3. Anritsu can accept the call.
+        4. Tear down call succeed.
+
+        Returns:
+            True if pass; False if fail
+        """
+        return self._setup_voice_call(
+            set_system_model_lte,
+            self._phone_setup_volte,
+            phone_idle_volte,
+            is_ims_call=True,
+            voice_number=self.voice_call_number,
+            wait_time_in_call=WAIT_TIME_IN_CALL_FOR_IMS)
+
+    """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveDataTest.py b/acts/tests/google/tel/live/TelLiveDataTest.py
index 8ff034f..87512e2 100644
--- a/acts/tests/google/tel/live/TelLiveDataTest.py
+++ b/acts/tests/google/tel/live/TelLiveDataTest.py
@@ -18,6 +18,7 @@
 """
 
 import time
+from acts.test_decorators import test_tracker_info
 from acts.base_test import BaseTestClass
 from queue import Empty
 from acts.test_utils.tel.tel_subscription_utils import \
@@ -51,27 +52,28 @@
 from acts.test_utils.tel.tel_data_utils import airplane_mode_test
 from acts.test_utils.tel.tel_data_utils import change_data_sim_and_verify_data
 from acts.test_utils.tel.tel_data_utils import data_connectivity_single_bearer
-from acts.test_utils.tel.tel_data_utils import ensure_wifi_connected
 from acts.test_utils.tel.tel_data_utils import tethering_check_internet_connection
 from acts.test_utils.tel.tel_data_utils import wifi_cell_switching
 from acts.test_utils.tel.tel_data_utils import wifi_tethering_cleanup
 from acts.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
-from acts.test_utils.tel.tel_test_utils import WifiUtils
 from acts.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts.test_utils.tel.tel_test_utils import check_is_wifi_connected
 from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
 from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
 from acts.test_utils.tel.tel_test_utils import ensure_network_generation
 from acts.test_utils.tel.tel_test_utils import \
     ensure_network_generation_for_subscription
+from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
 from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid
 from acts.test_utils.tel.tel_test_utils import get_network_rat_for_subscription
 from acts.test_utils.tel.tel_test_utils import hangup_call
 from acts.test_utils.tel.tel_test_utils import multithread_func
 from acts.test_utils.tel.tel_test_utils import set_call_state_listen_level
 from acts.test_utils.tel.tel_test_utils import setup_sim
+from acts.test_utils.tel.tel_test_utils import stop_wifi_tethering
 from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts.test_utils.tel.tel_test_utils import toggle_volte
-from acts.test_utils.tel.tel_test_utils import verify_http_connection
+from acts.test_utils.tel.tel_test_utils import verify_internet_connection
 from acts.test_utils.tel.tel_test_utils import verify_incall_state
 from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
 from acts.test_utils.tel.tel_test_utils import wait_for_network_rat
@@ -80,6 +82,11 @@
 from acts.test_utils.tel.tel_test_utils import \
     wait_for_data_attach_for_subscription
 from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts.test_utils.tel.tel_test_utils import wifi_reset
+from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts.test_utils.tel.tel_test_utils import WIFI_SSID_KEY
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
@@ -87,7 +94,6 @@
 from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
 from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
 from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts.test_utils.wifi.wifi_test_utils import WifiEnums
 from acts.utils import disable_doze
 from acts.utils import enable_doze
 from acts.utils import rand_ascii_str
@@ -99,12 +105,11 @@
 
         self.stress_test_number = self.get_stress_test_number()
         self.wifi_network_ssid = self.user_params.get(
-            "wifi_network_ssid") or self.user_params.get(
-                "wifi_network_ssid_2g")
+            "wifi_network_ssid") or self.user_params.get("wifi_network_ssid_2g")
         self.wifi_network_pass = self.user_params.get(
-            "wifi_network_pass") or self.user_params.get(
-                "wifi_network_pass_2g")
+            "wifi_network_pass") or self.user_params.get("wifi_network_pass_2g")
 
+    @test_tracker_info(uuid="1b0354f3-8668-4a28-90a5-3b3d2b2756d3")
     @TelephonyBaseTest.tel_test_wrap
     def test_airplane_mode(self):
         """ Test airplane mode basic on Phone and Live SIM.
@@ -119,6 +124,7 @@
         """
         return airplane_mode_test(self.log, self.android_devices[0])
 
+    @test_tracker_info(uuid="47430f01-583f-4efb-923a-285a51b75d50")
     @TelephonyBaseTest.tel_test_wrap
     def test_lte_wifi_switching(self):
         """Test data connection network switching when phone camped on LTE.
@@ -137,6 +143,7 @@
                                    self.wifi_network_ssid,
                                    self.wifi_network_pass, GEN_4G)
 
+    @test_tracker_info(uuid="8a836cf1-600b-4cf3-abfe-2e3da5c11396")
     @TelephonyBaseTest.tel_test_wrap
     def test_wcdma_wifi_switching(self):
         """Test data connection network switching when phone camped on WCDMA.
@@ -155,6 +162,7 @@
                                    self.wifi_network_ssid,
                                    self.wifi_network_pass, GEN_3G)
 
+    @test_tracker_info(uuid="c016f2e8-0af6-42e4-a3cb-a2b7d8b564d0")
     @TelephonyBaseTest.tel_test_wrap
     def test_gsm_wifi_switching(self):
         """Test data connection network switching when phone camped on GSM.
@@ -173,6 +181,7 @@
                                    self.wifi_network_ssid,
                                    self.wifi_network_pass, GEN_2G)
 
+    @test_tracker_info(uuid="78d6b258-82d4-47b4-8723-3b3a15412d2d")
     @TelephonyBaseTest.tel_test_wrap
     def test_lte_multi_bearer(self):
         """Test LTE data connection before call and in call. (VoLTE call)
@@ -194,6 +203,7 @@
             return False
         return self._test_data_connectivity_multi_bearer(GEN_4G)
 
+    @test_tracker_info(uuid="5c9cb076-0c26-4517-95dc-2ec4974e8ce3")
     @TelephonyBaseTest.tel_test_wrap
     def test_wcdma_multi_bearer(self):
         """Test WCDMA data connection before call and in call.
@@ -212,6 +222,7 @@
 
         return self._test_data_connectivity_multi_bearer(GEN_3G)
 
+    @test_tracker_info(uuid="314bbf1c-073f-4d48-9817-a6e14f96f3c0")
     @TelephonyBaseTest.tel_test_wrap
     def test_gsm_multi_bearer_mo(self):
         """Test gsm data connection before call and in call.
@@ -226,9 +237,10 @@
             False if failed.
         """
 
-        return self._test_data_connectivity_multi_bearer(GEN_2G,
-            False, DIRECTION_MOBILE_ORIGINATED)
+        return self._test_data_connectivity_multi_bearer(
+            GEN_2G, False, DIRECTION_MOBILE_ORIGINATED)
 
+    @test_tracker_info(uuid="549271ff-1034-4d02-8d92-b9d1b2bb912e")
     @TelephonyBaseTest.tel_test_wrap
     def test_gsm_multi_bearer_mt(self):
         """Test gsm data connection before call and in call.
@@ -243,9 +255,10 @@
             False if failed.
         """
 
-        return self._test_data_connectivity_multi_bearer(GEN_2G,
-            False, DIRECTION_MOBILE_TERMINATED)
+        return self._test_data_connectivity_multi_bearer(
+            GEN_2G, False, DIRECTION_MOBILE_TERMINATED)
 
+    @test_tracker_info(uuid="111de471-559a-4bc3-9d3e-de18f098c162")
     @TelephonyBaseTest.tel_test_wrap
     def test_wcdma_multi_bearer_stress(self):
         """Stress Test WCDMA data connection before call and in call.
@@ -277,14 +290,15 @@
                 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))))
+            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
 
+    @test_tracker_info(uuid="c7f14ba7-7ac3-45d2-b391-5ed5c4b0e70b")
     @TelephonyBaseTest.tel_test_wrap
     def test_lte_multi_bearer_stress(self):
         """Stress Test LTE data connection before call and in call. (VoLTE call)
@@ -316,17 +330,19 @@
                 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))))
+            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,
-        simultaneous_voice_data=True,
-        call_direction=DIRECTION_MOBILE_ORIGINATED):
+    def _test_data_connectivity_multi_bearer(
+            self,
+            nw_gen,
+            simultaneous_voice_data=True,
+            call_direction=DIRECTION_MOBILE_ORIGINATED):
         """Test data connection before call and in call.
 
         Turn off airplane mode, disable WiFi, enable Cellular Data.
@@ -351,28 +367,28 @@
         ad_list = [self.android_devices[0], self.android_devices[1]]
         ensure_phones_idle(self.log, ad_list)
 
-        if not ensure_network_generation_for_subscription(self.log,
-            self.android_devices[0],
-            self.android_devices[0].droid.subscriptionGetDefaultDataSubId(),
-            nw_gen, MAX_WAIT_TIME_NW_SELECTION,
-            NETWORK_SERVICE_DATA):
+        if not ensure_network_generation_for_subscription(
+                self.log, self.android_devices[0], self.android_devices[0]
+                .droid.subscriptionGetDefaultDataSubId(), nw_gen,
+                MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
             self.log.error("Device failed to reselect in {}s.".format(
                 MAX_WAIT_TIME_NW_SELECTION))
             return False
 
         if not wait_for_voice_attach_for_subscription(
-                self.log, self.android_devices[0], self.android_devices[
-                    0].droid.subscriptionGetDefaultVoiceSubId(),
+                self.log, self.android_devices[0], self.android_devices[0]
+                .droid.subscriptionGetDefaultVoiceSubId(),
                 MAX_WAIT_TIME_NW_SELECTION):
             return False
 
         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)
+        wifi_toggle_state(self.log, self.android_devices[0], False)
         self.android_devices[0].droid.telephonyToggleDataConnection(True)
         if (not wait_for_cell_data_connection(self.log,
                                               self.android_devices[0], True) or
-                not verify_http_connection(self.log, self.android_devices[0])):
+                not verify_internet_connection(self.log,
+                                               self.android_devices[0])):
             self.log.error("Data not available on cell")
             return False
 
@@ -386,46 +402,52 @@
                 ad_callee = self.android_devices[0]
             if not call_setup_teardown(self.log, ad_caller, ad_callee, None,
                                        None, None):
-                self.log.error("Failed to Establish {} Voice Call".format(
-                    call_direction))
+                self.log.error(
+                    "Failed to Establish {} Voice Call".format(call_direction))
                 return False
             if simultaneous_voice_data:
                 self.log.info("Step3 Verify internet.")
                 time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-                if not verify_http_connection(self.log, self.android_devices[0]):
+                if not verify_internet_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.telephonyToggleDataConnection(False)
+                self.android_devices[0].droid.telephonyToggleDataConnection(
+                    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]):
+                if verify_internet_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.telephonyToggleDataConnection(True)
+                self.android_devices[0].droid.telephonyToggleDataConnection(
+                    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]):
+                if not verify_internet_connection(self.log,
+                                                  self.android_devices[0]):
                     raise _LocalException("Internet Inaccessible when Enabled")
             else:
                 self.log.info("Step3 Verify no Internet and skip step 4-5.")
-                if verify_http_connection(self.log, self.android_devices[0],
-                    retry=0):
+                if verify_internet_connection(
+                        self.log, self.android_devices[0], retry=0):
                     raise _LocalException("Internet Accessible.")
 
             self.log.info("Step6 Verify phones still in call and Hang up.")
-            if not verify_incall_state(
-                    self.log,
-                [self.android_devices[0], self.android_devices[1]], True):
+            if not verify_incall_state(self.log, [
+                    self.android_devices[0], self.android_devices[1]
+            ], True):
                 return False
             if not hangup_call(self.log, self.android_devices[0]):
                 self.log.error("Failed to hang up call")
                 return False
-            if not verify_http_connection(self.log, self.android_devices[0]):
+            if not verify_internet_connection(self.log,
+                                              self.android_devices[0]):
                 raise _LocalException("Internet Inaccessible when Enabled")
 
         except _LocalException as e:
@@ -440,6 +462,7 @@
 
         return True
 
+    @test_tracker_info(uuid="dcb9bdc6-dbe2-47e1-9c2d-6f37c529d366")
     @TelephonyBaseTest.tel_test_wrap
     def test_2g(self):
         """Test data connection in 2G.
@@ -454,11 +477,12 @@
             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)
+        wifi_reset(self.log, self.android_devices[0])
+        wifi_toggle_state(self.log, self.android_devices[0], False)
         return data_connectivity_single_bearer(self.log,
                                                self.android_devices[0], RAT_2G)
 
+    @test_tracker_info(uuid="84197a49-d73f-44ce-8b9e-9479e5c4dfdc")
     @TelephonyBaseTest.tel_test_wrap
     def test_2g_wifi_not_associated(self):
         """Test data connection in 2G.
@@ -473,12 +497,13 @@
             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)
+        wifi_reset(self.log, self.android_devices[0])
+        wifi_toggle_state(self.log, self.android_devices[0], False)
+        wifi_toggle_state(self.log, self.android_devices[0], True)
         return data_connectivity_single_bearer(self.log,
                                                self.android_devices[0], RAT_2G)
 
+    @test_tracker_info(uuid="97067ebb-130a-4fcb-8e6b-f4ec5874828f")
     @TelephonyBaseTest.tel_test_wrap
     def test_3g(self):
         """Test data connection in 3G.
@@ -493,11 +518,12 @@
             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)
+        wifi_reset(self.log, self.android_devices[0])
+        wifi_toggle_state(self.log, self.android_devices[0], False)
         return data_connectivity_single_bearer(self.log,
                                                self.android_devices[0], RAT_3G)
 
+    @test_tracker_info(uuid="ffe2a392-95b8-4a4d-8a6f-bfa846c3462f")
     @TelephonyBaseTest.tel_test_wrap
     def test_3g_wifi_not_associated(self):
         """Test data connection in 3G.
@@ -512,12 +538,13 @@
             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)
+        wifi_reset(self.log, self.android_devices[0])
+        wifi_toggle_state(self.log, self.android_devices[0], False)
+        wifi_toggle_state(self.log, self.android_devices[0], True)
         return data_connectivity_single_bearer(self.log,
                                                self.android_devices[0], RAT_3G)
 
+    @test_tracker_info(uuid="9c2f459f-1aac-4c68-818b-8698e8124c8b")
     @TelephonyBaseTest.tel_test_wrap
     def test_4g(self):
         """Test data connection in 4g.
@@ -532,11 +559,12 @@
             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)
+        wifi_reset(self.log, self.android_devices[0])
+        wifi_toggle_state(self.log, self.android_devices[0], False)
         return data_connectivity_single_bearer(self.log,
                                                self.android_devices[0], RAT_4G)
 
+    @test_tracker_info(uuid="015a39a1-15ac-4b76-962b-d7d82d52d425")
     @TelephonyBaseTest.tel_test_wrap
     def test_4g_wifi_not_associated(self):
         """Test data connection in 4g.
@@ -551,12 +579,13 @@
             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)
+        wifi_reset(self.log, self.android_devices[0])
+        wifi_toggle_state(self.log, self.android_devices[0], False)
+        wifi_toggle_state(self.log, self.android_devices[0], True)
         return data_connectivity_single_bearer(self.log,
                                                self.android_devices[0], RAT_4G)
 
+    @test_tracker_info(uuid="44f47b64-f8bc-4a17-9195-42dcca0806bb")
     @TelephonyBaseTest.tel_test_wrap
     def test_3g_stress(self):
         """Stress Test data connection in 3G.
@@ -577,9 +606,8 @@
 
             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)
+            wifi_reset(self.log, self.android_devices[0])
+            wifi_toggle_state(self.log, self.android_devices[0], False)
 
             if data_connectivity_single_bearer(
                     self.log, self.android_devices[0], RAT_3G):
@@ -592,14 +620,15 @@
                 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))))
+            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
 
+    @test_tracker_info(uuid="c8876388-0441-4a51-81e6-ac2cb358a531")
     @TelephonyBaseTest.tel_test_wrap
     def test_4g_stress(self):
         """Stress Test data connection in 4g.
@@ -620,9 +649,8 @@
 
             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)
+            wifi_reset(self.log, self.android_devices[0])
+            wifi_toggle_state(self.log, self.android_devices[0], False)
 
             if data_connectivity_single_bearer(
                     self.log, self.android_devices[0], RAT_4G):
@@ -635,15 +663,15 @@
                 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))))
+            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=None):
+    def _test_setup_tethering(self, network_generation=None):
         """Pre setup steps for WiFi tethering test.
 
         Ensure all ads are idle.
@@ -655,38 +683,39 @@
             True if success.
             False if failed.
         """
-        ensure_phones_idle(self.log, ads)
-
-        if network_generation is not None:
-            if not ensure_network_generation_for_subscription(self.log,
-                self.android_devices[0],
-                self.android_devices[0].droid.subscriptionGetDefaultDataSubId(),
-                network_generation, MAX_WAIT_TIME_NW_SELECTION,
-                NETWORK_SERVICE_DATA):
-                self.log.error("Device failed to reselect in {}s.".format(
-                    MAX_WAIT_TIME_NW_SELECTION))
+        ensure_phones_idle(self.log, self.android_devices)
+        provider = self.android_devices[0]
+        if network_generation:
+            if not ensure_network_generation(
+                    self.log, provider, network_generation,
+                    MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+                provider.log.error("Device failed to connect to %s.",
+                                   network_generation)
                 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.telephonyToggleDataConnection(True)
-        if not wait_for_cell_data_connection(self.log, self.android_devices[0],
-                                             True):
-            self.log.error("Failed to enable data connection.")
+        toggle_airplane_mode(self.log, provider, False)
+        wifi_toggle_state(self.log, provider, False)
+        provider.droid.telephonyToggleDataConnection(True)
+        for ad in self.android_devices[1:]:
+            ad.droid.telephonyToggleDataConnection(False)
+
+        if not wait_for_cell_data_connection(self.log, provider, True):
+            provider.log.error("Provider 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.")
+        if not verify_internet_connection(self.log, provider):
+            provider.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])
+        if provider.droid.wifiIsApEnabled():
+            stop_wifi_tethering(self.log, provider)
 
         return True
 
+    @test_tracker_info(uuid="912a11a3-14b3-4928-885f-cea69f14a571")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_4g_to_2gwifi(self):
         """WiFi Tethering test: LTE to WiFI 2.4G Tethering
@@ -701,18 +730,18 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_4G):
+        if not self._test_setup_tethering(RAT_4G):
             self.log.error("Verify 4G Internet access failed.")
             return False
 
         return wifi_tethering_setup_teardown(
             self.log,
-            ads[0],
-            [ads[1]],
-            ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_2G,
             check_interval=10,
             check_iteration=10)
 
+    @test_tracker_info(uuid="743e3998-d39f-42b9-b11f-009dcee34f3f")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_4g_to_5gwifi(self):
         """WiFi Tethering test: LTE to WiFI 5G Tethering
@@ -727,18 +756,18 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_4G):
+        if not self._test_setup_tethering(RAT_4G):
             self.log.error("Verify 4G Internet access failed.")
             return False
 
         return wifi_tethering_setup_teardown(
             self.log,
-            ads[0],
-            [ads[1]],
-            ap_band=WifiUtils.WIFI_CONFIG_APBAND_5G,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_5G,
             check_interval=10,
             check_iteration=10)
 
+    @test_tracker_info(uuid="59be8d68-f05b-4448-8584-de971174fd81")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_3g_to_2gwifi(self):
         """WiFi Tethering test: 3G to WiFI 2.4G Tethering
@@ -753,18 +782,18 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_3G):
+        if not self._test_setup_tethering(RAT_3G):
             self.log.error("Verify 3G Internet access failed.")
             return False
 
         return wifi_tethering_setup_teardown(
             self.log,
-            ads[0],
-            [ads[1]],
-            ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_2G,
             check_interval=10,
             check_iteration=10)
 
+    @test_tracker_info(uuid="1be6b741-92e8-4ee1-9f59-e7f9f369b065")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_3g_to_5gwifi(self):
         """WiFi Tethering test: 3G to WiFI 5G Tethering
@@ -779,18 +808,18 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_3G):
+        if not self._test_setup_tethering(RAT_3G):
             self.log.error("Verify 3G Internet access failed.")
             return False
 
         return wifi_tethering_setup_teardown(
             self.log,
-            ads[0],
-            [ads[1]],
-            ap_band=WifiUtils.WIFI_CONFIG_APBAND_5G,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_5G,
             check_interval=10,
             check_iteration=10)
 
+    @test_tracker_info(uuid="f8c4e3d8-b0e5-40ac-a31e-5ae5705a42c6")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_4g_to_2gwifi_2clients(self):
         """WiFi Tethering test: LTE to WiFI 2.4G Tethering, with multiple clients
@@ -805,18 +834,18 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_4G):
+        if not self._test_setup_tethering(RAT_4G):
             self.log.error("Verify 4G Internet access failed.")
             return False
 
         return wifi_tethering_setup_teardown(
             self.log,
-            ads[0],
-            [ads[1], ads[2]],
-            ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+            ads[0], [ads[1], ads[2]],
+            ap_band=WIFI_CONFIG_APBAND_2G,
             check_interval=10,
             check_iteration=10)
 
+    @test_tracker_info(uuid="89fe6321-4c0d-40c0-89b2-54008ecca68f")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_2g_to_2gwifi(self):
         """WiFi Tethering test: 2G to WiFI 2.4G Tethering
@@ -831,18 +860,18 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_2G):
+        if not self._test_setup_tethering(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,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_2G,
             check_interval=10,
             check_iteration=10)
 
+    @test_tracker_info(uuid="b8258d51-9581-4d52-80b6-501941ec1191")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_2g_to_5gwifi(self):
         """WiFi Tethering test: 2G to WiFI 5G Tethering
@@ -857,24 +886,24 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_2G):
+        if not self._test_setup_tethering(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,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_5G,
             check_interval=10,
             check_iteration=10)
 
+    @test_tracker_info(uuid="8ed766a6-71c5-4b3b-8897-a4e796c75619")
     @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
+        1. DUT in data connected, 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
@@ -886,8 +915,8 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_4G):
-            self.log.error("Verify 4G Internet access failed.")
+        if not self._test_setup_tethering():
+            self.log.error("Verify provider Internet access failed.")
             return False
         self.log.info("Connect WiFi.")
         if not ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
@@ -895,25 +924,24 @@
             self.log.error("WiFi connect fail.")
             return False
         self.log.info("Start WiFi Tethering.")
-        if not wifi_tethering_setup_teardown(self.log,
-                                             ads[0],
-                                             [ads[1]],
-                                             check_interval=10,
-                                             check_iteration=2):
+        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
 
         if (not wait_for_wifi_data_connection(self.log, ads[0], True) or
-                not verify_http_connection(self.log, ads[0])):
+                not verify_internet_connection(self.log, ads[0])):
             self.log.error("Provider data did not return to Wifi")
             return False
         return True
 
+    @test_tracker_info(uuid="b879ceb2-1b80-4762-93f9-afef4d688c28")
     @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.
+        1. DUT data connection is on and 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
@@ -925,16 +953,15 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_4G):
-            self.log.error("Verify 4G Internet access failed.")
+        if not self._test_setup_tethering():
+            self.log.error("Provider Internet access check 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,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False,
@@ -943,45 +970,39 @@
                 return False
 
             if not ads[0].droid.wifiIsApEnabled():
-                self.log.error("Provider WiFi tethering stopped.")
+                ads[0].log.error("Provider WiFi tethering stopped.")
                 return False
 
-            self.log.info(
+            ads[0].log.info(
                 "Disable Data on Provider, verify no data on Client.")
             ads[0].droid.telephonyToggleDataConnection(False)
             time.sleep(WAIT_TIME_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
-            if verify_http_connection(self.log, ads[0]):
-                self.log.error("Disable data on provider failed.")
+            if verify_internet_connection(self.log, ads[0]):
+                ads[0].log.error("Disable data on provider failed.")
                 return False
             if not ads[0].droid.wifiIsApEnabled():
-                self.log.error("Provider WiFi tethering stopped.")
+                ads[0].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.")
+            if not check_is_wifi_connected(self.log, ads[1], ssid):
+                ads[1].log.error("Client WiFi is not connected")
                 return False
 
             self.log.info(
                 "Enable Data on Provider, verify data available on Client.")
             ads[0].droid.telephonyToggleDataConnection(True)
-            time.sleep(WAIT_TIME_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
-            if not verify_http_connection(self.log, ads[0]):
-                self.log.error("Enable data on provider failed.")
+            if not wait_for_cell_data_connection(self.log, ads[0], True):
+                ads[0].log.error("Provider failed to enable data connection.")
+                return False
+            if not verify_internet_connection(self.log, ads[0]):
+                ads[0].log.error("Provider internet connection check 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!")
+            if not check_is_wifi_connected(self.log, ads[1], ssid) or (
+                    not verify_internet_connection(self.log, ads[1])):
+                ads[1].log.error("Client wifi connection check failed!")
                 return False
         finally:
             if not wifi_tethering_cleanup(self.log, ads[0], [ads[1]]):
@@ -990,6 +1011,7 @@
 
     # Invalid Live Test. Can't rely on the result of this test with live network.
     # Network may decide not to change the RAT when data conenction is active.
+    @test_tracker_info(uuid="c92a961b-e85d-435c-8988-052928add591")
     @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,
@@ -1007,15 +1029,14 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_4G):
+        if not self._test_setup_tethering(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,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False):
@@ -1036,7 +1057,7 @@
                 self.log.error("Provider failed to reselect to 3G.")
                 return False
             time.sleep(WAIT_TIME_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
-            if not verify_http_connection(self.log, ads[0]):
+            if not verify_internet_connection(self.log, ads[0]):
                 self.log.error("Data not available on Provider.")
                 return False
             if not ads[0].droid.wifiIsApEnabled():
@@ -1052,6 +1073,7 @@
 
     # Invalid Live Test. Can't rely on the result of this test with live network.
     # Network may decide not to change the RAT when data conenction is active.
+    @test_tracker_info(uuid="eb5f0180-b70d-436f-8fcb-60c59307cc43")
     @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,
@@ -1069,15 +1091,14 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_3G):
+        if not self._test_setup_tethering(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,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False):
@@ -1098,7 +1119,7 @@
                 self.log.error("Provider failed to reselect to 4G.")
                 return False
             time.sleep(WAIT_TIME_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING)
-            if not verify_http_connection(self.log, ads[0]):
+            if not verify_internet_connection(self.log, ads[0]):
                 self.log.error("Data not available on Provider.")
                 return False
             if not ads[0].droid.wifiIsApEnabled():
@@ -1112,6 +1133,7 @@
                 return False
         return True
 
+    @test_tracker_info(uuid="12a6c910-fa96-4d9b-99a5-8391fea33732")
     @TelephonyBaseTest.tel_test_wrap
     def test_toggle_apm_during_active_wifi_tethering(self):
         """WiFi Tethering test: Toggle APM during active WiFi Tethering
@@ -1128,16 +1150,15 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_4G):
+        if not self._test_setup_tethering(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,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False,
@@ -1158,16 +1179,15 @@
             if ads[0].droid.wifiIsApEnabled():
                 self.log.error("Provider WiFi tethering not stopped.")
                 return False
-            if verify_http_connection(self.log, ads[1]):
+            if verify_internet_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))
+            if wifi_info[WIFI_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.")
@@ -1178,14 +1198,15 @@
             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]):
+            if not verify_internet_connection(self.log, ads[0]):
                 self.log.error("Provider should have Internet connection.")
                 return False
         finally:
             ads[1].droid.telephonyToggleDataConnection(True)
-            WifiUtils.wifi_reset(self.log, ads[1])
+            wifi_reset(self.log, ads[1])
         return True
 
+    @test_tracker_info(uuid="037e80fc-6eab-4cd1-846a-b9780a1d502d")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_entitlement_check(self):
         """Tethering Entitlement Check Test
@@ -1199,7 +1220,8 @@
 
         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])):
+                not verify_internet_connection(self.log,
+                                               self.android_devices[0])):
             self.log.error("Failed cell data call for entitlement check.")
             return False
 
@@ -1209,6 +1231,7 @@
             ad.serial, result))
         return result
 
+    @test_tracker_info(uuid="4972826e-39ea-42f7-aae0-06fe3aa9ecc6")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_4g_to_2gwifi_stress(self):
         """Stress Test LTE to WiFI 2.4G Tethering
@@ -1239,14 +1262,15 @@
                 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))))
+            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
 
+    @test_tracker_info(uuid="54e85aed-09e3-42e2-bb33-bca1005d93ab")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_wifi_ssid_quotes(self):
         """WiFi Tethering test: SSID name have quotes.
@@ -1259,22 +1283,22 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads):
+        if not self._test_setup_tethering():
             self.log.error("Verify Internet access failed.")
             return False
         ssid = "\"" + rand_ascii_str(10) + "\""
-        self.log.info("Starting WiFi Tethering test with ssid: {}".format(
-            ssid))
+        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,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_2G,
             check_interval=10,
             check_iteration=10,
             ssid=ssid)
 
+    @test_tracker_info(uuid="320326da-bf32-444d-81f9-f781c55dbc99")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_wifi_password_escaping_characters(self):
         """WiFi Tethering test: password have escaping characters.
@@ -1288,19 +1312,18 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads):
+        if not self._test_setup_tethering():
             self.log.error("Verify 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))
+        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,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_2G,
             check_interval=10,
             check_iteration=10,
             password=password)
@@ -1325,27 +1348,27 @@
         result = True
         # Turn off active SoftAP if any.
         if ad_host.droid.wifiIsApEnabled():
-            WifiUtils.stop_wifi_tethering(self.log, ad_host)
+            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):
+        if not start_wifi_tethering(self.log, ad_host, ssid, password,
+                                    WIFI_CONFIG_APBAND_2G):
             self.log.error("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))
+        if not 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):
+        if not stop_wifi_tethering(self.log, ad_host):
             self.log.error("Stop WiFi tethering failed.")
             result = False
         return result
 
+    @test_tracker_info(uuid="617c7e71-f166-465f-bfd3-b5a3a40cc0d4")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_wifi_ssid(self):
         """WiFi Tethering test: start WiFi tethering with all kinds of SSIDs.
@@ -1358,18 +1381,19 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_4G):
+        if not self._test_setup_tethering(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"]
+        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:
@@ -1385,6 +1409,7 @@
         else:
             return True
 
+    @test_tracker_info(uuid="9a5b5a34-b5cf-451d-94c4-8a64d456dfe5")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_wifi_password(self):
         """WiFi Tethering test: start WiFi tethering with all kinds of passwords.
@@ -1397,7 +1422,7 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, RAT_4G):
+        if not self._test_setup_tethering(RAT_4G):
             self.log.error("Setup Failed.")
             return False
         password_list = [
@@ -1426,11 +1451,12 @@
         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):
+    def _test_tethering_wifi_and_voice_call(self, provider_data_rat,
+                                            provider_setup_func,
+                                            provider_in_call_check_func):
+        provider = self.android_devices[0]
+        client = self.android_devices[1]
+        if not self._test_setup_tethering(provider_data_rat):
             self.log.error("Verify 4G Internet access failed.")
             return False
 
@@ -1444,9 +1470,8 @@
             self.log.info("1. Setup WiFi Tethering.")
             if not wifi_tethering_setup_teardown(
                     self.log,
-                    provider,
-                [client],
-                    ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                    provider, [client],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False):
@@ -1462,9 +1487,9 @@
                 self.log.error("Setup Call Failed.")
                 return False
             self.log.info("3. Verify data.")
-            if not verify_http_connection(self.log, provider):
+            if not verify_internet_connection(self.log, provider):
                 self.log.error("Provider have no Internet access.")
-            if not verify_http_connection(self.log, client):
+            if not verify_internet_connection(self.log, client):
                 self.log.error("Client have no Internet access.")
             hangup_call(self.log, provider)
 
@@ -1480,9 +1505,9 @@
                 self.log.error("Setup Call Failed.")
                 return False
             self.log.info("5. Verify data.")
-            if not verify_http_connection(self.log, provider):
+            if not verify_internet_connection(self.log, provider):
                 self.log.error("Provider have no Internet access.")
-            if not verify_http_connection(self.log, client):
+            if not verify_internet_connection(self.log, client):
                 self.log.error("Client have no Internet access.")
             hangup_call(self.log, provider)
 
@@ -1491,6 +1516,7 @@
                 return False
         return True
 
+    @test_tracker_info(uuid="216bdb8c-edbf-4ff8-8750-a0861ab44df6")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_wifi_volte_call(self):
         """WiFi Tethering test: VoLTE call during WiFi tethering
@@ -1506,9 +1532,9 @@
             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)
+            RAT_4G, phone_setup_volte, is_phone_in_call_volte)
 
+    @test_tracker_info(uuid="bcd430cc-6d33-47d1-825d-aae9f248addc")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_wifi_csfb_call(self):
         """WiFi Tethering test: CSFB call during WiFi tethering
@@ -1524,9 +1550,9 @@
             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)
+            RAT_4G, phone_setup_csfb, is_phone_in_call_csfb)
 
+    @test_tracker_info(uuid="19e0df23-6819-4c69-bfda-eea9cce802d8")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_wifi_3g_call(self):
         """WiFi Tethering test: 3G call during WiFi tethering
@@ -1542,9 +1568,9 @@
             False if failed.
         """
         return self._test_tethering_wifi_and_voice_call(
-            self.android_devices[0], self.android_devices[1], RAT_3G,
-            phone_setup_voice_3g, is_phone_in_call_3g)
+            RAT_3G, phone_setup_voice_3g, is_phone_in_call_3g)
 
+    @test_tracker_info(uuid="4acd98b5-fdef-4736-969f-3fa953990a58")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_wifi_no_password(self):
         """WiFi Tethering test: Start WiFi tethering with no password
@@ -1559,19 +1585,19 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads):
+        if not self._test_setup_tethering():
             self.log.error("Verify Internet access failed.")
             return False
 
         return wifi_tethering_setup_teardown(
             self.log,
-            ads[0],
-            [ads[1]],
-            ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_2G,
             check_interval=10,
             check_iteration=10,
             password="")
 
+    @test_tracker_info(uuid="86ad1680-bfb8-457e-8b4d-23321cb3f223")
     @TelephonyBaseTest.tel_test_wrap
     def test_tethering_wifi_reboot(self):
         """WiFi Tethering test: Start WiFi tethering then Reboot device
@@ -1588,15 +1614,14 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads):
+        if not self._test_setup_tethering():
             self.log.error("Verify 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,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False):
@@ -1618,11 +1643,12 @@
                 return False
         finally:
             ads[1].droid.telephonyToggleDataConnection(True)
-            WifiUtils.wifi_reset(self.log, ads[1])
+            wifi_reset(self.log, ads[1])
             if ads[0].droid.wifiIsApEnabled():
-                WifiUtils.stop_wifi_tethering(self.log, ads[0])
+                stop_wifi_tethering(self.log, ads[0])
         return True
 
+    @test_tracker_info(uuid="5cf04ca2-dfde-43d6-be74-78b9abdf6c26")
     @TelephonyBaseTest.tel_test_wrap
     def test_connect_wifi_start_tethering_wifi_reboot(self):
         """WiFi Tethering test: WiFI connected, then start WiFi tethering,
@@ -1641,7 +1667,7 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads):
+        if not self._test_setup_tethering():
             self.log.error("Verify Internet access failed.")
             return False
 
@@ -1649,16 +1675,15 @@
         if ((not ensure_wifi_connected(self.log, ads[0],
                                        self.wifi_network_ssid,
                                        self.wifi_network_pass)) or
-            (not verify_http_connection(self.log, ads[0]))):
+            (not verify_internet_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,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False):
@@ -1681,17 +1706,18 @@
 
             self.log.info("Make sure WiFi can connect automatically.")
             if (not wait_for_wifi_data_connection(self.log, ads[0], True) or
-                    not verify_http_connection(self.log, ads[0])):
+                    not verify_internet_connection(self.log, ads[0])):
                 self.log.error("Data did not return to WiFi")
                 return False
 
         finally:
             ads[1].droid.telephonyToggleDataConnection(True)
-            WifiUtils.wifi_reset(self.log, ads[1])
+            wifi_reset(self.log, ads[1])
             if ads[0].droid.wifiIsApEnabled():
-                WifiUtils.stop_wifi_tethering(self.log, ads[0])
+                stop_wifi_tethering(self.log, ads[0])
         return True
 
+    @test_tracker_info(uuid="e0621997-c5bd-4137-afa6-b43406e9c713")
     @TelephonyBaseTest.tel_test_wrap
     def test_connect_wifi_reboot_start_tethering_wifi(self):
         """WiFi Tethering test: DUT connected to WiFi, then reboot,
@@ -1710,7 +1736,7 @@
         """
         ads = self.android_devices
 
-        if not self._test_setup_tethering(ads):
+        if not self._test_setup_tethering():
             self.log.error("Verify Internet access failed.")
             return False
 
@@ -1718,7 +1744,7 @@
         if ((not ensure_wifi_connected(self.log, ads[0],
                                        self.wifi_network_ssid,
                                        self.wifi_network_pass)) or
-            (not verify_http_connection(self.log, ads[0]))):
+            (not verify_internet_connection(self.log, ads[0]))):
             self.log.error("WiFi connect fail.")
             return False
 
@@ -1729,12 +1755,12 @@
 
         return wifi_tethering_setup_teardown(
             self.log,
-            ads[0],
-            [ads[1]],
-            ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+            ads[0], [ads[1]],
+            ap_band=WIFI_CONFIG_APBAND_2G,
             check_interval=10,
             check_iteration=10)
 
+    @test_tracker_info(uuid="89a849ef-e2ed-4bf2-ac31-81d34aba672a")
     @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,
@@ -1753,15 +1779,14 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads):
+        if not self._test_setup_tethering():
             self.log.error("Verify 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,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False):
@@ -1772,21 +1797,21 @@
                 self.log.error("Provider WiFi tethering stopped.")
                 return False
 
-            self.log.info("Turn off screen on provider: <{}>.".format(ads[
-                0].serial))
+            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]):
+            if not verify_internet_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))
+            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]):
+            if not verify_internet_connection(self.log, ads[1]):
                 self.log.error("Client have no Internet access.")
                 return False
         finally:
@@ -1798,6 +1823,7 @@
                 return False
         return True
 
+    @test_tracker_info(uuid="695eef18-f759-4b41-8ad3-1fb329ee4b1b")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_switch_data_sim_2g(self):
         """Switch Data SIM on 2G network.
@@ -1817,28 +1843,32 @@
         """
         ad = self.android_devices[0]
         current_data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
-        current_sim_slot_index = get_slot_index_from_subid(self.log, ad,
-            current_data_sub_id)
+        current_sim_slot_index = get_slot_index_from_subid(
+            self.log, ad, current_data_sub_id)
         if current_sim_slot_index == SIM1_SLOT_INDEX:
             next_sim_slot_index = SIM2_SLOT_INDEX
         else:
             next_sim_slot_index = SIM1_SLOT_INDEX
         next_data_sub_id = get_subid_from_slot_index(self.log, ad,
-            next_sim_slot_index)
+                                                     next_sim_slot_index)
         self.log.info("Current Data is on subId: {}, SIM slot: {}".format(
             current_data_sub_id, current_sim_slot_index))
         if not ensure_network_generation_for_subscription(
-            self.log, ad, ad.droid.subscriptionGetDefaultDataSubId(), GEN_2G,
-            voice_or_data=NETWORK_SERVICE_DATA):
+                self.log,
+                ad,
+                ad.droid.subscriptionGetDefaultDataSubId(),
+                GEN_2G,
+                voice_or_data=NETWORK_SERVICE_DATA):
             self.log.error("Device data does not attach to 2G.")
             return False
-        if not verify_http_connection(self.log, ad):
+        if not verify_internet_connection(self.log, ad):
             self.log.error("No Internet access on default Data SIM.")
             return False
 
         self.log.info("Change Data to subId: {}, SIM slot: {}".format(
             next_data_sub_id, next_sim_slot_index))
-        if not change_data_sim_and_verify_data(self.log, ad, next_sim_slot_index):
+        if not change_data_sim_and_verify_data(self.log, ad,
+                                               next_sim_slot_index):
             self.log.error("Failed to change data SIM.")
             return False
 
@@ -1846,7 +1876,8 @@
         next_sim_slot_index = current_sim_slot_index
         self.log.info("Change Data back to subId: {}, SIM slot: {}".format(
             next_data_sub_id, next_sim_slot_index))
-        if not change_data_sim_and_verify_data(self.log, ad, next_sim_slot_index):
+        if not change_data_sim_and_verify_data(self.log, ad,
+                                               next_sim_slot_index):
             self.log.error("Failed to change data SIM.")
             return False
 
@@ -1871,41 +1902,43 @@
         """
         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]
+        wifi_toggles = [
+            True, False, True, False, False, True, False, False, False, False,
+            True, False, False, False, False, False, False, False, False
+        ]
 
         for toggle in wifi_toggles:
 
-            WifiUtils.wifi_reset(self.log, ad, toggle)
+            wifi_reset(self.log, ad, toggle)
 
             if not wait_for_cell_data_connection(
                     self.log, ad, True, MAX_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):
+            if not verify_internet_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)
+                wifi_toggle_state(self.log, ad, True)
 
-            WifiUtils.wifi_connect(self.log, ad, self.wifi_network_ssid,
-                                   self.wifi_network_pass)
+            wifi_connect(self.log, ad, self.wifi_network_ssid,
+                         self.wifi_network_pass)
 
             if not wait_for_wifi_data_connection(
                     self.log, ad, True, MAX_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):
+            if not verify_internet_connection(
+                    self.log, ad, 'http://www.google.com', 100, .1):
                 self.log.error("Failed to get user-plane traffic, aborting!")
                 return False
         return True
 
+    @test_tracker_info(uuid="9b8e92da-0ae1-472c-a72a-f6427e5405ce")
     @TelephonyBaseTest.tel_test_wrap
     def test_wifi_connect_disconnect_4g(self):
         """Perform multiple connects and disconnects from WiFi and verify that
@@ -1926,14 +1959,16 @@
         """
 
         ad = self.android_devices[0]
-        if not ensure_network_generation_for_subscription(self.log, ad,
-            ad.droid.subscriptionGetDefaultDataSubId(), GEN_4G,
-            MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+        if not ensure_network_generation_for_subscription(
+                self.log, ad,
+                ad.droid.subscriptionGetDefaultDataSubId(), GEN_4G,
+                MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
             self.log.error("Device {} failed to reselect in {}s.".format(
                 ad.serial, MAX_WAIT_TIME_NW_SELECTION))
             return False
         return self._test_wifi_connect_disconnect()
 
+    @test_tracker_info(uuid="09893b1f-a4a2-49d3-8027-c2c91cb8742e")
     @TelephonyBaseTest.tel_test_wrap
     def test_wifi_connect_disconnect_3g(self):
         """Perform multiple connects and disconnects from WiFi and verify that
@@ -1954,14 +1989,16 @@
         """
 
         ad = self.android_devices[0]
-        if not ensure_network_generation_for_subscription(self.log, ad,
-            ad.droid.subscriptionGetDefaultDataSubId(), GEN_3G,
-            MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+        if not ensure_network_generation_for_subscription(
+                self.log, ad,
+                ad.droid.subscriptionGetDefaultDataSubId(), GEN_3G,
+                MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
             self.log.error("Device {} failed to reselect in {}s.".format(
                 ad.serial, MAX_WAIT_TIME_NW_SELECTION))
             return False
         return self._test_wifi_connect_disconnect()
 
+    @test_tracker_info(uuid="0f095ca4-ce05-458f-9670-49a69f8c8270")
     @TelephonyBaseTest.tel_test_wrap
     def test_wifi_connect_disconnect_2g(self):
         """Perform multiple connects and disconnects from WiFi and verify that
@@ -1981,16 +2018,18 @@
             False if failed.
         """
         ad = self.android_devices[0]
-        if not ensure_network_generation_for_subscription(self.log, ad,
-            ad.droid.subscriptionGetDefaultDataSubId(), GEN_2G,
-            MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+        if not ensure_network_generation_for_subscription(
+                self.log, ad,
+                ad.droid.subscriptionGetDefaultDataSubId(), GEN_2G,
+                MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
             self.log.error("Device {} failed to reselect in {}s.".format(
                 ad.serial, MAX_WAIT_TIME_NW_SELECTION))
             return False
         return self._test_wifi_connect_disconnect()
 
-    def _test_wifi_tethering_enabled_add_voice_call(self, network_generation,
-        voice_call_direction, is_data_available_during_call):
+    def _test_wifi_tethering_enabled_add_voice_call(
+            self, network_generation, voice_call_direction,
+            is_data_available_during_call):
         """Tethering enabled + voice call.
 
         Steps:
@@ -2014,16 +2053,15 @@
             False if failed.
         """
         ads = self.android_devices
-        if not self._test_setup_tethering(ads, network_generation):
+        if not self._test_setup_tethering(network_generation):
             self.log.error("Verify Internet access failed.")
             return False
         try:
             # Start WiFi Tethering
             if not wifi_tethering_setup_teardown(
                     self.log,
-                    ads[0],
-                    [ads[1]],
-                    ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False):
@@ -2052,11 +2090,11 @@
                 self.log.error("Provider WiFi tethering stopped.")
                 return False
             if not is_data_available_during_call:
-                if verify_http_connection(self.log, ads[1], retry=0):
+                if verify_internet_connection(self.log, ads[1], retry=0):
                     self.log.error("Client should not have Internet Access.")
                     return False
             else:
-                if not verify_http_connection(self.log, ads[1]):
+                if not verify_internet_connection(self.log, ads[1]):
                     self.log.error("Client should have Internet Access.")
                     return False
 
@@ -2067,16 +2105,17 @@
             if not ads[0].droid.wifiIsApEnabled():
                 self.log.error("Provider WiFi tethering stopped.")
                 return False
-            if not verify_http_connection(self.log, ads[1]):
+            if not verify_internet_connection(self.log, ads[1]):
                 self.log.error("Client should have Internet Access.")
                 return False
         finally:
             ads[1].droid.telephonyToggleDataConnection(True)
-            WifiUtils.wifi_reset(self.log, ads[1])
+            wifi_reset(self.log, ads[1])
             if ads[0].droid.wifiIsApEnabled():
-                WifiUtils.stop_wifi_tethering(self.log, ads[0])
+                stop_wifi_tethering(self.log, ads[0])
         return True
 
+    @test_tracker_info(uuid="4d7a68c6-5eae-4242-a6e6-668f830caec3")
     @TelephonyBaseTest.tel_test_wrap
     def test_wifi_tethering_enabled_add_mo_voice_call_2g_dsds(self):
         """Tethering enabled + voice call
@@ -2099,9 +2138,10 @@
             False if failed.
         """
 
-        return self._test_wifi_tethering_enabled_add_voice_call(GEN_2G,
-            DIRECTION_MOBILE_ORIGINATED, False)
+        return self._test_wifi_tethering_enabled_add_voice_call(
+            GEN_2G, DIRECTION_MOBILE_ORIGINATED, False)
 
+    @test_tracker_info(uuid="de720069-a46c-4a6f-ae80-60b9349c8528")
     @TelephonyBaseTest.tel_test_wrap
     def test_wifi_tethering_enabled_add_mt_voice_call_2g_dsds(self):
         """Tethering enabled + voice call
@@ -2124,9 +2164,10 @@
             False if failed.
         """
 
-        return self._test_wifi_tethering_enabled_add_voice_call(GEN_2G,
-            DIRECTION_MOBILE_TERMINATED, False)
+        return self._test_wifi_tethering_enabled_add_voice_call(
+            GEN_2G, DIRECTION_MOBILE_TERMINATED, False)
 
+    @test_tracker_info(uuid="fad169c0-8ae6-45d2-98ba-3fb60466ff0b")
     @TelephonyBaseTest.tel_test_wrap
     def test_wifi_tethering_msim_switch_data_sim(self):
         """Tethering enabled + switch data SIM.
@@ -2148,20 +2189,19 @@
         """
         ads = self.android_devices
         current_data_sub_id = ads[0].droid.subscriptionGetDefaultDataSubId()
-        current_sim_slot_index = get_slot_index_from_subid(self.log, ads[0],
-            current_data_sub_id)
+        current_sim_slot_index = get_slot_index_from_subid(
+            self.log, ads[0], current_data_sub_id)
         self.log.info("Current Data is on subId: {}, SIM slot: {}".format(
             current_data_sub_id, current_sim_slot_index))
-        if not self._test_setup_tethering(ads):
+        if not self._test_setup_tethering():
             self.log.error("Verify Internet access failed.")
             return False
         try:
             # Start WiFi Tethering
             if not wifi_tethering_setup_teardown(
                     self.log,
-                    ads[0],
-                    [ads[1]],
-                    ap_band=WifiUtils.WIFI_CONFIG_APBAND_2G,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=2,
                     do_cleanup=False):
@@ -2171,23 +2211,24 @@
                 next_sim_slot_index = \
                     {SIM1_SLOT_INDEX : SIM2_SLOT_INDEX,
                      SIM2_SLOT_INDEX : SIM1_SLOT_INDEX}[current_sim_slot_index]
-                self.log.info("Change Data to SIM slot: {}".
-                    format(next_sim_slot_index))
+                self.log.info(
+                    "Change Data to SIM slot: {}".format(next_sim_slot_index))
                 if not change_data_sim_and_verify_data(self.log, ads[0],
-                    next_sim_slot_index):
+                                                       next_sim_slot_index):
                     self.log.error("Failed to change data SIM.")
                     return False
                 current_sim_slot_index = next_sim_slot_index
-                if not verify_http_connection(self.log, ads[1]):
+                if not verify_internet_connection(self.log, ads[1]):
                     self.log.error("Client should have Internet Access.")
                     return False
         finally:
             ads[1].droid.telephonyToggleDataConnection(True)
-            WifiUtils.wifi_reset(self.log, ads[1])
+            wifi_reset(self.log, ads[1])
             if ads[0].droid.wifiIsApEnabled():
-                WifiUtils.stop_wifi_tethering(self.log, ads[0])
+                stop_wifi_tethering(self.log, ads[0])
         return True
 
+    @test_tracker_info(uuid="8bb9383f-ddf9-400c-a831-c9462bae6b47")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_cell_data_switch_to_wifi_switch_data_sim_2g(self):
         """Switch Data SIM on 2G network.
@@ -2208,8 +2249,8 @@
         """
         ad = self.android_devices[0]
         current_data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
-        current_sim_slot_index = get_slot_index_from_subid(self.log, ad,
-                                                           current_data_sub_id)
+        current_sim_slot_index = get_slot_index_from_subid(
+            self.log, ad, current_data_sub_id)
         if current_sim_slot_index == SIM1_SLOT_INDEX:
             next_sim_slot_index = SIM2_SLOT_INDEX
         else:
@@ -2226,7 +2267,7 @@
                 voice_or_data=NETWORK_SERVICE_DATA):
             self.log.error("Device data does not attach to 2G.")
             return False
-        if not verify_http_connection(self.log, ad):
+        if not verify_internet_connection(self.log, ad):
             self.log.error("No Internet access on default Data SIM.")
             return False
 
@@ -2236,7 +2277,7 @@
             self.log.error("WiFi connect fail.")
             return False
         if (not wait_for_wifi_data_connection(self.log, ad, True) or
-                not verify_http_connection(self.log, ad)):
+                not verify_internet_connection(self.log, ad)):
             self.log.error("Data is not on WiFi")
             return False
 
@@ -2244,14 +2285,14 @@
             self.log.info(
                 "Change Data SIM, Disable WiFi and verify Internet access.")
             set_subid_for_data(ad, next_data_sub_id)
-            WifiUtils.wifi_toggle_state(self.log, ad, False)
+            wifi_toggle_state(self.log, ad, False)
             if not wait_for_data_attach_for_subscription(
                     self.log, ad, next_data_sub_id,
                     MAX_WAIT_TIME_NW_SELECTION):
                 self.log.error("Failed to attach data on subId:{}".format(
                     next_data_sub_id))
                 return False
-            if not verify_http_connection(self.log, ad):
+            if not verify_internet_connection(self.log, ad):
                 self.log.error("No Internet access after changing Data SIM.")
                 return False
 
@@ -2261,6 +2302,7 @@
 
         return True
 
+    @test_tracker_info(uuid="8a8cd773-77f5-4802-85ac-1a654bb4743c")
     @TelephonyBaseTest.tel_test_wrap
     def test_disable_data_on_non_active_data_sim(self):
         """Switch Data SIM on 2G network.
@@ -2279,8 +2321,8 @@
         """
         ad = self.android_devices[0]
         current_data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
-        current_sim_slot_index = get_slot_index_from_subid(self.log, ad,
-                                                           current_data_sub_id)
+        current_sim_slot_index = get_slot_index_from_subid(
+            self.log, ad, current_data_sub_id)
         if current_sim_slot_index == SIM1_SLOT_INDEX:
             non_active_sim_slot_index = SIM2_SLOT_INDEX
         else:
@@ -2298,7 +2340,7 @@
                 voice_or_data=NETWORK_SERVICE_DATA):
             self.log.error("Device data does not attach to 2G.")
             return False
-        if not verify_http_connection(self.log, ad):
+        if not verify_internet_connection(self.log, ad):
             self.log.error("No Internet access on default Data SIM.")
             return False
 
diff --git a/acts/tests/google/tel/live/TelLiveMobilityStressTest.py b/acts/tests/google/tel/live/TelLiveMobilityStressTest.py
new file mode 100644
index 0000000..de49b11
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveMobilityStressTest.py
@@ -0,0 +1,281 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+    Test Script for Telephony Mobility Stress Test
+"""
+
+import collections
+import random
+import time
+from acts.test_decorators import test_tracker_info
+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 CELL_WEAK_RSSI_VALUE
+from acts.test_utils.tel.tel_defines import CELL_STRONG_RSSI_VALUE
+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 WFC_MODE_WIFI_PREFERRED
+from acts.test_utils.tel.tel_defines import WIFI_WEAK_RSSI_VALUE
+from acts.test_utils.tel.tel_test_utils import active_file_download_test
+from acts.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
+from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import is_voice_attached
+from acts.test_utils.tel.tel_test_utils import run_multithread_func
+from acts.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts.utils import rand_ascii_str
+from TelWifiVoiceTest import TelWifiVoiceTest
+from TelWifiVoiceTest import ATTEN_NAME_FOR_WIFI_2G
+from TelWifiVoiceTest import ATTEN_NAME_FOR_WIFI_5G
+from TelWifiVoiceTest import ATTEN_NAME_FOR_CELL_3G
+from TelWifiVoiceTest import ATTEN_NAME_FOR_CELL_4G
+
+
+class TelLiveMobilityStressTest(TelWifiVoiceTest):
+    def setup_class(self):
+        super().setup_class()
+        #super(TelWifiVoiceTest, self).setup_class()
+        self.user_params["telephony_auto_rerun"] = False
+        self.max_phone_call_duration = int(
+            self.user_params.get("max_phone_call_duration", 600))
+        self.max_sleep_time = int(self.user_params.get("max_sleep_time", 120))
+        self.max_run_time = int(self.user_params.get("max_run_time", 18000))
+        self.max_sms_length = int(self.user_params.get("max_sms_length", 1000))
+        self.max_mms_length = int(self.user_params.get("max_mms_length", 160))
+        self.crash_check_interval = int(
+            self.user_params.get("crash_check_interval", 300))
+        self.signal_change_interval = int(
+            self.user_params.get("signal_change_interval", 10))
+        self.singal_change_step = int(
+            self.user_params.get("signal_change_step", 5))
+        self.dut = self.android_devices[0]
+        self.helper = self.android_devices[1]
+
+        return True
+
+    def _setup_volte_wfc_wifi_preferred(self):
+        return self._wfc_phone_setup(
+            False, WFC_MODE_WIFI_PREFERRED, volte_mode=True)
+
+    def _setup_volte_wfc_cell_preferred(self):
+        return self._wfc_phone_setup(
+            False, WFC_MODE_CELLULAR_PREFERRED, volte_mode=True)
+
+    def _setup_csfb_wfc_wifi_preferred(self):
+        return self._wfc_phone_setup(
+            False, WFC_MODE_WIFI_PREFERRED, volte_mode=False)
+
+    def _setup_csfb_wfc_cell_preferred(self):
+        return self._wfc_phone_setup(
+            False, WFC_MODE_CELLULAR_PREFERRED, volte_mode=False)
+
+    def _send_message(self, ads):
+        selection = random.randrange(0, 2)
+        message_type_map = {0: "SMS", 1: "MMS"}
+        max_length_map = {0: self.max_sms_length, 1: self.max_mms_length}
+        length = random.randrange(0, max_length_map[selection] + 1)
+        text = rand_ascii_str(length)
+        message_content_map = {0: [text], 1: [("Mms Message", text, None)]}
+        message_func_map = {
+            0: sms_send_receive_verify,
+            1: mms_send_receive_verify
+        }
+        if not message_func_map[selection](self.log, ads[0], ads[1],
+                                           message_content_map[selection]):
+            self.log.error("%s of length %s from %s to %s fails",
+                           message_type_map[selection], length, ads[0].serial,
+                           ads[1].serial)
+            return False
+        else:
+            self.log.info("%s of length %s from %s to %s succeed",
+                          message_type_map[selection], length, ads[0].serial,
+                          ads[1].serial)
+            return False
+
+    def _make_phone_call(self, ads):
+        if not call_setup_teardown(
+                self.log,
+                ads[0],
+                ads[1],
+                ad_hangup=ads[random.randrange(0, 2)],
+                verify_caller_func=is_voice_attached,
+                verify_callee_func=is_voice_attached,
+                wait_time_in_call=random.randrange(
+                    1, self.max_phone_call_duration)):
+            ads[0].log.error("Setup phone Call failed.")
+            return False
+        ads[0].log.info("Setup call successfully.")
+        return True
+
+    def _download_file(self):
+        #file_names = ["5MB", "10MB", "20MB", "50MB", "200MB", "512MB", "1GB"]
+        #wifi download is very slow in lab, limit the file size upto 200MB
+        file_names = ["5MB", "10MB", "20MB", "50MB", "200MB"]
+        selection = random.randrange(0, 7)
+        return active_file_download_test(self.log, self.dut,
+                                         file_names[selection])
+
+    def check_crash(self):
+        new_crash = self.dut.check_crash_report()
+        crash_diff = set(new_crash).difference(set(self.dut.crash_report))
+        self.dut.crash_report = new_crash
+        if crash_diff:
+            self.dut.log.error("Find new crash reports %s", list(crash_diff))
+            self.dut.pull_files(list(crash_diff))
+            return False
+        return True
+
+    def crash_check_test(self):
+        failure = 0
+        while time.time() < self.finishing_time:
+            if not self.check_crash():
+                failure += 1
+                self.log.error("Crash found count: %s", failure)
+                self._take_bug_report("%s_crash_found" % self.test_name,
+                                      time.strftime("%m-%d-%Y-%H-%M-%S"))
+            self.dut.droid.goToSleepNow()
+            time.sleep(self.crash_check_interval)
+        return failure
+
+    def change_environment(self):
+        while time.time() < self.finishing_time:
+            self._wfc_set_wifi_strong_cell_strong()
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                     MIN_RSSI_RESERVED_VALUE)
+            #gratually decrease wifi 5g
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
+                     self.wifi_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE,
+                     self.signal_change_step, self.signal_change_interval)
+            #gratually increase wifi 5g
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
+                     MIN_RSSI_RESERVED_VALUE, MAX_RSSI_RESERVED_VALUE,
+                     self.signal_change_step, self.signal_change_interval)
+
+            self._wfc_set_wifi_strong_cell_strong()
+            #gratually decrease cell 4G
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G],
+                     self.cell_rssi_with_no_atten, CELL_WEAK_RSSI_VALUE,
+                     self.signal_change_step, self.signal_change_interval)
+            #gratually decrease cell 3G
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G],
+                     self.cell_rssi_with_no_atten, CELL_WEAK_RSSI_VALUE,
+                     self.signal_change_step, self.signal_change_interval)
+            #gradtually increase cell 3G
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G],
+                     MIN_RSSI_RESERVED_VALUE, MAX_RSSI_RESERVED_VALUE,
+                     self.signal_change_step, self.signal_change_interval)
+            #gradtually increase cell 4G
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G],
+                     MIN_RSSI_RESERVED_VALUE, MAX_RSSI_RESERVED_VALUE,
+                     self.signal_change_step, self.signal_change_interval)
+
+    def call_test(self):
+        failure = 0
+        while time.time() < self.finishing_time:
+            ads = [self.dut, self.helper]
+            random.shuffle(ads)
+            if not self._make_phone_call(ads):
+                failure += 1
+                self.log.error("Call test failure count: %s", failure)
+                self._take_bug_report("%s_call_failure" % self.test_name,
+                                      time.strftime("%m-%d-%Y-%H-%M-%S"))
+            self.dut.droid.goToSleepNow()
+            time.sleep(random.randrange(0, self.max_sleep_time))
+        return failure
+
+    def message_test(self):
+        failure = 0
+        while time.time() < self.finishing_time:
+            ads = [self.dut, self.helper]
+            random.shuffle(ads)
+            if not self._send_message(ads):
+                failure += 1
+                self.log.error("Messaging test failure count: %s", failure)
+                #self._take_bug_report("%s_messaging_failure" % self.test_name,
+                #                      time.strftime("%m-%d-%Y-%H-%M-%S"))
+            self.dut.droid.goToSleepNow()
+            time.sleep(random.randrange(0, self.max_sleep_time))
+        return failure
+
+    def data_test(self):
+        failure = 0
+        while time.time() < self.finishing_time:
+            if not self._download_file():
+                failure += 1
+                self.log.error("File download test failure count: %s", failure)
+                #self._take_bug_report("%s_download_failure" % self.test_name,
+                #                      time.strftime("%m-%d-%Y-%H-%M-%S"))
+            self.dut.droid.goToSleepNow()
+            time.sleep(random.randrange(0, self.max_sleep_time))
+        return failure
+
+    def parallel_tests(self, setup_func=None):
+        if setup_func and not setup_func():
+            self.log.error("Test setup %s failed", setup_func.__name__)
+            return False
+        self.finishing_time = time.time() + self.max_run_time
+        results = run_multithread_func(self.log, [(self.call_test, []), (
+            self.message_test, []), (self.data_test, []), (
+                self.change_environment, []), (self.crash_check_test, [])])
+        self.log.info("Call failures: %s", results[0])
+        self.log.info("Messaging failures: %s", results[1])
+        self.log.info("Data failures: %s", results[2])
+        self.log.info("Crash failures: %s", results[4])
+        for result in results:
+            if result: return False
+
+    """ Tests Begin """
+
+    @test_tracker_info(uuid="6fcba97c-3572-47d7-bcac-9608f1aa5304")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_wfc_wifi_preferred_parallel_stress(self):
+        return self.parallel_tests(
+            setup_func=self._setup_volte_wfc_wifi_preferred)
+
+    @test_tracker_info(uuid="df78a9a8-2a14-40bf-a7aa-719502f975be")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_volte_wfc_cell_preferred_parallel_stress(self):
+        return self.parallel_tests(
+            setup_func=self._setup_volte_wfc_cell_preferred)
+
+    @test_tracker_info(uuid="4cb47315-c420-44c2-ac47-a8bdca6d0e25")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wfc_wifi_preferred_parallel_stress(self):
+        return self.parallel_tests(
+            setup_func=self._setup_csfb_wfc_wifi_preferred)
+
+    @test_tracker_info(uuid="92821ef7-542a-4139-b3b0-22278e2b06c4")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_wfc_cell_preferred_parallel_stress(self):
+        return self.parallel_tests(
+            setup_func=self._setup_csfb_wfc_cell_preferred)
+
+    """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLivePostflightTest.py b/acts/tests/google/tel/live/TelLivePostflightTest.py
new file mode 100644
index 0000000..1899c38
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLivePostflightTest.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+    Test Script for Telephony Post Flight check.
+"""
+
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.asserts import fail
+
+
+class TelLivePostflightTest(TelephonyBaseTest):
+    @TelephonyBaseTest.tel_test_wrap
+    def test_check_crash(self):
+        msg = ""
+        for ad in self.android_devices:
+            post_crash = ad.check_crash_report()
+            pre_crash = getattr(ad, "crash_report_preflight", [])
+            crash_diff = list(set(post_crash).difference(set(pre_crash)))
+            if crash_diff:
+                msg += "%s find new crash reports %s" % (ad.serial, crash_diff)
+                ad.pull_files(crash_diff)
+                ad.log.error("Find new crash reports %s", crash_diff)
+        if msg:
+            fail(msg)
+        return True
diff --git a/acts/tests/google/tel/live/TelLivePreflightTest.py b/acts/tests/google/tel/live/TelLivePreflightTest.py
index fc5bac2..b0d809a 100644
--- a/acts/tests/google/tel/live/TelLivePreflightTest.py
+++ b/acts/tests/google/tel/live/TelLivePreflightTest.py
@@ -35,8 +35,9 @@
 from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT
 from acts.test_utils.tel.tel_lookup_tables import device_capabilities
 from acts.test_utils.tel.tel_lookup_tables import operator_capabilities
-from acts.test_utils.tel.tel_test_utils import WifiUtils
+from acts.test_utils.tel.tel_test_utils import abort_all_tests
 from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
 from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
 from acts.test_utils.tel.tel_test_utils import get_operator_name
 from acts.test_utils.tel.tel_test_utils import setup_droid_properties
@@ -46,8 +47,10 @@
 from acts.test_utils.tel.tel_test_utils import verify_http_connection
 from acts.test_utils.tel.tel_test_utils import wait_for_voice_attach_for_subscription
 from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
+from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
 from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
 from acts.asserts import abort_all
+from acts.asserts import fail
 
 
 class TelLivePreflightTest(TelephonyBaseTest):
@@ -55,9 +58,11 @@
         TelephonyBaseTest.__init__(self, controllers)
 
         self.wifi_network_ssid = self.user_params.get(
-              "wifi_network_ssid") or self.user_params.get("wifi_network_ssid_2g")
+            "wifi_network_ssid") or self.user_params.get(
+                "wifi_network_ssid_2g")
         self.wifi_network_pass = self.user_params.get(
-              "wifi_network_pass") or self.user_params.get("wifi_network_pass_2g")
+            "wifi_network_pass") or self.user_params.get(
+                "wifi_network_pass_2g")
 
     """ Tests Begin """
 
@@ -68,18 +73,15 @@
         # 1. Connect to WiFi.
         # 2. Check WiFi have Internet access.
         toggle_airplane_mode(self.log, ad, False, strict_checking=False)
-
         try:
             if not ensure_wifi_connected(self.log, ad, self.wifi_network_ssid,
                                          self.wifi_network_pass):
-                self._preflight_fail("{}: WiFi connect fail.".format(
-                    ad.serial))
+                abort_all_tests(ad.log, "WiFi connect fail")
             if (not wait_for_wifi_data_connection(self.log, ad, True) or
                     not verify_http_connection(self.log, ad)):
-                self._preflight_fail("{}: Data not available on WiFi.".format(
-                    ad.serial))
+                abort_all_tests(ad.log, "Data not available on WiFi")
         finally:
-            WifiUtils.wifi_toggle_state(self.log, ad, False)
+            wifi_toggle_state(self.log, ad, False)
         # TODO: add more environment check here.
         return True
 
@@ -87,29 +89,16 @@
     def test_pre_flight_check(self):
         for ad in self.android_devices:
             #check for sim and service
-            subInfo = ad.droid.subscriptionGetAllSubInfoList()
-            if not subInfo or len(subInfo) < 1:
-                self._preflight_fail(
-                    "{}: Unable to find A valid subscription!".format(
-                        ad.serial))
-            toggle_airplane_mode(self.log, ad, False, strict_checking=False)
-            if ad.droid.subscriptionGetDefaultDataSubId() <= INVALID_SUB_ID:
-                self._preflight_fail("{}: No Default Data Sub ID".format(
-                    ad.serial))
-            elif ad.droid.subscriptionGetDefaultVoiceSubId() <= INVALID_SUB_ID:
-                self._preflight_fail("{}: No Valid Voice Sub ID".format(
-                    ad.serial))
-            sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
-            if not wait_for_voice_attach_for_subscription(
-                    self.log, ad, sub_id, MAX_WAIT_TIME_NW_SELECTION):
-                self._preflight_fail(
-                    "{}: Did Not Attach For Voice Services".format(ad.serial))
+            if not ensure_phone_subscription(self.log, ad):
+                abort_all_tests(ad.log, "Unable to find a valid subscription!")
         return True
 
-    def _preflight_fail(self, message):
-        self.log.error(
-            "Aborting all ongoing tests due to preflight check failure.")
-        abort_all(message)
-
-
-""" Tests End """
+    @TelephonyBaseTest.tel_test_wrap
+    def test_check_crash(self):
+        for ad in self.android_devices:
+            ad.crash_report_preflight = ad.check_crash_report()
+            if ad.crash_report_preflight:
+                msg = "Find crash reports %s before test starts" % (
+                    ad.crash_report_preflight)
+                ad.log.warn(msg)
+        return True
diff --git a/acts/tests/google/tel/live/TelLiveRebootStressTest.py b/acts/tests/google/tel/live/TelLiveRebootStressTest.py
index 8c7462d..48f4143 100644
--- a/acts/tests/google/tel/live/TelLiveRebootStressTest.py
+++ b/acts/tests/google/tel/live/TelLiveRebootStressTest.py
@@ -19,6 +19,7 @@
 
 import collections
 import time
+from acts.test_decorators import test_tracker_info
 from acts.controllers.sl4a_types import Sl4aNetworkInfo
 from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts.test_utils.tel.tel_data_utils import wifi_tethering_setup_teardown
@@ -81,8 +82,8 @@
             self.wifi_network_pass = None
 
         self.dut = self.android_devices[0]
-        self.ad_reference = self.android_devices[1] if len(
-            self.android_devices) > 1 else None
+        self.ad_reference = self.android_devices[
+            1] if len(self.android_devices) > 1 else None
         self.dut_model = get_model_name(self.dut)
         self.dut_operator = get_operator_name(self.log, self.dut)
 
@@ -120,6 +121,13 @@
             return False
         return True
 
+    def _get_list_average(self, input_list):
+        total_sum = float(sum(input_list))
+        total_count = float(len(input_list))
+        if input_list == []:
+            return False
+        return float(total_sum/total_count)
+
     def _check_lte_data(self):
         self.log.info("Check LTE data.")
         if not phone_setup_csfb(self.log, self.dut):
@@ -275,8 +283,8 @@
             if method in kwargs: required_methods.append(method)
 
         for i in range(1, self.stress_test_number + 1):
-            self.log.info("Reboot Stress Test {} Iteration: <{}> / <{}>".format(
-                self.test_name, i, self.stress_test_number))
+            self.log.info("Reboot Stress Test {} Iteration: <{}> / <{}>".
+                          format(self.test_name, i, self.stress_test_number))
 
             self.log.info("{} reboot!".format(self.dut.serial))
             self.dut.reboot()
@@ -288,8 +296,9 @@
                 if not test_method_mapping[check]():
                     fail_count[check] += 1
                     iteration_result = "fail"
-            self.log.info("Reboot Stress Test {} Iteration: <{}> / <{}> {}".format(
-                self.test_name, i, self.stress_test_number, iteration_result))
+            self.log.info("Reboot Stress Test {} Iteration: <{}> / <{}> {}".
+                          format(self.test_name, i, self.stress_test_number,
+                                 iteration_result))
 
             # TODO: Check if crash happens.
 
@@ -300,8 +309,99 @@
                 test_result = False
         return test_result
 
+    def _telephony_bootup_time_test(self, **kwargs):
+        """Telephony Bootup Perf Test
+
+        Arguments:
+            check_lte_data: whether to check the LTE data.
+            check_volte: whether to check Voice over LTE.
+            check_wfc: whether to check Wifi Calling.
+
+        Expected Results:
+            Time
+
+        Returns:
+            True is pass, False if fail.
+        """
+        ad = self.dut
+        toggle_airplane_mode(self.log, ad, False)
+        if not phone_setup_volte(self.log, ad):
+            ad.log.error("Failed to setup VoLTE.")
+            return False
+        fail_count = collections.defaultdict(int)
+        test_result = True
+        keyword_time_dict = {}
+
+        for i in range(1, self.stress_test_number + 1):
+            ad.log.info("Telephony Bootup Time Test %s Iteration: %d / %d",
+                self.test_name, i, self.stress_test_number)
+            ad.log.info("reboot!")
+            ad.reboot()
+            iteration_result = "pass"
+
+            time.sleep(30)
+            text_search_mapping = {
+            'boot_complete' : "processing action (sys.boot_completed=1)",
+            'Voice_Reg' : "< VOICE_REGISTRATION_STATE {.regState = REG_HOME",
+            'Data_Reg' : "< DATA_REGISTRATION_STATE {.regState = REG_HOME",
+            'Data_Call_Up' : "onSetupConnectionCompleted result=SUCCESS",
+            'VoLTE_Enabled' : "isVolteEnabled=true",
+            }
+
+            text_obj_mapping = {"boot_complete" : None,
+                                "Voice_Reg" : None,
+                                "Data_Reg" : None,
+                                "Data_Call_Up" : None,
+                                "VoLTE_Enabled" : None,}
+            blocked_for_calculate = ["boot_complete"]
+
+            for tel_state in text_search_mapping:
+                dict_match = ad.search_logcat(text_search_mapping[tel_state])
+                if len(dict_match) != 0:
+                    text_obj_mapping[tel_state] = dict_match[0]['datetime_obj']
+                else:
+                    ad.log.error("Cannot Find Text %s in logcat",
+                                   text_search_mapping[tel_state])
+                    blocked_for_calculate.append(tel_state)
+                    fail_count[tel_state] += 1
+
+            for tel_state in text_search_mapping:
+                if tel_state not in blocked_for_calculate:
+                    time_diff = text_obj_mapping[tel_state] - \
+                                text_obj_mapping['boot_complete']
+                    if time_diff.seconds > 100:
+                        continue
+                    if tel_state in keyword_time_dict:
+                        keyword_time_dict[tel_state].append(time_diff.seconds)
+                    else:
+                        keyword_time_dict[tel_state] = [time_diff.seconds,]
+
+            ad.log.info("Telephony Bootup Time Test %s Iteration: %d / %d %s",
+                self.test_name, i, self.stress_test_number, iteration_result)
+
+        for tel_state in text_search_mapping:
+            if tel_state not in blocked_for_calculate:
+                avg_time = self._get_list_average(keyword_time_dict[tel_state])
+                if avg_time < 12.0:
+                    ad.log.info("Average %s for %d iterations = %.2f seconds",
+                                tel_state, self.stress_test_number, avg_time)
+                else:
+                    ad.log.error("Average %s for %d iterations = %.2f seconds",
+                                tel_state, self.stress_test_number, avg_time)
+                    fail_count[tel_state] += 1
+
+        ad.log.info("Bootup Time Dict {}".format(keyword_time_dict))
+        for failure, count in fail_count.items():
+            if count:
+                ad.log.error("%d %d failures in %d iterations",
+                              count, failure, self.stress_test_number)
+                test_result = False
+        return test_result
+
+
     """ Tests Begin """
 
+    @test_tracker_info(uuid="4d9b425b-f804-45f4-8f47-0ba3f01a426b")
     @TelephonyBaseTest.tel_test_wrap
     def test_reboot_stress(self):
         """Reboot Reliability Test
@@ -340,6 +440,7 @@
             check_data_roaming=False,
             clear_provision=True)
 
+    @test_tracker_info(uuid="39a822e5-0360-44ce-97c7-f75468eba8d7")
     @TelephonyBaseTest.tel_test_wrap
     def test_reboot_stress_without_clear_provisioning(self):
         """Reboot Reliability Test without Clear Provisioning
@@ -377,6 +478,7 @@
             check_data_roaming=False,
             clear_provision=False)
 
+    @test_tracker_info(uuid="8b0e2c06-02bf-40fd-a374-08860e482757")
     @TelephonyBaseTest.tel_test_wrap
     def test_reboot_stress_check_phone_call_only(self):
         """Reboot Reliability Test
@@ -394,8 +496,10 @@
         Returns:
             True is pass, False if fail.
         """
-        return self._stress_test(check_provision=True, check_call_setup_teardown=True)
+        return self._stress_test(
+            check_provision=True, check_call_setup_teardown=True)
 
+    @test_tracker_info(uuid="6c243b53-379a-4cda-9848-84fcec4019bd")
     @TelephonyBaseTest.tel_test_wrap
     def test_reboot_stress_data_roaming(self):
         """Reboot Reliability Test
@@ -415,6 +519,34 @@
         """
         return self._reboot_stress_test(check_data_roaming=True)
 
+    @TelephonyBaseTest.tel_test_wrap
+    def test_bootup_optimized_stress(self):
+        """Bootup Optimized Reliability Test
+
+        Steps:
+            1. Reboot DUT.
+            2. Check Provisioning bit (if support provisioning)
+            3. Wait for DUT to camp on LTE, Verify Data.
+            4. Enable VoLTE, check IMS registration. Wait for DUT report VoLTE
+                enabled, make VoLTE call. And verify VoLTE SMS.
+                (if support VoLTE)
+            5. Connect WiFi, enable WiFi Calling, wait for DUT report WiFi
+                Calling enabled and make a WFC call and verify SMS.
+                Disconnect WiFi. (if support WFC)
+            6. Wait for DUT to camp on 3G, Verify Data.
+            7. Make CS call and verify SMS.
+            8. Verify Tethering Entitlement Check and Verify WiFi Tethering.
+            9. Check crashes.
+            10. Repeat Step 1~9 for N times. (before reboot, clear Provisioning
+                bit if provisioning is supported)
+
+        Expected Results:
+            No crash happens in stress test.
+
+        Returns:
+            True is pass, False if fail.
+        """
+        return self._telephony_bootup_time_test()
+
 
 """ Tests End """
-
diff --git a/acts/tests/google/tel/live/TelLiveSettingsTest.py b/acts/tests/google/tel/live/TelLiveSettingsTest.py
index bc4cfba..8def985 100644
--- a/acts/tests/google/tel/live/TelLiveSettingsTest.py
+++ b/acts/tests/google/tel/live/TelLiveSettingsTest.py
@@ -42,13 +42,15 @@
 from acts.test_utils.tel.tel_test_utils import wait_for_wfc_disabled
 from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled
 from acts.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts.test_utils.tel.tel_test_utils import WifiUtils
+from acts.test_utils.tel.tel_test_utils import wifi_reset
+from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
 from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
 from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
 from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
 from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
 from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
 
+
 class TelLiveSettingsTest(TelephonyBaseTest):
 
     _TEAR_DOWN_OPERATION_DISCONNECT_WIFI = "disconnect_wifi"
@@ -57,37 +59,7 @@
 
     def __init__(self, controllers):
         TelephonyBaseTest.__init__(self, controllers)
-        self.tests = (
-            "test_lte_volte_wifi_connected_toggle_wfc",
-            "test_lte_wifi_connected_toggle_wfc",
-            "test_3g_wifi_connected_toggle_wfc",
-            "test_apm_wifi_connected_toggle_wfc",
 
-            "test_lte_volte_wfc_enabled_toggle_wifi",
-            "test_lte_wfc_enabled_toggle_wifi",
-            "test_3g_wfc_enabled_toggle_wifi",
-            "test_apm_wfc_enabled_toggle_wifi",
-
-            "test_lte_wfc_enabled_wifi_connected_toggle_volte",
-
-            "test_lte_volte_wfc_wifi_preferred_to_cellular_preferred",
-            "test_lte_wfc_wifi_preferred_to_cellular_preferred",
-            "test_3g_wfc_wifi_preferred_to_cellular_preferred",
-            "test_apm_wfc_wifi_preferred_to_cellular_preferred",
-            "test_lte_volte_wfc_cellular_preferred_to_wifi_preferred",
-            "test_lte_wfc_cellular_preferred_to_wifi_preferred",
-            "test_3g_wfc_cellular_preferred_to_wifi_preferred",
-            "test_apm_wfc_cellular_preferred_to_wifi_preferred",
-
-            "test_apm_wfc_wifi_preferred_turn_off_apm",
-            "test_apm_wfc_cellular_preferred_turn_off_apm",
-
-            "test_wfc_setup_timing",
-            "test_lte_volte_wfc_enabled_toggle_wifi_stress",
-            "test_lte_volte_wfc_enabled_reset_wifi_stress",
-            "test_lte_volte_wfc_wifi_preferred_to_cellular_preferred_stress"
-
-        )
         self.ad = self.android_devices[0]
         self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
         try:
@@ -97,16 +69,19 @@
 
         self.stress_test_number = self.get_stress_test_number()
 
-    def _wifi_connected_enable_wfc_teardown_wfc(self,
-        tear_down_operation, initial_setup_wifi=True,
-        initial_setup_wfc_mode=WFC_MODE_WIFI_PREFERRED,
-        check_volte_after_wfc_disabled=False):
+    def _wifi_connected_enable_wfc_teardown_wfc(
+            self,
+            tear_down_operation,
+            initial_setup_wifi=True,
+            initial_setup_wfc_mode=WFC_MODE_WIFI_PREFERRED,
+            check_volte_after_wfc_disabled=False):
         if initial_setup_wifi and not ensure_wifi_connected(
-            self.log, self.ad, self.wifi_network_ssid, self.wifi_network_pass):
+                self.log, self.ad, self.wifi_network_ssid,
+                self.wifi_network_pass):
             self.log.error("Failed to connect WiFi")
             return False
-        if initial_setup_wfc_mode and not set_wfc_mode(
-            self.log, self.ad, initial_setup_wfc_mode):
+        if initial_setup_wfc_mode and not set_wfc_mode(self.log, self.ad,
+                                                       initial_setup_wfc_mode):
             self.log.error("Failed to set WFC mode.")
             return False
         if not phone_idle_iwlan(self.log, self.ad):
@@ -115,11 +90,11 @@
 
         # Tear Down WFC based on tear_down_operation
         if tear_down_operation == self._TEAR_DOWN_OPERATION_DISCONNECT_WIFI:
-            if not WifiUtils.wifi_toggle_state(self.log, self.ad, False):
+            if not wifi_toggle_state(self.log, self.ad, False):
                 self.log.error("Failed to turn off WiFi.")
                 return False
         elif tear_down_operation == self._TEAR_DOWN_OPERATION_RESET_WIFI:
-            if not WifiUtils.wifi_reset(self.log, self.ad, False):
+            if not wifi_reset(self.log, self.ad, False):
                 self.log.error("Failed to reset WiFi")
                 return False
         elif tear_down_operation == self._TEAR_DOWN_OPERATION_DISABLE_WFC:
@@ -130,8 +105,11 @@
             self.log.error("No tear down operation")
             return False
 
-        if not wait_for_not_network_rat(self.log, self.ad, RAT_FAMILY_WLAN,
-            voice_or_data=NETWORK_SERVICE_DATA):
+        if not wait_for_not_network_rat(
+                self.log,
+                self.ad,
+                RAT_FAMILY_WLAN,
+                voice_or_data=NETWORK_SERVICE_DATA):
             self.log.error("Data Rat is still iwlan.")
             return False
         if not wait_for_wfc_disabled(self.log, self.ad):
@@ -141,20 +119,22 @@
         # If VoLTE was previous available, after tear down WFC, DUT should have
         # VoLTE service.
         if check_volte_after_wfc_disabled and not wait_for_volte_enabled(
-            self.log, self.ad, MAX_WAIT_TIME_VOLTE_ENABLED):
+                self.log, self.ad, MAX_WAIT_TIME_VOLTE_ENABLED):
             self.log.error("Device failed to acquire VoLTE service")
             return False
         return True
 
-    def _wifi_connected_set_wfc_mode_change_wfc_mode(self,
-        initial_wfc_mode,
-        new_wfc_mode,
-        is_wfc_available_in_initial_wfc_mode,
-        is_wfc_available_in_new_wfc_mode,
-        initial_setup_wifi=True,
-        check_volte_after_wfc_disabled=False):
+    def _wifi_connected_set_wfc_mode_change_wfc_mode(
+            self,
+            initial_wfc_mode,
+            new_wfc_mode,
+            is_wfc_available_in_initial_wfc_mode,
+            is_wfc_available_in_new_wfc_mode,
+            initial_setup_wifi=True,
+            check_volte_after_wfc_disabled=False):
         if initial_setup_wifi and not ensure_wifi_connected(
-            self.log, self.ad, self.wifi_network_ssid, self.wifi_network_pass):
+                self.log, self.ad, self.wifi_network_ssid,
+                self.wifi_network_pass):
             self.log.error("Failed to connect WiFi")
             return False
         # Set to initial_wfc_mode first, then change to new_wfc_mode
@@ -162,8 +142,8 @@
             [(initial_wfc_mode, is_wfc_available_in_initial_wfc_mode),
              (new_wfc_mode, is_wfc_available_in_new_wfc_mode)]:
             current_wfc_status = is_wfc_enabled(self.log, self.ad)
-            self.log.info("Current WFC: {}, Set WFC to {}".
-                format(current_wfc_status, wfc_mode))
+            self.log.info("Current WFC: {}, Set WFC to {}".format(
+                current_wfc_status, wfc_mode))
             if not set_wfc_mode(self.log, self.ad, wfc_mode):
                 self.log.error("Failed to set WFC mode.")
                 return False
@@ -194,13 +174,13 @@
                         self.log.error("WFC is available.")
                         return False
                 if check_volte_after_wfc_disabled and not wait_for_volte_enabled(
-                    self.log, self.ad, MAX_WAIT_TIME_VOLTE_ENABLED):
+                        self.log, self.ad, MAX_WAIT_TIME_VOLTE_ENABLED):
                     self.log.error("Device failed to acquire VoLTE service")
                     return False
         return True
 
-    def _wifi_connected_set_wfc_mode_turn_off_apm(self, wfc_mode,
-        is_wfc_available_after_turn_off_apm):
+    def _wifi_connected_set_wfc_mode_turn_off_apm(
+            self, wfc_mode, is_wfc_available_after_turn_off_apm):
         if not ensure_wifi_connected(self.log, self.ad, self.wifi_network_ssid,
                                      self.wifi_network_pass):
             self.log.error("Failed to connect WiFi")
@@ -216,12 +196,12 @@
             return False
         is_wfc_not_available = wait_for_wfc_disabled(self.log, self.ad)
         if is_wfc_available_after_turn_off_apm and is_wfc_not_available:
-                self.log.error("WFC is not available.")
-                return False
+            self.log.error("WFC is not available.")
+            return False
         elif (not is_wfc_available_after_turn_off_apm and
               not is_wfc_not_available):
-                self.log.error("WFC is available.")
-                return False
+            self.log.error("WFC is available.")
+            return False
         return True
 
     @TelephonyBaseTest.tel_test_wrap
@@ -450,10 +430,9 @@
         if not phone_setup_volte(self.log, self.ad):
             self.log.error("Failed to setup VoLTE.")
             return False
-        if not phone_setup_iwlan(self.log, self.ad, False,
-                                 WFC_MODE_WIFI_PREFERRED,
-                                 self.wifi_network_ssid,
-                                 self.wifi_network_pass):
+        if not phone_setup_iwlan(
+                self.log, self.ad, False, WFC_MODE_WIFI_PREFERRED,
+                self.wifi_network_ssid, self.wifi_network_pass):
             self.log.error("Failed to setup WFC.")
             return False
         # Turn Off VoLTE, then Turn On VoLTE
@@ -491,8 +470,10 @@
             self.log.error("Failed to setup VoLTE.")
             return False
         return self._wifi_connected_set_wfc_mode_change_wfc_mode(
-            WFC_MODE_WIFI_PREFERRED, WFC_MODE_CELLULAR_PREFERRED,
-            True, False,
+            WFC_MODE_WIFI_PREFERRED,
+            WFC_MODE_CELLULAR_PREFERRED,
+            True,
+            False,
             check_volte_after_wfc_disabled=True)
 
     @TelephonyBaseTest.tel_test_wrap
@@ -515,8 +496,10 @@
             self.log.error("Failed to setup LTE.")
             return False
         return self._wifi_connected_set_wfc_mode_change_wfc_mode(
-            WFC_MODE_WIFI_PREFERRED, WFC_MODE_CELLULAR_PREFERRED,
-            True, False,
+            WFC_MODE_WIFI_PREFERRED,
+            WFC_MODE_CELLULAR_PREFERRED,
+            True,
+            False,
             check_volte_after_wfc_disabled=False)
 
     @TelephonyBaseTest.tel_test_wrap
@@ -539,8 +522,7 @@
             self.log.error("Failed to setup 3G.")
             return False
         return self._wifi_connected_set_wfc_mode_change_wfc_mode(
-            WFC_MODE_WIFI_PREFERRED, WFC_MODE_CELLULAR_PREFERRED,
-            True, False)
+            WFC_MODE_WIFI_PREFERRED, WFC_MODE_CELLULAR_PREFERRED, True, False)
 
     @TelephonyBaseTest.tel_test_wrap
     def test_3g_wfc_wifi_preferred_to_cellular_preferred(self):
@@ -562,8 +544,7 @@
             self.log.error("Failed to setup 3G.")
             return False
         return self._wifi_connected_set_wfc_mode_change_wfc_mode(
-            WFC_MODE_WIFI_PREFERRED, WFC_MODE_CELLULAR_PREFERRED,
-            True, False)
+            WFC_MODE_WIFI_PREFERRED, WFC_MODE_CELLULAR_PREFERRED, True, False)
 
     @TelephonyBaseTest.tel_test_wrap
     def test_apm_wfc_wifi_preferred_to_cellular_preferred(self):
@@ -585,8 +566,7 @@
             self.log.error("Failed to turn on airplane mode")
             return False
         return self._wifi_connected_set_wfc_mode_change_wfc_mode(
-            WFC_MODE_WIFI_PREFERRED, WFC_MODE_CELLULAR_PREFERRED,
-            True, True)
+            WFC_MODE_WIFI_PREFERRED, WFC_MODE_CELLULAR_PREFERRED, True, True)
 
     @TelephonyBaseTest.tel_test_wrap
     def test_lte_volte_wfc_cellular_preferred_to_wifi_preferred(self):
@@ -609,8 +589,10 @@
             self.log.error("Failed to setup VoLTE.")
             return False
         return self._wifi_connected_set_wfc_mode_change_wfc_mode(
-            WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_WIFI_PREFERRED,
-            False, True,
+            WFC_MODE_CELLULAR_PREFERRED,
+            WFC_MODE_WIFI_PREFERRED,
+            False,
+            True,
             check_volte_after_wfc_disabled=True)
 
     @TelephonyBaseTest.tel_test_wrap
@@ -633,8 +615,10 @@
             self.log.error("Failed to setup LTE.")
             return False
         return self._wifi_connected_set_wfc_mode_change_wfc_mode(
-            WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_WIFI_PREFERRED,
-            False, True,
+            WFC_MODE_CELLULAR_PREFERRED,
+            WFC_MODE_WIFI_PREFERRED,
+            False,
+            True,
             check_volte_after_wfc_disabled=False)
 
     @TelephonyBaseTest.tel_test_wrap
@@ -657,8 +641,7 @@
             self.log.error("Failed to setup 3G.")
             return False
         return self._wifi_connected_set_wfc_mode_change_wfc_mode(
-            WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_WIFI_PREFERRED,
-            False, True)
+            WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_WIFI_PREFERRED, False, True)
 
     @TelephonyBaseTest.tel_test_wrap
     def test_apm_wfc_cellular_preferred_to_wifi_preferred(self):
@@ -680,8 +663,7 @@
             self.log.error("Failed to turn on airplane mode")
             return False
         return self._wifi_connected_set_wfc_mode_change_wfc_mode(
-            WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_WIFI_PREFERRED,
-            True, True)
+            WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_WIFI_PREFERRED, True, True)
 
     @TelephonyBaseTest.tel_test_wrap
     def test_apm_wfc_wifi_preferred_turn_off_apm(self):
@@ -760,7 +742,7 @@
             'mo_call_success': 0
         }
 
-        WifiUtils.wifi_reset(self.log, ad)
+        wifi_reset(self.log, ad)
         toggle_airplane_mode(self.log, ad, True)
 
         set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED)
@@ -769,14 +751,13 @@
 
         self.log.info("Start Time {}s".format(time_values['start']))
 
-        WifiUtils.wifi_toggle_state(self.log, ad, True)
+        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)
-
+        ensure_wifi_connected(self.log, ad, self.wifi_network_ssid,
+                              self.wifi_network_pass)
         ad.droid.wakeUpNow()
 
         if not wait_for_wifi_data_connection(self.log, ad, True,
@@ -797,10 +778,9 @@
         self.log.info("WifiData After {}s".format(time_values[
             'wifi_data'] - time_values['wifi_connected']))
 
-        if not wait_for_network_rat(self.log,
-                                    ad,
-                                    RAT_FAMILY_WLAN,
-                                    voice_or_data=NETWORK_SERVICE_DATA):
+        if not wait_for_network_rat(
+                self.log, ad, RAT_FAMILY_WLAN,
+                voice_or_data=NETWORK_SERVICE_DATA):
             self.log.error("Failed to set-up iwlan, aborting!")
             if is_droid_in_rat_family(self.log, ad, RAT_FAMILY_WLAN,
                                       NETWORK_SERVICE_DATA):
@@ -829,10 +809,8 @@
 
         set_wfc_mode(self.log, ad, WFC_MODE_DISABLED)
 
-        wait_for_not_network_rat(self.log,
-                                 ad,
-                                 RAT_FAMILY_WLAN,
-                                 voice_or_data=NETWORK_SERVICE_DATA)
+        wait_for_not_network_rat(
+            self.log, ad, RAT_FAMILY_WLAN, voice_or_data=NETWORK_SERVICE_DATA)
 
         self.log.info("\n\n------------------summary-----------------")
         self.log.info("WiFi Enabled After {0:.2f} s".format(time_values[
@@ -945,16 +923,18 @@
         if not phone_setup_volte(self.log, self.ad):
             self.log.error("Failed to setup VoLTE.")
             return False
-        if not ensure_wifi_connected(
-            self.log, self.ad, self.wifi_network_ssid, self.wifi_network_pass):
+        if not ensure_wifi_connected(self.log, self.ad, self.wifi_network_ssid,
+                                     self.wifi_network_pass):
             self.log.error("Failed to connect WiFi")
             return False
 
         for i in range(1, self.stress_test_number + 1):
             self.log.info("Start Iteration {}.".format(i))
             result = self._wifi_connected_set_wfc_mode_change_wfc_mode(
-                WFC_MODE_WIFI_PREFERRED, WFC_MODE_CELLULAR_PREFERRED,
-                True, False,
+                WFC_MODE_WIFI_PREFERRED,
+                WFC_MODE_CELLULAR_PREFERRED,
+                True,
+                False,
                 initial_setup_wifi=False,
                 check_volte_after_wfc_disabled=True)
             if not result:
diff --git a/acts/tests/google/tel/live/TelLiveSmokeTest.py b/acts/tests/google/tel/live/TelLiveSmokeTest.py
index ac7e88d..b36105a 100644
--- a/acts/tests/google/tel/live/TelLiveSmokeTest.py
+++ b/acts/tests/google/tel/live/TelLiveSmokeTest.py
@@ -30,7 +30,7 @@
 from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
 from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
 from acts.test_utils.tel.tel_lookup_tables import is_rat_svd_capable
-from acts.test_utils.tel.tel_test_utils import WifiUtils
+from acts.test_utils.tel.tel_test_utils import stop_wifi_tethering
 from acts.test_utils.tel.tel_test_utils import call_setup_teardown
 from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
 from acts.test_utils.tel.tel_test_utils import get_network_rat
@@ -39,6 +39,7 @@
 from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
 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 WIFI_CONFIG_APBAND_2G
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
@@ -51,16 +52,10 @@
 
 SKIP = 'Skip'
 
+
 class TelLiveSmokeTest(TelephonyBaseTest):
     def __init__(self, controllers):
         TelephonyBaseTest.__init__(self, controllers)
-        self.tests = (
-            "test_smoke_volte_call_data_sms",
-            "test_smoke_csfb_3g_call_data_sms",
-            "test_smoke_3g_call_data_sms",
-            "test_smoke_wfc_call_sms",
-            "test_smoke_data_airplane_mode_network_switch_tethering"
-            )
 
         self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
         try:
@@ -69,6 +64,7 @@
             self.wifi_network_pass = None
 
     """ Tests Begin """
+
     @TelephonyBaseTest.tel_test_wrap
     def test_smoke_volte_call_data_sms(self):
         try:
@@ -79,7 +75,8 @@
             data_incall_result = False
             call_result = False
 
-            self.log.info("--------start test_smoke_volte_call_data_sms--------")
+            self.log.info(
+                "--------start test_smoke_volte_call_data_sms--------")
             ensure_phones_default_state(self.log, ads)
             tasks = [(phone_setup_volte, (self.log, ads[0])),
                      (phone_setup_volte, (self.log, ads[1]))]
@@ -113,8 +110,8 @@
                 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)])
+            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
@@ -135,9 +132,8 @@
                 "Data idle: {}, SMS in call: {}, Data in call: {}, "
                 "Voice Call: {}".format(
                     getattr(self, Config.ikey_testbed_name.value),
-                    sms_idle_result, data_idle_result,
-                    sms_incall_result, data_incall_result,
-                    call_result))
+                    sms_idle_result, data_idle_result, sms_incall_result,
+                    data_incall_result, call_result))
 
     @TelephonyBaseTest.tel_test_wrap
     def test_smoke_csfb_3g_call_data_sms(self):
@@ -149,7 +145,8 @@
             data_incall_result = False
             call_result = False
 
-            self.log.info("--------start test_smoke_csfb_3g_call_data_sms--------")
+            self.log.info(
+                "--------start test_smoke_csfb_3g_call_data_sms--------")
             ensure_phones_default_state(self.log, ads)
             tasks = [(phone_setup_csfb, (self.log, ads[0])),
                      (phone_setup_csfb, (self.log, ads[1]))]
@@ -171,22 +168,23 @@
                 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):
+            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)])
+            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 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
@@ -210,9 +208,8 @@
                 "Data idle: {}, SMS in call: {}, Data in call: {}, "
                 "Voice Call: {}".format(
                     getattr(self, Config.ikey_testbed_name.value),
-                    sms_idle_result, data_idle_result,
-                    sms_incall_result, data_incall_result,
-                    call_result))
+                    sms_idle_result, data_idle_result, sms_incall_result,
+                    data_incall_result, call_result))
 
     @TelephonyBaseTest.tel_test_wrap
     def test_smoke_3g_call_data_sms(self):
@@ -241,22 +238,23 @@
                 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):
+            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)])
+            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 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
@@ -280,9 +278,8 @@
                 "Data idle: {}, SMS in call: {}, Data in call: {}, "
                 "Voice Call: {}".format(
                     getattr(self, Config.ikey_testbed_name.value),
-                    sms_idle_result, data_idle_result,
-                    sms_incall_result, data_incall_result,
-                    call_result))
+                    sms_idle_result, data_idle_result, sms_incall_result,
+                    data_incall_result, call_result))
 
     @TelephonyBaseTest.tel_test_wrap
     def test_smoke_wfc_call_sms(self):
@@ -314,12 +311,13 @@
                 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):
+            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
@@ -341,8 +339,7 @@
                 "Summary for test run. Testbed:<{}>. <WFC> SMS idle: {}, "
                 "SMS in call: {}, Voice Call: {}".format(
                     getattr(self, Config.ikey_testbed_name.value),
-                    sms_idle_result, sms_incall_result,
-                    call_result))
+                    sms_idle_result, sms_incall_result, call_result))
 
     @TelephonyBaseTest.tel_test_wrap
     def test_smoke_data_airplane_mode_network_switch_tethering(self):
@@ -353,24 +350,24 @@
             tethering_result = False
 
             self.log.info("--------start test_smoke_data_airplane_mode_network"
-                "_switch_tethering--------")
+                          "_switch_tethering--------")
             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, GEN_4G)
+            nw_switch_result = wifi_cell_switching(
+                self.log, ads[0], self.wifi_network_ssid,
+                self.wifi_network_pass, GEN_4G)
             if ads[0].droid.carrierConfigIsTetheringModeAllowed(
-                    TETHERING_MODE_WIFI, MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK):
+                    TETHERING_MODE_WIFI,
+                    MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK):
                 self.log.info("3. Verify WiFi Tethering.")
                 if ads[0].droid.wifiIsApEnabled():
-                    WifiUtils.stop_wifi_tethering(self.log, ads[0])
+                    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,
+                    ads[0], [ads[1]],
+                    ap_band=WIFI_CONFIG_APBAND_2G,
                     check_interval=10,
                     check_iteration=4)
                 # check_interval=10, check_iteration=4: in this Smoke test,
@@ -382,13 +379,13 @@
                               "Tethering not allowed on SIM.")
                 tethering_result = SKIP
 
-            return (apm_result and nw_switch_result and
-                    ((tethering_result is True) or (tethering_result == SKIP)))
+            return (apm_result and nw_switch_result and (
+                (tethering_result is True) or (tethering_result == SKIP)))
         finally:
             self.log.info(
                 "Summary for test run. Testbed:<{}>. <Data> Airplane Mode: {}, "
                 "WiFi-Cell Network Switch: {}, Tethering: {}".format(
-                    getattr(self, Config.ikey_testbed_name.value),
-                    apm_result, nw_switch_result, tethering_result))
+                    getattr(self, Config.ikey_testbed_name.value), apm_result,
+                    nw_switch_result, tethering_result))
 
     """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveSmsTest.py b/acts/tests/google/tel/live/TelLiveSmsTest.py
index db94173..f3d6ea2 100644
--- a/acts/tests/google/tel/live/TelLiveSmsTest.py
+++ b/acts/tests/google/tel/live/TelLiveSmsTest.py
@@ -19,6 +19,7 @@
 
 import time
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts.test_utils.tel.tel_defines import GEN_3G
 from acts.test_utils.tel.tel_defines import GEN_4G
@@ -32,6 +33,7 @@
 from acts.test_utils.tel.tel_test_utils import \
     ensure_network_generation_for_subscription
 from acts.test_utils.tel.tel_test_utils import ensure_network_generation
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
 from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
 from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
 from acts.test_utils.tel.tel_test_utils import mms_receive_verify_after_call_hangup
@@ -58,6 +60,9 @@
 from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
 from acts.utils import rand_ascii_str
 
+SMS_OVER_WIFI_PROVIDERS = ("vzw", "tmo", "fi", "rogers", "rjio", "eeuk",
+                           "dtag")
+
 
 class TelLiveSmsTest(TelephonyBaseTest):
     def __init__(self, controllers):
@@ -66,17 +71,39 @@
         # The path for "sim config file" should be set
         # in "testbed.config" entry "sim_conf_file".
         self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+        self.wifi_network_pass = self.user_params.get("wifi_network_pass")
+        # Try to put SMS and call on different help device
+        # If it is a three phone test bed, use the first one as dut,
+        # use the second one as sms/mms help device, use the third one
+        # as the active call help device.
+        self.caller = self.android_devices[0]
+        if len(self.android_devices) > 2:
+            self.callee = self.android_devices[2]
+        else:
+            self.callee = self.android_devices[1]
+        self.message_lengths = (50, 160, 180)
 
-        try:
-            self.wifi_network_pass = self.user_params["wifi_network_pass"]
-        except KeyError:
-            self.wifi_network_pass = None
-
+    def setup_class(self):
+        TelephonyBaseTest.setup_class(self)
+        is_roaming = False
         for ad in self.android_devices:
+            ad.sms_over_wifi = False
+            #verizon supports sms over wifi. will add more carriers later
+            for sub in ad.cfg["subscription"].values():
+                if sub["operator"] in SMS_OVER_WIFI_PROVIDERS:
+                    ad.sms_over_wifi = True
             ad.adb.shell("su root setenforce 0")
             #not needed for now. might need for image attachment later
             #ad.adb.shell("pm grant com.google.android.apps.messaging "
             #             "android.permission.READ_EXTERNAL_STORAGE")
+            if getattr(ad, 'roaming', False):
+                is_roaming = True
+        if is_roaming:
+            # roaming device does not allow message of length 180
+            self.message_lengths = (50, 160)
+
+    def teardown_test(self):
+        ensure_phones_idle(self.log, self.android_devices)
 
     def _sms_test(self, ads):
         """Test SMS between two phones.
@@ -85,18 +112,16 @@
             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
-
+        for length in self.message_lengths:
+            message_array = [rand_ascii_str(length)]
+            if not sms_send_receive_verify(self.log, ads[0], ads[1],
+                                           message_array):
+                ads[0].log.warning("SMS of length %s test failed", length)
+                return False
+            else:
+                ads[0].log.info("SMS of length %s test succeeded", length)
+        self.log.info("SMS test of length %s characters succeeded.",
+                      self.message_lengths)
         return True
 
     def _mms_test(self, ads):
@@ -106,9 +131,17 @@
             True if success.
             False if failed.
         """
-        return mms_send_receive_verify(
-            self.log, ads[0], ads[1],
-            [("Test Message", "Basic Message Body", None)])
+        for length in self.message_lengths:
+            message_array = [("Test Message", rand_ascii_str(length), None)]
+            if not mms_send_receive_verify(self.log, ads[0], ads[1],
+                                           message_array):
+                self.log.warning("MMS of body length %s test failed", length)
+                return False
+            else:
+                self.log.info("MMS of body length %s test succeeded", length)
+        self.log.info("MMS test of body lengths %s succeeded",
+                      self.message_lengths)
+        return True
 
     def _mms_test_after_call_hangup(self, ads):
         """Test MMS send out after call hang up.
@@ -117,18 +150,21 @@
             True if success.
             False if failed.
         """
-        args = [self.log, ads[0], ads[1],
-                [("Test Message", "Basic Message Body", None)]]
+        args = [
+            self.log, ads[0], ads[1], [("Test Message", "Basic Message Body",
+                                        None)]
+        ]
         if not mms_send_receive_verify(*args):
             self.log.info("MMS send in call is suspended.")
             if not mms_receive_verify_after_call_hangup(*args):
-                self.log.error("MMS is not send out after call release.")
+                self.log.error(
+                    "MMS is not send and received after call release.")
                 return False
             else:
-                self.log.info("MMS is send out after call release.")
+                self.log.info("MMS is send and received after call release.")
                 return True
         else:
-            self.log.info("MMS is send out successfully in call.")
+            self.log.info("MMS is send and received successfully in call.")
             return True
 
     def _sms_test_mo(self, ads):
@@ -153,8 +189,8 @@
         self.log.info("Begin In Call SMS Test.")
         if not call_setup_teardown(
                 self.log,
-                ads[0],
-                ads[1],
+                self.caller,
+                self.callee,
                 ad_hangup=None,
                 verify_caller_func=is_phone_in_call_3g,
                 verify_callee_func=None):
@@ -170,8 +206,8 @@
         self.log.info("Begin In Call SMS Test.")
         if not call_setup_teardown(
                 self.log,
-                ads[0],
-                ads[1],
+                self.caller,
+                self.callee,
                 ad_hangup=None,
                 verify_caller_func=is_phone_in_call_3g,
                 verify_callee_func=None):
@@ -183,46 +219,44 @@
 
         return True
 
-    def _mo_mms_in_3g_call(self, ads):
+    def _mo_mms_in_3g_call(self, ads, wifi=False):
         self.log.info("Begin In Call MMS Test.")
         if not call_setup_teardown(
                 self.log,
-                ads[0],
-                ads[1],
+                self.caller,
+                self.callee,
                 ad_hangup=None,
                 verify_caller_func=is_phone_in_call_3g,
                 verify_callee_func=None):
             return False
 
-        if not self._mms_test_mo(ads):
-            self.log.error("MMS test fail.")
-            return False
+        if ads[0].sms_over_wifi and wifi:
+            return self._mms_test_mo(ads)
+        else:
+            return self._mms_test_mo_after_call_hangup(ads)
 
-        return True
-
-    def _mt_mms_in_3g_call(self, ads):
+    def _mt_mms_in_3g_call(self, ads, wifi=False):
         self.log.info("Begin In Call MMS Test.")
         if not call_setup_teardown(
                 self.log,
-                ads[0],
-                ads[1],
+                self.caller,
+                self.callee,
                 ad_hangup=None,
                 verify_caller_func=is_phone_in_call_3g,
                 verify_callee_func=None):
             return False
 
-        if not self._mms_test_mt(ads):
-            self.log.error("MMS test fail.")
-            return False
-
-        return True
+        if ads[0].sms_over_wifi and wifi:
+            return self._mms_test_mt(ads)
+        else:
+            return self._mms_test_mt_after_call_hangup(ads)
 
     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],
+                self.caller,
+                self.callee,
                 ad_hangup=None,
                 verify_caller_func=is_phone_in_call_2g,
                 verify_callee_func=None):
@@ -238,8 +272,8 @@
         self.log.info("Begin In Call SMS Test.")
         if not call_setup_teardown(
                 self.log,
-                ads[0],
-                ads[1],
+                self.caller,
+                self.callee,
                 ad_hangup=None,
                 verify_caller_func=is_phone_in_call_2g,
                 verify_callee_func=None):
@@ -251,40 +285,171 @@
 
         return True
 
-    def _mo_mms_in_2g_call(self, ads):
+    def _mo_mms_in_2g_call(self, ads, wifi=False):
         self.log.info("Begin In Call MMS Test.")
         if not call_setup_teardown(
                 self.log,
-                ads[0],
-                ads[1],
+                self.caller,
+                self.callee,
                 ad_hangup=None,
                 verify_caller_func=is_phone_in_call_2g,
                 verify_callee_func=None):
             return False
 
-        if not self._mms_test_mo(ads):
-            self.log.error("MMS test fail.")
-            return False
+        if ads[0].sms_over_wifi and wifi:
+            return self._mms_test_mo(ads)
+        else:
+            return self._mms_test_mo_after_call_hangup(ads)
 
-        return True
-
-    def _mt_mms_in_2g_call(self, ads):
+    def _mt_mms_in_2g_call(self, ads, wifi=False):
         self.log.info("Begin In Call MMS Test.")
         if not call_setup_teardown(
                 self.log,
-                ads[0],
-                ads[1],
+                self.caller,
+                self.callee,
                 ad_hangup=None,
                 verify_caller_func=is_phone_in_call_2g,
                 verify_callee_func=None):
             return False
 
-        if not self._mms_test_mt(ads):
-            self.log.error("MMS test fail.")
+        if ads[0].sms_over_wifi and wifi:
+            return self._mms_test_mt(ads)
+        else:
+            return self._mms_test_mt_after_call_hangup(ads)
+
+    def _mo_sms_in_1x_call(self, ads):
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(
+                self.log,
+                self.caller,
+                self.callee,
+                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
 
+    def _mt_sms_in_1x_call(self, ads):
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(
+                self.log,
+                self.caller,
+                self.callee,
+                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
+
+    def _mo_mms_in_1x_call(self, ads, wifi=False):
+        self.log.info("Begin In Call MMS Test.")
+        if not call_setup_teardown(
+                self.log,
+                self.caller,
+                self.callee,
+                ad_hangup=None,
+                verify_caller_func=is_phone_in_call_1x,
+                verify_callee_func=None):
+            return False
+
+        if ads[0].sms_over_wifi and wifi:
+            return self._mms_test_mo(ads)
+        else:
+            return self._mms_test_mo_after_call_hangup(ads)
+
+    def _mt_mms_in_1x_call(self, ads, wifi=False):
+        self.log.info("Begin In Call MMS Test.")
+        if not call_setup_teardown(
+                self.log,
+                self.caller,
+                self.callee,
+                ad_hangup=None,
+                verify_caller_func=is_phone_in_call_1x,
+                verify_callee_func=None):
+            return False
+
+        if ads[0].sms_over_wifi and wifi:
+            return self._mms_test_mt(ads)
+        else:
+            return self._mms_test_mt_after_call_hangup(ads)
+
+    def _mo_sms_in_csfb_call(self, ads):
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(
+                self.log,
+                self.caller,
+                self.callee,
+                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
+
+    def _mt_sms_in_csfb_call(self, ads):
+        self.log.info("Begin In Call SMS Test.")
+        if not call_setup_teardown(
+                self.log,
+                self.caller,
+                self.callee,
+                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
+
+    def _mo_mms_in_csfb_call(self, ads, wifi=False):
+        self.log.info("Begin In Call MMS Test.")
+        if not call_setup_teardown(
+                self.log,
+                self.caller,
+                self.callee,
+                ad_hangup=None,
+                verify_caller_func=is_phone_in_call_csfb,
+                verify_callee_func=None):
+            return False
+
+        if ads[0].sms_over_wifi and wifi:
+            return self._mms_test_mo(ads)
+        else:
+            return self._mms_test_mo_after_call_hangup(ads)
+
+    def _mt_mms_in_csfb_call(self, ads, wifi=False):
+        self.log.info("Begin In Call MMS Test.")
+        if not call_setup_teardown(
+                self.log,
+                self.caller,
+                self.callee,
+                ad_hangup=None,
+                verify_caller_func=is_phone_in_call_csfb,
+                verify_callee_func=None):
+            return False
+
+        if ads[0].sms_over_wifi and wifi:
+            return self._mms_test_mt(ads)
+        else:
+            return self._mms_test_mt_after_call_hangup(ads)
+
+    @test_tracker_info(uuid="480b6ba2-1e5f-4a58-9d88-9b75c8fab1b6")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_general(self):
         """Test SMS basic function between two phone. Phones in any network.
@@ -307,6 +472,7 @@
 
         return self._sms_test_mo(ads)
 
+    @test_tracker_info(uuid="aa87fe73-8236-44c7-865c-3fe3b733eeb4")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_general(self):
         """Test SMS basic function between two phone. Phones in any network.
@@ -329,6 +495,7 @@
 
         return self._sms_test_mt(ads)
 
+    @test_tracker_info(uuid="57db830c-71eb-46b3-adaa-915c641de18d")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_general(self):
         """Test MMS basic function between two phone. Phones in any network.
@@ -351,6 +518,7 @@
 
         return self._mms_test_mo(ads)
 
+    @test_tracker_info(uuid="f2779e1e-7d09-43f0-8b5c-87eae5d146be")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_general(self):
         """Test MMS basic function between two phone. Phones in any network.
@@ -373,6 +541,7 @@
 
         return self._mms_test_mt(ads)
 
+    @test_tracker_info(uuid="2c229a4b-c954-4ba3-94ba-178dc7784d03")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_2g(self):
         """Test SMS basic function between two phone. Phones in 3g network.
@@ -395,6 +564,7 @@
 
         return self._sms_test_mo(ads)
 
+    @test_tracker_info(uuid="17fafc41-7e12-47ab-a4cc-fb9bd94e79b9")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_2g(self):
         """Test SMS basic function between two phone. Phones in 3g network.
@@ -417,6 +587,7 @@
 
         return self._sms_test_mt(ads)
 
+    @test_tracker_info(uuid="b4919317-18b5-483c-82f4-ced37a04f28d")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_2g(self):
         """Test MMS basic function between two phone. Phones in 3g network.
@@ -439,6 +610,7 @@
 
         return self._mms_test_mo(ads)
 
+    @test_tracker_info(uuid="cd56bb8a-0794-404d-95bd-c5fd00f4b35a")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_2g(self):
         """Test MMS basic function between two phone. Phones in 3g network.
@@ -461,6 +633,59 @@
 
         return self._mms_test_mt(ads)
 
+    @test_tracker_info(uuid="b39fbc30-9cc2-4d86-a9f4-6f0c1dd0a905")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_2g_wifi(self):
+        """Test MMS basic function between two phone. Phones in 3g network.
+
+        Airplane mode is off. Phone in 2G.
+        Connect to Wifi.
+        Send MMS 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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mms_test_mo(ads)
+
+    @test_tracker_info(uuid="b158a0a7-9697-4b3b-8d5b-f9b6b6bc1c03")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_2g_wifi(self):
+        """Test MMS basic function between two phone. Phones in 3g network.
+
+        Airplane mode is off. Phone in 2G.
+        Connect to Wifi.
+        Send MMS 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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mms_test_mt(ads)
+
+    @test_tracker_info(uuid="f094e3da-2523-4f92-a1f3-7cf9edcff850")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_3g(self):
         """Test SMS basic function between two phone. Phones in 3g network.
@@ -484,6 +709,7 @@
 
         return self._sms_test_mo(ads)
 
+    @test_tracker_info(uuid="2186e152-bf83-4d6e-93eb-b4bf9ae2d76e")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_3g(self):
         """Test SMS basic function between two phone. Phones in 3g network.
@@ -507,11 +733,12 @@
 
         return self._sms_test_mt(ads)
 
+    @test_tracker_info(uuid="e716c678-eee9-4a0d-a9cd-ca9eae4fea51")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_3g(self):
         """Test MMS basic function between two phone. Phones in 3g network.
 
-        Airplane mode is off.
+        Airplane mode is off. Phone in 3G.
         Send MMS from PhoneA to PhoneB.
         Verify received message on PhoneB is correct.
 
@@ -530,11 +757,12 @@
 
         return self._mms_test_mo(ads)
 
+    @test_tracker_info(uuid="e864a99e-d935-4bd9-95f6-8183cdd3d760")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_3g(self):
         """Test MMS basic function between two phone. Phones in 3g network.
 
-        Airplane mode is off.
+        Airplane mode is off. Phone in 3G.
         Send MMS from PhoneB to PhoneA.
         Verify received message on PhoneA is correct.
 
@@ -553,6 +781,61 @@
 
         return self._mms_test_mt(ads)
 
+    @test_tracker_info(uuid="c6cfba55-6cde-41cd-93bb-667c317a0127")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_3g_wifi(self):
+        """Test MMS basic function between two phone. Phones in 3g network.
+
+        Airplane mode is off. Phone in 3G.
+        Connect to Wifi.
+        Send MMS 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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mms_test_mo(ads)
+
+    @test_tracker_info(uuid="83c5dd99-f2fe-433d-9775-80a36d0d493b")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_3g_wifi(self):
+        """Test MMS basic function between two phone. Phones in 3g network.
+
+        Airplane mode is off. Phone in 3G.
+        Connect to Wifi.
+        Send MMS 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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mms_test_mt(ads)
+
+    @test_tracker_info(uuid="c97687e2-155a-4cf3-9f51-22543b89d53e")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_4g(self):
         """Test SMS basic function between two phone. Phones in LTE network.
@@ -577,6 +860,7 @@
 
         return self._sms_test_mo(ads)
 
+    @test_tracker_info(uuid="e2e01a47-2b51-4d00-a7b2-dbd3c8ffa6ae")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_4g(self):
         """Test SMS basic function between two phone. Phones in LTE network.
@@ -602,6 +886,7 @@
 
         return self._sms_test_mt(ads)
 
+    @test_tracker_info(uuid="eae3c1cb-dc9d-4682-9ab5-ef39d207766d")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_4g(self):
         """Test MMS text function between two phone. Phones in LTE network.
@@ -615,9 +900,6 @@
             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])),
@@ -628,11 +910,12 @@
 
         return self._mms_test_mo(ads)
 
+    @test_tracker_info(uuid="2f4b3f56-6995-4d11-9a03-67c18a126c4e")
     @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.
+        Airplane mode is off. Phone in 4G.
         Send MMS from PhoneB to PhoneA.
         Verify received message on PhoneA is correct.
 
@@ -651,6 +934,60 @@
 
         return self._mms_test_mt(ads)
 
+    @test_tracker_info(uuid="c7349fdf-a376-4846-b466-1f329bd1557f")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_4g_wifi(self):
+        """Test MMS text function between two phone. Phones in LTE network.
+
+        Airplane mode is off. Phone in 4G.
+        Connect to Wifi.
+        Send MMS 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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+        return self._mms_test_mo(ads)
+
+    @test_tracker_info(uuid="1affab34-e03c-49dd-9062-e9ed8eac406b")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_4g_wifi(self):
+        """Test MMS text function between two phone. Phones in LTE network.
+
+        Airplane mode is off. Phone in 4G.
+        Connect to Wifi.
+        Send MMS 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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mms_test_mt(ads)
+
+    @test_tracker_info(uuid="7ee57edb-2962-4d20-b6eb-79cebce91fff")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_in_call_volte(self):
         """ Test MO SMS during a MO VoLTE call.
@@ -686,6 +1023,7 @@
 
         return True
 
+    @test_tracker_info(uuid="5576276b-4ca1-41cc-bb74-31ccd71f9f96")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_in_call_volte(self):
         """ Test MT SMS during a MO VoLTE call.
@@ -721,6 +1059,7 @@
 
         return True
 
+    @test_tracker_info(uuid="3bf8ff74-baa6-4dc6-86eb-c13816fa9bc8")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_in_call_volte(self):
         """ Test MO MMS during a MO VoLTE call.
@@ -756,6 +1095,7 @@
 
         return True
 
+    @test_tracker_info(uuid="289e6516-5f66-403a-b292-50d067151730")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_in_call_volte(self):
         """ Test MT MMS during a MO VoLTE call.
@@ -791,6 +1131,85 @@
 
         return True
 
+    @test_tracker_info(uuid="5654d974-3c32-4cce-9d07-0c96213dacc5")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_in_call_volte_wifi(self):
+        """ Test MO MMS during a MO VoLTE call.
+
+        Make sure PhoneA is in LTE mode (with VoLTE).
+        Make sure PhoneB is able to make/receive call.
+        Connect PhoneA to Wifi.
+        Call from PhoneA to PhoneB, accept on PhoneB, send MMS 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
+
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+        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._mms_test_mo(ads):
+            self.log.error("MMS test fail.")
+            return False
+
+        return True
+
+    @test_tracker_info(uuid="cbd5ab3d-d76a-4ece-ac09-62efeead7550")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_in_call_volte_wifi(self):
+        """ Test MT MMS during a MO VoLTE call.
+
+        Make sure PhoneA is in LTE mode (with VoLTE).
+        Make sure PhoneB is able to make/receive call.
+        Connect PhoneA to Wifi.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive MMS 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
+
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+        self.log.info("Begin In Call MMS 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._mms_test_mt(ads):
+            self.log.error("MMS test fail.")
+            return False
+
+        return True
+
+    @test_tracker_info(uuid="516457ae-5f99-41c1-b145-bfe72876b872")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_in_call_wcdma(self):
         """ Test MO SMS during a MO wcdma call.
@@ -816,6 +1235,7 @@
 
         return self._mo_sms_in_3g_call(ads)
 
+    @test_tracker_info(uuid="d99697f4-5be2-46f2-9d95-aa73b5d9cebc")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_in_call_wcdma(self):
         """ Test MT SMS during a MO wcdma call.
@@ -841,6 +1261,7 @@
 
         return self._mt_sms_in_3g_call(ads)
 
+    @test_tracker_info(uuid="2a2d64cc-88db-4ec0-9c2d-1da24a0f9eaf")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_in_call_wcdma(self):
         """ Test MO MMS during a MO wcdma call.
@@ -866,6 +1287,7 @@
 
         return self._mo_mms_in_3g_call(ads)
 
+    @test_tracker_info(uuid="20df9556-a8af-4346-97b8-b97596d146a4")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_in_call_wcdma(self):
         """ Test MT MMS during a MO wcdma call.
@@ -891,6 +1313,65 @@
 
         return self._mt_mms_in_3g_call(ads)
 
+    @test_tracker_info(uuid="c4a39519-44d8-4194-8dfc-68b1dd723b39")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_in_call_wcdma_wifi(self):
+        """ Test MO MMS during a MO wcdma call.
+
+        Make sure PhoneA is in wcdma mode.
+        Make sure PhoneB is able to make/receive call.
+        Connect PhoneA to Wifi.
+        Call from PhoneA to PhoneB, accept on PhoneB, send MMS 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.telephonyGetPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma MMS 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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mo_mms_in_3g_call(ads, wifi=True)
+
+    @test_tracker_info(uuid="bcc5b02d-2fef-431a-8c0b-f31c98999bfb")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_in_call_wcdma_wifi(self):
+        """ Test MT MMS during a MO wcdma call.
+
+        Make sure PhoneA is in wcdma mode.
+        Make sure PhoneB is able to make/receive call.
+        Connect PhoneA to Wifi.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive MMS 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.telephonyGetPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this wcdma MMS 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
+
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+        return self._mt_mms_in_3g_call(ads, wifi=True)
+
+    @test_tracker_info(uuid="b6e9ce80-8577-48e5-baa7-92780932f278")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_in_call_csfb(self):
         """ Test MO SMS during a MO csfb wcdma/gsm call.
@@ -914,22 +1395,9 @@
             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
+        return self._mo_sms_in_csfb_call(ads)
 
-        if not self._sms_test_mo(ads):
-            self.log.error("SMS test fail.")
-            return False
-
-        return True
-
+    @test_tracker_info(uuid="93f0b58a-01e9-4bc9-944f-729d455597dd")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_in_call_csfb(self):
         """ Test MT SMS during a MO csfb wcdma/gsm call.
@@ -953,22 +1421,9 @@
             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
+        return self._mt_sms_in_csfb_call(ads)
 
-        if not self._sms_test_mt(ads):
-            self.log.error("SMS test fail.")
-            return False
-
-        return True
-
+    @test_tracker_info(uuid="bd8e9e80-1955-429f-b122-96b127771bbb")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_in_call_csfb(self):
         """ Test MO MMS during a MO csfb wcdma/gsm call.
@@ -992,22 +1447,9 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        self.log.info("Begin In Call MMS 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
+        return self._mo_mms_in_csfb_call(ads)
 
-        if not self._mms_test_mo(ads):
-            self.log.error("MMS test fail.")
-            return False
-
-        return True
-
+    @test_tracker_info(uuid="89d65fd2-fc75-4fc5-a018-2d05a4364304")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_in_call_csfb(self):
         """ Test MT MMS during a MO csfb wcdma/gsm call.
@@ -1031,22 +1473,67 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        self.log.info("Begin In Call MMS 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 self._mt_mms_in_csfb_call(ads)
+
+    @test_tracker_info(uuid="9c542b5d-3b8f-4d4a-80de-fb804f066c3d")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_in_call_csfb_wifi(self):
+        """ Test MO MMS 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.
+        Connect PhoneA to Wifi.
+        Call from PhoneA to PhoneB, accept on PhoneB, send MMS 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.telephonyGetPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this csfb wcdma SMS test.")
             return False
 
-        if not self._mms_test_mt(ads):
-            self.log.error("MMS test fail.")
+        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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mo_mms_in_csfb_call(ads, wifi=True)
+
+    @test_tracker_info(uuid="c1bed6f5-f65c-4f4d-aa06-0e9f5c867819")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_in_call_csfb_wifi(self):
+        """ Test MT MMS 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.
+        Connect PhoneA to Wifi.
+        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.telephonyGetPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this csfb wcdma MMS test.")
             return False
 
-        return True
+        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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
 
+        return self._mt_mms_in_csfb_call(ads, wifi=True)
+
+    @test_tracker_info(uuid="60996028-b4b2-4a16-9e4b-eb6ef80179a7")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_in_call_1x(self):
         """ Test MO SMS during a MO 1x call.
@@ -1070,22 +1557,9 @@
             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
+        return self._mo_sms_in_1x_call(ads)
 
-        if not self._sms_test_mo(ads):
-            self.log.error("SMS test fail.")
-            return False
-
-        return True
-
+    @test_tracker_info(uuid="6b352aac-9b4e-4062-8980-3b1c0e61015b")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_in_call_1x(self):
         """ Test MT SMS during a MO 1x call.
@@ -1109,22 +1583,9 @@
             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
+        return self._mt_sms_in_1x_call(ads)
 
-        if not self._sms_test_mt(ads):
-            self.log.error("SMS test fail.")
-            return False
-
-        return True
-
+    @test_tracker_info(uuid="cfae3613-c490-4ce0-b00b-c13286d85027")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_in_call_1x(self):
         """ Test MO MMS during a MO 1x call.
@@ -1149,18 +1610,9 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        self.log.info("Begin In Call MMS 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
+        return self._mo_mms_in_1x_call(ads)
 
-        return self._mms_test_mo_after_call_hangup(ads)
-
+    @test_tracker_info(uuid="42fc8c16-4a30-4f63-9728-2639f2b79c4c")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_in_call_1x(self):
         """ Test MT MMS during a MO 1x call.
@@ -1184,18 +1636,66 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        self.log.info("Begin In Call MMS 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 self._mt_mms_in_1x_call(ads)
+
+    @test_tracker_info(uuid="18093f87-aab5-4d86-b178-8085a1651828")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_in_call_1x_wifi(self):
+        """ Test MO MMS 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 MMS on PhoneA during the call, MMS is send out after call is released.
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+        # Make sure PhoneA is CDMA phone before proceed.
+        if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("Not CDMA phone, abort this 1x MMS test.")
             return False
 
-        return self._mms_test_mt_after_call_hangup(ads)
+        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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
 
+        return self._mo_mms_in_1x_call(ads, wifi=True)
+
+    @test_tracker_info(uuid="8fe3359a-0857-401f-a043-c47a2a2acb47")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_in_call_1x_wifi(self):
+        """ Test MT MMS 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 MMS 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.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("Not CDMA phone, abort this 1x MMS 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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mt_mms_in_1x_call(ads, wifi=True)
+
+    @test_tracker_info(uuid="96214c7c-2843-4242-8cfa-1d08241514b0")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_in_call_csfb_1x(self):
         """ Test MO SMS during a MO csfb 1x call.
@@ -1219,22 +1719,9 @@
             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
+        return self._mo_sms_in_1x_call(ads)
 
-        if not self._sms_test_mo(ads):
-            self.log.error("SMS test fail.")
-            return False
-
-        return True
-
+    @test_tracker_info(uuid="3780a8e5-2649-45e6-bf6b-9ab1e86456eb")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_in_call_csfb_1x(self):
         """ Test MT SMS during a MO csfb 1x call.
@@ -1258,22 +1745,9 @@
             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
+        return self._mt_sms_in_1x_call(ads)
 
-        if not self._sms_test_mt(ads):
-            self.log.error("SMS test fail.")
-            return False
-
-        return True
-
+    @test_tracker_info(uuid="5de29f86-1aa8-46ff-a679-97309c314fe2")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_in_call_csfb_1x(self):
         """ Test MO MMS during a MO csfb 1x call.
@@ -1297,18 +1771,9 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        self.log.info("Begin In Call MMS 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
+        return self._mo_mms_in_1x_call(ads)
 
-        return self._mms_test_mo_after_call_hangup(ads)
-
+    @test_tracker_info(uuid="4311cb8c-626d-48a9-955b-6505b41c7519")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_in_call_csfb_1x(self):
         """ Test MT MMS during a MO csfb 1x call.
@@ -1332,18 +1797,65 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        self.log.info("Begin In Call MMS 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 self._mt_mms_in_1x_call(ads)
+
+    @test_tracker_info(uuid="12e05635-7934-4f14-a27e-430d0fc52edb")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_in_call_csfb_1x_wifi(self):
+        """ Test MO MMS 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 MMS 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.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("Not CDMA phone, abort this csfb 1x SMS test.")
             return False
 
-        return self._mms_test_mt_after_call_hangup(ads)
+        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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
 
+        return self._mo_mms_in_1x_call(ads, wifi=True)
+
+    @test_tracker_info(uuid="bd884be7-756b-4f0f-b233-052dc79233c0")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_in_call_csfb_1x_wifi(self):
+        """ Test MT MMS 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 MMS 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.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
+            self.log.error("Not CDMA phone, abort this csfb 1x MMS 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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mt_mms_in_1x_call(ads, wifi=True)
+
+    @test_tracker_info(uuid="ed720013-e366-448b-8901-bb09d26cea05")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_iwlan(self):
         """ Test MO SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
@@ -1369,6 +1881,7 @@
 
         return self._sms_test_mo(ads)
 
+    @test_tracker_info(uuid="4d4b0b7b-bf00-44f6-a0ed-23b438c30fc2")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_iwlan(self):
         """ Test MT SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
@@ -1394,6 +1907,7 @@
 
         return self._sms_test_mt(ads)
 
+    @test_tracker_info(uuid="264e2557-e18c-41c0-8d99-49cee3fe6f07")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_iwlan(self):
         """ Test MO MMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
@@ -1419,6 +1933,7 @@
 
         return self._mms_test_mo(ads)
 
+    @test_tracker_info(uuid="330db618-f074-4bfc-bf5e-78939fbee532")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_iwlan(self):
         """ Test MT MMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
@@ -1444,6 +1959,7 @@
 
         return self._mms_test_mt(ads)
 
+    @test_tracker_info(uuid="075933a2-df7f-4374-a405-92f96bcc7770")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_apm_wifi_wfc_off(self):
         """ Test MO SMS, Phone in APM, WiFi connected, WFC off.
@@ -1467,6 +1983,7 @@
 
         return self._sms_test_mo(ads)
 
+    @test_tracker_info(uuid="637af228-29fc-4b74-a963-883f66ddf080")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_apm_wifi_wfc_off(self):
         """ Test MT SMS, Phone in APM, WiFi connected, WFC off.
@@ -1490,6 +2007,7 @@
 
         return self._sms_test_mt(ads)
 
+    @test_tracker_info(uuid="502aba0d-8895-4807-b394-50a44208ecf7")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_apm_wifi_wfc_off(self):
         """ Test MO MMS, Phone in APM, WiFi connected, WFC off.
@@ -1513,6 +2031,7 @@
 
         return self._mms_test_mo(ads)
 
+    @test_tracker_info(uuid="235bfdbf-4275-4d89-99f5-41b5b7de8345")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_apm_wifi_wfc_off(self):
         """ Test MT MMS, Phone in APM, WiFi connected, WFC off.
@@ -1536,6 +2055,7 @@
 
         return self._mms_test_mt(ads)
 
+    @test_tracker_info(uuid="e5a31b94-1cb6-4770-a2bc-5a0ddba51502")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_in_call_iwlan(self):
         """ Test MO SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
@@ -1572,6 +2092,7 @@
 
         return self._sms_test_mo(ads)
 
+    @test_tracker_info(uuid="d6d30cc5-f75b-42df-b517-401456ee8466")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_in_call_iwlan(self):
         """ Test MT SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
@@ -1608,6 +2129,7 @@
 
         return self._sms_test_mt(ads)
 
+    @test_tracker_info(uuid="a98a5a97-3864-4ff8-9085-995212eada20")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_in_call_iwlan(self):
         """ Test MO MMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
@@ -1644,6 +2166,7 @@
 
         return self._mms_test_mo(ads)
 
+    @test_tracker_info(uuid="0464a87b-d45b-4b03-9895-17ece360a796")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_in_call_iwlan(self):
         """ Test MT MMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
@@ -1680,6 +2203,7 @@
 
         return self._mms_test_mt(ads)
 
+    @test_tracker_info(uuid="9f1933bb-c4cb-4655-8655-327c1f38e8ee")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_in_call_vt(self):
         """ Test MO SMS, Phone in ongoing VT call.
@@ -1712,6 +2236,7 @@
 
         return self._sms_test_mo(ads)
 
+    @test_tracker_info(uuid="0a07e737-4862-4492-9b48-8d94799eab91")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_in_call_vt(self):
         """ Test MT SMS, Phone in ongoing VT call.
@@ -1744,6 +2269,7 @@
 
         return self._sms_test_mt(ads)
 
+    @test_tracker_info(uuid="55d70548-6aee-40e9-b94d-d10de84fb50f")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_in_call_vt(self):
         """ Test MO MMS, Phone in ongoing VT call.
@@ -1776,6 +2302,7 @@
 
         return self._mms_test_mo(ads)
 
+    @test_tracker_info(uuid="75f97c9a-4397-42f1-bb00-8fc6d04fdf6d")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_in_call_vt(self):
         """ Test MT MMS, Phone in ongoing VT call.
@@ -1808,6 +2335,7 @@
 
         return self._mms_test_mt(ads)
 
+    @test_tracker_info(uuid="2a72ecc6-702d-4add-a7a2-8c1001628bb6")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mo_in_call_gsm(self):
         """ Test MO SMS during a MO gsm call.
@@ -1847,6 +2375,7 @@
 
         return True
 
+    @test_tracker_info(uuid="facd1814-8d69-42a2-9f80-b6a28cc0c9d2")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_in_call_gsm(self):
         """ Test MT SMS during a MO gsm call.
@@ -1886,6 +2415,7 @@
 
         return True
 
+    @test_tracker_info(uuid="2bd94d69-3621-4b94-abc7-bd24c4325485")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mo_in_call_gsm(self):
         """ Test MO MMS during a MO gsm call.
@@ -1909,22 +2439,9 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        self.log.info("Begin In Call MMS 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
+        return self._mo_mms_in_2g_call(ads)
 
-        if not self._mms_test_mo(ads):
-            self.log.error("MMS test fail.")
-            return False
-
-        return True
-
+    @test_tracker_info(uuid="e20be70d-99d6-4344-a742-f69581b66d8f")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_in_call_gsm(self):
         """ Test MT MMS during a MO gsm call.
@@ -1948,18 +2465,60 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        self.log.info("Begin In Call MMS 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 self._mt_mms_in_2g_call(ads)
+
+    @test_tracker_info(uuid="3510d368-4b16-4716-92a3-9dd01842ba79")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mo_in_call_gsm_wifi(self):
+        """ Test MO MMS during a MO gsm call.
+
+        Make sure PhoneA is in gsm mode with Wifi connected.
+        Make sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, send MMS 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.telephonyGetPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this gsm MMS test.")
             return False
 
-        if not self._mms_test_mt(ads):
-            self.log.error("MMS test fail.")
+        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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mo_mms_in_2g_call(ads, wifi=True)
+
+    @test_tracker_info(uuid="060def89-01bd-4b44-a49b-a4536fe39165")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_mms_mt_in_call_gsm_wifi(self):
+        """ Test MT MMS during a MO gsm call.
+
+        Make sure PhoneA is in gsm mode with wifi connected.
+        Make sure PhoneB is able to make/receive call.
+        Call from PhoneA to PhoneB, accept on PhoneB, receive MMS 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.telephonyGetPhoneType() != PHONE_TYPE_GSM):
+            self.log.error("Not GSM phone, abort this gsm MMS test.")
             return False
 
-        return True
+        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
+        ensure_wifi_connected(self.log, ads[0], self.wifi_network_ssid,
+                              self.wifi_network_pass)
+
+        return self._mt_mms_in_2g_call(ads, wifi=True)
diff --git a/acts/tests/google/tel/live/TelLiveStressCallTest.py b/acts/tests/google/tel/live/TelLiveStressCallTest.py
index 247d76b..b3e120f 100644
--- a/acts/tests/google/tel/live/TelLiveStressCallTest.py
+++ b/acts/tests/google/tel/live/TelLiveStressCallTest.py
@@ -19,16 +19,20 @@
 
 import collections
 import time
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
 from acts.test_utils.tel.tel_test_utils import call_setup_teardown
 from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
+from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
 from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
 from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
 from acts.test_utils.tel.tel_test_utils import hangup_call
 from acts.test_utils.tel.tel_test_utils import set_wfc_mode
 from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
 from acts.test_utils.tel.tel_test_utils import verify_incall_state
+from acts.test_utils.tel.tel_test_utils import multithread_func
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
@@ -39,28 +43,29 @@
 from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
 from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
 from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts.test_utils.tel.tel_video_utils import phone_setup_video
+from acts.test_utils.tel.tel_video_utils import video_call_setup
+from acts.test_utils.tel.tel_video_utils import \
+    is_phone_in_call_video_bidirectional
 from acts.utils import rand_ascii_str
 
-class TelLiveStressCallTest(TelephonyBaseTest):
-    def __init__(self, controllers):
-        TelephonyBaseTest.__init__(self, controllers)
 
+class TelLiveStressCallTest(TelephonyBaseTest):
     def setup_class(self):
-        super().setup_class()
+        super(TelLiveStressCallTest, self).setup_class()
         self.caller = self.android_devices[0]
         self.callee = self.android_devices[1]
+        self.user_params["telephony_auto_rerun"] = False
         self.wifi_network_ssid = self.user_params.get(
-            "wifi_network_ssid") or self.user_params.get(
-                "wifi_network_ssid_2g")
+            "wifi_network_ssid") or self.user_params.get("wifi_network_ssid_2g")
         self.wifi_network_pass = self.user_params.get(
-            "wifi_network_pass") or self.user_params.get(
-                "wifi_network_pass_2g")
-        self.phone_call_iteration = int(self.user_params.get(
-            "phone_call_iteration", 500))
-        self.phone_call_duration = int(self.user_params.get(
-            "phone_call_duration", 60))
-        self.sleep_time_between_test_iterations = int(self.user_params.get(
-            "sleep_time_between_test_iterations", 0))
+            "wifi_network_pass") or self.user_params.get("wifi_network_pass_2g")
+        self.phone_call_iteration = int(
+            self.user_params.get("phone_call_iteration", 500))
+        self.phone_call_duration = int(
+            self.user_params.get("phone_call_duration", 60))
+        self.sleep_time_between_test_iterations = int(
+            self.user_params.get("sleep_time_between_test_iterations", 0))
 
         return True
 
@@ -85,6 +90,15 @@
             ad.log.info("Phone is in WFC enabled state.")
         return True
 
+    def _setup_vt(self):
+        ads = self.android_devices
+        tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
+                                                           (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            return False
+        return True
+
     def _setup_lte_volte_enabled(self):
         for ad in self.android_devices:
             if not phone_setup_volte(self.log, ad):
@@ -117,11 +131,19 @@
             ad.log.info("RAT 2G is enabled successfully.")
         return True
 
-    def _setup_phone_call(self):
-        if not call_setup_teardown(
-                self.log, self.caller, self.callee, ad_hangup=None):
-            self.log.error("Setup Call failed.")
-            return False
+    def _setup_phone_call(self, test_video):
+        if test_video:
+            if not video_call_setup(
+                    self.log,
+                    self.android_devices[0],
+                    self.android_devices[1], ):
+                self.log.error("Failed to setup Video call")
+                return False
+        else:
+            if not call_setup_teardown(
+                    self.log, self.caller, self.callee, ad_hangup=None):
+                self.log.error("Setup Call failed.")
+                return False
         self.log.info("Setup call successfully.")
         return True
 
@@ -129,7 +151,15 @@
         for ad in self.android_devices:
             hangup_call(self.log, ad)
 
-    def stress_test(self, setup_func=None, network_check_func=None, test_sms=False):
+    def stress_test(self,
+                    setup_func=None,
+                    network_check_func=None,
+                    test_sms=False,
+                    test_video=False):
+        for ad in self.android_devices:
+            #check for sim and service
+            ensure_phone_subscription(self.log, ad)
+
         if setup_func and not setup_func():
             self.log.error("Test setup %s failed", setup_func.__name__)
             return False
@@ -140,58 +170,61 @@
             self.log.info(msg)
             iteration_result = True
             ensure_phones_idle(self.log, self.android_devices)
-            if not self._setup_phone_call():
+
+            if not self._setup_phone_call(test_video):
                 fail_count["dialing"] += 1
                 iteration_result = False
                 self.log.error("%s call dialing failure.", msg)
+            else:
+                if network_check_func and not network_check_func(
+                        self.log, self.caller):
+                    fail_count["caller_network_check"] += 1
+                    iteration_result = False
+                    self.log.error("%s network check %s failure.", msg,
+                                   network_check_func.__name__)
 
-            if network_check_func and not network_check_func(self.log,
-                                                             self.caller):
-                fail_count["caller_network_check"] += 1
-                iteration_result = False
-                self.log.error("%s network check %s failure.", msg,
-                               network_check_func.__name__)
+                if network_check_func and not network_check_func(
+                        self.log, self.callee):
+                    fail_count["callee_network_check"] += 1
+                    iteration_result = False
+                    self.log.error("%s network check failure.", msg)
 
-            if network_check_func and not network_check_func(self.log,
-                                                             self.callee):
-                fail_count["callee_network_check"] += 1
-                iteration_result = False
-                self.log.error("%s network check failure.", msg)
+                time.sleep(self.phone_call_duration)
 
-            time.sleep(self.phone_call_duration)
+                if not verify_incall_state(self.log,
+                                           [self.caller, self.callee], True):
+                    self.log.error("%s call dropped.", msg)
+                    iteration_result = False
+                    fail_count["drop"] += 1
 
-            if not verify_incall_state(self.log, [self.caller, self.callee],
-                                       True):
-                self.log.error("%s call dropped.", msg)
-                iteration_result = False
-                fail_count["drop"] += 1
-
-            self._hangup_call()
+                self._hangup_call()
 
             if test_sms and not sms_send_receive_verify(
                     self.log, self.caller, self.callee, [rand_ascii_str(180)]):
                 fail_count["sms"] += 1
 
-            self.log.info("%s %s" % (msg, str(iteration_result)))
+            self.log.info("%s %s", msg, iteration_result)
             if not iteration_result:
-                self._take_bug_report("%s_%s" % (self.test_name, i), self.begin_time)
+                self._take_bug_report("%s_%s" % (self.test_name, i),
+                                      self.begin_time)
 
             if self.sleep_time_between_test_iterations:
                 self.caller.droid.goToSleepNow()
                 self.callee.droid.goToSleepNow()
                 time.sleep(self.sleep_time_between_test_iterations)
 
-
         test_result = True
         for failure, count in fail_count.items():
             if count:
-                self.log.error("%s: %s %s failures in %s iterations".format(
-                    self.test_name, count, failure, self.phone_call_iteration))
+                self.log.error("%s: %s %s failures in %s iterations",
+                               self.test_name, count, failure,
+                               self.phone_call_iteration)
                 test_result = False
         return test_result
 
     """ Tests Begin """
 
+    @test_tracker_info(uuid="3c3daa08-e66a-451a-a772-634ec522c965")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_default_stress(self):
         """ Default state call stress test
@@ -211,6 +244,7 @@
         """
         return self.stress_test()
 
+    @test_tracker_info(uuid="b7fd730a-d4c7-444c-9e36-12389679b430")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_and_sms_longevity(self):
         """ Default state call stress test
@@ -230,6 +264,7 @@
         """
         return self.stress_test(test_sms=True)
 
+    @test_tracker_info(uuid="3b711843-de27-4b0a-a163-8c439c901e24")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_stress(self):
         """ VoLTE call stress test
@@ -251,12 +286,13 @@
             setup_func=self._setup_lte_volte_enabled,
             network_check_func=is_phone_in_call_volte)
 
+    @test_tracker_info(uuid="518516ea-1c0a-494d-ad44-272f21075d39")
     @TelephonyBaseTest.tel_test_wrap
-    def test_call_lte_volte_disabled_stress(self):
-        """ LTE non-VoLTE call stress test
+    def test_call_csfb_stress(self):
+        """ LTE CSFB call stress test
 
         Steps:
-        1. Make Sure PhoneA and PhoneB in LTE mode.
+        1. Make Sure PhoneA in LTE CSFB mode.
         2. Call from PhoneA to PhoneB, hang up on PhoneA.
         3, Repeat 2 around N times based on the config setup
 
@@ -272,6 +308,7 @@
             setup_func=self._setup_lte_volte_disabled,
             network_check_func=is_phone_in_call_csfb)
 
+    @test_tracker_info(uuid="887608cb-e5c6-4b19-b02b-3461c1e78f2d")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_wifi_calling_stress(self):
         """ Wifi calling call stress test
@@ -293,6 +330,7 @@
             setup_func=self._setup_wfc,
             network_check_func=is_phone_in_call_iwlan)
 
+    @test_tracker_info(uuid="8af0454b-b4db-46d8-b5cc-e13ec5bc59ab")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_3g_stress(self):
         """ 3G call stress test
@@ -313,6 +351,7 @@
         return self.stress_test(
             setup_func=self._setup_3g, network_check_func=is_phone_in_call_3g)
 
+    @test_tracker_info(uuid="12380823-2e7f-4c41-95c0-5f8c483f9510")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_2g_stress(self):
         """ 2G call stress test
@@ -333,5 +372,27 @@
         return self.stress_test(
             setup_func=self._setup_2g, network_check_func=is_phone_in_call_2g)
 
-    """ Tests End """
+    @test_tracker_info(uuid="28a88b44-f239-4b77-b01f-e9068373d749")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_stress(self):
+        """ VT call stress test
 
+        Steps:
+        1. Make Sure PhoneA and PhoneB in VoLTE mode (ViLTE provisioned).
+        2. Call from PhoneA to PhoneB, hang up on PhoneA.
+        3, Repeat 2 around N times based on the config setup
+
+        Expected Results:
+        1, Verify phone is at IDLE state
+        2, Verify the phone is at ACTIVE, if it is in dialing, then we retry
+        3, Verify the phone is IDLE after hung up
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self.stress_test(
+            setup_func=self._setup_vt,
+            network_check_func=is_phone_in_call_video_bidirectional,
+            test_video=True)
+
+    """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveStressTest.py b/acts/tests/google/tel/live/TelLiveStressTest.py
new file mode 100644
index 0000000..0d0c234
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveStressTest.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+    Test Script for Telephony Stress Call Test
+"""
+
+import collections
+import random
+import time
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
+from acts.test_utils.tel.tel_test_utils import active_file_download_test
+from acts.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
+from acts.test_utils.tel.tel_test_utils import ensure_phone_subscription
+from acts.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import run_multithread_func
+from acts.test_utils.tel.tel_test_utils import set_wfc_mode
+from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify
+from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
+from acts.test_utils.tel.tel_test_utils import verify_incall_state
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts.test_utils.tel.tel_voice_utils import phone_setup_csfb
+from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts.test_utils.tel.tel_voice_utils import phone_setup_volte
+from acts.utils import rand_ascii_str
+
+
+class TelLiveStressTest(TelephonyBaseTest):
+    def setup_class(self):
+        super(TelLiveStressTest, self).setup_class()
+        self.dut = self.android_devices[0]
+        self.helper = self.android_devices[1]
+        self.user_params["telephony_auto_rerun"] = False
+        self.wifi_network_ssid = self.user_params.get(
+            "wifi_network_ssid") or self.user_params.get("wifi_network_ssid_2g")
+        self.wifi_network_pass = self.user_params.get(
+            "wifi_network_pass") or self.user_params.get("wifi_network_pass_2g")
+        self.phone_call_iteration = int(
+            self.user_params.get("phone_call_iteration", 500))
+        self.max_phone_call_duration = int(
+            self.user_params.get("max_phone_call_duration", 600))
+        self.max_sleep_time = int(self.user_params.get("max_sleep_time", 120))
+        self.max_run_time = int(self.user_params.get("max_run_time", 18000))
+        self.max_sms_length = int(self.user_params.get("max_sms_length", 1000))
+        self.max_mms_length = int(self.user_params.get("max_mms_length", 160))
+        self.crash_check_interval = int(
+            self.user_params.get("crash_check_interval", 300))
+
+        return True
+
+    def _setup_wfc(self):
+        for ad in self.android_devices:
+            if not ensure_wifi_connected(
+                    ad.log,
+                    ad,
+                    self.wifi_network_ssid,
+                    self.wifi_network_pass,
+                    retry=3):
+                ad.log.error("Phone Wifi connection fails.")
+                return False
+            ad.log.info("Phone WIFI is connected successfully.")
+            if not set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED):
+                ad.log.error("Phone failed to enable Wifi-Calling.")
+                return False
+            ad.log.info("Phone is set in Wifi-Calling successfully.")
+            if not phone_idle_iwlan(self.log, self.ad):
+                ad.log.error("Phone is not in WFC enabled state.")
+                return False
+            ad.log.info("Phone is in WFC enabled state.")
+        return True
+
+    def _setup_lte_volte_enabled(self):
+        for ad in self.android_devices:
+            if not phone_setup_volte(self.log, ad):
+                ad.log.error("Phone failed to enable VoLTE.")
+                return False
+            ad.log.info("Phone VOLTE is enabled successfully.")
+        return True
+
+    def _setup_lte_volte_disabled(self):
+        for ad in self.android_devices:
+            if not phone_setup_csfb(self.log, ad):
+                ad.log.error("Phone failed to setup CSFB.")
+                return False
+            ad.log.info("Phone VOLTE is disabled successfully.")
+        return True
+
+    def _setup_3g(self):
+        for ad in self.android_devices:
+            if not phone_setup_voice_3g(self.log, ad):
+                ad.log.error("Phone failed to setup 3g.")
+                return False
+            ad.log.info("Phone RAT 3G is enabled successfully.")
+        return True
+
+    def _setup_2g(self):
+        for ad in self.android_devices:
+            if not phone_setup_voice_2g(self.log, ad):
+                ad.log.error("Phone failed to setup 2g.")
+                return False
+            ad.log.info("RAT 2G is enabled successfully.")
+        return True
+
+    def _send_message(self, ads):
+        selection = random.randrange(0, 2)
+        message_type_map = {0: "SMS", 1: "MMS"}
+        max_length_map = {0: self.max_sms_length, 1: self.max_mms_length}
+        length = random.randrange(0, max_length_map[selection] + 1)
+        text = rand_ascii_str(length)
+        message_content_map = {0: [text], 1: [("Mms Message", text, None)]}
+        message_func_map = {
+            0: sms_send_receive_verify,
+            1: mms_send_receive_verify
+        }
+        if not message_func_map[selection](self.log, ads[0], ads[1],
+                                           message_content_map[selection]):
+            self.log.error("%s of length %s from %s to %s fails",
+                           message_type_map[selection], length, ads[0].serial,
+                           ads[1].serial)
+            return False
+        else:
+            self.log.info("%s of length %s from %s to %s succeed",
+                          message_type_map[selection], length, ads[0].serial,
+                          ads[1].serial)
+            return False
+
+    def _make_phone_call(self, ads):
+        if not call_setup_teardown(
+                self.log,
+                ads[0],
+                ads[1],
+                ad_hangup=ads[random.randrange(0, 2)],
+                wait_time_in_call=random.randrange(
+                    1, self.max_phone_call_duration)):
+            ads[0].log.error("Setup phone Call failed.")
+            return False
+        ads[0].log.info("Setup call successfully.")
+        return True
+
+    def _download_file(self):
+        file_names = ["5MB", "10MB", "20MB", "50MB", "200MB", "512MB", "1GB"]
+        selection = random.randrange(0, 7)
+        return active_file_download_test(self.log, self.dut,
+                                         file_names[selection])
+
+    def check_crash(self):
+        new_crash = self.dut.check_crash_report()
+        crash_diff = set(new_crash).difference(set(self.dut.crash_report))
+        self.dut.crash_report = new_crash
+        if crash_diff:
+            self.dut.log.error("Find new crash reports %s", list(crash_diff))
+            self.dut.pull_files(list(crash_diff))
+            return False
+        return True
+
+    def crash_check_test(self):
+        failure = 0
+        while time.time() < self.finishing_time:
+            if not self.check_crash():
+                failure += 1
+                self.log.error("Crash found count: %s", failure)
+                self._take_bug_report("%s_crash_found" % self.test_name,
+                                      time.strftime("%m-%d-%Y-%H-%M-%S"))
+            self.dut.droid.goToSleepNow()
+            time.sleep(self.crash_check_interval)
+        return failure
+
+    def call_test(self):
+        failure = 0
+        while time.time() < self.finishing_time:
+            ads = [self.dut, self.helper]
+            random.shuffle(ads)
+            if not self._make_phone_call(ads):
+                failure += 1
+                self.log.error("Call test failure count: %s", failure)
+                self._take_bug_report("%s_call_failure" % self.test_name,
+                                      time.strftime("%m-%d-%Y-%H-%M-%S"))
+            self.dut.droid.goToSleepNow()
+            time.sleep(random.randrange(0, self.max_sleep_time))
+        return failure
+
+    def message_test(self):
+        failure = 0
+        while time.time() < self.finishing_time:
+            ads = [self.dut, self.helper]
+            random.shuffle(ads)
+            if not self._send_message(ads):
+                failure += 1
+                self.log.error("Messaging test failure count: %s", failure)
+                self._take_bug_report("%s_messaging_failure" % self.test_name,
+                                      time.strftime("%m-%d-%Y-%H-%M-%S"))
+            self.dut.droid.goToSleepNow()
+            time.sleep(random.randrange(0, self.max_sleep_time))
+        return failure
+
+    def data_test(self):
+        failure = 0
+        while time.time() < self.finishing_time:
+            if not self._download_file():
+                failure += 1
+                self.log.error("File download test failure count: %s", failure)
+                self._take_bug_report("%s_download_failure" % self.test_name,
+                                      time.strftime("%m-%d-%Y-%H-%M-%S"))
+            self.dut.droid.goToSleepNow()
+            time.sleep(random.randrange(0, self.max_sleep_time))
+        return failure
+
+    def parallel_tests(self, setup_func=None):
+        if setup_func and not setup_func():
+            self.log.error("Test setup %s failed", setup_func.__name__)
+            return False
+        self.finishing_time = time.time() + self.max_run_time
+        results = run_multithread_func(self.log, [(self.call_test, []), (
+            self.message_test, []), (self.data_test, []),
+                                                  (self.crash_check_test, [])])
+        self.log.info("Call failures: %s", results[0])
+        self.log.info("Messaging failures: %s", results[1])
+        self.log.info("Data failures: %s", results[2])
+        self.log.info("Crash failures: %s", results[3])
+        for result in results:
+            if result: return False
+
+    """ Tests Begin """
+
+    @test_tracker_info(uuid="d035e5b9-476a-4e3d-b4e9-6fd86c51a68d")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_parallel_stress(self):
+        """ Default state stress test"""
+        return self.parallel_tests()
+
+    @test_tracker_info(uuid="c21e1f17-3282-4f0b-b527-19f048798098")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_lte_volte_parallel_stress(self):
+        """ VoLTE on stress test"""
+        return self.parallel_tests(setup_func=self._setup_lte_volte_enabled)
+
+    @test_tracker_info(uuid="a317c23a-41e0-4ef8-af67-661451cfefcf")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_csfb_parallel_stress(self):
+        """ LTE non-VoLTE stress test"""
+        return self.parallel_tests(setup_func=self._setup_lte_volte_disabled)
+
+    @test_tracker_info(uuid="fdb791bf-c414-4333-9fa3-cc18c9b3b234")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wfc_parallel_stress(self):
+        """ Wifi calling on stress test"""
+        return self.parallel_tests(setup_func=self._setup_wfc)
+
+    @test_tracker_info(uuid="4566eef6-55de-4ac8-87ee-58f2ef41a3e8")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_3g_parallel_stress(self):
+        """ 3G stress test"""
+        return self.parallel_tests(setup_func=self._setup_3g)
+
+    @test_tracker_info(uuid="f34f1a31-3948-4675-8698-372a83b8088d")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_2g_parallel_stress(self):
+        """ 2G call stress test"""
+        return self.parallel_tests(setup_func=self._setup_2g)
+
+    """ Tests End """
diff --git a/acts/tests/google/tel/live/TelLiveVideoDataTest.py b/acts/tests/google/tel/live/TelLiveVideoDataTest.py
index 5031aa6..095215c 100644
--- a/acts/tests/google/tel/live/TelLiveVideoDataTest.py
+++ b/acts/tests/google/tel/live/TelLiveVideoDataTest.py
@@ -30,9 +30,6 @@
 class TelLiveVideoDataTest(TelephonyBaseTest):
     def __init__(self, controllers):
         TelephonyBaseTest.__init__(self, controllers)
-        self.tests = (
-            # Data during VT call
-            "test_internet_access_during_video_call", )
 
         self.stress_test_number = self.get_stress_test_number()
         self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
diff --git a/acts/tests/google/tel/live/TelLiveVideoTest.py b/acts/tests/google/tel/live/TelLiveVideoTest.py
index ed7f100..367c484 100644
--- a/acts/tests/google/tel/live/TelLiveVideoTest.py
+++ b/acts/tests/google/tel/live/TelLiveVideoTest.py
@@ -19,6 +19,7 @@
 
 import time
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
 from acts.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
@@ -66,67 +67,24 @@
 from acts.test_utils.tel.tel_voice_utils import set_audio_route
 from acts.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
 
+DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION = 1 * 60 * 60  # default 1 hour
+
 
 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",
-
-            # VT conference - Conference Event Package
-            "test_call_volte_add_mo_video_accept_as_voice_merge_drop_cep",
-            "test_call_volte_add_mt_video_accept_as_voice_merge_drop_cep",
-            "test_call_video_add_mo_voice_swap_downgrade_merge_drop_cep",
-            "test_call_video_add_mt_voice_swap_downgrade_merge_drop_cep",
-            "test_call_volte_add_mo_video_downgrade_merge_drop_cep",
-            "test_call_volte_add_mt_video_downgrade_merge_drop_cep",
-
-            # Disable Data, VT not available
-            "test_disable_data_vt_unavailable", )
 
         self.stress_test_number = self.get_stress_test_number()
-        self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+        self.wifi_network_ssid = self.user_params.get("wifi_network_ssid")
+        self.wifi_network_pass = self.user_params.get("wifi_network_pass")
 
-        try:
-            self.wifi_network_pass = self.user_params["wifi_network_pass"]
-        except KeyError:
-            self.wifi_network_pass = None
+        self.long_duration_call_total_duration = self.user_params.get(
+            "long_duration_call_total_duration",
+            DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION)
 
     """ Tests Begin """
 
+    @test_tracker_info(uuid="9f0b7c98-b010-4f9b-bd80-9925fe1cb5f8")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_to_video(self):
         """ Test VT<->VT call functionality.
@@ -159,6 +117,43 @@
 
         return True
 
+    @test_tracker_info(uuid="345e6ae9-4e9f-45e6-86a1-b661a84b6293")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_video_to_video_long(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.
+        Keep the VT call ON for 60 mins.
+        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,
+                wait_time_in_call=self.long_duration_call_total_duration):
+            self.log.error("Failed to setup+teardown long call")
+            return False
+
+        return True
+
+    @test_tracker_info(uuid="6eaef46f-dd73-4835-be9d-c9529fc0ad3d")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_accept_as_voice(self):
         """ Test VT<->VT call functionality.
@@ -190,6 +185,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="dcd43fd5-4c92-4f09-90f8-04ccce66d396")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_to_video_mo_disable_camera(self):
         """ Test VT<->VT call functionality.
@@ -225,14 +221,16 @@
 
         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],
+                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])
 
+    @test_tracker_info(uuid="088c0590-ffd0-4337-9576-569f27c4c527")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_to_video_mt_disable_camera(self):
         """ Test VT<->VT call functionality.
@@ -268,14 +266,16 @@
 
         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],
+                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])
 
+    @test_tracker_info(uuid="879579ac-7106-4c4b-a8d0-64695108f6f7")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_to_video_mo_mt_disable_camera(self):
         """ Test VT<->VT call functionality.
@@ -313,8 +313,9 @@
 
         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],
+                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.")
@@ -322,14 +323,16 @@
 
         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],
+                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])
 
+    @test_tracker_info(uuid="13ff7df6-bf13-4f60-80a1-d9cbeae8e1df")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_to_video_mt_mo_disable_camera(self):
         """ Test VT<->VT call functionality.
@@ -367,8 +370,9 @@
 
         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],
+                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.")
@@ -376,8 +380,9 @@
 
         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],
+                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.")
@@ -431,6 +436,7 @@
 
         return hangup_call(self.log, ads[0])
 
+    @test_tracker_info(uuid="e56eea96-467c-49ce-a135-f82f12302369")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_accept_as_voice_mo_upgrade_bidirectional(self):
         """ Test Upgrading from VoLTE to Bi-Directional VT.
@@ -463,6 +469,7 @@
 
         return self._mo_upgrade_bidirectional(ads)
 
+    @test_tracker_info(uuid="c1f58f4a-28aa-4cd0-9835-f294cdcff854")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_volte_mo_upgrade_bidirectional(self):
         """ Test Upgrading from VoLTE to Bi-Directional VT.
@@ -526,6 +533,7 @@
 
         return hangup_call(self.log, ads[0])
 
+    @test_tracker_info(uuid="427b0906-f082-4f6d-9d94-4f9c4d5005a5")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_volte_mo_upgrade_reject(self):
         """ Test Upgrading from VoLTE to Bi-Directional VT and reject.
@@ -555,6 +563,7 @@
 
         return self._mo_upgrade_reject(ads)
 
+    @test_tracker_info(uuid="f733f694-c0c2-4da0-b3c2-ff21df026426")
     @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.
@@ -599,8 +608,8 @@
             call_id_requester, EVENT_VIDEO_SESSION_EVENT)
         ad_responder.droid.telecomCallVideoStartListeningForEvent(
             call_id_responder, EVENT_VIDEO_SESSION_EVENT)
-        self.log.info("Put In-Call UI on {} to background.".format(
-            ad_requester.serial))
+        self.log.info(
+            "Put In-Call UI on {} to background.".format(ad_requester.serial))
         ad_requester.droid.showHomeScreen()
         try:
             event_on_responder = ad_responder.ed.pop_event(
@@ -638,8 +647,8 @@
                 VT_STATE_BIDIRECTIONAL_PAUSED, CALL_STATE_ACTIVE):
             return False
 
-        self.log.info("Put In-Call UI on {} to foreground.".format(
-            ad_requester.serial))
+        self.log.info(
+            "Put In-Call UI on {} to foreground.".format(ad_requester.serial))
         ad_requester.droid.telecomCallVideoStartListeningForEvent(
             call_id_requester, EVENT_VIDEO_SESSION_EVENT)
         ad_responder.droid.telecomCallVideoStartListeningForEvent(
@@ -683,6 +692,7 @@
 
         return True
 
+    @test_tracker_info(uuid="f78b40a4-3be7-46f2-882f-0333f733e334")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_to_video_mo_to_backgroundpause_foregroundresume(self):
         ads = self.android_devices
@@ -708,6 +718,7 @@
         return self._test_put_call_to_backgroundpause_and_foregroundresume(
             ads[0], ads[1])
 
+    @test_tracker_info(uuid="9aafdf6a-6535-4137-a801-4fbb67fdb281")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_to_video_mt_to_backgroundpause_foregroundresume(self):
         ads = self.android_devices
@@ -753,6 +764,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="cde91e7d-dbc5-40f5-937d-36840c77667e")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mo_voice(self):
         """
@@ -823,6 +835,7 @@
 
         return self._vt_test_multi_call_hangup(ads)
 
+    @test_tracker_info(uuid="60511b22-7004-4539-9164-1331220e4d18")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mt_voice(self):
         """
@@ -893,6 +906,7 @@
 
         return self._vt_test_multi_call_hangup(ads)
 
+    @test_tracker_info(uuid="782847f4-8eab-42db-a036-ebf8de28eb23")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mo_video(self):
         """
@@ -966,6 +980,7 @@
 
         return self._vt_test_multi_call_hangup(ads)
 
+    @test_tracker_info(uuid="bc3ac5b0-4bf7-4068-9bd0-2f8301c2ad05")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mt_video(self):
         """
@@ -1041,6 +1056,7 @@
 
         return self._vt_test_multi_call_hangup(ads)
 
+    @test_tracker_info(uuid="97c7f5c3-c994-477b-839e-cea1d450d4e7")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mt_voice_swap_once_local_drop(self):
         """
@@ -1147,6 +1163,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="6b2c8701-eb65-47cd-a190-a074bc60ebfa")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mt_voice_swap_twice_remote_drop_voice_unhold_video(
             self):
@@ -1252,8 +1269,8 @@
         # Audio will goto earpiece in here
         for ad in [ads[0], ads[1]]:
             if get_audio_route(self.log, ad) != AUDIO_ROUTE_EARPIECE:
-                self.log.error("{} Audio is not on EARPIECE.".format(
-                    ad.serial))
+                self.log.error(
+                    "{} Audio is not on EARPIECE.".format(ad.serial))
                 # TODO: b/26337892 Define expected audio route behavior.
 
         time.sleep(WAIT_TIME_IN_CALL)
@@ -1281,8 +1298,8 @@
         # Audio will goto earpiece in here
         for ad in [ads[0], ads[1]]:
             if get_audio_route(self.log, ad) != AUDIO_ROUTE_EARPIECE:
-                self.log.error("{} Audio is not on EARPIECE.".format(
-                    ad.serial))
+                self.log.error(
+                    "{} Audio is not on EARPIECE.".format(ad.serial))
                 # TODO: b/26337892 Define expected audio route behavior.
 
         time.sleep(WAIT_TIME_IN_CALL)
@@ -1298,6 +1315,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="9d897505-efed-4b04-b5c8-3f9ba9d26861")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mo_video(self):
         """
@@ -1368,6 +1386,7 @@
 
         return self._vt_test_multi_call_hangup(ads)
 
+    @test_tracker_info(uuid="d501a744-fda7-4a0c-a25d-a1ed4e7a356e")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mt_video(self):
         """
@@ -1453,6 +1472,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="26c9c6f6-b68e-492a-b188-ce8109a4ba34")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_mt_video_add_mt_video(self):
         """
@@ -1538,6 +1558,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="5ceb6eb2-c128-405e-8ba4-69a4646842a0")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_mt_video_add_mo_video(self):
         """
@@ -1697,7 +1718,8 @@
         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.")
+                "No call with children. Probably CEP not enabled or merge failed."
+            )
             return False
         calls.remove(call_conf_id)
         if (set(ads[0].droid.telecomCallGetCallChildren(call_conf_id)) !=
@@ -1707,14 +1729,14 @@
                     ads[0].droid.telecomCallGetCallChildren(call_conf_id)))
             return False
 
-        if (CALL_PROPERTY_CONFERENCE not in
-                ads[0].droid.telecomCallGetProperties(call_conf_id)):
+        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 False
 
-        if (CALL_CAPABILITY_MANAGE_CONFERENCE not in
-                ads[0].droid.telecomCallGetCapabilities(call_conf_id)):
+        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 False
@@ -1753,6 +1775,7 @@
 
         return True
 
+    @test_tracker_info(uuid="51731afc-e278-4f72-a5e1-590d49ba348d")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mo_video_accept_as_voice_merge_drop(self):
         """Conference call
@@ -1770,6 +1793,7 @@
         return self._test_call_volte_add_mo_video_accept_as_voice_merge_drop(
             False)
 
+    @test_tracker_info(uuid="23e3a071-5453-48da-8439-bd75bc79547f")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mo_video_accept_as_voice_merge_drop_cep(self):
         """Conference call
@@ -1843,10 +1867,12 @@
                 CALL_STATE_ACTIVE):
             return False
 
-        return {False: self._test_vt_conference_merge_drop,
-                True: self._test_vt_conference_merge_drop_cep}[use_cep](
-                    ads, call_ab_id, call_ac_id)
+        return {
+            False: self._test_vt_conference_merge_drop,
+            True: self._test_vt_conference_merge_drop_cep
+        }[use_cep](ads, call_ab_id, call_ac_id)
 
+    @test_tracker_info(uuid="5ac216a6-4ce3-4bd6-a132-8b1294e461a7")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mt_video_accept_as_voice_merge_drop(self):
         """Conference call
@@ -1864,6 +1890,7 @@
         return self._test_call_volte_add_mt_video_accept_as_voice_merge_drop(
             False)
 
+    @test_tracker_info(uuid="be1d2337-ed0d-4293-afe8-0fa677b6bee1")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mt_video_accept_as_voice_merge_drop_cep(self):
         """Conference call
@@ -1936,10 +1963,12 @@
                 CALL_STATE_ACTIVE):
             return False
 
-        return {False: self._test_vt_conference_merge_drop,
-                True: self._test_vt_conference_merge_drop_cep}[use_cep](
-                    ads, call_ab_id, call_ac_id)
+        return {
+            False: self._test_vt_conference_merge_drop,
+            True: self._test_vt_conference_merge_drop_cep
+        }[use_cep](ads, call_ab_id, call_ac_id)
 
+    @test_tracker_info(uuid="ead8b199-2703-4e6c-b55b-57c5280b52e8")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mo_voice_swap_downgrade_merge_drop(self):
         """Conference call
@@ -1959,6 +1988,7 @@
         return self._test_call_video_add_mo_voice_swap_downgrade_merge_drop(
             False)
 
+    @test_tracker_info(uuid="fb52dc54-0b11-46d4-a619-412eda2df390")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mo_voice_swap_downgrade_merge_drop_cep(self):
         """Conference call
@@ -2042,8 +2072,8 @@
         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)))
+            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)
@@ -2059,16 +2089,16 @@
             return False
 
         self.log.info("Step5: Disable camera on PhoneA and PhoneB.")
-        if not video_call_downgrade(self.log, ads[0], call_id_video_ab, ads[1],
-                                    get_call_id_in_video_state(
-                                        self.log, ads[1],
-                                        VT_STATE_BIDIRECTIONAL)):
+        if not video_call_downgrade(
+                self.log, ads[0], call_id_video_ab, ads[1],
+                get_call_id_in_video_state(self.log, ads[1],
+                                           VT_STATE_BIDIRECTIONAL)):
             self.log.error("Failed to disable video on PhoneA.")
             return False
-        if not video_call_downgrade(
-                self.log, ads[1], get_call_id_in_video_state(
-                    self.log, ads[1], VT_STATE_TX_ENABLED), ads[0],
-                call_id_video_ab):
+        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
 
@@ -2084,10 +2114,12 @@
                 CALL_STATE_HOLDING):
             return False
 
-        return {False: self._test_vt_conference_merge_drop,
-                True: self._test_vt_conference_merge_drop_cep}[use_cep](
-                    ads, call_id_video_ab, call_id_voice_ac)
+        return {
+            False: self._test_vt_conference_merge_drop,
+            True: self._test_vt_conference_merge_drop_cep
+        }[use_cep](ads, call_id_video_ab, call_id_voice_ac)
 
+    @test_tracker_info(uuid="cd051485-80be-47a3-b249-e09bf786a012")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mt_voice_swap_downgrade_merge_drop(self):
         """Conference call
@@ -2107,6 +2139,7 @@
         return self._test_call_video_add_mt_voice_swap_downgrade_merge_drop(
             False)
 
+    @test_tracker_info(uuid="3e171185-7bfc-4db3-8b0b-f0f1a1b79698")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_video_add_mt_voice_swap_downgrade_merge_drop_cep(self):
         """Conference call
@@ -2209,16 +2242,16 @@
             return False
 
         self.log.info("Step5: Disable camera on PhoneA and PhoneB.")
-        if not video_call_downgrade(self.log, ads[0], call_id_video_ab, ads[1],
-                                    get_call_id_in_video_state(
-                                        self.log, ads[1],
-                                        VT_STATE_BIDIRECTIONAL)):
+        if not video_call_downgrade(
+                self.log, ads[0], call_id_video_ab, ads[1],
+                get_call_id_in_video_state(self.log, ads[1],
+                                           VT_STATE_BIDIRECTIONAL)):
             self.log.error("Failed to disable video on PhoneA.")
             return False
-        if not video_call_downgrade(
-                self.log, ads[1], get_call_id_in_video_state(
-                    self.log, ads[1], VT_STATE_TX_ENABLED), ads[0],
-                call_id_video_ab):
+        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
 
@@ -2234,10 +2267,12 @@
                 CALL_STATE_HOLDING):
             return False
 
-        return {False: self._test_vt_conference_merge_drop,
-                True: self._test_vt_conference_merge_drop_cep}[use_cep](
-                    ads, call_id_video_ab, call_id_voice_ac)
+        return {
+            False: self._test_vt_conference_merge_drop,
+            True: self._test_vt_conference_merge_drop_cep
+        }[use_cep](ads, call_id_video_ab, call_id_voice_ac)
 
+    @test_tracker_info(uuid="3dd68dee-87ca-4e9b-8de8-dba6dc8f5725")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mo_video_downgrade_merge_drop(self):
         """Conference call
@@ -2255,6 +2290,7 @@
         """
         return self._test_call_volte_add_mo_video_downgrade_merge_drop(False)
 
+    @test_tracker_info(uuid="823f9b6a-7812-4f17-9534-e784a623e7e2")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mo_video_downgrade_merge_drop_cep(self):
         """Conference call
@@ -2335,16 +2371,16 @@
             return False
 
         self.log.info("Step4: Disable camera on PhoneA and PhoneC.")
-        if not video_call_downgrade(self.log, ads[0], call_id_video_ac, ads[2],
-                                    get_call_id_in_video_state(
-                                        self.log, ads[2],
-                                        VT_STATE_BIDIRECTIONAL)):
+        if not video_call_downgrade(
+                self.log, ads[0], call_id_video_ac, ads[2],
+                get_call_id_in_video_state(self.log, ads[2],
+                                           VT_STATE_BIDIRECTIONAL)):
             self.log.error("Failed to disable video on PhoneA.")
             return False
-        if not video_call_downgrade(
-                self.log, ads[2], get_call_id_in_video_state(
-                    self.log, ads[2], VT_STATE_TX_ENABLED), ads[0],
-                call_id_video_ac):
+        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
 
@@ -2360,10 +2396,12 @@
                 CALL_STATE_HOLDING):
             return False
 
-        return {False: self._test_vt_conference_merge_drop,
-                True: self._test_vt_conference_merge_drop_cep}[use_cep](
-                    ads, call_id_video_ac, call_id_voice_ab)
+        return {
+            False: self._test_vt_conference_merge_drop,
+            True: self._test_vt_conference_merge_drop_cep
+        }[use_cep](ads, call_id_video_ac, call_id_voice_ab)
 
+    @test_tracker_info(uuid="9926fb63-8230-461a-8c8c-1a9556fbb2a9")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mt_video_downgrade_merge_drop(self):
         """Conference call
@@ -2381,6 +2419,7 @@
         """
         return self._test_call_volte_add_mt_video_downgrade_merge_drop(False)
 
+    @test_tracker_info(uuid="26b72fda-1a25-47fb-8acb-6f750be8237a")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_add_mt_video_downgrade_merge_drop_cep(self):
         """Conference call
@@ -2463,16 +2502,16 @@
             return False
 
         self.log.info("Step4: Disable camera on PhoneA and PhoneC.")
-        if not video_call_downgrade(self.log, ads[0], call_id_video_ac, ads[2],
-                                    get_call_id_in_video_state(
-                                        self.log, ads[2],
-                                        VT_STATE_BIDIRECTIONAL)):
+        if not video_call_downgrade(
+                self.log, ads[0], call_id_video_ac, ads[2],
+                get_call_id_in_video_state(self.log, ads[2],
+                                           VT_STATE_BIDIRECTIONAL)):
             self.log.error("Failed to disable video on PhoneA.")
             return False
-        if not video_call_downgrade(
-                self.log, ads[2], get_call_id_in_video_state(
-                    self.log, ads[2], VT_STATE_TX_ENABLED), ads[0],
-                call_id_video_ac):
+        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
 
@@ -2488,10 +2527,12 @@
                 CALL_STATE_HOLDING):
             return False
 
-        return {False: self._test_vt_conference_merge_drop,
-                True: self._test_vt_conference_merge_drop_cep}[use_cep](
-                    ads, call_id_video_ac, call_id_voice_ab)
+        return {
+            False: self._test_vt_conference_merge_drop,
+            True: self._test_vt_conference_merge_drop_cep
+        }[use_cep](ads, call_id_video_ac, call_id_voice_ab)
 
+    @test_tracker_info(uuid="4031040c-d077-4bf1-8a86-82f484693e64")
     @TelephonyBaseTest.tel_test_wrap
     def test_disable_data_vt_unavailable(self):
         """Disable Data, phone should no be able to make VT call.
diff --git a/acts/tests/google/tel/live/TelLiveVoiceConfTest.py b/acts/tests/google/tel/live/TelLiveVoiceConfTest.py
index 8168669..b29e48c 100644
--- a/acts/tests/google/tel/live/TelLiveVoiceConfTest.py
+++ b/acts/tests/google/tel/live/TelLiveVoiceConfTest.py
@@ -74,318 +74,6 @@
 
     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",
-        )
 
         self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
 
@@ -686,14 +374,15 @@
 
         # make sure PhoneA is CDMA phone before proceed.
         if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
-            self.log.error("{} not CDMA phone, abort this 1x test.".format(ads[
-                0].serial))
+            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_voice_3g, phone_setup_voice_general,
-             phone_setup_voice_general], [is_phone_in_call_1x, None, None])
+            [ads[0], ads[1], ads[2]], [
+                phone_setup_voice_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
@@ -703,8 +392,8 @@
         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)):
+            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
@@ -727,14 +416,15 @@
 
         # make sure PhoneA is CDMA phone before proceed.
         if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
-            self.log.error("{} not CDMA phone, abort this 1x test.".format(ads[
-                0].serial))
+            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_voice_3g, phone_setup_voice_general,
-             phone_setup_voice_general], [is_phone_in_call_1x, None, None])
+            [ads[0], ads[1], ads[2]], [
+                phone_setup_voice_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
@@ -745,8 +435,8 @@
         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)):
+            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
@@ -781,14 +471,15 @@
 
         # make sure PhoneA is CDMA phone before proceed.
         if (ads[0].droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
-            self.log.error("{} not CDMA phone, abort this 1x test.".format(ads[
-                0].serial))
+            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_voice_3g, phone_setup_voice_general,
-             phone_setup_voice_general], [is_phone_in_call_1x, None, None])
+            [ads[0], ads[1], ads[2]], [
+                phone_setup_voice_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
@@ -799,8 +490,8 @@
         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)):
+            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
@@ -882,8 +573,9 @@
         self.log.info("Drop current call on Host.")
         if not self._hangup_call(host, "Host"):
             return False
-        if not wait_and_answer_call(self.log, host, get_phone_number(
-                self.log, held_participant_ad)):
+        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)
@@ -897,8 +589,9 @@
         if not self._hangup_call(host, "Host"):
             return False
         time.sleep(WAIT_TIME_IN_CALL)
-        if not verify_incall_state(self.log, [host, held_participant_ad,
-                                              active_participant_ad], False):
+        if not verify_incall_state(self.log, [
+                host, held_participant_ad, active_participant_ad
+        ], False):
             return False
         return True
 
@@ -978,9 +671,10 @@
 
         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])
+            [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
@@ -1023,9 +717,10 @@
 
         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])
+            [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
@@ -1068,9 +763,10 @@
 
         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])
+            [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
@@ -1120,9 +816,10 @@
 
         call_ab_id = self._three_phone_call_mo_add_mo(
             [ads[0], ads[1], ads[2]],
-            [phone_setup_volte, phone_setup_voice_3g, phone_setup_voice_3g],
-            [is_phone_in_call_volte, is_phone_in_call_wcdma,
-             is_phone_in_call_wcdma])
+            [phone_setup_volte, phone_setup_voice_3g, phone_setup_voice_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
@@ -1172,9 +869,10 @@
 
         call_ab_id = self._three_phone_call_mo_add_mt(
             [ads[0], ads[1], ads[2]],
-            [phone_setup_volte, phone_setup_voice_3g, phone_setup_voice_3g],
-            [is_phone_in_call_volte, is_phone_in_call_wcdma,
-             is_phone_in_call_wcdma])
+            [phone_setup_volte, phone_setup_voice_3g, phone_setup_voice_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
@@ -1224,9 +922,10 @@
 
         call_ab_id = self._three_phone_call_mt_add_mt(
             [ads[0], ads[1], ads[2]],
-            [phone_setup_volte, phone_setup_voice_3g, phone_setup_voice_3g],
-            [is_phone_in_call_volte, is_phone_in_call_wcdma,
-             is_phone_in_call_wcdma])
+            [phone_setup_volte, phone_setup_voice_3g, phone_setup_voice_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
@@ -1270,8 +969,8 @@
         # make sure PhoneB and PhoneC are CDMA phone before proceed.
         for ad in [ads[1], ads[2]]:
             if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
-                self.log.error("{} not CDMA phone, abort 1x swap test.".format(
-                    ad.serial))
+                self.log.error(
+                    "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
                 return None, None
 
         call_ab_id = self._three_phone_call_mo_add_mo(
@@ -1321,8 +1020,8 @@
         # make sure PhoneB and PhoneC are CDMA phone before proceed.
         for ad in [ads[1], ads[2]]:
             if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
-                self.log.error("{} not CDMA phone, abort 1x swap test.".format(
-                    ad.serial))
+                self.log.error(
+                    "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
                 return None, None
 
         call_ab_id = self._three_phone_call_mo_add_mt(
@@ -1372,8 +1071,8 @@
         # make sure PhoneB and PhoneC are CDMA phone before proceed.
         for ad in [ads[1], ads[2]]:
             if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
-                self.log.error("{} not CDMA phone, abort 1x swap test.".format(
-                    ad.serial))
+                self.log.error(
+                    "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
                 return None, None
 
         call_ab_id = self._three_phone_call_mt_add_mt(
@@ -1427,9 +1126,10 @@
             return None, None
 
         call_ab_id = self._three_phone_call_mo_add_mo(
-            [ads[0], ads[1], ads[2]],
-            [phone_setup_voice_3g, phone_setup_voice_general,
-             phone_setup_voice_general], [is_phone_in_call_3g, None, None])
+            [ads[0], ads[1], ads[2]], [
+                phone_setup_voice_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
@@ -1477,9 +1177,10 @@
             return None, None
 
         call_ab_id = self._three_phone_call_mt_add_mt(
-            [ads[0], ads[1], ads[2]],
-            [phone_setup_voice_3g, phone_setup_voice_general,
-             phone_setup_voice_general], [is_phone_in_call_3g, None, None])
+            [ads[0], ads[1], ads[2]], [
+                phone_setup_voice_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
@@ -1527,9 +1228,10 @@
             return None, None
 
         call_ab_id = self._three_phone_call_mo_add_mt(
-            [ads[0], ads[1], ads[2]],
-            [phone_setup_voice_3g, phone_setup_voice_general,
-             phone_setup_voice_general], [is_phone_in_call_wcdma, None, None])
+            [ads[0], ads[1], ads[2]], [
+                phone_setup_voice_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
@@ -1577,9 +1279,10 @@
             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_csfb, None, None])
+            [ads[0], ads[1], ads[2]], [
+                phone_setup_csfb, phone_setup_voice_general,
+                phone_setup_voice_general
+            ], [is_phone_in_call_csfb, None, None])
         if call_ab_id is None:
             self.log.error("Failed to get call_ab_id")
             return None, None
@@ -1627,9 +1330,10 @@
             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_csfb, None, None])
+            [ads[0], ads[1], ads[2]], [
+                phone_setup_csfb, phone_setup_voice_general,
+                phone_setup_voice_general
+            ], [is_phone_in_call_csfb, None, None])
         if call_ab_id is None:
             self.log.error("Failed to get call_ab_id")
             return None, None
@@ -1762,14 +1466,14 @@
                     ads[0].droid.telecomCallGetCallChildren(call_conf_id)))
             return None
 
-        if (CALL_PROPERTY_CONFERENCE not in
-                ads[0].droid.telecomCallGetProperties(call_conf_id)):
+        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)):
+        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
@@ -2101,8 +1805,8 @@
 
         """
 
-        self.log.info("Hangup at {}, verify call continues.".format(
-            ad_hangup.serial))
+        self.log.info(
+            "Hangup at {}, verify call continues.".format(ad_hangup.serial))
         if not self._hangup_call(ad_hangup):
             ad_hangup.log.error("Phone fails to hang up")
             return False
@@ -2110,8 +1814,8 @@
 
         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))
+                call_id,
+                ad_verify.droid.telecomCallGetCallState(call_id), call_state))
             return False
         ad_verify.log.info("Call in expected %s state", call_state)
         # TODO: b/26296375 add voice check.
@@ -2146,9 +1850,10 @@
         # 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])
+            [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
@@ -2192,9 +1897,10 @@
         # 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])
+            [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
@@ -2238,9 +1944,10 @@
         # 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])
+            [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
@@ -2284,9 +1991,10 @@
         # 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])
+            [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
@@ -2330,9 +2038,10 @@
         # 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])
+            [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
@@ -2376,9 +2085,10 @@
         # 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])
+            [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
@@ -2429,9 +2139,10 @@
         # 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])
+            [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
@@ -2482,9 +2193,10 @@
         # 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])
+            [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
@@ -2535,9 +2247,10 @@
         # 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])
+            [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
@@ -2581,8 +2294,8 @@
         # make sure PhoneB and PhoneC are CDMA phone before proceed.
         for ad in [ads[1], ads[2]]:
             if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
-                self.log.error("{} not CDMA phone, abort 1x swap test.".format(
-                    ad.serial))
+                self.log.error(
+                    "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
                 return None, None
 
         # To make thing simple, for epdg, setup should be called before calling
@@ -2633,8 +2346,8 @@
         # make sure PhoneB and PhoneC are CDMA phone before proceed.
         for ad in [ads[1], ads[2]]:
             if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
-                self.log.error("{} not CDMA phone, abort 1x swap test.".format(
-                    ad.serial))
+                self.log.error(
+                    "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
                 return None, None
 
         # To make thing simple, for epdg, setup should be called before calling
@@ -2685,8 +2398,8 @@
         # make sure PhoneB and PhoneC are CDMA phone before proceed.
         for ad in [ads[1], ads[2]]:
             if (ad.droid.telephonyGetPhoneType() != PHONE_TYPE_CDMA):
-                self.log.error("{} not CDMA phone, abort 1x swap test.".format(
-                    ad.serial))
+                self.log.error(
+                    "{} not CDMA phone, abort 1x swap test.".format(ad.serial))
                 return None, None
 
         # To make thing simple, for epdg, setup should be called before calling
@@ -9642,9 +9355,10 @@
             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])
+            [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
@@ -9692,9 +9406,10 @@
             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])
+            [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
@@ -9848,6 +9563,30 @@
         return self._test_gsm_conference_merge_drop(call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
+    def test_gsm_mo_mo_add_swap_twice_drop_active(self):
+        """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.
+        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_gsm_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=self._get_expected_call_state(ads[0]),
+            ads_active=[ads[0], ads[1]])
+
+    @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.
diff --git a/acts/tests/google/tel/live/TelLiveVoiceTest.py b/acts/tests/google/tel/live/TelLiveVoiceTest.py
index 9826a99..4daa395 100644
--- a/acts/tests/google/tel/live/TelLiveVoiceTest.py
+++ b/acts/tests/google/tel/live/TelLiveVoiceTest.py
@@ -18,6 +18,7 @@
 """
 
 import time
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.tel.tel_subscription_utils import \
     get_subid_from_slot_index
 from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
@@ -26,9 +27,15 @@
 from acts.test_utils.tel.tel_subscription_utils import \
     set_subid_for_outgoing_call
 from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts.test_utils.tel.tel_defines import GEN_2G
+from acts.test_utils.tel.tel_defines import GEN_3G
+from acts.test_utils.tel.tel_defines import GEN_4G
 from acts.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
 from acts.test_utils.tel.tel_defines import CALL_STATE_HOLDING
 from acts.test_utils.tel.tel_defines import GEN_3G
+from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
 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
@@ -51,7 +58,10 @@
     call_voicemail_erase_all_pending_voicemail
 from acts.test_utils.tel.tel_test_utils import \
     ensure_network_generation_for_subscription
+from acts.test_utils.tel.tel_test_utils import active_file_download_task
+from acts.utils import adb_shell_ping
 from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
+from acts.test_utils.tel.tel_test_utils import ensure_network_generation
 from acts.test_utils.tel.tel_test_utils import get_phone_number
 from acts.test_utils.tel.tel_test_utils import hangup_call
 from acts.test_utils.tel.tel_test_utils import initiate_call
@@ -59,15 +69,18 @@
 from acts.test_utils.tel.tel_test_utils import multithread_func
 from acts.test_utils.tel.tel_test_utils import num_active_calls
 from acts.test_utils.tel.tel_test_utils import phone_number_formatter
+from acts.test_utils.tel.tel_test_utils import run_multithread_func
 from acts.test_utils.tel.tel_test_utils import set_call_state_listen_level
 from acts.test_utils.tel.tel_test_utils import set_phone_number
 from acts.test_utils.tel.tel_test_utils import set_wfc_mode
 from acts.test_utils.tel.tel_test_utils import setup_sim
 from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts.test_utils.tel.tel_test_utils import verify_http_connection
 from acts.test_utils.tel.tel_test_utils import verify_incall_state
+from acts.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
 from acts.test_utils.tel.tel_test_utils import wait_for_ringing_call
 from acts.test_utils.tel.tel_test_utils import wait_for_not_network_rat
-from acts.test_utils.tel.tel_test_utils import WifiUtils
+from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_1x
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
 from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
@@ -95,120 +108,24 @@
 from acts.test_utils.tel.tel_voice_utils import two_phone_call_short_seq
 
 DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION = 1 * 60 * 60  # default value 1 hour
-
+DEFAULT_PING_DURATION = 120 # in seconds
 
 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",
-
-            # 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",
-
-            # long duration voice call (to measure drop rate)
-            "test_call_long_duration_volte",
-            "test_call_long_duration_wfc",
-            "test_call_long_duration_3g")
 
         self.stress_test_number = self.get_stress_test_number()
         self.wifi_network_ssid = self.user_params["wifi_network_ssid"]
+        self.wifi_network_pass = self.user_params.get("wifi_network_pass")
 
-        try:
-            self.wifi_network_pass = self.user_params["wifi_network_pass"]
-        except KeyError:
-            self.wifi_network_pass = None
-
-        if "long_duration_call_total_duration" in self.user_params:
-            self.long_duration_call_total_duration = self.user_params[
-                "long_duration_call_total_duration"]
-        else:
-            self.long_duration_call_total_duration = DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION
+        self.long_duration_call_total_duration = self.user_params.get(
+            "long_duration_call_total_duration",
+            DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION)
 
     """ Tests Begin """
 
     @TelephonyBaseTest.tel_test_wrap
+    @test_tracker_info(uuid="8036004e-e42e-441f-b32d-96069be71ec2")
     def test_call_mo_voice_general(self):
         """ General voice to voice call.
 
@@ -232,6 +149,7 @@
                                         None, None)
 
     @TelephonyBaseTest.tel_test_wrap
+    @test_tracker_info(uuid="448e1597-c28f-4e1d-88fd-3158e6b7c630")
     def test_call_mt_voice_general(self):
         """ General voice to voice call.
 
@@ -254,6 +172,7 @@
         return two_phone_call_short_seq(self.log, ads[1], None, None, ads[0],
                                         None, None)
 
+    @test_tracker_info(uuid="b2de097b-70e1-4242-b555-c1aa0a5acd8c")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_volte(self):
         """ VoLTE to VoLTE call test
@@ -279,6 +198,7 @@
             phone_idle_volte, is_phone_in_call_volte, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="3c7f5a09-0177-4469-9994-cd5e7dd7c7fe")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_volte_7_digit_dialing(self):
         """ VoLTE to VoLTE call test, dial with 7 digit number
@@ -312,6 +232,7 @@
             set_phone_number(self.log, ads[1], callee_default_number)
         return result
 
+    @test_tracker_info(uuid="721ef935-a03c-4d0f-85b9-4753d857162f")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_volte_10_digit_dialing(self):
         """ VoLTE to VoLTE call test, dial with 10 digit number
@@ -345,6 +266,7 @@
             set_phone_number(self.log, ads[1], callee_default_number)
         return result
 
+    @test_tracker_info(uuid="4fd3aa62-2398-4cee-994e-7fc5cadbcbc1")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_volte_11_digit_dialing(self):
         """ VoLTE to VoLTE call test, dial with 11 digit number
@@ -378,6 +300,7 @@
             set_phone_number(self.log, ads[1], callee_default_number)
         return result
 
+    @test_tracker_info(uuid="969abdac-6a57-442a-9c40-48199bd8d556")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_volte_12_digit_dialing(self):
         """ VoLTE to VoLTE call test, dial with 12 digit number
@@ -411,6 +334,7 @@
             set_phone_number(self.log, ads[1], callee_default_number)
         return result
 
+    @test_tracker_info(uuid="6b13a03d-c9ff-43d7-9798-adbead7688a4")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_csfb_3g(self):
         """ VoLTE to CSFB 3G call test
@@ -435,6 +359,7 @@
             self.log, ads[0], phone_idle_volte, is_phone_in_call_volte, ads[1],
             phone_idle_csfb, is_phone_in_call_csfb, None)
 
+    @test_tracker_info(uuid="38096fdb-324a-4ce0-8836-8bbe713cffc2")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_csfb_for_tmo(self):
         """ VoLTE to CSFB 3G call test for TMobile
@@ -459,6 +384,7 @@
                                         None, ads[1], phone_idle_csfb,
                                         is_phone_in_call_csfb, None)
 
+    @test_tracker_info(uuid="82f9515d-a52b-4dec-93a5-997ffdbca76c")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_csfb_1x_long(self):
         """ VoLTE to CSFB 1x call test
@@ -490,6 +416,7 @@
             self.log, ads[0], phone_idle_volte, is_phone_in_call_volte, ads[1],
             phone_idle_csfb, is_phone_in_call_1x, None)
 
+    @test_tracker_info(uuid="2e57fad6-5eaf-4e7d-8353-8aa6f4c52776")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_csfb_long(self):
         """ VoLTE to CSFB WCDMA call test
@@ -521,6 +448,7 @@
             self.log, ads[0], phone_idle_volte, is_phone_in_call_volte, ads[1],
             phone_idle_csfb, is_phone_in_call_csfb, None)
 
+    @test_tracker_info(uuid="4bab759f-7610-4cec-893c-0a8aed95f70c")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_3g(self):
         """ VoLTE to 3G call test
@@ -545,6 +473,7 @@
             self.log, ads[0], phone_idle_volte, is_phone_in_call_volte, ads[1],
             phone_idle_3g, is_phone_in_call_3g, None)
 
+    @test_tracker_info(uuid="b394cdc5-d88d-4659-8a26-0e58fde69974")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_3g_1x_long(self):
         """ VoLTE to 3G 1x call test
@@ -575,6 +504,7 @@
             self.log, ads[0], phone_idle_volte, is_phone_in_call_volte, ads[1],
             phone_idle_3g, is_phone_in_call_1x, None)
 
+    @test_tracker_info(uuid="b39a74a9-2a89-4c0b-ac4e-71ed9317bd75")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_3g_wcdma_long(self):
         """ VoLTE to 3G WCDMA call test
@@ -606,6 +536,7 @@
             self.log, ads[0], phone_idle_volte, is_phone_in_call_volte, ads[1],
             phone_idle_3g, is_phone_in_call_wcdma, None)
 
+    @test_tracker_info(uuid="573bbcf1-6cbd-4084-9cb7-e14fb6c9521e")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_2g(self):
         """ VoLTE to 2G call test
@@ -653,7 +584,6 @@
         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,
@@ -662,11 +592,26 @@
             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)
+        ad_ping = ads[0]
 
+        call_task = (two_phone_call_short_seq,
+                     (self.log, ads[0], phone_idle_iwlan,
+                      is_phone_in_call_iwlan, ads[1], phone_idle_iwlan,
+                      is_phone_in_call_iwlan, None, WAIT_TIME_IN_CALL_FOR_IMS))
+        ping_task = (adb_shell_ping, (ad_ping, DEFAULT_PING_DURATION))
+
+        results = run_multithread_func(self.log, [ping_task, call_task])
+        if not results[1]:
+            self.log.error("Call setup failed in active ICMP transfer.")
+            return False
+        if results[0]:
+            self.log.info("ICMP transfer succeeded with parallel phone call.")
+            return True
+        else:
+            self.log.error("ICMP transfer failed with parallel phone call.")
+            return False
+
+    @test_tracker_info(uuid="a4a043c0-f4ba-4405-9262-42c752cc4487")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_epdg_wfc_wifi_only(self):
         """ WiFi Only, WiFi calling to WiFi Calling test
@@ -683,6 +628,7 @@
             self.android_devices, False, WFC_MODE_WIFI_ONLY,
             self.wifi_network_ssid, self.wifi_network_pass)
 
+    @test_tracker_info(uuid="ae171d58-d4c1-43f7-aa93-4860b4b28d53")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_epdg_wfc_wifi_preferred(self):
         """ WiFi Preferred, WiFi calling to WiFi Calling test
@@ -699,6 +645,7 @@
             self.android_devices, False, WFC_MODE_WIFI_PREFERRED,
             self.wifi_network_ssid, self.wifi_network_pass)
 
+    @test_tracker_info(uuid="ece58857-fedc-49a9-bf10-b76bd78a51f2")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_epdg_wfc_cellular_preferred(self):
         """ Cellular Preferred, WiFi calling to WiFi Calling test
@@ -712,9 +659,8 @@
             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)),
+        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))]
@@ -726,6 +672,7 @@
             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)
 
+    @test_tracker_info(uuid="0d63c250-d9e7-490c-8c48-0a6afbad5f88")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_epdg_apm_wfc_wifi_only(self):
         """ Airplane + WiFi Only, WiFi calling to WiFi Calling test
@@ -742,6 +689,7 @@
             self.android_devices, True, WFC_MODE_WIFI_ONLY,
             self.wifi_network_ssid, self.wifi_network_pass)
 
+    @test_tracker_info(uuid="7678e4ee-29c6-4319-93ab-d555501d1876")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_epdg_apm_wfc_wifi_preferred(self):
         """ Airplane + WiFi Preferred, WiFi calling to WiFi Calling test
@@ -758,6 +706,7 @@
             self.android_devices, True, WFC_MODE_WIFI_PREFERRED,
             self.wifi_network_ssid, self.wifi_network_pass)
 
+    @test_tracker_info(uuid="8f5c637e-683a-448d-9443-b2b39626ab19")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_epdg_apm_wfc_cellular_preferred(self):
         """ Airplane + Cellular Preferred, WiFi calling to WiFi Calling test
@@ -774,6 +723,7 @@
             self.android_devices, True, WFC_MODE_CELLULAR_PREFERRED,
             self.wifi_network_ssid, self.wifi_network_pass)
 
+    @test_tracker_info(uuid="0b51666e-c83c-40b5-ba0f-737e64bc82a2")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_volte_wfc_wifi_only(self):
         """ WiFi Only, WiFi calling to VoLTE test
@@ -801,6 +751,7 @@
             phone_idle_volte, is_phone_in_call_volte, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="6e0630a9-63b2-4ea1-8ec9-6560f001905c")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_volte_wfc_wifi_preferred(self):
         """ WiFi Preferred, WiFi calling to VoLTE test
@@ -828,6 +779,7 @@
             phone_idle_volte, is_phone_in_call_volte, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="51077985-2229-491f-9a54-1ff53871758c")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_volte_apm_wfc_wifi_only(self):
         """ Airplane + WiFi Only, WiFi calling to VoLTE test
@@ -855,6 +807,7 @@
             phone_idle_volte, is_phone_in_call_volte, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="fff9edcd-1ace-4f2d-a09b-06f3eea56cca")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_volte_apm_wfc_wifi_preferred(self):
         """ Airplane + WiFi Preferred, WiFi calling to VoLTE test
@@ -882,6 +835,7 @@
             phone_idle_volte, is_phone_in_call_volte, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="8591554e-4e38-406c-97bf-8921d5329c47")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_csfb_3g_wfc_wifi_only(self):
         """ WiFi Only, WiFi calling to CSFB 3G test
@@ -908,6 +862,7 @@
             self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
             phone_idle_csfb, is_phone_in_call_csfb, None)
 
+    @test_tracker_info(uuid="9711888d-5b1e-4d05-86e9-98f94f46098b")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_csfb_3g_wfc_wifi_preferred(self):
         """ WiFi Preferred, WiFi calling to CSFB 3G test
@@ -934,6 +889,7 @@
             self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
             phone_idle_csfb, is_phone_in_call_csfb, None)
 
+    @test_tracker_info(uuid="902c96a4-858f-43ff-bd56-6d7d27004320")
     @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
@@ -960,6 +916,7 @@
             self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
             phone_idle_csfb, is_phone_in_call_csfb, None)
 
+    @test_tracker_info(uuid="362a5396-ebda-4706-a73a-d805e5028fd7")
     @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
@@ -986,6 +943,7 @@
             self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
             phone_idle_csfb, is_phone_in_call_csfb, None)
 
+    @test_tracker_info(uuid="647bb859-46bc-4e3e-b6ab-7944d3bbcc26")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_3g_wfc_wifi_only(self):
         """ WiFi Only, WiFi calling to 3G test
@@ -1012,6 +970,7 @@
             self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
             phone_idle_3g, is_phone_in_call_3g, None)
 
+    @test_tracker_info(uuid="3688ea1f-a52d-4a35-9df4-d5ed0985e49b")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_3g_wfc_wifi_preferred(self):
         """ WiFi Preferred, WiFi calling to 3G test
@@ -1038,6 +997,7 @@
             self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
             phone_idle_3g, is_phone_in_call_3g, None)
 
+    @test_tracker_info(uuid="f4efc821-fbaf-4ec2-b89b-5a47354344f0")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_3g_apm_wfc_wifi_only(self):
         """ Airplane + WiFi Only, WiFi calling to 3G test
@@ -1064,6 +1024,7 @@
             self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
             phone_idle_3g, is_phone_in_call_3g, None)
 
+    @test_tracker_info(uuid="2b1345b7-3b62-44bd-91ad-9c5a4925b0e1")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_3g_apm_wfc_wifi_preferred(self):
         """ Airplane + WiFi Preferred, WiFi calling to 3G test
@@ -1090,6 +1051,7 @@
             self.log, ads[0], phone_idle_iwlan, is_phone_in_call_iwlan, ads[1],
             phone_idle_3g, is_phone_in_call_3g, None)
 
+    @test_tracker_info(uuid="7b3fea22-114a-442e-aa12-dde3b6001681")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_csfb_3g_to_csfb_3g(self):
         """ CSFB 3G to CSFB 3G call test
@@ -1114,6 +1076,7 @@
             self.log, ads[0], phone_idle_csfb, is_phone_in_call_csfb, ads[1],
             phone_idle_csfb, is_phone_in_call_csfb, None)
 
+    @test_tracker_info(uuid="91d751ea-40c8-4ffc-b9d3-03d0ad0902bd")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_3g_to_3g(self):
         """ 3G to 3G call test
@@ -1138,6 +1101,7 @@
             self.log, ads[0], phone_idle_3g, is_phone_in_call_3g, ads[1],
             phone_idle_3g, is_phone_in_call_3g, None)
 
+    @test_tracker_info(uuid="df57c481-010a-4d21-a5c1-5116917871b2")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_volte_long(self):
         """ VoLTE to VoLTE call test
@@ -1165,6 +1129,7 @@
             phone_idle_volte, is_phone_in_call_volte, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="b0712d8a-71cf-405f-910c-8592da082660")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_epdg_long_wfc_wifi_only(self):
         """ WiFi Only, WiFi calling to WiFi Calling test
@@ -1196,6 +1161,7 @@
             phone_idle_iwlan, is_phone_in_call_iwlan, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="7049de19-3abf-48df-868f-18d0af829393")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_to_epdg_long_wfc_wifi_preferred(self):
         """ WiFi Preferred, WiFi calling to WiFi Calling test
@@ -1227,6 +1193,7 @@
             phone_idle_iwlan, is_phone_in_call_iwlan, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="029af2a7-aba4-406b-9095-b32da57a7cdb")
     @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
@@ -1258,6 +1225,7 @@
             phone_idle_iwlan, is_phone_in_call_iwlan, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="2b926e4a-f493-41fa-98af-20d25ec132bb")
     @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
@@ -1289,6 +1257,7 @@
             phone_idle_iwlan, is_phone_in_call_iwlan, None,
             WAIT_TIME_IN_CALL_FOR_IMS)
 
+    @test_tracker_info(uuid="30d5d573-043f-4d8b-98e0-e7f7bc9b8d6f")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_csfb_3g_to_csfb_3g_long(self):
         """ CSFB 3G to CSFB 3G call test
@@ -1315,6 +1284,7 @@
             self.log, ads[0], phone_idle_csfb, is_phone_in_call_csfb, ads[1],
             phone_idle_csfb, is_phone_in_call_csfb, None)
 
+    @test_tracker_info(uuid="54768178-818f-4126-9e50-4f49e43a6fd3")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_3g_to_3g_long(self):
         """ 3G to 3G call test
@@ -1341,6 +1311,7 @@
             self.log, ads[0], phone_idle_3g, is_phone_in_call_3g, ads[1],
             phone_idle_3g, is_phone_in_call_3g, None)
 
+    @test_tracker_info(uuid="")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_to_volte_loop(self):
         """ Stress test: VoLTE to VoLTE call test
@@ -1387,14 +1358,15 @@
                 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:
+            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
 
+    @test_tracker_info(uuid="dfa2c1a7-0e9a-42f2-b3ba-7e196df87e1b")
     @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
@@ -1445,14 +1417,15 @@
                 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:
+            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
 
+    @test_tracker_info(uuid="382f97ad-65d4-4ebb-a31b-aa243e01bce4")
     @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
@@ -1503,14 +1476,15 @@
                 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:
+            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
 
+    @test_tracker_info(uuid="c820e2ea-8a14-421c-b608-9074b716f7dd")
     @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
@@ -1561,14 +1535,15 @@
                 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:
+            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
 
+    @test_tracker_info(uuid="3b8cb344-1551-4244-845d-b864501f2fb4")
     @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
@@ -1619,14 +1594,15 @@
                 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:
+            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
 
+    @test_tracker_info(uuid="")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_csfb_3g_to_csfb_3g_loop(self):
         """ Stress test: CSFB 3G to CSFB 3G call test
@@ -1673,12 +1649,13 @@
 
         self.log.info("Final Count - Success: {}, Failure: {}".format(
             success_count, fail_count))
-        if success_count / (
-                success_count + fail_count) >= MINIMUM_SUCCESS_RATE:
+        if success_count / (success_count + fail_count
+                            ) >= MINIMUM_SUCCESS_RATE:
             return True
         else:
             return False
 
+    @test_tracker_info(uuid="")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_3g_to_3g_loop(self):
         """ Stress test: 3G to 3G call test
@@ -1725,8 +1702,8 @@
 
         self.log.info("Final Count - Success: {}, Failure: {}".format(
             success_count, fail_count))
-        if success_count / (
-                success_count + fail_count) >= MINIMUM_SUCCESS_RATE:
+        if success_count / (success_count + fail_count
+                            ) >= MINIMUM_SUCCESS_RATE:
             return True
         else:
             return False
@@ -1788,6 +1765,7 @@
 
         return True
 
+    @test_tracker_info(uuid="4043c68a-c5d4-4e1d-9010-ef65b205cab1")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_mo_hold_unhold_wfc_wifi_only(self):
         """ WiFi Only, WiFi calling MO call hold/unhold test
@@ -1817,12 +1795,13 @@
             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):
+        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):
@@ -1831,6 +1810,7 @@
 
         return True
 
+    @test_tracker_info(uuid="0667535e-dcad-49f0-9b4b-fa45d6c75f5b")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_mo_hold_unhold_wfc_wifi_preferred(self):
         """ WiFi Preferred, WiFi calling MO call hold/unhold test
@@ -1860,12 +1840,13 @@
             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):
+        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):
@@ -1874,6 +1855,7 @@
 
         return True
 
+    @test_tracker_info(uuid="cf318b4c-c920-4e80-b73f-2f092c03a144")
     @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
@@ -1903,12 +1885,13 @@
             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):
+        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):
@@ -1917,6 +1900,7 @@
 
         return True
 
+    @test_tracker_info(uuid="ace36801-1e7b-4f06-aa0b-17affc8df069")
     @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
@@ -1946,12 +1930,13 @@
             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):
+        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):
@@ -1960,6 +1945,7 @@
 
         return True
 
+    @test_tracker_info(uuid="2ad32874-0d39-4475-8ae3-d6dccda675f5")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_mt_hold_unhold_wfc_wifi_only(self):
         """ WiFi Only, WiFi calling MT call hold/unhold test
@@ -1989,12 +1975,13 @@
             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):
+        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):
@@ -2003,6 +1990,7 @@
 
         return True
 
+    @test_tracker_info(uuid="3efd5d59-30ee-45f5-8966-56ce8fadf9a1")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_mt_hold_unhold_wfc_wifi_preferred(self):
         """ WiFi Preferred, WiFi calling MT call hold/unhold test
@@ -2032,12 +2020,13 @@
             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):
+        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):
@@ -2046,6 +2035,7 @@
 
         return True
 
+    @test_tracker_info(uuid="35ed0f89-7435-4d3b-9ebc-c5cdc3f7e32b")
     @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
@@ -2075,12 +2065,13 @@
             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):
+        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):
@@ -2089,6 +2080,7 @@
 
         return True
 
+    @test_tracker_info("37ad003b-6426-42f7-b528-ec7c1842fd18")
     @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
@@ -2118,12 +2110,13 @@
             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):
+        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):
@@ -2132,6 +2125,7 @@
 
         return True
 
+    @test_tracker_info(uuid="fa37cd37-c30a-4caa-80b4-52507995ec77")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_mo_hold_unhold(self):
         """ VoLTE MO call hold/unhold test
@@ -2159,12 +2153,13 @@
             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):
+        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):
@@ -2173,6 +2168,7 @@
 
         return True
 
+    @test_tracker_info(uuid="28a9acb3-83e8-4dd1-82bf-173da8bd2eca")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_volte_mt_hold_unhold(self):
         """ VoLTE MT call hold/unhold test
@@ -2200,12 +2196,13 @@
             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):
+        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):
@@ -2214,6 +2211,7 @@
 
         return True
 
+    @test_tracker_info(uuid="ffe724ae-4223-4c15-9fed-9aba17de9a63")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_wcdma_mo_hold_unhold(self):
         """ MO WCDMA hold/unhold test
@@ -2245,12 +2243,13 @@
             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):
+        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):
@@ -2259,6 +2258,7 @@
 
         return True
 
+    @test_tracker_info(uuid="23805165-01ce-4351-83d3-73c9fb3bda76")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_wcdma_mt_hold_unhold(self):
         """ MT WCDMA hold/unhold test
@@ -2290,12 +2290,13 @@
             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):
+        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):
@@ -2304,6 +2305,7 @@
 
         return True
 
+    @test_tracker_info(uuid="08c846c7-1978-4ece-8f2c-731129947699")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_csfb_mo_hold_unhold(self):
         """ MO CSFB WCDMA/GSM hold/unhold test
@@ -2335,12 +2337,13 @@
             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):
+        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):
@@ -2349,6 +2352,7 @@
 
         return True
 
+    @test_tracker_info(uuid="a6405fe6-c732-4ae6-bbae-e912a124f4a2")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_csfb_mt_hold_unhold(self):
         """ MT CSFB WCDMA/GSM hold/unhold test
@@ -2380,12 +2384,13 @@
             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):
+        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):
@@ -2394,6 +2399,7 @@
 
         return True
 
+    @test_tracker_info(uuid="5edc5034-90ef-4113-926f-05407ed60a87")
     @TelephonyBaseTest.tel_test_wrap
     def test_erase_all_pending_voicemail(self):
         """Script for TMO/ATT/SPT phone to erase all pending voice mail.
@@ -2422,6 +2428,7 @@
         return call_voicemail_erase_all_pending_voicemail(
             self.log, self.android_devices[1])
 
+    @test_tracker_info(uuid="c81156a2-089b-4b10-ba80-7afea61d06c6")
     @TelephonyBaseTest.tel_test_wrap
     def test_voicemail_indicator_volte(self):
         """Test Voice Mail notification in LTE (VoLTE enabled).
@@ -2449,6 +2456,7 @@
         return two_phone_call_leave_voice_mail(self.log, ads[0], None, None,
                                                ads[1], phone_idle_volte)
 
+    @test_tracker_info(uuid="529e12cb-3178-4d2c-b155-d5cfb1eac0c9")
     @TelephonyBaseTest.tel_test_wrap
     def test_voicemail_indicator_lte(self):
         """Test Voice Mail notification in LTE (VoLTE disabled).
@@ -2476,6 +2484,7 @@
         return two_phone_call_leave_voice_mail(self.log, ads[0], None, None,
                                                ads[1], phone_idle_csfb)
 
+    @test_tracker_info(uuid="60cef7dd-f990-4913-af9a-75e9336fc80a")
     @TelephonyBaseTest.tel_test_wrap
     def test_voicemail_indicator_3g(self):
         """Test Voice Mail notification in 3G
@@ -2503,6 +2512,7 @@
         return two_phone_call_leave_voice_mail(self.log, ads[0], None, None,
                                                ads[1], phone_idle_3g)
 
+    @test_tracker_info(uuid="e4c83cfa-db60-4258-ab69-15f7de3614b0")
     @TelephonyBaseTest.tel_test_wrap
     def test_voicemail_indicator_2g(self):
         """Test Voice Mail notification in 2G
@@ -2530,6 +2540,7 @@
         return two_phone_call_leave_voice_mail(self.log, ads[1], None, None,
                                                ads[0], phone_idle_2g)
 
+    @test_tracker_info(uuid="f0cb02fb-a028-43da-9c87-5b21b2f8549b")
     @TelephonyBaseTest.tel_test_wrap
     def test_voicemail_indicator_iwlan(self):
         """Test Voice Mail notification in WiFI Calling
@@ -2559,6 +2570,7 @@
         return two_phone_call_leave_voice_mail(self.log, ads[0], None, None,
                                                ads[1], phone_idle_iwlan)
 
+    @test_tracker_info(uuid="9bd0550e-abfd-436b-912f-571810f973d7")
     @TelephonyBaseTest.tel_test_wrap
     def test_voicemail_indicator_apm_iwlan(self):
         """Test Voice Mail notification in WiFI Calling
@@ -2588,6 +2600,7 @@
         return two_phone_call_leave_voice_mail(self.log, ads[0], None, None,
                                                ads[1], phone_idle_iwlan)
 
+    @test_tracker_info(uuid="6bd5cf0f-522e-4e4a-99bf-92ae46261d8c")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_2g_to_2g(self):
         """ Test 2g<->2g call functionality.
@@ -2612,6 +2625,7 @@
             self.log, ads[0], phone_idle_2g, is_phone_in_call_2g, ads[1],
             phone_idle_2g, is_phone_in_call_2g, None)
 
+    @test_tracker_info(uuid="6e24e64f-aa0e-4101-89ed-4cc30c738c7e")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_2g_to_2g_long(self):
         """ Test 2g<->2g call functionality.
@@ -2638,58 +2652,7 @@
             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)
-
+    @test_tracker_info(uuid="d109df55-ac2f-493f-9324-9be1d3d7d6d3")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_gsm_mo_hold_unhold(self):
         """ Test GSM call hold/unhold functionality.
@@ -2720,12 +2683,13 @@
             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):
+        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):
@@ -2734,6 +2698,7 @@
 
         return True
 
+    @test_tracker_info(uuid="a8279cda-73b3-470a-8ca7-a331ef99270b")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_gsm_mt_hold_unhold(self):
         """ Test GSM call hold/unhold functionality.
@@ -2764,12 +2729,13 @@
             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):
+        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):
@@ -2782,13 +2748,15 @@
         ads = self.android_devices
         self.log.info("Long Duration Call Test. Total duration = {}".format(
             total_duration))
-        return call_setup_teardown(self.log,
-                                   ads[0],
-                                   ads[1],
-                                   ads[0],
-                                   verify_caller_func=dut_incall_check_func,
-                                   wait_time_in_call=total_duration)
+        return call_setup_teardown(
+            self.log,
+            ads[0],
+            ads[1],
+            ads[0],
+            verify_caller_func=dut_incall_check_func,
+            wait_time_in_call=total_duration)
 
+    @test_tracker_info(uuid="d0008b51-25ed-414a-9b82-3ffb139a6e0d")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_long_duration_volte(self):
         """ Test call drop rate for VoLTE long duration call.
@@ -2816,6 +2784,7 @@
         return self._test_call_long_duration(
             is_phone_in_call_volte, self.long_duration_call_total_duration)
 
+    @test_tracker_info(uuid="d4c1aec0-df05-403f-954c-496faf18605a")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_long_duration_wfc(self):
         """ Test call drop rate for WiFi Calling long duration call.
@@ -2845,6 +2814,7 @@
         return self._test_call_long_duration(
             is_phone_in_call_iwlan, self.long_duration_call_total_duration)
 
+    @test_tracker_info(uuid="bc44f3ca-2616-4024-b959-3a5a85503dfd")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_long_duration_3g(self):
         """ Test call drop rate for 3G long duration call.
@@ -2896,13 +2866,13 @@
 
         ad_caller.droid.telecomCallClearCallList()
         if num_active_calls(self.log, ad_caller) != 0:
-            self.log.error("Phone {} has ongoing calls.".format(
-                ad_caller.serial))
+            self.log.error(
+                "Phone {} has ongoing calls.".format(ad_caller.serial))
             return False
 
         if not initiate_call(self.log, ad_caller, callee_number):
-            self.log.error("Phone was {} unable to initate a call".format(ads[
-                0].serial))
+            self.log.error(
+                "Phone was {} unable to initate a call".format(ads[0].serial))
             return False
 
         if not wait_for_ringing_call(self.log, ad_callee, caller_number):
@@ -2915,6 +2885,7 @@
 
         return True
 
+    @test_tracker_info(uuid="ef4fb42d-9040-46f2-9626-d0a2e1dd854f")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_mo_hangup_while_ringing(self):
         """ Call a phone and verify ringing, then hangup from the originator
@@ -2930,6 +2901,7 @@
         return self._test_call_hangup_while_ringing(self.android_devices[0],
                                                     self.android_devices[1])
 
+    @test_tracker_info(uuid="f514ac72-d551-4e21-b5af-bd87b6cdf34a")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_mt_hangup_while_ringing(self):
         """ Call a phone and verify ringing, then hangup from the originator
@@ -2945,5 +2917,427 @@
         return self._test_call_hangup_while_ringing(self.android_devices[1],
                                                     self.android_devices[0])
 
+    def _test_call_setup_in_active_data_transfer(
+            self,
+            nw_gen=None,
+            call_direction=DIRECTION_MOBILE_ORIGINATED,
+            allow_data_transfer_interruption=False):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+
+        def _call_setup_teardown(log, ad_caller, ad_callee, ad_hangup,
+                                 caller_verifier, callee_verifier,
+                                 wait_time_in_call):
+            #wait time for active data transfer
+            time.sleep(10)
+            return call_setup_teardown(log, ad_caller, ad_callee, ad_hangup,
+                                       caller_verifier, callee_verifier,
+                                       wait_time_in_call)
+
+        if nw_gen:
+            if not ensure_network_generation(
+                    self.log, self.android_devices[0], nw_gen,
+                    MAX_WAIT_TIME_NW_SELECTION, NETWORK_SERVICE_DATA):
+                self.log.error("Device failed to reselect in %s.",
+                               MAX_WAIT_TIME_NW_SELECTION)
+                return False
+
+            #toggle_airplane_mode(self.log, self.android_devices[0], False)
+            #wifi_toggle_state(self.log, self.android_devices[0], False)
+
+            self.android_devices[0].droid.telephonyToggleDataConnection(True)
+            if not wait_for_cell_data_connection(
+                    self.log, self.android_devices[0], True):
+                self.log.error("Data connection is not on cell")
+                return False
+
+        if not verify_http_connection(self.log, self.android_devices[0]):
+            self.log.error("HTTP connection is not available")
+            return False
+
+        if call_direction == DIRECTION_MOBILE_ORIGINATED:
+            ad_caller = self.android_devices[0]
+            ad_callee = self.android_devices[1]
+        else:
+            ad_caller = self.android_devices[1]
+            ad_callee = self.android_devices[0]
+        ad_download = self.android_devices[0]
+
+        call_task = (_call_setup_teardown, (self.log, ad_caller, ad_callee,
+                                            ad_caller, None, None, 60))
+        download_task = active_file_download_task(self.log, ad_download)
+        results = run_multithread_func(self.log, [download_task, call_task])
+        if not results[1]:
+            self.log.error("Call setup failed in active data transfer.")
+            return False
+        if not allow_data_transfer_interruption:
+            if results[0]:
+                self.log.info(
+                    "Data transfer succeeded with parallel phone call.")
+                return True
+            else:
+                self.log.error(
+                    "Data transfer failed with parallel phone call.")
+                return False
+        self.log.info("Retry data transfer after call hung up")
+        #return http_file_download_by_adb(self.log, self.android_devices[0],
+        #                                 url, output_path, file_size)
+        return download_task[0](*download_task[1])
+
+    @test_tracker_info(uuid="aa40e7e1-e64a-480b-86e4-db2242449555")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mo_voice_general_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MO voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        return self._test_call_setup_in_active_data_transfer(
+            None, DIRECTION_MOBILE_ORIGINATED)
+
+    @test_tracker_info(uuid="d750d66b-2091-4e8d-baa2-084b9d2bbff5")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mt_voice_general_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MT voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        return self._test_call_setup_in_active_data_transfer(
+            None, DIRECTION_MOBILE_TERMINATED)
+
+    @test_tracker_info(uuid="35703e83-b3e6-40af-aeaf-6b983d6205f4")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mo_voice_volte_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MO voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_volte(self.log, self.android_devices[0]):
+            self.android_devices[0].log.error("Failed to setup VoLTE")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            GEN_4G, DIRECTION_MOBILE_ORIGINATED)
+
+    @test_tracker_info(uuid="a0f658d9-4212-44db-b3e8-7202f1eec04d")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mt_voice_volte_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MT voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_volte(self.log, self.android_devices[0]):
+            self.android_devices[0].log.error("Failed to setup VoLTE")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            GEN_4G, DIRECTION_MOBILE_TERMINATED)
+
+    @test_tracker_info(uuid="e0b264ec-fc29-411e-b018-684b7ff5a37e")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mo_voice_csfb_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MO voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_csfb(self.log, self.android_devices[0]):
+            self.android_devices[0].log.error("Failed to setup VoLTE")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            GEN_4G,
+            DIRECTION_MOBILE_ORIGINATED,
+            allow_data_transfer_interruption=True)
+
+    @test_tracker_info(uuid="98f04a27-74e1-474d-90d1-a4a45cdb6f5b")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mt_voice_csfb_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MT voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_csfb(self.log, self.android_devices[0]):
+            self.android_devices[0].log.error("Failed to setup VoLTE")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            GEN_4G,
+            DIRECTION_MOBILE_TERMINATED,
+            allow_data_transfer_interruption=True)
+
+    @test_tracker_info(uuid="359b1ee1-36a6-427b-9d9e-4d77231fcb09")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mo_voice_3g_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MO voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_csfb(self.log, self.android_devices[0]):
+            self.android_devices[0].log.error("Failed to setup VoLTE")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            GEN_3G,
+            DIRECTION_MOBILE_ORIGINATED,
+            allow_data_transfer_interruption=True)
+
+    @test_tracker_info(uuid="b172bbb4-2d6e-4d83-a381-ebfdf23bc30e")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mt_voice_3g_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MT voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_csfb(self.log, self.android_devices[0]):
+            self.android_devices[0].log.error("Failed to setup VoLTE")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            GEN_3G,
+            DIRECTION_MOBILE_TERMINATED,
+            allow_data_transfer_interruption=True)
+
+    @test_tracker_info(uuid="f5d9bfd0-0996-4c18-b11e-c6113dc201e2")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mo_voice_2g_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MO voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_csfb(self.log, self.android_devices[0]):
+            self.android_devices[0].log.error("Failed to setup VoLTE")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            GEN_2G,
+            DIRECTION_MOBILE_ORIGINATED,
+            allow_data_transfer_interruption=True)
+
+    @test_tracker_info(uuid="99cfd1be-b992-48bf-a50e-fc3eec8e5a67")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mt_voice_2g_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, disable WiFi, enable Cellular Data.
+        Make sure phone in <nw_gen>.
+        Starting downloading file from Internet.
+        Initiate a MT voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+        Note: file download will be suspended when call is initiated if voice
+              is using voice channel and voice channel and data channel are
+              on different RATs.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_csfb(self.log, self.android_devices[0]):
+            self.android_devices[0].log.error("Failed to setup VoLTE")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            GEN_2G,
+            DIRECTION_MOBILE_TERMINATED,
+            allow_data_transfer_interruption=True)
+
+    @test_tracker_info(uuid="12677cf2-40d3-4bb1-8afa-91ebcbd0f862")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mo_voice_wifi_wfc_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, turn on wfc and wifi.
+        Starting downloading file from Internet.
+        Initiate a MO voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_iwlan(self.log, self.android_devices[0], False,
+                                 WFC_MODE_WIFI_PREFERRED,
+                                 self.wifi_network_ssid,
+                                 self.wifi_network_pass):
+            self.android_devices[0].log.error(
+                "Failed to setup IWLAN with NON-APM WIFI WFC on")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            None, DIRECTION_MOBILE_ORIGINATED)
+
+    @test_tracker_info(uuid="84adcc19-43bb-4ea3-9284-7322ab139aac")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mt_voice_wifi_wfc_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn off airplane mode, turn on wfc and wifi.
+        Starting downloading file from Internet.
+        Initiate a MT voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_iwlan(self.log, self.android_devices[0], False,
+                                 WFC_MODE_WIFI_PREFERRED,
+                                 self.wifi_network_ssid,
+                                 self.wifi_network_pass):
+            self.android_devices[0].log.error(
+                "Failed to setup iwlan with APM off and WIFI and WFC on")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            None, DIRECTION_MOBILE_TERMINATED)
+
+    @test_tracker_info(uuid="42566255-c33f-406c-abab-932a0aaa01a8")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mo_voice_apm_wifi_wfc_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn on wifi-calling, airplane mode and wifi.
+        Starting downloading file from Internet.
+        Initiate a MO voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_iwlan(self.log, self.android_devices[0], True,
+                                 WFC_MODE_WIFI_PREFERRED,
+                                 self.wifi_network_ssid,
+                                 self.wifi_network_pass):
+            self.android_devices[0].log.error(
+                "Failed to setup iwlan with APM, WIFI and WFC on")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            None, DIRECTION_MOBILE_ORIGINATED)
+
+    @test_tracker_info(uuid="fbf52f60-449b-46f2-9486-36d338a1b070")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_mt_voice_apm_wifi_wfc_in_active_data_transfer(self):
+        """Test call can be established during active data connection.
+
+        Turn on wifi-calling, airplane mode and wifi.
+        Starting downloading file from Internet.
+        Initiate a MT voice call. Verify call can be established.
+        Hangup Voice Call, verify file is downloaded successfully.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        if not phone_setup_iwlan(self.log, self.android_devices[0], True,
+                                 WFC_MODE_WIFI_PREFERRED,
+                                 self.wifi_network_ssid,
+                                 self.wifi_network_pass):
+            self.android_devices[0].log.error(
+                "Failed to setup iwlan with APM, WIFI and WFC on")
+            return False
+        return self._test_call_setup_in_active_data_transfer(
+            None, DIRECTION_MOBILE_TERMINATED)
+
 
 """ Tests End """
diff --git a/acts/tests/google/tel/live/TelPowerTest.py b/acts/tests/google/tel/live/TelPowerTest.py
index 0171dd9..e5543f1 100644
--- a/acts/tests/google/tel/live/TelPowerTest.py
+++ b/acts/tests/google/tel/live/TelPowerTest.py
@@ -91,37 +91,6 @@
 class TelPowerTest(TelephonyBaseTest):
     def __init__(self, controllers):
         TelephonyBaseTest.__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_baseline_wifi_connected",
-            "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",
-
-            # Mobile Data Always On
-            "test_power_mobile_data_always_on_lte",
-            "test_power_mobile_data_always_on_wcdma",
-            "test_power_mobile_data_always_on_gsm",
-            "test_power_mobile_data_always_on_1x",
-            "test_power_mobile_data_always_on_lte_wifi_on",
-            "test_power_mobile_data_always_on_wcdma_wifi_on",
-            "test_power_mobile_data_always_on_gsm_wifi_on",
-            "test_power_mobile_data_always_on_1x_wifi_on")
 
     def setup_class(self):
         super().setup_class()
diff --git a/acts/tests/google/tel/live/TelWifiDataTest.py b/acts/tests/google/tel/live/TelWifiDataTest.py
index 0585a9f..4ee1f2e 100644
--- a/acts/tests/google/tel/live/TelWifiDataTest.py
+++ b/acts/tests/google/tel/live/TelWifiDataTest.py
@@ -14,6 +14,7 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+from acts.test_decorators import test_tracker_info
 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
@@ -27,29 +28,144 @@
 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
+from acts.test_utils.tel.tel_test_utils import run_multithread_func
+from acts.test_utils.tel.tel_test_utils import active_file_download_test
+from acts.utils import adb_shell_ping
 
 # Attenuator name
-ATTEN_NAME_FOR_WIFI = 'wifi0'
-ATTEN_NAME_FOR_CELL = 'cell0'
+ATTEN_NAME_FOR_WIFI_2G = 'wifi0'
+ATTEN_NAME_FOR_WIFI_5G = 'wifi1'
+ATTEN_NAME_FOR_CELL_3G = 'cell0'
+ATTEN_NAME_FOR_CELL_4G = 'cell1'
+
+DEFAULT_PING_DURATION = 120
+DEFAULT_IRAT_DURATION = 60
 
 
 class TelWifiDataTest(TelephonyBaseTest):
     def __init__(self, controllers):
         TelephonyBaseTest.__init__(self, controllers)
-        self.tests = ("test_wifi_cell_switching_stress", )
+
         self.stress_test_number = self.get_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.live_network_pwd = self.user_params.get("wifi_network_pass")
 
         self.attens = {}
         for atten in self.attenuators:
             self.attens[atten.path] = atten
+        attentuator_name_list = [
+            ATTEN_NAME_FOR_WIFI_2G, ATTEN_NAME_FOR_WIFI_5G,
+            ATTEN_NAME_FOR_CELL_3G, ATTEN_NAME_FOR_CELL_4G
+        ]
+        for atten_name in attentuator_name_list:
+            set_rssi(self.log, self.attens[atten_name], 0,
+                     MAX_RSSI_RESERVED_VALUE)
+
+    def teardown_test(self):
+        super().teardown_test()
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        return True
+
+    def _basic_connectivity_check(self):
+        """
+        Set Attenuator Value for WiFi and Cell to 0
+        Make sure DUT get Cell Data coverage (LTE)
+        Make sure DUT WiFi is connected
+        """
+        toggle_airplane_mode(self.log, self.android_devices[0], False)
+        if not ensure_network_generation(self.log, self.android_devices[0],
+                                         GEN_4G, NETWORK_SERVICE_DATA):
+            return False
+
+        if not ensure_wifi_connected(self.log, self.android_devices[0],
+                                     self.live_network_ssid,
+                                     self.live_network_pwd):
+            ad.log.error("%s connect WiFI failed")
+            return False
+        return True
+
+    def _atten_setup_wifi_cell(self):
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+
+    def _atten_setup_cell_only(self):
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+
+    def _atten_setup_lte_only(self):
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+
+    def _atten_setup_wcdma_only(self):
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+
+    def _atten_setup_wifi_only(self):
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
 
     @TelephonyBaseTest.tel_test_wrap
-    def test_wifi_cell_switching_stress(self):
+    def _wifi_cell_irat_task(self, ad, irat_wait_time=60):
+        """
+        Atten only WiFi to MIN and MAX
+        WiFi --> Cellular
+        """
+        self._atten_setup_wifi_cell()
+        if (not wait_for_wifi_data_connection(self.log, ad, True,
+                                              irat_wait_time) or
+                not verify_http_connection(self.log, ad)):
+            ad.log.error("Data not on WiFi")
+            return False
+
+        ad.log.info("Triggering WiFi to Cellular IRAT")
+        self._atten_setup_cell_only()
+        if (not wait_for_cell_data_connection(self.log, ad, True,
+                                              irat_wait_time) or
+                not verify_http_connection(self.log, ad)):
+            ad.log.error("Data not on Cell")
+            return False
+        return True
+
+    @test_tracker_info(uuid="b223f74b-59f4-4eec-8785-67420bd96bd1")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wifi_cell_irat_stress_ping_continuous(self):
         """Test for data switch between WiFi and Cell. DUT go in and out WiFi
         coverage for multiple times.
 
@@ -70,70 +186,216 @@
         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_generation(self.log, self.android_devices[0],
-                                         GEN_4G, 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))
+        if not self._basic_connectivity_check():
+            self.log.error("Basic Connectivity Check Failed")
             return False
 
         total_iteration = self.stress_test_number
-        self.log.info("Stress test. Total iteration = {}.".format(
-            total_iteration))
+        ad = self.android_devices[0]
+        ping_task = (adb_shell_ping, (ad, DEFAULT_PING_DURATION,
+                                      "www.google.com", 200, 40))
+        irat_task = (self._wifi_cell_irat_task, (ad, DEFAULT_IRAT_DURATION))
         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")
+            self.log.info(">----Current iteration = %d/%d----<",
+                          current_iteration, total_iteration)
+            results = run_multithread_func(self.log, [ping_task, irat_task])
+            if not results[1]:
+                ad.log.error("Data IRAT failed in active ICMP transfer")
                 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")
+            if results[0]:
+                ad.log.info("ICMP transfer succeeded with parallel IRAT")
+            else:
+                ad.log.error("ICMP transfer failed with parallel IRAT")
                 break
-
-            self.log.info(">----Iteration : {}/{} succeed.----<".format(
-                current_iteration, total_iteration))
+            self.log.info(">----Iteration : %d/%d succeed.----<",
+                          current_iteration, total_iteration)
             current_iteration += 1
         if current_iteration <= total_iteration:
-            self.log.info(">----Iteration : {}/{} failed.----<".format(
-                current_iteration, total_iteration))
+            self.log.info(">----Iteration : %d/%d failed.----<",
+                          current_iteration, total_iteration)
             return False
         else:
             return True
 
+    @test_tracker_info(uuid="72d2aa4d-c395-417e-99c5-12dc22ea90a1")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wifi_cell_irat_stress_http_dl(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 able to download file
+        5. Set WiFi RSSI to MIN (WiFi attenuator value to MAX).
+        6. Verify DUT report Cellular Data connected and able to download file
+        7. Repeat Step 3~6 for stress number.
+
+        Expected Results:
+        4. DUT report WiFi connected and able to download file
+        6. DUT report Cellular Data connected and able to download file
+        7. Stress test should pass.
+
+        Returns:
+        True if Pass. False if fail.
+        """
+        ad = self.android_devices[0]
+        if not self._basic_connectivity_check():
+            self.log.error("Basic Connectivity Check Failed")
+            return False
+
+        total_iteration = self.stress_test_number
+        self.log.info("Stress test. Total iteration = %d.", total_iteration)
+        current_iteration = 1
+        while (current_iteration <= total_iteration):
+            self.log.info(">----Current iteration = %d/%d----<",
+                          current_iteration, total_iteration)
+
+            self._atten_setup_wifi_cell()
+            if (not wait_for_wifi_data_connection(self.log, ad, True)):
+                ad.log.error("Data not on WiFi")
+                break
+            if not active_file_download_test(self.log, ad):
+                ad.log.error("HTTP file download failed on WiFi")
+                break
+
+            self._atten_setup_cell_only()
+            if (not wait_for_cell_data_connection(self.log, ad, True)):
+                ad.log.error("Data not on Cell")
+                break
+            if not active_file_download_test(self.log, ad):
+                ad.log.error("HTTP file download failed on cell")
+                break
+
+            self.log.info(">----Iteration : %d/%d succeed.----<",
+                          current_iteration, total_iteration)
+            current_iteration += 1
+
+        if current_iteration <= total_iteration:
+            self.log.info(">----Iteration : %d/%d failed.----<",
+                          current_iteration, total_iteration)
+            return False
+        else:
+            return True
+
+    @test_tracker_info(uuid="bce71469-114c-489f-b9c4-26c53c29a553")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wifi_cell_irat_stress_ping(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.
+        """
+        ad = self.android_devices[0]
+        if not self._basic_connectivity_check():
+            self.log.error("Basic Connectivity Check Failed")
+            return False
+
+        total_iteration = self.stress_test_number
+        self.log.info("Stress test. Total iteration = %d.", total_iteration)
+        current_iteration = 1
+        while (current_iteration <= total_iteration):
+            self.log.info(">----Current iteration = %d/%d----<",
+                          current_iteration, total_iteration)
+
+            self._atten_setup_wifi_cell()
+            if (not wait_for_wifi_data_connection(self.log, ad, True) or
+                    not verify_http_connection(self.log, ad)):
+                ad.log.error("Data not on WiFi")
+                break
+
+            self._atten_setup_cell_only()
+            if (not wait_for_cell_data_connection(self.log, ad, True) or
+                    not verify_http_connection(self.log, ad)):
+                ad.log.error("Data not on Cell")
+                break
+            self.log.info(">----Iteration : %d/%d succeed.----<",
+                          current_iteration, total_iteration)
+            current_iteration += 1
+        if current_iteration <= total_iteration:
+            self.log.info(">----Iteration : %d/%d failed.----<",
+                          current_iteration, total_iteration)
+            return False
+        else:
+            return True
+
+    @test_tracker_info(uuid="696f22ef-39cd-4e15-bbb2-f836d2ee47f1")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wifi_only_http_dl(self):
+        """Test for 10MB file download on WiFi Only
+
+        Steps:
+        1. Set WiFi atten to MIN and Cellular to MAX
+        2. Start downloading 1GB file from net
+        3. Verify is the download is successfull
+
+        Expected Results:
+        1. File should download over WiFi
+
+        Returns:
+        True if Pass. False if fail.
+        """
+        ad = self.android_devices[0]
+        if not self._basic_connectivity_check():
+            self.log.error("Basic Connectivity Check Failed")
+            return False
+        self._atten_setup_wifi_only()
+        if (not wait_for_wifi_data_connection(self.log, ad, True) or
+                not verify_http_connection(self.log, ad)):
+            ad.log.error("Data not on WiFi")
+            return False
+        if not active_file_download_test(self.log, ad, "10MB"):
+            ad.log.error("HTTP file download failed on WiFi")
+            return False
+        return True
+
+    @test_tracker_info(uuid="6c9bf89b-5469-4b08-acf4-0ef651b1a318")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_lte_only_http_dl(self):
+        """Test for 1GB file download on WiFi Only
+
+        Steps:
+        1. Set WiFi atten to MIN and Cellular to MAX
+        2. Start downloading 1GB file from net
+        3. Verify is the download is successfull
+
+        Expected Results:
+        1. File should download over WiFi
+
+        Returns:
+        True if Pass. False if fail.
+        """
+        ad = self.android_devices[0]
+        if not self._basic_connectivity_check():
+            self.log.error("Basic Connectivity Check Failed")
+            return False
+        self._atten_setup_lte_only()
+        if (not wait_for_cell_data_connection(self.log, ad, True) or
+                not verify_http_connection(self.log, ad)):
+            ad.log.error("Data not on LTE")
+            return False
+        if not active_file_download_test(self.log, ad, "1GB"):
+            ad.log.error("HTTP file download failed on LTE")
+            return False
+        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
index 1c6f14f..4c437ae 100755
--- a/acts/tests/google/tel/live/TelWifiVoiceTest.py
+++ b/acts/tests/google/tel/live/TelWifiVoiceTest.py
@@ -19,6 +19,7 @@
 
 import time
 from queue import Empty
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts.test_utils.tel.tel_atten_utils import set_rssi
 from acts.test_utils.tel.tel_defines import CELL_STRONG_RSSI_VALUE
@@ -53,7 +54,7 @@
 from acts.test_utils.tel.tel_defines import NetworkCallbackAvailable
 from acts.test_utils.tel.tel_defines import NetworkCallbackLost
 from acts.test_utils.tel.tel_defines import SignalStrengthContainer
-from acts.test_utils.tel.tel_test_utils import WifiUtils
+from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
 from acts.test_utils.tel.tel_test_utils import ensure_network_generation
 from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
 from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
@@ -83,11 +84,14 @@
 from acts.test_utils.tel.tel_voice_utils import phone_idle_3g
 from acts.test_utils.tel.tel_voice_utils import phone_idle_csfb
 from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts.test_utils.tel.tel_voice_utils import phone_idle_not_iwlan
 from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
 
 # Attenuator name
-ATTEN_NAME_FOR_WIFI = 'wifi0'
-ATTEN_NAME_FOR_CELL = 'cell0'
+ATTEN_NAME_FOR_WIFI_2G = 'wifi0'
+ATTEN_NAME_FOR_WIFI_5G = 'wifi1'
+ATTEN_NAME_FOR_CELL_3G = 'cell0'
+ATTEN_NAME_FOR_CELL_4G = 'cell1'
 
 # WiFi RSSI settings for ROVE_IN test
 WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN = -60
@@ -103,153 +107,13 @@
 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_NOT_HAND_OUT = -60
 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",
-
-            # 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.stress_test_number = self.get_stress_test_number()
         self.live_network_ssid = self.user_params["wifi_network_ssid"]
 
@@ -261,7 +125,7 @@
         self.attens = {}
         for atten in self.attenuators:
             self.attens[atten.path] = atten
-            atten.set_atten(atten.get_max_atten()) # Default all attens to max
+            atten.set_atten(atten.get_max_atten())  # Default all attens to max
 
     def setup_class(self):
 
@@ -273,13 +137,21 @@
             0].droid.telephonyStartTrackingSignalStrengthChange()
 
         # Do WiFi RSSI calibration.
-        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI], 0,
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
                  MAX_RSSI_RESERVED_VALUE)
-        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL], 0,
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
                  MAX_RSSI_RESERVED_VALUE)
-        if not ensure_network_generation(self.log, self.android_devices[0],
-                                         GEN_4G, voice_or_data=NETWORK_SERVICE_DATA,
-                                         toggle_apm_after_setting=True):
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+
+        if not ensure_network_generation(
+                self.log,
+                self.android_devices[0],
+                GEN_4G,
+                voice_or_data=NETWORK_SERVICE_DATA,
+                toggle_apm_after_setting=True):
             self.log.error("Setup_class: phone failed to select to LTE.")
             return False
         if not ensure_wifi_connected(self.log, self.android_devices[0],
@@ -313,12 +185,11 @@
         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.telephonyGetSignalStrength()[
-                SignalStrengthContainer.SIGNAL_STRENGTH_LTE_DBM])
-        self.log.info(
-            "Cellular RSSI calibration info: atten=0, RSSI={}".format(
-                self.cell_rssi_with_no_atten))
+        setattr(self, "cell_rssi_with_no_atten",
+                self.android_devices[0].droid.telephonyGetSignalStrength()[
+                    SignalStrengthContainer.SIGNAL_STRENGTH_LTE_DBM])
+        self.log.info("Cellular RSSI calibration info: atten=0, RSSI={}".
+                      format(self.cell_rssi_with_no_atten))
         return True
 
     def teardown_class(self):
@@ -332,10 +203,13 @@
     def teardown_test(self):
 
         super().teardown_test()
-
-        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI], 0,
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
                  MAX_RSSI_RESERVED_VALUE)
-        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL], 0,
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
                  MAX_RSSI_RESERVED_VALUE)
         return True
 
@@ -475,8 +349,8 @@
                 return True
             else:
                 self.log.info(
-                    "Unexpected exception happened: <{}>, return False.".format(
-                        e))
+                    "Unexpected exception happened: <{}>, return False.".
+                    format(e))
                 return False
         finally:
             ensure_phones_default_state(self.log, [ads[0], ads[1]])
@@ -489,7 +363,7 @@
         return phone_idle_iwlan(self.log, self.android_devices[0])
 
     def _phone_idle_not_iwlan(self):
-        return not self._phone_idle_iwlan()
+        return phone_idle_not_iwlan(self.log, self.android_devices[0])
 
     def _phone_idle_volte(self):
         return phone_idle_volte(self.log, self.android_devices[0])
@@ -519,9 +393,8 @@
         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))
+            self.log.error("_phone_wait_for_wfc Data Rat is {}, expecting {}".
+                           format(nw_type, RAT_IWLAN))
             return False
         return True
 
@@ -557,8 +430,11 @@
     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_generation(self.log, self.android_devices[0],
-                                         GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+        if not ensure_network_generation(
+                self.log,
+                self.android_devices[0],
+                GEN_4G,
+                voice_or_data=NETWORK_SERVICE_DATA):
             return False
 
         if not set_wfc_mode(self.log, self.android_devices[0], wfc_mode):
@@ -579,12 +455,16 @@
 
     def _wfc_phone_setup_cellular_absent(self, wfc_mode):
         is_exception_happened = False
+        time.sleep(60)
         try:
             if not toggle_airplane_mode(self.log, self.android_devices[0],
                                         False):
                 raise Exception("Toggle APM failed.")
-            if not ensure_network_generation(self.log, self.android_devices[0],
-                                             GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+            if not ensure_network_generation(
+                    self.log,
+                    self.android_devices[0],
+                    GEN_4G,
+                    voice_or_data=NETWORK_SERVICE_DATA):
                 raise Exception("Ensure LTE failed.")
         except Exception:
             is_exception_happened = True
@@ -658,8 +538,11 @@
                                      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_generation(self.log, self.android_devices[0],
-                                         GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+        if not ensure_network_generation(
+                self.log,
+                self.android_devices[0],
+                GEN_4G,
+                voice_or_data=NETWORK_SERVICE_DATA):
             return False
 
         if not set_wfc_mode(self.log, self.android_devices[0], wfc_mode):
@@ -672,21 +555,24 @@
 
         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))
+                                 self.live_network_pwd, 1):
+            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
+        time.sleep(60)
         try:
             if not toggle_airplane_mode(self.log, self.android_devices[0],
                                         False):
                 raise Exception("Toggle APM failed.")
-            if not ensure_network_generation(self.log, self.android_devices[0],
-                                             GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+            if not ensure_network_generation(
+                    self.log,
+                    self.android_devices[0],
+                    GEN_4G,
+                    voice_or_data=NETWORK_SERVICE_DATA):
                 raise Exception("Ensure LTE failed.")
         except Exception:
             is_exception_happened = True
@@ -708,10 +594,9 @@
 
         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))
+                                 self.live_network_pwd, 1):
+            self.log.error("{} connect WiFI succeed, expected not succeed".
+                           format(self.android_devices[0].serial))
             return False
         return True
 
@@ -755,8 +640,11 @@
     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_generation(self.log, self.android_devices[0],
-                                         GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+        if not ensure_network_generation(
+                self.log,
+                self.android_devices[0],
+                GEN_4G,
+                voice_or_data=NETWORK_SERVICE_DATA):
             return False
 
         if not set_wfc_mode(self.log, self.android_devices[0], wfc_mode):
@@ -767,17 +655,21 @@
         toggle_airplane_mode(self.log, self.android_devices[0],
                              is_airplane_mode)
 
-        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        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
+        time.sleep(60)
         try:
             if not toggle_airplane_mode(self.log, self.android_devices[0],
                                         False):
                 raise Exception("Toggle APM failed.")
-            if not ensure_network_generation(self.log, self.android_devices[0],
-                                             GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+            if not ensure_network_generation(
+                    self.log,
+                    self.android_devices[0],
+                    GEN_4G,
+                    voice_or_data=NETWORK_SERVICE_DATA):
                 raise Exception("Ensure LTE failed.")
         except Exception:
             is_exception_happened = True
@@ -797,7 +689,7 @@
                 self.android_devices[0].serial))
             return False
 
-        WifiUtils.wifi_toggle_state(self.log, self.android_devices[0], False)
+        wifi_toggle_state(self.log, self.android_devices[0], False)
         return True
 
     def _wfc_phone_setup_apm_wifi_disabled_wifi_only(self):
@@ -837,78 +729,115 @@
 
     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)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G],
+                 self.cell_rssi_with_no_atten, CELL_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G],
                  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)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
                  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)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
+                 self.wifi_rssi_with_no_atten, WIFI_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
                  self.wifi_rssi_with_no_atten, WIFI_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
+                 self.wifi_rssi_with_no_atten, WIFI_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G],
+                 self.cell_rssi_with_no_atten, CELL_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G],
+                 self.cell_rssi_with_no_atten, CELL_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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
                  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)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
+                 self.wifi_rssi_with_no_atten, WIFI_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 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)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G],
+                 self.cell_rssi_with_no_atten, CELL_WEAK_RSSI_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G],
                  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)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G], 0,
+                 MIN_RSSI_RESERVED_VALUE)
         return True
 
     """ Tests Begin """
 
+    @test_tracker_info(uuid="a9a369bc-b8cc-467b-a847-82d004db851d")
     @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
@@ -939,6 +868,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="c88999d7-7fe7-4163-9430-4aee88852e7b")
     @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
@@ -969,6 +899,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="a4464c2c-753e-4702-b4fc-73d7bb6265da")
     @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
@@ -999,6 +930,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="797ad987-db48-456e-b092-d27be110b7ff")
     @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
@@ -1029,6 +961,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="00000f11-1749-47e9-a9b3-d67a43f97470")
     @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
@@ -1059,6 +992,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="db3e96f4-bbb6-48f8-9eb6-71f489987f8f")
     @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
@@ -1089,6 +1023,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="121574c3-4c58-4fd5-abbb-c626c5f777c8")
     @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
@@ -1120,6 +1055,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="bccff410-ada3-407a-8b50-c84b0064bd8a")
     @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
@@ -1151,6 +1087,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="a9722c73-5b6e-46d0-962c-e612df84b7b7")
     @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
@@ -1182,6 +1119,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="6ed1be09-b825-43a4-8317-822070023329")
     @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
@@ -1213,6 +1151,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="c4059ea2-732c-4a22-943c-f19ad65d5fe9")
     @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
@@ -1244,6 +1183,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="a4c000a1-b3cd-4fe2-8d82-d857fb3b2d62")
     @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
@@ -1275,6 +1215,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="d37909d3-40b4-4989-a444-fca60bd355cf")
     @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
@@ -1288,7 +1229,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -1305,6 +1245,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="bba0d159-ec45-43ea-9c1f-94f6e3a0cff8")
     @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
@@ -1334,6 +1275,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="39d6808c-37f3-4ae1-babb-2218a4827da9")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_cellular_preferred_wifi_strong_cellular_strong(
             self):
@@ -1366,6 +1308,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="ccc4973b-254f-4f12-a101-324c17e114d1")
     @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
@@ -1379,7 +1322,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -1396,6 +1338,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="cdbfca5e-06a3-4fd4-87a7-cc8028335c94")
     @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
@@ -1425,6 +1368,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="dbcbc77a-8551-4a40-88c3-3493d7c4d506")
     @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
@@ -1456,6 +1400,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="2a52dbf7-b63b-42d9-8406-09d168878041")
     @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
@@ -1487,6 +1432,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="31ae1aee-977c-45ec-abfc-24121fcd2fe9")
     @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
@@ -1518,6 +1464,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="314bf912-0fef-4047-bdeb-5cae2e89bbe6")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_cellular_preferred_wifi_absent_cellular_strong(
             self):
@@ -1550,6 +1497,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="27516aa7-0f28-4cf6-9562-4f0ad7378f64")
     @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
@@ -1581,6 +1529,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="e031a0f4-6896-454a-af70-8472a9805432")
     @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
@@ -1612,6 +1561,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="1aff2cb3-fcbe-425e-be46-2cd693b1d239")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_cellular_preferred_wifi_disabled_cellular_strong(
             self):
@@ -1644,6 +1594,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="fb24722e-3c11-4443-a65e-3bc64cff55ef")
     @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
@@ -1657,7 +1608,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -1674,6 +1624,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="5b649c6d-1fa2-4044-b487-79cb897a803f")
     @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
@@ -1703,6 +1654,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="225cc438-620f-45c4-9682-ae5f13c81b03")
     @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
@@ -1716,7 +1668,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -1733,6 +1684,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="837de903-06f2-4a60-a623-b3478a5d6639")
     @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
@@ -1746,7 +1698,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -1761,6 +1712,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="57f70488-19ec-4ca9-9837-e6acec2494ae")
     @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
@@ -1774,7 +1726,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -1791,6 +1742,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="845a62ba-3457-42ed-8a74-0f7fdde44011")
     @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
@@ -1804,7 +1756,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -1823,6 +1774,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="298f92e5-c509-42d7-bd85-d29337e391df")
     @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
@@ -1836,7 +1788,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -1855,6 +1806,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="845278c5-1442-4a3a-93cd-c661190a5574")
     @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
@@ -1886,6 +1838,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="63319009-aaef-424a-bfb1-da56d3f9a2b2")
     @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
@@ -1917,6 +1870,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="ff3f474c-5d7a-4440-b2d4-a99ccb5d2dd7")
     @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
@@ -1930,7 +1884,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -1949,6 +1902,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="7ad6d6e3-1113-4304-96fa-961983380207")
     @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
@@ -1980,6 +1934,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="3062d062-17f1-4265-8dec-ed75d5d275ee")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_cellular_preferred_wifi_disabled_cellular_weak(
             self):
@@ -2012,6 +1967,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="0f40d344-25b4-459e-a21e-79c84bb5db41")
     @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
@@ -2025,7 +1981,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2042,6 +1997,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="62c0e1c7-9fd7-4d98-87c8-a2d8df69cbd6")
     @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
@@ -2055,7 +2011,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2072,6 +2027,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="2b13f862-71e4-4b33-be0b-e83a61e3f443")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_cellular_preferred_wifi_strong_cellular_absent(
             self):
@@ -2086,7 +2042,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2103,6 +2058,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="49aa09d1-afd5-4b8a-9155-b351b1cecd83")
     @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
@@ -2116,7 +2072,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2133,6 +2088,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="29e20d21-33bd-444f-ba51-487332e8bcbb")
     @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
@@ -2146,7 +2102,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2163,6 +2118,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="f1aae6c2-0b40-4ee5-a8a9-f0036130dcf1")
     @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
@@ -2176,7 +2132,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2193,6 +2148,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="7be33498-8f1c-4462-909b-09cd9abab053")
     @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
@@ -2206,7 +2162,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2225,6 +2180,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="406c86cc-f3bb-4356-9ce7-6ae336e164f3")
     @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
@@ -2238,7 +2194,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2257,6 +2212,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="e172082b-5e88-4229-9da9-e16a74da8fbb")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_cellular_preferred_wifi_absent_cellular_absent(
             self):
@@ -2271,25 +2227,25 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
-            self._wfc_set_wifi_absent_cell_absent,
-            self._wfc_phone_setup_cellular_absent_wifi_absent_cellular_preferred,
+            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, DIRECTION_MOBILE_TERMINATED,
-            self._wfc_set_wifi_absent_cell_absent,
-            self._wfc_phone_setup_cellular_absent_wifi_absent_cellular_preferred,
+            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))
 
+    @test_tracker_info(uuid="f4a93a88-4a20-4e4a-9168-a7b1f1ad5462")
     @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
@@ -2303,7 +2259,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2322,6 +2277,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="c8f62254-2dba-4c54-a9d1-5741b6c05f10")
     @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
@@ -2335,7 +2291,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
@@ -2354,6 +2309,7 @@
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
         return ((mo_result is True) and (mt_result is True))
 
+    @test_tracker_info(uuid="1c593836-fbdd-415c-a997-5835303069ad")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_cellular_preferred_wifi_disabled_cellular_absent(
             self):
@@ -2368,25 +2324,25 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
-            self._wfc_set_wifi_strong_cell_absent,
-            self._wfc_phone_setup_cellular_absent_wifi_disabled_cellular_preferred,
+            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, DIRECTION_MOBILE_TERMINATED,
-            self._wfc_set_wifi_strong_cell_absent,
-            self._wfc_phone_setup_cellular_absent_wifi_disabled_cellular_preferred,
+            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))
 
+    @test_tracker_info(uuid="41ec5080-810a-47f0-93c6-281fc19c4d12")
     @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
@@ -2404,18 +2360,19 @@
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
             self._wfc_set_wifi_strong_cell_strong,
-            self._wfc_phone_setup_wfc_disabled, self._phone_idle_not_iwlan,
+            self._wfc_phone_setup_wfc_disabled, None,
             self._is_phone_in_call_not_iwlan, None, True)
 
         mt_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_TERMINATED,
             self._wfc_set_wifi_strong_cell_strong,
-            self._wfc_phone_setup_wfc_disabled, self._phone_idle_not_iwlan,
+            self._wfc_phone_setup_wfc_disabled, None,
             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))
 
+    @test_tracker_info(uuid="8c9dd7a3-c840-474d-b929-07e557428648")
     @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
@@ -2433,18 +2390,19 @@
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
             self._wfc_set_wifi_strong_cell_weak,
-            self._wfc_phone_setup_wfc_disabled, self._phone_idle_not_iwlan,
+            self._wfc_phone_setup_wfc_disabled, None,
             self._is_phone_in_call_not_iwlan, None, True)
 
         mt_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_TERMINATED,
             self._wfc_set_wifi_strong_cell_weak,
-            self._wfc_phone_setup_wfc_disabled, self._phone_idle_not_iwlan,
+            self._wfc_phone_setup_wfc_disabled, None,
             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))
 
+    @test_tracker_info(uuid="ddb3372d-23d2-492c-881a-e509e7ac4c8d")
     @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
@@ -2458,25 +2416,25 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         ads = [self.android_devices[0], self.android_devices[1]]
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
             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,
+            self._wfc_phone_setup_cellular_absent_wfc_disabled, None,
+            self._is_phone_not_in_call, None,
             "initiate_call fail.")
 
         mt_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_TERMINATED,
             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,
+            self._wfc_phone_setup_cellular_absent_wfc_disabled, None,
+            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))
 
+    @test_tracker_info(uuid="91232d7e-ccb5-4581-8093-1ab42eca3815")
     @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
@@ -2494,13 +2452,13 @@
         mo_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_ORIGINATED,
             self._wfc_set_wifi_strong_cell_strong,
-            self._wfc_phone_setup_apm_wfc_disabled, self._phone_idle_not_iwlan,
+            self._wfc_phone_setup_apm_wfc_disabled, None,
             self._is_phone_not_in_call, None, "initiate_call fail.")
 
         mt_result = self._wfc_call_sequence(
             ads, DIRECTION_MOBILE_TERMINATED,
             self._wfc_set_wifi_strong_cell_strong,
-            self._wfc_phone_setup_apm_wfc_disabled, self._phone_idle_not_iwlan,
+            self._wfc_phone_setup_apm_wfc_disabled, None,
             self._is_phone_not_in_call, None, "wait_and_answer_call fail.")
 
         self.log.info("MO: {}, MT: {}".format(mo_result, mt_result))
@@ -2521,8 +2479,11 @@
         # 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_generation(self.log, self.android_devices[0],
-                                         cellular_gen, voice_or_data=NETWORK_SERVICE_DATA):
+        if not ensure_network_generation(
+                self.log,
+                self.android_devices[0],
+                cellular_gen,
+                voice_or_data=NETWORK_SERVICE_DATA):
             self.log.error("_rove_in_test: {} failed to be in rat: {}".format(
                 self.android_devices[0].serial, cellular_rat))
             return False
@@ -2533,14 +2494,16 @@
 
         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))
+                                 self.live_network_pwd, 1):
+            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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_NOT_ROVE_IN, 5, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  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,
@@ -2555,7 +2518,10 @@
             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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN, 1, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  self.wifi_rssi_with_no_atten,
                  WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN, 1, 1)
         if not self._phone_idle_iwlan():
@@ -2581,17 +2547,25 @@
         Make a call.
         """
         # set up cell strong
-        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G],
+                 self.cell_rssi_with_no_atten, MAX_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G],
                  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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_INITIAL_STATE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  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_generation(self.log, self.android_devices[0],
-                                         GEN_4G, voice_or_data=NETWORK_SERVICE_DATA):
+        if not ensure_network_generation(
+                self.log,
+                self.android_devices[0],
+                GEN_4G,
+                voice_or_data=NETWORK_SERVICE_DATA):
             self.log.error("_rove_out_test: {} failed to be in rat: {}".format(
                 self.android_devices[0].serial, cellular_rat))
             return False
@@ -2616,7 +2590,10 @@
             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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_NOT_ROVE_OUT, 1, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  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,
@@ -2630,7 +2607,10 @@
             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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT, 2, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  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,
@@ -2665,6 +2645,7 @@
         else:
             return False
 
+    @test_tracker_info(uuid="7f9779c1-75c6-413e-9e3b-7e4d9896379a")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_out_in_stress(self):
         """WiFi Calling Rove out/in stress test.
@@ -2698,16 +2679,18 @@
                 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))
+        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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT, 2, 1)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      self.wifi_rssi_with_no_atten,
                      WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT, 2, 1)
             if (not wait_for_wifi_data_connection(
@@ -2722,7 +2705,10 @@
                 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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN, 2, 1)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      self.wifi_rssi_with_no_atten,
                      WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN, 2, 1)
             if (not wait_for_wifi_data_connection(
@@ -2747,6 +2733,7 @@
         else:
             return True
 
+    @test_tracker_info(uuid="3ba22f37-a9fd-4890-9805-941d80f5600d")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_in_out_stress(self):
         """WiFi Calling Rove in/out stress test.
@@ -2772,16 +2759,18 @@
             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))
+        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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN, 2, 1)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      self.wifi_rssi_with_no_atten,
                      WIFI_RSSI_FOR_ROVE_IN_TEST_PHONE_ROVE_IN, 2, 1)
             if (not wait_for_wifi_data_connection(
@@ -2797,7 +2786,10 @@
             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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT, 2, 1)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      self.wifi_rssi_with_no_atten,
                      WIFI_RSSI_FOR_ROVE_OUT_TEST_PHONE_ROVE_OUT, 2, 1)
             if (not wait_for_wifi_data_connection(
@@ -2822,6 +2814,7 @@
         else:
             return True
 
+    @test_tracker_info(uuid="791bce68-d875-41cd-addd-355ff61773b9")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_in_lte_wifi_preferred(self):
         """ Test WFC rove-in features.
@@ -2840,6 +2833,7 @@
         """
         return self._rove_in_test(GEN_4G, WFC_MODE_WIFI_PREFERRED)
 
+    @test_tracker_info(uuid="f4b70fbb-cc44-4e7c-805e-c5f28c78e2dd")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_in_lte_wifi_only(self):
         """ Test WFC rove-in features.
@@ -2856,9 +2850,9 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         return self._rove_in_test(GEN_4G, WFC_MODE_WIFI_ONLY)
 
+    @test_tracker_info(uuid="b9f3648e-7168-4c82-aec5-f4a7cc77ad99")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_in_wcdma_wifi_preferred(self):
         """ Test WFC rove-in features.
@@ -2877,6 +2871,7 @@
         """
         return self._rove_in_test(GEN_3G, WFC_MODE_WIFI_PREFERRED)
 
+    @test_tracker_info(uuid="8ce03ae7-0b21-49e4-828f-cf8dcd54ba35")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_in_wcdma_wifi_only(self):
         """ Test WFC rove-in features.
@@ -2893,9 +2888,9 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         return self._rove_in_test(GEN_3G, WFC_MODE_WIFI_ONLY)
 
+    @test_tracker_info(uuid="7784923f-10f1-4dca-bdc1-8de55b7b9d40")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_out_lte_wifi_preferred(self):
         """ Test WFC rove-out features.
@@ -2914,6 +2909,7 @@
         """
         return self._rove_out_test(GEN_4G, WFC_MODE_WIFI_PREFERRED)
 
+    @test_tracker_info(uuid="6bb42ab2-02bd-4637-b3b6-3ce4cffffda8")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_out_lte_wifi_only(self):
         """ Test WFC rove-out features.
@@ -2930,9 +2926,9 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         return self._rove_out_test(GEN_4G, WFC_MODE_WIFI_ONLY)
 
+    @test_tracker_info(uuid="3261b432-01de-4dd9-a309-ff53059df521")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_out_wcdma_wifi_preferred(self):
         """ Test WFC rove-out features.
@@ -2951,6 +2947,7 @@
         """
         return self._rove_out_test(GEN_3G, WFC_MODE_WIFI_PREFERRED)
 
+    @test_tracker_info(uuid="89fac8bf-b8e1-443a-8be7-f21f306c0d6c")
     @TelephonyBaseTest.tel_test_wrap
     def test_rove_out_wcdma_wifi_only(self):
         """ Test WFC rove-out features.
@@ -2967,7 +2964,6 @@
         Returns:
             True if pass; False if fail.
         """
-        ###########
         return self._rove_out_test(GEN_3G, WFC_MODE_WIFI_ONLY)
 
     def _increase_wifi_rssi_check_phone_hand_in(self):
@@ -2981,7 +2977,10 @@
         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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_NOT_HAND_IN, 5, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  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.
@@ -2995,7 +2994,10 @@
             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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN, 2, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  self.wifi_rssi_with_no_atten,
                  WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN, 2, 1)
         # Make sure phone hand in to iwlan.
@@ -3007,6 +3009,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="e8ba5d0f-afc5-42c2-b7f0-9f5d06774556")
     @TelephonyBaseTest.tel_test_wrap
     def test_hand_in_wifi_preferred(self):
         """WiFi Hand-In Threshold - WiFi Preferred
@@ -3035,6 +3038,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="2a20d499-80e1-4d4f-beca-05763863fbdb")
     @TelephonyBaseTest.tel_test_wrap
     def test_hand_in_out_wifi_preferred(self):
         """WiFi Hand-In-Out Threshold - WiFi Preferred
@@ -3072,7 +3076,9 @@
         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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G],
+                 self.cell_rssi_with_no_atten, CELL_WEAK_RSSI_VALUE, 1, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G],
                  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():
@@ -3087,6 +3093,7 @@
             return True
         return False
 
+    @test_tracker_info(uuid="a83734c1-db91-4ea2-9d79-02d7f803905a")
     @TelephonyBaseTest.tel_test_wrap
     def test_hand_in_cellular_preferred(self):
         """WiFi Hand-In Not Attempted - Cellular Preferred
@@ -3117,7 +3124,10 @@
         """
         # 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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_NOT_HAND_OUT, 2, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  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.
@@ -3136,7 +3146,10 @@
 
         # 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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten,
+                 WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT, 2, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  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.
@@ -3155,6 +3168,7 @@
 
         return True
 
+    @test_tracker_info(uuid="03fbc3b1-06df-4076-a91b-903a31ae3dae")
     @TelephonyBaseTest.tel_test_wrap
     def test_hand_out_wifi_preferred(self):
         """WiFi Hand-Out Threshold - WiFi Preferred
@@ -3183,6 +3197,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="59fc0104-5f4c-4aa5-b58b-e50d94fb90fe")
     @TelephonyBaseTest.tel_test_wrap
     def test_hand_out_in_wifi_preferred(self):
         """WiFi Hand-Out Threshold - WiFi Preferred
@@ -3215,9 +3230,8 @@
 
     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))
+        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.")
@@ -3229,7 +3243,10 @@
             # 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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT, 2, 1)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      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.
@@ -3249,7 +3266,10 @@
                 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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN, 2, 1)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      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.
@@ -3277,6 +3297,7 @@
         else:
             return True
 
+    @test_tracker_info(uuid="330ef8d7-3bbb-4492-84d0-43fdfe3fe78b")
     @TelephonyBaseTest.tel_test_wrap
     def test_hand_out_in_stress(self):
         """WiFi Calling Hand out/in stress test.
@@ -3305,9 +3326,8 @@
 
     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))
+        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.")
@@ -3319,7 +3339,10 @@
             # 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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_HAND_IN_TEST_PHONE_HAND_IN, 2, 1)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      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.
@@ -3339,7 +3362,10 @@
 
             # 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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     WIFI_RSSI_FOR_HAND_OUT_TEST_PHONE_HAND_OUT, 2, 1)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      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.
@@ -3368,6 +3394,7 @@
         else:
             return True
 
+    @test_tracker_info(uuid="97ae2018-935c-4dfc-bf5a-747777201ae4")
     @TelephonyBaseTest.tel_test_wrap
     def test_hand_in_out_stress(self):
         """WiFi Calling Hand in/out stress test.
@@ -3402,7 +3429,9 @@
         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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_3G],
+                 self.cell_rssi_with_no_atten, CELL_STRONG_RSSI_VALUE, 1, 1)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_CELL_4G],
                  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():
@@ -3419,6 +3448,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="2242aa49-474c-496b-be1b-ccd900523a54")
     @TelephonyBaseTest.tel_test_wrap
     def test_hand_out_cellular_preferred(self):
         """WiFi Hand-Out Threshold - Cellular Preferred
@@ -3445,7 +3475,9 @@
         PhoneA data should be on LTE.
         """
         # Decrease WiFi RSSI to <-100dBm in 30 seconds
-        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  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,
@@ -3466,6 +3498,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="40aa17a5-d9e0-4cff-9ca8-c187d7ae3ad1")
     @TelephonyBaseTest.tel_test_wrap
     def test_hand_out_wifi_only(self):
         """WiFi Hand-Out Not Attempted - WiFi Only
@@ -3484,6 +3517,7 @@
             self._is_phone_in_call_iwlan,
             self._decrease_wifi_rssi_check_phone_not_hand_out, True)
 
+    @test_tracker_info(uuid="75a0ae08-3e17-4011-8201-2634026a1fb5")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_wifi_preferred_e4g_disabled(self):
         """WiFi Calling with E4G disabled.
@@ -3498,6 +3532,7 @@
             self._wfc_phone_setup_wifi_preferred_e4g_disabled,
             self._phone_idle_iwlan, self._is_phone_in_call_iwlan, None, True)
 
+    @test_tracker_info(uuid="b6b4b423-e7bd-480d-b460-6260556ce73b")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_wifi_preferred_e4g_disabled_wifi_not_connected(
             self):
@@ -3520,7 +3555,9 @@
         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],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten, MIN_RSSI_RESERVED_VALUE)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  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,
@@ -3535,6 +3572,7 @@
             return False
         return True
 
+    @test_tracker_info(uuid="0de1cf4f-9ae3-4da6-8f0c-666b3968009b")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_epdg_wfc_wifi_preferred_e4g_disabled_leave_wifi_coverage(
             self):
@@ -3583,7 +3621,9 @@
 
         ad = self.android_devices[0]
 
-        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI],
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                 self.wifi_rssi_with_no_atten, INITIAL_RSSI)
+        set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                  self.wifi_rssi_with_no_atten, INITIAL_RSSI)
         if not ensure_wifi_connected(self.log, ad, self.live_network_ssid,
                                      self.live_network_pwd):
@@ -3595,10 +3635,9 @@
             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))
+            self.log.info("Initial RSSI: {},"
+                          "rssi_monitoring_id_lower should be available.".
+                          format(INITIAL_RSSI))
             try:
                 event = ad.ed.wait_for_event(
                     EventNetworkCallback,
@@ -3613,7 +3652,12 @@
 
             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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     HIGHER_RSSI_THRESHOLD + RSSI_THRESHOLD_MARGIN,
+                     WIFI_RSSI_CHANGE_STEP_SIZE,
+                     WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      self.wifi_rssi_with_no_atten,
                      HIGHER_RSSI_THRESHOLD + RSSI_THRESHOLD_MARGIN,
                      WIFI_RSSI_CHANGE_STEP_SIZE,
@@ -3632,7 +3676,12 @@
 
             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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     HIGHER_RSSI_THRESHOLD - RSSI_THRESHOLD_MARGIN,
+                     WIFI_RSSI_CHANGE_STEP_SIZE,
+                     WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      self.wifi_rssi_with_no_atten,
                      HIGHER_RSSI_THRESHOLD - RSSI_THRESHOLD_MARGIN,
                      WIFI_RSSI_CHANGE_STEP_SIZE,
@@ -3651,7 +3700,12 @@
 
             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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     LOWER_RSSI_THRESHOLD - RSSI_THRESHOLD_MARGIN,
+                     WIFI_RSSI_CHANGE_STEP_SIZE,
+                     WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      self.wifi_rssi_with_no_atten,
                      LOWER_RSSI_THRESHOLD - RSSI_THRESHOLD_MARGIN,
                      WIFI_RSSI_CHANGE_STEP_SIZE,
@@ -3670,7 +3724,12 @@
 
             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],
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_2G],
+                     self.wifi_rssi_with_no_atten,
+                     LOWER_RSSI_THRESHOLD + RSSI_THRESHOLD_MARGIN,
+                     WIFI_RSSI_CHANGE_STEP_SIZE,
+                     WIFI_RSSI_CHANGE_DELAY_PER_STEP)
+            set_rssi(self.log, self.attens[ATTEN_NAME_FOR_WIFI_5G],
                      self.wifi_rssi_with_no_atten,
                      LOWER_RSSI_THRESHOLD + RSSI_THRESHOLD_MARGIN,
                      WIFI_RSSI_CHANGE_STEP_SIZE,
diff --git a/acts/tests/google/tel/live/TelephonyConnectivitySanityTest.py b/acts/tests/google/tel/live/TelephonyConnectivitySanityTest.py
new file mode 100644
index 0000000..762117c
--- /dev/null
+++ b/acts/tests/google/tel/live/TelephonyConnectivitySanityTest.py
@@ -0,0 +1,546 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Sanity tests for connectivity tests in telephony
+"""
+
+import time
+
+from acts.controllers.anritsu_lib.md8475a import BtsServiceState
+from acts.controllers.anritsu_lib.md8475a import BtsTechnology
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.controllers.anritsu_lib.md8475a import ProcessingStatus
+from acts.controllers.anritsu_lib.md8475a import TriggerMessageIDs
+from acts.controllers.anritsu_lib.md8475a import TriggerMessageReply
+from acts.test_utils.tel import tel_test_utils
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+
+class TelephonyConnectivitySanityTest(TelephonyBaseTest):
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.anritsu = MD8475A(tel_test_utils.MD8475A_IP_ADDRESS)
+
+    def setup_test(self):
+        self.lte_bts, self.wcdma_bts = tel_test_utils.set_system_model(
+            self.anritsu, "LTE_WCDMA")
+        tel_test_utils.init_phone(self.droid, self.ed)
+        self.droid.telephonyStartTrackingServiceStateChange()
+        self.droid.telephonyStartTrackingDataConnectionStateChange()
+        self.log.info("Starting Simulation")
+        self.anritsu.start_simulation()
+        return True
+
+    def teardown_test(self):
+        self.droid.telephonyStopTrackingServiceStateChange()
+        self.droid.telephonyStopTrackingDataConnectionStateChange()
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        # turn off modem
+        tel_test_utils.turn_off_modem(self.droid)
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+
+    """ Tests Begin """
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_network_registration(self):
+        '''
+        Test ID: TEL-CO-01
+        Checks the Network service state  after bootup. Verifies the
+        network registration
+
+        Steps
+        -----
+        1. The device is in airplane mode
+        2. Turn off the airplane mode (This simulates the Boot up for modem)
+        3. check for the service state. expecting IN SERVICE
+        '''
+        test_status = "failed"
+        # turn on modem to start registration
+        tel_test_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_test_utils.wait_for_network_registration(
+            self.ed, self.anritsu, self.log)
+
+        if test_status == "passed":
+            self.log.info(
+                "TEL-CO-01:Network registration verification: Passed")
+            return True
+        else:
+            self.log.info(
+                "TEL-CO-01:Network registration verification: Failed")
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_network_params_verification(self):
+        '''
+        Test ID: TEL-CO-02
+        verifies the network registration parameters
+
+        Steps
+        -----
+        1. The device is in airplane mode
+        2. Turn off the airplane mode (This simulates the Boot up for modem)
+        3. check for the service state. expecting IN SERVICE
+        4. verifies the values for different parameters
+        '''
+        test_status = "failed"
+        # turn on modem to start registration
+        tel_test_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_test_utils.wait_for_network_registration(
+            self.ed, self.anritsu, self.log)
+
+        if test_status == "passed":
+            self.log.info("Verifying the NW Service Parameters")
+            expected_voice_nwtype = None
+            operator_name = None
+            mcc = None
+            mnc = None
+
+            bts_number, rat_info = self.anritsu.get_camping_cell()
+            if rat_info == BtsTechnology.WCDMA.value:
+                expected_voice_nwtype = "UMTS"
+                operator_name = tel_test_utils.WCDMA_NW_NAME
+                mcc = tel_test_utils.NW_MCC
+                mnc = tel_test_utils.NW_MNC
+            elif rat_info == BtsTechnology.LTE.value:
+                expected_voice_nwtype = "LTE"
+                operator_name = tel_test_utils.LTE_NW_NAME
+                mcc = tel_test_utils.NW_MCC
+                mnc = tel_test_utils.NW_MNC
+
+            self.log.info("VoiceNwState :{}".format(event['data'][
+                'VoiceRegState']))
+            self.log.info("VoiceNetworkType :{}".format(event['data'][
+                'VoiceNetworkType']))
+            self.log.info("DataRegState :{}".format(event['data'][
+                'DataRegState']))
+            self.log.info("DataNetworkType :{}".format(event['data'][
+                'DataNetworkType']))
+            self.log.info("OperatorName :{}".format(event['data'][
+                'OperatorName']))
+            self.log.info("OperatorId :{}".format(event['data']['OperatorId']))
+            self.log.info("Roaming :{}".format(event['data']['Roaming']))
+
+            if event['data']['VoiceNetworkType'] != expected_voice_nwtype:
+                test_status = "failed"
+                self.log.info("Error:Expected NW Type is not received")
+            if event['data']['OperatorId'][:3] != mcc:
+                test_status = "failed"
+                self.log.info("Error:Expected MNC is not received")
+            if event['data']['OperatorId'][3:] != mnc:
+                test_status = "failed"
+                self.log.info("Error:Expected MCC is not received")
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            self.log.info("Waiting for data state: DATA_CONNECTED")
+            test_status, event = tel_test_utils.wait_for_data_state(
+                self.ed, self.log, "DATA_CONNECTED", 120)
+
+        if test_status == "passed":
+            self.log.info("TEL-CO-02: Network registration parameters"
+                          " verification: Passed")
+            return True
+        else:
+            self.log.info("TEL-CO-02: Network registration parameters"
+                          " verification: Failed")
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_network_deregistration(self):
+        '''
+        Test ID: TEL-CO-03
+        verifies the network de registration
+
+        Steps
+        -----
+        1. The device is in airplane mode
+        2. Turn off the airplane mode (This simulates the Boot up for modem)
+        3. check for the service state. expecting IN SERVICE
+        4. Turn on Airplane mode (This simulates network de registration)
+        5. check for the service state. expecting POWER_OFF
+        '''
+        test_status = "failed"
+        # turn on modem to start registration
+        tel_test_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_test_utils.wait_for_network_registration(
+            self.ed, self.anritsu, self.log)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            test_status = "failed"
+            self.ed.clear_all_events()
+            self.log.info("Making device to detach from network")
+            self.droid.connectivityToggleAirplaneMode(True)
+            self.log.info("Waiting for service state: POWER_OFF")
+            test_status, event = tel_test_utils.wait_for_network_state(
+                self.ed, self.log, "POWER_OFF", 60)
+
+        if test_status == "passed":
+            self.log.info("TEL-CO-03: Network de-registration"
+                          " verification: Passed")
+            return True
+        else:
+            self.log.info("TEL-CO-03: Network de-registration"
+                          " verification: Failed")
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_network_out_of_service(self):
+        '''
+        Test ID: TEL-CO-04
+        verifies the network out of state
+
+        Steps
+        -----
+        1. The device is in airplane mode
+        2. Turn off the airplane mode (This simulates the Boot up for modem)
+        3. check for the service state. expecting IN SERVICE
+        4. Make network out of service
+        5. check for the service state. expecting OUT_OF_SERVICE
+        '''
+
+        test_status = "failed"
+        # turn on modem to start registration
+        tel_test_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_test_utils.wait_for_network_registration(
+            self.ed, self.anritsu, self.log)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            test_status = "failed"
+            self.ed.clear_all_events()
+            # This sleep is required.Sometimes Anritsu box doesn't behave as
+            # expected in executing the commands send to it without this delay.
+            # May be it is in state transition.so the test doesn't proceed.
+            # hence introduced this delay.
+            time.sleep(5)
+            bts_number, rat_info = self.anritsu.get_camping_cell()
+            self.log.info("Making the attached NW as OUT_OF_STATE")
+            if rat_info == BtsTechnology.LTE.value:
+                self.lte_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
+            else:
+                self.wcdma_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
+            self.log.info("Waiting for service state: OUT_OF_SERVICE")
+            test_status, event = tel_test_utils.wait_for_network_state(
+                self.ed, self.log, "OUT_OF_SERVICE", 90)
+
+        if test_status == "passed":
+            self.log.info("TEL-CO-04: Network out-of-service"
+                          " verification: Passed")
+            return True
+        else:
+            self.log.info("TEL-CO-04: Network out-of-service"
+                          " verification: Failed")
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_network_return_inservice(self):
+        '''
+        Test ID: TEL-CO-06
+        verifies the network returns to IN_SERVICE from OUT_OF_SERVICE
+
+        Steps
+        -----
+        1. The device is in airplane mode
+        2. Turn off the airplane mode (This simulates the Boot up for modem)
+        3. check for the service state. expecting IN SERVICE
+        4. Make the network out of service
+        5. check for the service state. expecting OUT_OF_SERVICE
+        6. Bring back the device to IN_SERVICE
+        7. check for the service state. expecting IN_SERVICE
+        '''
+        test_status = "failed"
+        # turn on modem to start registration
+        tel_test_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_test_utils.wait_for_network_registration(
+            self.ed, self.anritsu, self.log)
+        self.log.info("Waiting for data state: DATA_CONNECTED")
+        test_status, event = tel_test_utils.wait_for_data_state(
+            self.ed, self.log, "DATA_CONNECTED", 120)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            test_status = "failed"
+            self.ed.clear_all_events()
+            # This sleep is required.Sometimes Anritsu box doesn't behave as
+            # expected in executing the commands send to it without this delay.
+            # May be it is in state transition.so the test doesn't proceed.
+            # hence introduced this delay.
+            time.sleep(5)
+            bts_number, rat_info = self.anritsu.get_camping_cell()
+            self.log.info("Making the attached NW as OUT_OF_STATE")
+            if rat_info == BtsTechnology.LTE.value:
+                self.lte_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
+            else:
+                self.wcdma_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
+            self.log.info("Waiting for service state: OUT_OF_SERVICE")
+            test_status, event = tel_test_utils.wait_for_network_state(
+                self.ed, self.log, "OUT_OF_SERVICE", 120)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            test_status = "failed"
+            self.log.info("Waiting for Network registration")
+            test_status, event = tel_test_utils.wait_for_network_registration(
+                self.ed, self.anritsu, self.log)
+            self.log.info("Waiting for data state: DATA_CONNECTED")
+            test_status, event = tel_test_utils.wait_for_data_state(
+                self.ed, self.log, "DATA_CONNECTED", 120)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            test_status = "failed"
+            self.ed.clear_all_events()
+            # This sleep is required.Sometimes Anritsu box doesn't behave as
+            #  expected in executing the commands send to it without this delay.
+            # May be it is in state transition.so the test doesn't proceed.
+            # hence introduced this delay.
+            time.sleep(5)
+            bts_number, rat_info = self.anritsu.get_camping_cell()
+            self.log.info("Making the attached NW as OUT_OF_STATE")
+            if rat_info == BtsTechnology.LTE.value:
+                self.lte_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
+            else:
+                self.wcdma_bts.service_state = BtsServiceState.SERVICE_STATE_OUT
+            self.log.info("Waiting for service state: OUT_OF_SERVICE")
+            test_status, event = tel_test_utils.wait_for_network_state(
+                self.ed, self.log, "OUT_OF_SERVICE", 120)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            test_status = "failed"
+            self.ed.clear_all_events()
+            # This sleep is required.Sometimes Anritsu box doesn't behave as
+            # expected in executing the commands send to it without this delay.
+            # May be it is in state transition.so the test doesn't proceed.
+            # hence introduced this delay.
+            time.sleep(5)
+            self.log.info("Making the NW service IN_SERVICE")
+            self.lte_bts.service_state = BtsServiceState.SERVICE_STATE_IN
+            self.log.info("Waiting for Network registration")
+            test_status, event = tel_test_utils.wait_for_network_registration(
+                self.ed, self.anritsu, self.log)
+            self.log.info("Waiting for data state: DATA_CONNECTED")
+            test_status, event = tel_test_utils.wait_for_data_state(
+                self.ed, self.log, "DATA_CONNECTED", 120)
+
+        if test_status == "passed":
+            self.log.info("TEL-CO-06: Network returning to IN_SERVICE"
+                          " verification: Passed")
+            return True
+        else:
+            self.log.info("TEL-CO-06: Network returning to IN_SERVICE"
+                          " verification: Failed")
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_set_preferred_network(self):
+        '''
+        Test ID: TEL-CO-07
+        verifies the network is registered on Preferred network
+
+        Steps
+        -----
+        1. The device is in airplane mode
+        2. Turn off the airplane mode (This simulates the Boot up for modem)
+        3. check for the service state. expecting IN SERVICE
+        4. Set the preferred network type
+        5. check for the service state  and registered network
+        '''
+        test_status = "failed"
+        # turn on modem to start registration
+        tel_test_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_test_utils.wait_for_network_registration(
+            self.ed, self.anritsu, self.log)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            test_status = "failed"
+            pref_nwtype = 0
+            expected_nwtype = ""
+            bts_number, rat_info = self.anritsu.get_camping_cell()
+            if rat_info == BtsTechnology.WCDMA.value:
+                pref_nwtype = tel_test_utils.NETWORK_MODE_LTE_ONLY
+                expected_nwtype = "LTE"
+            elif rat_info == BtsTechnology.LTE.value:
+                pref_nwtype = tel_test_utils.NETWORK_MODE_WCDMA_ONLY
+                expected_nwtype = "UMTS"
+            else:
+                raise ValueError("Incorrect value of RAT returned by MD8475A")
+            self.log.info("Setting preferred Network to " + expected_nwtype)
+            self.droid.telephonySetPreferredNetwork(pref_nwtype)
+            self.log.info("Waiting for service state: IN_SERVICE in " +
+                          expected_nwtype)
+            test_status, event = tel_test_utils.wait_for_network_registration(
+                self.ed, self.anritsu, self.log, expected_nwtype)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            test_status = "failed"
+            pref_nwtype = 0
+            expected_nwtype = ""
+            bts_number, rat_info = self.anritsu.get_camping_cell()
+            if rat_info == BtsTechnology.WCDMA.value:
+                pref_nwtype = tel_test_utils.NETWORK_MODE_LTE_ONLY
+                expected_nwtype = "LTE"
+            elif rat_info == BtsTechnology.LTE.value:
+                pref_nwtype = tel_test_utils.NETWORK_MODE_WCDMA_ONLY
+                expected_nwtype = "UMTS"
+            else:
+                raise ValueError("Incorrect value of RAT returned by MD8475A")
+            self.log.info("Setting preferred Network to " + expected_nwtype)
+            self.droid.telephonySetPreferredNetwork(pref_nwtype)
+            self.log.info("Waiting for service state: IN_SERVICE in " +
+                          expected_nwtype)
+            test_status, event = tel_test_utils.wait_for_network_registration(
+                self.ed, self.anritsu, self.log, expected_nwtype)
+        # setting the preferred network type to default
+        self.droid.telephonySetPreferredNetwork(
+            tel_test_utils.NETWORK_MODE_LTE_GSM_WCDMA)
+
+        if test_status == "passed":
+            self.log.info("TEL-CO-07: Setting preferred Network"
+                          "verification: Passed")
+            return True
+        else:
+            self.log.info("TEL-CO-07: Setting preferred Network"
+                          "verification: Failed")
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_network_emergency(self):
+        '''
+        Test ID: TEL-CO-05
+        verifies the network state - emergency
+
+        Steps
+        -----
+        1. The device is in airplane mode
+        2. Turn off the airplane mode (This simulates the Boot up for modem)
+        3. check for the service state. expecting IN SERVICE
+        4. Make the device emergency only
+        5. check for the service state. expecting EMERGENCY_ONLY
+        '''
+        test_status = "failed"
+        CAUSE_LA_NOTALLOWED = 12
+        CAUSE_EPS_NOTALLOWED = 7
+        triggermessage = self.anritsu.get_TriggerMessage()
+        triggermessage.set_reply_type(TriggerMessageIDs.ATTACH_REQ,
+                                      TriggerMessageReply.REJECT)
+        triggermessage.set_reject_cause(TriggerMessageIDs.ATTACH_REQ,
+                                        CAUSE_EPS_NOTALLOWED)
+        # This sleep is required.Sometimes Anritsu box doesn't behave as
+        # expected in executing the commands send to it without this delay.
+        # May be it is in state transition.so the test doesn't proceed.
+        # hence introduced this delay.
+        time.sleep(5)
+        triggermessage.set_reply_type(TriggerMessageIDs.MM_LOC_UPDATE_REQ,
+                                      TriggerMessageReply.REJECT)
+        triggermessage.set_reject_cause(TriggerMessageIDs.MM_LOC_UPDATE_REQ,
+                                        CAUSE_LA_NOTALLOWED)
+
+        # turn on modem to start registration
+        tel_test_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for service state: emergency")
+        test_status, event = tel_test_utils.wait_for_network_state(
+            self.ed, self.log, "EMERGENCY_ONLY", 300)
+
+        if test_status == "passed":
+            self.droid.connectivityToggleAirplaneMode(True)
+            time_to_wait = 60
+            sleep_interval = 1
+            # Waiting for POWER OFF state in Anritsu
+            start_time = time.time()
+            end_time = start_time + time_to_wait
+            while True:
+                ue_status = self.anritsu.get_ue_status()
+                if ue_status == ProcessingStatus.PROCESS_STATUS_POWEROFF:
+                    break
+
+                if time.time() <= end_time:
+                    time.sleep(sleep_interval)
+                    time_to_wait = end_time - time.time()
+                else:
+                    self.log.info("MD8475A has not come to POWEROFF state")
+                    break
+
+            # This sleep is required.Sometimes Anritsu box doesn't behave as
+            # expected in executing the commands send to it without this delay.
+            # May be it is in state transition.so the test doesn't proceed.
+            # hence introduced this delay.
+            time.sleep(10)
+            triggermessage.set_reply_type(TriggerMessageIDs.ATTACH_REQ,
+                                          TriggerMessageReply.ACCEPT)
+            triggermessage.set_reply_type(TriggerMessageIDs.MM_LOC_UPDATE_REQ,
+                                          TriggerMessageReply.ACCEPT)
+            self.droid.connectivityToggleAirplaneMode(False)
+            tel_test_utils.wait_for_network_registration(self.ed, self.anritsu,
+                                                         self.log)
+
+        if test_status == "passed":
+            self.log.info("TEL-CO-05: Network emergency state"
+                          " verification: Passed")
+            return True
+        else:
+            self.log.info("TEL-CO-05: Network emergency state"
+                          " verification: Failed")
+
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_manual_operator_selection(self):
+        '''
+        verifies the Manual Operator Selection
+
+        Steps
+        -----
+        1. The device is in airplane mode
+        2. Turn off the airplane mode (This simulates the Boot up for modem)
+        3. check for the service state. expecting IN SERVICE
+        4. search for NW operators and manually select a non-subscribed operator
+        5. search for NW operators and manually select the subscribed operator
+        6. verify the device is camped on subscribed operator
+        '''
+        # Android public APIs not available for this operation
+        pass
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_auto_operator_selection(self):
+        '''
+        verifies the Automatic Operator Selection
+
+        Steps
+        -----
+        1. The device is in airplane mode
+        2. Turn off the airplane mode (This simulates the Boot up for modem)
+        3. check for the service state. expecting IN SERVICE
+        4. search for NW operators and manually select a non-subscribed operator
+        5. select the the subscribed operator automatically
+        6. verify the device is camped on subscribed operator
+        '''
+        # Android public APIs not available for this operation
+        pass
+
+    """ Tests End """
diff --git a/acts/tests/google/tel/live/TelephonyDataSanityTest.py b/acts/tests/google/tel/live/TelephonyDataSanityTest.py
new file mode 100644
index 0000000..4d6ae38
--- /dev/null
+++ b/acts/tests/google/tel/live/TelephonyDataSanityTest.py
@@ -0,0 +1,150 @@
+#/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""
+Sanity tests for connectivity tests in telephony
+"""
+
+import time
+from queue import Empty
+
+from acts.controllers.anritsu_lib.md8475a import BtsNumber
+from acts.controllers.anritsu_lib.md8475a import BtsTechnology
+from acts.controllers.anritsu_lib.md8475a import MD8475A
+from acts.test_utils.tel import tel_test_utils
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+
+
+class TelephonyDataSanityTest(TelephonyBaseTest):
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+        self.anritsu = MD8475A(tel_test_utils.MD8475A_IP_ADDRESS)
+
+    def setup_test(self):
+        self.lte_bts, self.wcdma_bts = tel_test_utils.set_system_model(
+            self.anritsu, "LTE_WCDMA")
+        tel_test_utils.init_phone(self.droid, self.ed)
+        self.droid.telephonyStartTrackingServiceStateChange()
+        self.droid.telephonyStartTrackingDataConnectionStateChange()
+        self.log.info("Starting Simulation")
+        self.anritsu.start_simulation()
+        return True
+
+    def teardown_test(self):
+        self.droid.telephonyStopTrackingServiceStateChange()
+        self.droid.telephonyStopTrackingDataConnectionStateChange()
+        self.log.info("Stopping Simulation")
+        self.anritsu.stop_simulation()
+        # turn off modem
+        tel_test_utils.turn_off_modem(self.droid)
+
+    def teardown_class(self):
+        self.anritsu.disconnect()
+
+    def _wait_for_bts_state(self, btsnumber, state, timeout=30):
+        ''' Wait till BTS state changes. state value are "IN" and "OUT" '''
+        sleep_interval = 2
+        status = "failed"
+
+        wait_time = timeout
+        while (wait_time > 0):
+            if state == btsnumber.service_state:
+                print(btsnumber.service_state)
+                status = "passed"
+                break
+            else:
+                time.sleep(sleep_interval)
+                waiting_time = waiting_time - sleep_interval
+
+        if status == "failed":
+            self.log.info("Timeout: Expected state is not received.")
+
+    """ Tests Begin """
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_data_conn_state_when_access_enabled(self):
+        '''
+        Check data conenction state after boot up
+
+        Steps
+        -----
+        1. Get the device is IN_SERVICE state
+        2. check the data conenction status
+        '''
+        test_status = "failed"
+        # turn on modem to start registration
+        tel_test_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_test_utils.wait_for_network_registration(
+            self.ed, self.anritsu, self.log)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            self.log.info("Waiting for data state: DATA_CONNECTED")
+            test_status, event = tel_test_utils.wait_for_data_state(
+                self.ed, self.log, "DATA_CONNECTED", 120)
+
+        if test_status == "passed":
+            self.log.info("Data connection state(access enabled) "
+                          "verification: Passed")
+            return True
+        else:
+            self.log.info("Data connection state(access enabled) "
+                          "verification: Failed")
+            return False
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_data_conn_state_when_access_disabled(self):
+        '''
+        Check data conenction state after disabling data access
+
+        Steps
+        -----
+        1. Get the device is IN_SERVICE state
+        2. check the data conenction status ( data access enabled)
+        3. disable the data access
+        4. check the data conenction status ( data access enabled)
+        '''
+        test_status = "failed"
+        # turn on modem to start registration
+        tel_test_utils.turn_on_modem(self.droid)
+        self.log.info("Waiting for Network registration")
+        test_status, event = tel_test_utils.wait_for_network_registration(
+            self.ed, self.anritsu, self.log)
+
+        # proceed with next step only if previous step is success
+        if test_status == "passed":
+            self.log.info("Waiting for data state: DATA_CONNECTED")
+            test_status, event = tel_test_utils.wait_for_data_state(
+                self.ed, self.log, "DATA_CONNECTED", 120)
+
+        if test_status == "passed":
+            time.sleep(20)
+            self.log.info("Disabling data access")
+            self.droid.telephonyToggleDataConnection(False)
+            self.log.info("Waiting for data state: DATA_DISCONNECTED")
+            test_status, event = tel_test_utils.wait_for_data_state(
+                self.ed, self.log, "DATA_DISCONNECTED", 120)
+
+        if test_status == "passed":
+            self.log.info("Data connection state(access disabled) "
+                          "verification: Passed")
+            return True
+        else:
+            self.log.info("Data connection state(access disabled) "
+                          "verification: Failed")
+            return False
+
+    """ Tests End """
diff --git a/acts/tests/google/wifi/WifiAutoJoinTest.py b/acts/tests/google/wifi/WifiAutoJoinTest.py
index d3b4a38..829fc6c 100755
--- a/acts/tests/google/wifi/WifiAutoJoinTest.py
+++ b/acts/tests/google/wifi/WifiAutoJoinTest.py
@@ -17,40 +17,35 @@
 import time
 
 from acts import asserts
-from acts.base_test import BaseTestClass
-from acts.test_utils.wifi.wifi_test_utils import wifi_forget_network
-from acts.test_utils.wifi.wifi_test_utils import wifi_test_device_init
-from acts.test_utils.wifi.wifi_test_utils import WifiEnums
-from acts.test_utils.wifi.wifi_test_utils import start_wifi_connection_scan
-from acts.test_utils.wifi.wifi_test_utils import check_internet_connection
-from acts.test_utils.wifi.wifi_test_utils import track_connection
+from acts import base_test
+from acts.test_utils.wifi import wifi_constants
+from acts.test_utils.wifi import wifi_test_utils as wutils
 
+WifiEnums = wutils.WifiEnums
 NETWORK_ID_ERROR = "Network don't have ID"
 NETWORK_ERROR = "Device is not connected to reference network"
 
-class WifiAutoJoinTest(BaseTestClass):
 
+class WifiAutoJoinTest(base_test.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",
-            )
+        base_test.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
@@ -62,27 +57,30 @@
             True if successfully configured the requirements for testing.
         """
         self.dut = self.android_devices[0]
-        wifi_test_device_init(self.dut)
+        wutils.wifi_test_device_init(self.dut)
         req_params = ("reference_networks", "other_network", "atten_val",
-                      "ping_addr", "max_bugreports" )
+                      "ping_addr", "max_bugreports")
         self.unpack_userparams(req_params)
         self.log.debug("Connect networks :: {}".format(self.other_network))
         configured_networks = self.dut.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']:
+        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
+            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']:
+            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']:
+            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
+            return
         else:
             self.log.info("Configured networks for testing")
             self.attenuators[0].set_atten(0)
@@ -92,27 +90,32 @@
             self.dut.droid.wakeLockAcquireBright()
             self.dut.droid.wakeUpNow()
             try:
-                self.dut.droid.wifiPriorityConnect(self.reference_networks[0]['2g'])
-                connect_result = self.dut.ed.pop_event("WifiManagerPriorityConnectOnSuccess", 1)
+                self.dut.droid.wifiConnectByConfig(self.reference_networks[0][
+                    '2g'])
+                connect_result = self.dut.ed.pop_event(
+                    wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 1)
                 self.log.info(connect_result)
                 time.sleep(wait_time)
-                if self.ref_ssid_count == 2: #add 5g network as well
-                    self.dut.droid.wifiPriorityConnect(self.reference_networks[0]['5g'])
-                    connect_result = self.dut.ed.pop_event("WifiManagerPriorityConnectOnSuccess", 1)
+                if self.ref_ssid_count == 2:  #add 5g network as well
+                    self.dut.droid.wifiConnectByConfig(self.reference_networks[
+                        0]['5g'])
+                    connect_result = self.dut.ed.pop_event(
+                        wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 1)
                     self.log.info(connect_result)
                     time.sleep(wait_time)
-                self.dut.droid.wifiPriorityConnect(self.other_network)
-                connect_result = self.dut.ed.pop_event("WifiManagerPriorityConnectOnSuccess")
+                self.dut.droid.wifiConnectByConfig(self.other_network)
+                connect_result = self.dut.ed.pop_event(
+                    wifi_constants.CONNECT_BY_CONFIG_SUCCESS)
                 self.log.info(connect_result)
-                track_connection(self.dut, self.other_network["ssid"], 1)
-                wifi_forget_network(self.dut, self.other_network["ssid"])
+                wutils.track_connection(self.dut, self.other_network["ssid"], 1)
+                wutils.wifi_forget_network(self.dut, self.other_network["ssid"])
                 time.sleep(wait_time)
                 current_network = self.dut.droid.wifiGetConnectionInfo()
                 self.log.info("Current network: {}".format(current_network))
-                asserts.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
-                asserts.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR)
-                self.ip_address = self.dut.droid.wifiGetConfigFile();
-                self.log.info("IP info: {}".format(self.ip_address))
+                asserts.assert_true('network_id' in current_network,
+                                    NETWORK_ID_ERROR)
+                asserts.assert_true(current_network['network_id'] >= 0,
+                                    NETWORK_ERROR)
             finally:
                 self.dut.droid.wifiLockRelease()
                 self.dut.droid.goToSleepNow()
@@ -124,7 +127,7 @@
         Returns:
             True if connection to given network happen, else return False.
         """
-        time.sleep(40) #time for connection state to be updated
+        time.sleep(40)  #time for connection state to be updated
         self.log.info("Check network for {}".format(network_bssid))
         current_network = self.dut.droid.wifiGetConnectionInfo()
         self.log.debug("Current network:  {}".format(current_network))
@@ -148,12 +151,14 @@
         self.dut.droid.wakeLockAcquireBright()
         self.dut.droid.wakeUpNow()
         try:
-            asserts.assert_true(self.check_connection(bssid),
-                    "Device is not connected to required bssid {}".format(bssid))
-            time.sleep(10) #wait for connection to be active
-            asserts.assert_true(check_internet_connection(self.dut, self.ping_addr),
-                             "Error, No Internet connection for current bssid {}".
-                             format(bssid))
+            asserts.assert_true(
+                self.check_connection(bssid),
+                "Device is not connected to required bssid {}".format(bssid))
+            time.sleep(10)  #wait for connection to be active
+            asserts.assert_true(
+                wutils.validate_connection(self.dut, self.ping_addr),
+                "Error, No Internet connection for current bssid {}".format(
+                    bssid))
         finally:
             self.dut.droid.wifiLockRelease()
             self.dut.droid.goToSleepNow()
@@ -165,6 +170,7 @@
         self.dut.cat_adb_log(test_name, begin_time)
 
     """ Tests Begin """
+
     def test_autojoin_Ap1_2g(self):
         """Test wifi auto join functionality move in range of AP1.
 
@@ -173,19 +179,21 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[0]["2g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_Ap1_2g failed {}".
-                         format(len(failed)))
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[0]["2g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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.
@@ -195,20 +203,20 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[0]["5g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_Ap1_2gto5g failed {}".
-                         format(len(failed)))
-
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[0]["5g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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.
@@ -218,19 +226,21 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[0]["2g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_in_AP1_5gto2g failed {}".
-                         format(len(failed)))
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[0]["2g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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
@@ -241,19 +251,21 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[1]["2g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_swtich_AP1toAp2 failed {}".
-                         format(len(failed)))
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[1]["2g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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.
@@ -263,19 +275,20 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[1]["5g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_Ap2_2gto5g failed {}".
-                         format(len(failed)))
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[1]["5g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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.
@@ -284,19 +297,20 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[1]["2g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_Ap2_5gto2g failed {}".
-                         format(len(failed)))
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[1]["2g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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.
@@ -312,15 +326,16 @@
         self.dut.droid.wakeLockAcquireBright()
         self.dut.droid.wakeUpNow()
         try:
-            start_wifi_connection_scan(self.dut)
+            wutils.start_wifi_connection_scan(self.dut)
             wifi_results = self.dut.droid.wifiGetScanResults()
             self.log.debug("Scan result {}".format(wifi_results))
             time.sleep(20)
             current_network = self.dut.droid.wifiGetConnectionInfo()
             self.log.info("Current network: {}".format(current_network))
-            asserts.assert_true(('network_id' in current_network and
-                              current_network['network_id'] == -1),
-                             "Device is connected to network {}".format(current_network))
+            asserts.assert_true(
+                ('network_id' in current_network and
+                 current_network['network_id'] == -1),
+                "Device is connected to network {}".format(current_network))
         finally:
             self.dut.droid.wifiLockRelease()
             self.dut.droid.goToSleepNow()
@@ -333,20 +348,21 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[1]["2g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_Ap2_2g failed {}".
-                         format(len(failed)))
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[1]["2g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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
@@ -357,19 +373,20 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[1]["2g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_in_Ap2_5gto2g failed {}".
-                         format(len(failed)))
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[1]["2g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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
@@ -380,19 +397,21 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[0]["2g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_swtich_AP2toAp1 failed {}".
-                         format(len(failed)))
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[0]["2g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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.
@@ -402,19 +421,20 @@
          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"]
+        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])
+        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,
-                        args = (self.reference_networks[0]["2g"]['bssid'],),
-                        name_func = name_func)
-        asserts.assert_true(not failed, "Number of test_autojoin_Ap1_5gto2g failed {}".
-                         format(len(failed)))
+            self.set_attn_and_validate_connection,
+            attenuations,
+            args=(self.reference_networks[0]["2g"]['bssid'], ),
+            name_func=name_func)
+        asserts.assert_false(
+            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.
@@ -424,8 +444,9 @@
          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'])
+        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.
@@ -441,16 +462,17 @@
         self.dut.droid.wakeLockAcquireBright()
         self.dut.droid.wakeUpNow()
         try:
-            start_wifi_connection_scan(self.dut)
+            wutils.start_wifi_connection_scan(self.dut)
             wifi_results = self.dut.droid.wifiGetScanResults()
             self.log.debug("Scan result {}".format(wifi_results))
             time.sleep(20)
             current_network = self.dut.droid.wifiGetConnectionInfo()
             self.log.info("Current network: {}".format(current_network))
-            asserts.assert_true(('network_id' in current_network and
-                              current_network['network_id'] == -1),
-                             "Device is still connected to blacklisted network {}".
-                             format(current_network))
+            asserts.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.dut.droid.wifiLockRelease()
             self.dut.droid.goToSleepNow()
@@ -462,8 +484,8 @@
          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'])
+        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/WifiAwareManagerTest.py b/acts/tests/google/wifi/WifiAwareManagerTest.py
new file mode 100644
index 0000000..a3d4874
--- /dev/null
+++ b/acts/tests/google/wifi/WifiAwareManagerTest.py
@@ -0,0 +1,1180 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import json
+import pprint
+import re
+import queue
+import statistics
+import time
+
+from acts import asserts
+from acts import base_test
+from acts.signals import generated_test
+from acts.test_utils.net import connectivity_const as con_const
+from acts.test_utils.net import nsd_const as nsd_const
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi import wifi_aware_const as aware_const
+
+# arbitrary timeout for events
+EVENT_TIMEOUT = 30
+
+
+class WifiAwareManagerTest(base_test.BaseTestClass):
+    # configuration parameters used by tests
+    publish_config = {"ServiceName": "GoogleTestServiceX",
+                      "ServiceSpecificInfo": "Data XYZ",
+                      "MatchFilter": {"int0": 14,
+                                      "data0": "MESSAGE_ALL"},
+                      "PublishType": 0,
+                      "TtlSec": 0}
+    subscribe_config = {"ServiceName": "GoogleTestServiceX",
+                        "ServiceSpecificInfo": "Data ABC",
+                        "MatchFilter": {"int0": 14,
+                                        "data0": "MESSAGE_ALL"},
+                        "SubscribeType": 0,
+                        "TtlSec": 0}
+    rtt_24_20 = {"deviceType": 5,
+                 "requestType": 2,
+                 "frequency": 2437,
+                 "channelWidth": 0,
+                 "centerFreq0": 2437,
+                 "centerFreq1": 0,
+                 "numberBurst": 0,
+                 "numSamplesPerBurst": 5,
+                 "numRetriesPerMeasurementFrame": 3,
+                 "numRetriesPerFTMR": 3,
+                 "LCIRequest": False,
+                 "LCRRequest": False,
+                 "burstTimeout": 15,
+                 "preamble": 2,
+                 "bandwidth": 4}
+    rtt_50_40 = {"deviceType": 5,
+                 "requestType": 2,
+                 "frequency": 5200,
+                 "channelWidth": 1,
+                 "centerFreq0": 5190,
+                 "centerFreq1": 0,
+                 "numberBurst": 0,
+                 "numSamplesPerBurst": 5,
+                 "numRetriesPerMeasurementFrame": 3,
+                 "numRetriesPerFTMR": 3,
+                 "LCIRequest": False,
+                 "LCRRequest": False,
+                 "burstTimeout": 15,
+                 "preamble": 2,
+                 "bandwidth": 8}
+    rtt_50_80 = {"deviceType": 5,
+                 "requestType": 2,
+                 "frequency": 5200,
+                 "channelWidth": 2,
+                 "centerFreq0": 5210,
+                 "centerFreq1": 0,
+                 "numberBurst": 0,
+                 "numSamplesPerBurst": 5,
+                 "numRetriesPerMeasurementFrame": 3,
+                 "numRetriesPerFTMR": 3,
+                 "LCIRequest": False,
+                 "LCRRequest": False,
+                 "burstTimeout": 15,
+                 "preamble": 4,
+                 "bandwidth": 16}
+    network_req = {"TransportType": 5}
+    nsd_service_info = {"serviceInfoServiceName": "sl4aTestAwareIperf",
+                        "serviceInfoServiceType": "_simple-tx-rx._tcp.",
+                        "serviceInfoPort": 2257}
+
+    def setup_test(self):
+        self.msg_id = 10
+        for ad in self.android_devices:
+            wutils.wifi_toggle_state(ad, True)
+            aware_usage_enabled = ad.droid.wifiIsAwareAvailable()
+            if not aware_usage_enabled:
+                self.log.info('Aware not enabled. Waiting for %s',
+                              aware_const.BROADCAST_WIFI_AWARE_AVAILABLE)
+                try:
+                    ad.ed.pop_event(aware_const.BROADCAST_WIFI_AWARE_AVAILABLE,
+                                    EVENT_TIMEOUT)
+                    self.log.info(aware_const.BROADCAST_WIFI_AWARE_AVAILABLE)
+                except queue.Empty:
+                    asserts.fail('Timed out while waiting for %s' %
+                                 aware_const.BROADCAST_WIFI_AWARE_AVAILABLE)
+
+    def teardown_test(self):
+        for ad in self.android_devices:
+            ad.droid.wifiAwareDestroyAll()
+    #         asserts.assert_true(
+    #             wutils.wifi_toggle_state(ad, False),
+    #             "Failed disabling Wi-Fi interface")
+
+    def extract_stats(self, data, results, key_prefix, log_prefix):
+        """Extract statistics from the data, store in the dict dictionary, and
+        output to the info log.
+
+        Args:
+            data: A list containing the data to be analyzed.
+            results: A dictionary into which to place the statistics.
+            key_prefix: A string prefix to use for the dict keys storing the
+                        extracted stats.
+            log_prefix: A string prefix to use for the info log.
+            include_data: If True includes the raw data in the dictionary,
+                          otherwise just the stats.
+        """
+        num_samples = len(data)
+        results['%s_num_samples' % key_prefix] = num_samples
+
+        if not data:
+            return
+
+        data_min = min(data)
+        data_max = max(data)
+        data_mean = statistics.mean(data)
+
+        results['%s_min' % key_prefix] = data_min
+        results['%s_max' % key_prefix] = data_max
+        results['%s_mean' % key_prefix] = data_mean
+        results['%s_raw_data' % key_prefix] = data
+
+        if num_samples > 1:
+            data_stdev = statistics.stdev(data)
+            results['%s_stdev' % key_prefix] = data_stdev
+            self.log.info(
+                '%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, stdev=%.2f',
+                log_prefix, num_samples, data_min, data_max, data_mean,
+                data_stdev)
+        else:
+            self.log.info('%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f',
+                          log_prefix, num_samples, data_min, data_max,
+                          data_mean)
+
+    def get_interface_mac(self, device, interface):
+        """Get the HW MAC address of the specified interface.
+
+        Returns the HW MAC address or raises an exception on failure.
+
+        Args:
+            device: The 'AndroidDevice' on which to query the interface.
+            interface: The name of the interface to query.
+
+        Returns:
+            mac: MAC address of the interface.
+        """
+        out = device.adb.shell("ifconfig %s" % interface)
+        completed = out.decode('utf-8').strip()
+        res = re.match(".* HWaddr (\S+).*", completed, re.S)
+        asserts.assert_true(
+            res, 'Unable to obtain MAC address for interface %s' % interface)
+        return res.group(1)
+
+    def get_interface_ipv6_link_local(self, device, interface):
+        """Get the link-local IPv6 address of the specified interface.
+
+        Returns the link-local IPv6 address of the interface or raises an
+        exception on failure.
+
+        Args:
+            device: The 'AndroidDevice' on which to query the interface.
+            interface: The name of the interface to query.
+
+        Returns:
+            addr: link-local IPv6 address of the interface.
+        """
+        out = device.adb.shell("ifconfig %s" % interface)
+        completed = out.decode('utf-8').strip()
+        res = re.match(".*inet6 addr: (\S+)/64 Scope: Link.*", completed, re.S)
+        asserts.assert_true(
+            res,
+            'Unable to obtain IPv6 link-local for interface %s' % interface)
+        return res.group(1)
+
+    def exec_connect(self, device, name, config=None):
+        """Executes the Aware connection creation operation.
+
+        Creates an Aware connection (client) and waits for a confirmation event
+        of success. Raise test failure signal if no such event received.
+
+        Args:
+            device: The 'AndroidDevice' on which to set up the connection.
+            name: An arbitary name used for logging.
+            config: An optional ConfigRequest (JSON-configured) structure.
+        """
+        session_id = device.droid.wifiAwareAttach(config)
+        try:
+            event = device.ed.pop_event(aware_const.EVENT_CB_ON_ATTACHED,
+                                        EVENT_TIMEOUT)
+            self.log.info('%s: %s', aware_const.EVENT_CB_ON_ATTACHED,
+                          event['data'])
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on %s' %
+                         (aware_const.EVENT_CB_ON_ATTACHED, name))
+        self.log.debug(event)
+        return session_id
+
+    def reliable_tx(self, device, session_id, peer, msg):
+        """Sends an Aware message.
+
+        Sends a message to the peer and waits for success confirmation. Raises
+        an exception on failure or timeout.
+
+        The message is sent using the MAX retransmission count.
+
+        Args:
+            device: The 'AndroidDevice' on which to send the message.
+            session_id: The session ID context from which to send the message.
+                This is the value returned by wifiAwarePublish() or
+                wifiAwareSubscribe().
+            peer: The peer ID to send the message to. Obtained through a match
+                or a received message.
+            msg: The message to be transmitted to the peer.
+        """
+        events_regex = '%s|%s' % (aware_const.SESSION_CB_ON_MESSAGE_SEND_FAILED,
+                                  aware_const.SESSION_CB_ON_MESSAGE_SENT)
+        self.msg_id = self.msg_id + 1
+
+        while True:
+            try:
+                device.droid.wifiAwareSendMessage(session_id, peer, self.msg_id,
+                                                  msg,
+                                                  aware_const.MAX_TX_RETRIES)
+                events = device.ed.pop_events(events_regex, EVENT_TIMEOUT)
+            except queue.Empty:
+                asserts.fail('Timed out while waiting for %s', events_regex)
+
+            for event in events:
+                self.log.info('%s: %s', event['name'], event['data'])
+                if event['data']['messageId'] == self.msg_id:
+                    asserts.assert_equal(event['name'],
+                                         aware_const.SESSION_CB_ON_MESSAGE_SENT,
+                                         'Failed (re)transmission')
+                    return
+
+    def exec_rtt(self, device, session_id, peer_id, rtt_param, label,
+                 repeat_count):
+        """Executes an RTT operation.
+
+        Args:
+            device: The 'AndroidDevice' on which to send the message.
+            session_id: The session ID context from which to send the message.
+                This is the value returned by wifiAwarePublish() or
+                wifiAwareSubscribe().
+            peer_id: The peer ID to send the message to. Obtained through a
+                match or a received message.
+            rtt_param: RTT session parameters.
+            msg: Message/tag describing RTT experiment.
+            repeat_count: Number of RTT measurements to execute.
+        """
+        rtt_param['bssid'] = peer_id
+        rtt_stats = {
+            'failure_codes': {},
+            'distance': {
+                'sum': 0,
+                'num_samples': 0
+            }
+        }
+        for i in range(repeat_count):
+            device.droid.wifiAwareStartRanging(0, session_id, [rtt_param])
+
+            events_regex = '%s|%s|%s' % (aware_const.RTT_LISTENER_CB_ON_SUCCESS,
+                                         aware_const.RTT_LISTENER_CB_ON_FAILURE,
+                                         aware_const.RTT_LISTENER_CB_ON_ABORT)
+            try:
+                events_pub_range = device.ed.pop_events(events_regex,
+                                                        EVENT_TIMEOUT)
+                for event in events_pub_range:
+                    self.log.debug('%s: %s: %s', label, event['name'],
+                                   event['data'])
+                    results = event['data']['Results']
+                    for rtt_result in results:
+                        rtt_status = rtt_result['status']
+                        if rtt_status == 0:
+                            distance = rtt_result['distance']
+                            self.log.info('%s: distance=%d', label, distance)
+                            rtt_stats['distance']['sum'] = (
+                                rtt_stats['distance']['sum'] + distance)
+                            rtt_stats['distance']['num_samples'] = (
+                                rtt_stats['distance']['num_samples'] + 1)
+                        else:
+                            self.log.info('%s: status=%d', label, rtt_status)
+                            if rtt_status not in rtt_stats['failure_codes']:
+                                rtt_stats['failure_codes'][rtt_status] = 0
+                            rtt_stats['failure_codes'][rtt_status] = (
+                                rtt_stats['failure_codes'][rtt_status] + 1)
+            except queue.Empty:
+                self.log.info('%s: Timed out while waiting for RTT events %s',
+                              label, events_regex)
+        self.log.info('%s:\n\tParam: %s\n\tRTT statistics: %s', label,
+                      rtt_param, rtt_stats)
+
+    def test_cluster_latency(self):
+        """Measure the time it takes to make Aware available to an app from
+        the time a request is made.
+
+        Configuration: 1 device
+
+        Logical steps:
+          * DUT initiate Aware clustering
+          * DUT wait for attach event (measure time)
+          * DUT wait for identity change event (measure time)
+          * DUT destroy Aware session
+        """
+        results = {}
+        results['num_iterations'] = 100
+
+        self.dut = self.android_devices[0]
+
+        attach_latency = []
+        identity_change_latency = []
+        results['num_failed_attaches'] = 0
+        for i in range(results['num_iterations']):
+            session_id = self.dut.droid.wifiAwareAttach()
+            try:
+                event_attached = self.dut.ed.pop_event(
+                    aware_const.EVENT_CB_ON_ATTACHED, EVENT_TIMEOUT)
+                attach_latency.append(event_attached['data'][
+                    aware_const.EVENT_CB_KEY_LATENCY_MS])
+                self.log.debug('%s: %s', aware_const.EVENT_CB_ON_ATTACHED,
+                               event_attached['data'])
+
+                event_identity_change = self.dut.ed.pop_event(
+                    aware_const.EVENT_CB_ON_IDENTITY_CHANGED, EVENT_TIMEOUT)
+                identity_change_latency.append(event_identity_change['data'][
+                    aware_const.EVENT_CB_KEY_TIMESTAMP_MS] - event_attached[
+                        'data'][
+                            aware_const.SESSION_CB_KEY_TIMESTAMP_MS])
+            except queue.Empty:
+                results['num_failed_attaches'] += 1
+                self.log.debug('Timed out while waiting for %s|%s on DUT',
+                               aware_const.EVENT_CB_ON_ATTACHED,
+                               aware_const.EVENT_CB_ON_IDENTITY_CHANGED)
+            self.dut.droid.wifiAwareDestroy(session_id)
+
+        self.extract_stats(attach_latency, results, 'attach_latency', 'Attach')
+        self.extract_stats(identity_change_latency, results,
+                           'identity_change_latency', 'Identity Change')
+        asserts.explicit_pass('test_cluster_latency finished', extras=results)
+
+    def run_aware_discovery_session(self, discovery_config):
+        """Perform Aware configuration, discovery, and message exchange.
+
+        Configuration: 2 devices, one acting as Publisher (P) and the
+        other as Subscriber (S)
+
+        Logical steps:
+          * P & S initiate Aware clustering (if not already up)
+          * P & S wait for Aware connection confirmation
+          * P starts publishing
+          * S starts subscribing
+          * S waits for a match (discovery) notification
+          * S sends a message to P, confirming that sent successfully
+          * P waits for a message and confirms that received (uncorrupted)
+          * P sends a message to S, confirming that sent successfully
+          * S waits for a message and confirms that received (uncorrupted)
+
+        Args:
+            discovery_configs: {'Title': description,
+                'PublishConfig': publish_config,
+                'SubscribeConfig': subscribe_config}
+
+        Returns:
+            True if discovery succeeds, else false.
+        """
+        # Configure Test
+        self.publisher = self.android_devices[0]
+        self.subscriber = self.android_devices[1]
+
+        sub2pub_msg = "How are you doing? 你好嗎?"
+        pub2sub_msg = "Doing ok - thanks! 做的不錯 - 謝謝!"
+
+        # Start Test
+        pub_connect_id = self.exec_connect(self.publisher, "publisher")
+        sub_connect_id = self.exec_connect(self.subscriber, "subscriber")
+
+        # Configuration
+        publish_config = discovery_config['PublishConfig']
+        subscribe_config = discovery_config['SubscribeConfig']
+        self.log.debug('Publish config=%s, Subscribe config=%s', publish_config,
+                       subscribe_config)
+
+        pub_id = self.publisher.droid.wifiAwarePublish(pub_connect_id,
+                                                       publish_config)
+        sub_id = self.subscriber.droid.wifiAwareSubscribe(sub_connect_id,
+                                                          subscribe_config)
+
+        try:
+            event_sub_match = self.subscriber.ed.pop_event(
+                aware_const.SESSION_CB_ON_SERVICE_DISCOVERED, EVENT_TIMEOUT)
+            self.log.info('%s: %s',
+                          aware_const.SESSION_CB_ON_SERVICE_DISCOVERED,
+                          event_sub_match['data'])
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on Subscriber' %
+                         aware_const.SESSION_CB_ON_SERVICE_DISCOVERED)
+        self.log.debug(event_sub_match)
+
+        self.reliable_tx(self.subscriber, sub_id,
+                         event_sub_match['data']['peerId'], sub2pub_msg)
+
+        try:
+            event_pub_rx = self.publisher.ed.pop_event(
+                aware_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT)
+            self.log.info('%s: %s', aware_const.SESSION_CB_ON_MESSAGE_RECEIVED,
+                          event_pub_rx['data'])
+            asserts.assert_equal(event_pub_rx['data']['messageAsString'],
+                                 sub2pub_msg,
+                                 "Subscriber -> publisher message corrupted")
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on publisher' %
+                         aware_const.SESSION_CB_ON_MESSAGE_RECEIVED)
+
+        self.reliable_tx(self.publisher, pub_id, event_pub_rx['data']['peerId'],
+                         pub2sub_msg)
+
+        try:
+            event_sub_rx = self.subscriber.ed.pop_event(
+                aware_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT)
+            self.log.info('%s: %s', aware_const.SESSION_CB_ON_MESSAGE_RECEIVED,
+                          event_sub_rx['data'])
+            asserts.assert_equal(event_sub_rx['data']['messageAsString'],
+                                 pub2sub_msg,
+                                 "Publisher -> subscriber message corrupted")
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on subscriber' %
+                         aware_const.SESSION_CB_ON_MESSAGE_RECEIVED)
+
+        if publish_config['TtlSec'] != 0:
+            try:
+                event_pub_term = self.publisher.ed.pop_event(
+                    aware_const.SESSION_CB_ON_SESSION_TERMINATED,
+                    publish_config['TtlSec'] + 5)
+                self.log.info('%s: %s',
+                              aware_const.SESSION_CB_ON_SESSION_TERMINATED,
+                              event_pub_term['data'])
+            except queue.Empty:
+                asserts.fail('Timed out while waiting for %s on publisher' %
+                             aware_const.SESSION_CB_ON_SESSION_TERMINATED)
+        if subscribe_config['TtlSec'] != 0:
+            try:
+                event_sub_term = self.subscriber.ed.pop_event(
+                    aware_const.SESSION_CB_ON_SESSION_TERMINATED,
+                    subscribe_config['TtlSec'] + 5)
+                self.log.info('%s: %s',
+                              aware_const.SESSION_CB_ON_SESSION_TERMINATED,
+                              event_sub_term['data'])
+            except queue.Empty:
+                asserts.fail('Timed out while waiting for %s on subscriber' %
+                             aware_const.SESSION_CB_ON_SESSION_TERMINATED)
+
+    @generated_test
+    def test_aware_discovery_session(self):
+        """Perform Aware configuration, discovery, and message exchange.
+
+        Test multiple discovery types:
+        - Unsolicited publish + passive subscribe
+        - Solicited publish + active subscribe
+        """
+
+        discovery_configs = (
+            {'Title': 'ActivePub',
+             'PublishConfig': self.publish_config,
+             'SubscribeConfig': self.subscribe_config},
+            {'Title': 'ActiveSub',
+             'PublishConfig': dict(self.publish_config, **{'PublishType': 1}),
+             'SubscribeConfig': dict(self.subscribe_config,
+                                     **{'SubscribeType': 1})},
+            {'Title': 'ActivePub-LimitedTtl',
+             'PublishConfig': dict(self.publish_config, **{"TtlSec": 20}),
+             'SubscribeConfig': dict(self.subscribe_config, **{"TtlSec": 20})})
+        name_func = lambda discovery_config: ("test_aware_discovery_session__%s") % discovery_config['Title']
+        self.run_generated_testcases(self.run_aware_discovery_session,
+                                     discovery_configs,
+                                     name_func=name_func)
+
+    def run_aware_discovery_latency(self, dw_interval):
+        """Measure the latency of Aware discovery on the subscriber.
+
+        Args:
+            dw_interval: Discovery Window Interval configuration
+
+        Configuration: 2 devices, one acting as Publisher (P) and the
+        other as Subscriber (S)
+
+        Logical steps:
+          * P & S if not new_session initiate Aware clustering (if not already up)
+          * P & S if not new session wait for Aware connection confirmation
+          * P starts publishing
+          * Wait for a few seconds to make sure everyone is ready (measuring
+          * discovery - not clustering).
+          * Loop:
+          *   S start subscribing
+          *   Measure latency to S registering a discovery
+          *   S terminates subscribe
+        """
+        self.publisher = self.android_devices[0]
+        self.subscriber = self.android_devices[1]
+        results = {}
+        results['num_iterations'] = 100
+
+        # Start Test
+        pub_connect_id = self.exec_connect(self.publisher, "publisher",
+                                           {"DiscoveryWindowInterval": [dw_interval, dw_interval]})
+        sub_connect_id = self.exec_connect(self.subscriber, "subscriber",
+                                           {"DiscoveryWindowInterval": [dw_interval, dw_interval]})
+
+        pub_id = self.publisher.droid.wifiAwarePublish(pub_connect_id,
+                                                       self.publish_config)
+        try:
+            self.publisher.ed.pop_event(
+                aware_const.SESSION_CB_ON_PUBLISH_STARTED, EVENT_TIMEOUT)
+        except:
+            asserts.fail('Timed out while waiting for %s on Publisher' %
+                         aware_const.SESSION_CB_ON_PUBLISH_STARTED)
+
+        # another arbitrary long sleep time to make sure that publisher is
+        # on-the-air
+        time.sleep(10)
+
+        sub_session_setup_latency = []
+        sub_session_discovery_latency = []
+        results['num_failed_discovery'] = 0
+        for i in range(results['num_iterations']):
+            sub_id = self.subscriber.droid.wifiAwareSubscribe(
+                sub_connect_id, self.subscribe_config)
+            try:
+                event_sub = self.subscriber.ed.pop_event(
+                    aware_const.SESSION_CB_ON_SUBSCRIBE_STARTED, EVENT_TIMEOUT)
+                sub_session_setup_latency.append(event_sub['data'][
+                    aware_const.SESSION_CB_KEY_LATENCY_MS])
+                self.log.debug('%s: %s',
+                               aware_const.SESSION_CB_ON_SUBSCRIBE_STARTED,
+                               event_sub['data'])
+
+                event_discovery = self.subscriber.ed.pop_event(
+                    aware_const.SESSION_CB_ON_SERVICE_DISCOVERED, EVENT_TIMEOUT)
+                sub_session_discovery_latency.append(event_discovery['data'][
+                    aware_const.SESSION_CB_KEY_TIMESTAMP_MS] - event_sub[
+                        'data'][aware_const.SESSION_CB_KEY_TIMESTAMP_MS])
+                self.log.debug('%s: %s',
+                               aware_const.SESSION_CB_ON_SERVICE_DISCOVERED,
+                               event_discovery['data'])
+            except queue.Empty:
+                results['num_failed_discovery'] += 1
+                self.log.debug(
+                    'Timed out while waiting for %s|%s on Subscriber',
+                    aware_const.SESSION_CB_ON_SUBSCRIBE_STARTED,
+                    aware_const.SESSION_CB_ON_SERVICE_DISCOVERED)
+            self.subscriber.droid.wifiAwareDestroyDiscoverySession(sub_id)
+
+        self.extract_stats(sub_session_setup_latency, results,
+                           'sub_session_setup_latency',
+                           'Subscribe Session Setup')
+        self.extract_stats(sub_session_discovery_latency, results,
+                           'sub_session_discovery_latency',
+                           'Subscribe Session Discovery')
+        asserts.explicit_pass('test_aware_discovery_latency finished',
+                              extras=results)
+
+    @generated_test
+    def test_aware_discovery_latency(self):
+        """Measure the latency of Aware discovery on the subscriber.
+
+        Test different discovery window intervals: 1, 2, 3, 4, 5
+        """
+
+        name_func = lambda dw_interval: "test_aware_discovery_latency__dw_%d" % dw_interval
+        self.run_generated_testcases(self.run_aware_discovery_latency,
+                                     [1, 2, 3, 4, 5],
+                                     name_func=name_func)
+
+    def run_aware_messaging(self, retry_count):
+        """Perform Aware configuration, discovery, and large message exchange.
+
+        Args:
+            retry_count: retransmission count - from 0 to aware_const.MAX_TX_RETRIES
+
+        Configuration: 2 devices, one acting as Publisher (P) and the
+        other as Subscriber (S)
+
+        Logical steps:
+          * P & S initiate Aware clustering (if not already up)
+          * P & S wait for Aware connection confirmation
+          * P starts publishing
+          * S starts subscribing
+          * S waits for a match (discovery) notification
+          * S sends XX messages to P
+          * S confirms that all XXX messages were transmitted
+          * P confirms that all XXX messages are received
+        """
+        self.publisher = self.android_devices[0]
+        self.subscriber = self.android_devices[1]
+        results = {}
+        results['num_non_empty_messages'] = 100
+        results[
+            'num_null_and_empty_messages'] = 10  # one of each in sequence until reach count
+
+        # Start Test
+        pub_connect_id = self.exec_connect(self.publisher, "publisher")
+        sub_connect_id = self.exec_connect(self.subscriber, "subscriber")
+
+        pub_id = self.publisher.droid.wifiAwarePublish(pub_connect_id,
+                                                       self.publish_config)
+        sub_id = self.subscriber.droid.wifiAwareSubscribe(sub_connect_id,
+                                                          self.subscribe_config)
+
+        try:
+            event_sub_match = self.subscriber.ed.pop_event(
+                aware_const.SESSION_CB_ON_SERVICE_DISCOVERED, EVENT_TIMEOUT)
+            self.log.info('%s: %s',
+                          aware_const.SESSION_CB_ON_SERVICE_DISCOVERED,
+                          event_sub_match['data'])
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on Subscriber' %
+                         aware_const.SESSION_CB_ON_SERVICE_DISCOVERED)
+        self.log.debug(event_sub_match)
+
+        # send all messages at once
+        for i in range(results['num_non_empty_messages']):
+            self.msg_id = self.msg_id + 1
+            self.subscriber.droid.wifiAwareSendMessage(
+                sub_id, event_sub_match['data']['peerId'], self.msg_id,
+                "msg %s" % i, retry_count)
+
+        # send all empty & null messages
+        for i in range(results['num_null_and_empty_messages']):
+            self.msg_id = self.msg_id + 1
+            msg_to_send = None if (i % 2) else ""  # flip between null and ""
+            self.subscriber.droid.wifiAwareSendMessage(
+                sub_id, event_sub_match['data']['peerId'], self.msg_id,
+                msg_to_send, retry_count)
+
+        # wait for all messages to be transmitted correctly
+        results['num_tx_ok'] = 0
+        results['num_tx_fail'] = 0
+        tx_ok_stats = []
+        tx_fail_stats = []
+        events_regex = '%s|%s' % (aware_const.SESSION_CB_ON_MESSAGE_SEND_FAILED,
+                                  aware_const.SESSION_CB_ON_MESSAGE_SENT)
+        while (results['num_tx_ok'] + results['num_tx_fail']) < (
+                results['num_non_empty_messages'] +
+                results['num_null_and_empty_messages']):
+            try:
+                events = self.subscriber.ed.pop_events(events_regex,
+                                                       EVENT_TIMEOUT)
+
+                for event in events:
+                    if event['name'] == aware_const.SESSION_CB_ON_MESSAGE_SENT:
+                        results['num_tx_ok'] = results['num_tx_ok'] + 1
+                        if aware_const.SESSION_CB_KEY_LATENCY_MS in event[
+                                'data']:
+                            tx_ok_stats.append(event['data'][
+                                aware_const.SESSION_CB_KEY_LATENCY_MS])
+                    if event[
+                            'name'] == aware_const.SESSION_CB_ON_MESSAGE_SEND_FAILED:
+                        results['num_tx_fail'] = results['num_tx_fail'] + 1
+                        if aware_const.SESSION_CB_KEY_LATENCY_MS in event[
+                                'data']:
+                            tx_fail_stats.append(event['data'][
+                                aware_const.SESSION_CB_KEY_LATENCY_MS])
+            except queue.Empty:
+                self.log.warning('Timed out while waiting for %s on Subscriber'
+                                 ' - %d events received', events_regex,
+                                 results['num_tx_ok'] + results['num_tx_fail'])
+                break
+        self.log.info('Transmission stats: %d success, %d fail',
+                      results['num_tx_ok'], results['num_tx_fail'])
+        self.extract_stats(tx_ok_stats, results, 'tx_ok_latency',
+                           'Successful tx')
+        self.extract_stats(tx_fail_stats, results, 'tx_fail_latency', 'Fail tx')
+
+        # validate that all messages are received (not just the correct
+        # number of messages - since on occasion there may be duplicates
+        # received).
+        results['num_non_empty_received'] = 0
+        results['num_unique_received'] = 0
+        results['num_empty_received'] = 0
+        messages = {}
+        while (results['num_unique_received'] + results['num_empty_received'] <
+               results['num_tx_ok']):
+            try:
+                event = self.publisher.ed.pop_event(
+                    aware_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT)
+                msg = event['data']['messageAsString']
+                if msg:
+                    results['num_non_empty_received'] = results[
+                        'num_non_empty_received'] + 1
+                    if msg not in messages:
+                        results['num_unique_received'] = results[
+                            'num_unique_received'] + 1
+                        messages[msg] = 0
+                    messages[msg] = messages[msg] + 1
+                else:
+                    results['num_empty_received'] = results[
+                        'num_empty_received'] + 1
+                self.log.debug('%s: %s',
+                               aware_const.SESSION_CB_ON_MESSAGE_RECEIVED, msg)
+            except queue.Empty:
+                asserts.fail(
+                    'Timed out while waiting for %s on Publisher: %d non-empty '
+                    'messages received, %d unique messages, %d empty messages' %
+                    (aware_const.SESSION_CB_ON_MESSAGE_RECEIVED,
+                     results['num_non_empty_received'],
+                     results['num_unique_received'],
+                     results['num_empty_received']),
+                    extras=results)
+        self.log.info(
+            'Reception stats: %d non-empty received (%d unique), %d empty',
+            results['num_non_empty_received'], results['num_unique_received'],
+            results['num_empty_received'])
+        if results['num_non_empty_received'] != results['num_unique_received']:
+            self.log.info('%d duplicate receptions of %d messages: %s',
+                          results['num_non_empty_received'] -
+                          results['num_unique_received'],
+                          results['num_non_empty_received'], messages)
+        if results['num_empty_received'] != results[
+                'num_null_and_empty_messages']:
+            self.log.info('%d extra empty/null message reception',
+                          results['num_empty_received'] -
+                          results['num_null_and_empty_messages'])
+        asserts.explicit_pass("run_aware_messaging pass finished successfully",
+                              extras=results)
+
+    @generated_test
+    def test_aware_messaging(self):
+        """Perform Aware configuration, discovery, and large message exchange.
+
+        Test multiple message send retry counts.
+        """
+        name_func = lambda retry_count: "test_aware_messaging__retries_%d" % retry_count
+        self.run_generated_testcases(self.run_aware_messaging,
+                                     [0, aware_const.MAX_TX_RETRIES],
+                                     name_func=name_func)
+
+    def test_aware_messaging_latency(self):
+        """Measure the latency of Aware message transmission which are not queued. Unqueued
+        message transmission data is a function of raw protocol and firmware behavior.
+
+        Configuration: 2 devices, one acting as Publisher (P) and the
+        other as Subscriber (S)
+
+        Logical steps:
+          * P & S initiate Aware clustering (if not already up)
+          * P & S wait for Aware connection confirmation
+          * P starts publishing
+          * S starts subscribing
+          * S waits for a match (discovery) notification
+          * Loop:
+          *    S sends 1 message to P
+          *    S confirms that message transmitted and measures latency
+        """
+        self.publisher = self.android_devices[0]
+        self.subscriber = self.android_devices[1]
+        results = {}
+        results['num_messages'] = 100
+
+        # Start Test
+        pub_connect_id = self.exec_connect(self.publisher, "publisher")
+        sub_connect_id = self.exec_connect(self.subscriber, "subscriber")
+
+        pub_id = self.publisher.droid.wifiAwarePublish(pub_connect_id,
+                                                       self.publish_config)
+        sub_id = self.subscriber.droid.wifiAwareSubscribe(sub_connect_id,
+                                                          self.subscribe_config)
+
+        try:
+            event_sub_match = self.subscriber.ed.pop_event(
+                aware_const.SESSION_CB_ON_SERVICE_DISCOVERED, EVENT_TIMEOUT)
+            self.log.info('%s: %s',
+                          aware_const.SESSION_CB_ON_SERVICE_DISCOVERED,
+                          event_sub_match['data'])
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on Subscriber' %
+                         aware_const.SESSION_CB_ON_SERVICE_DISCOVERED)
+        self.log.debug(event_sub_match)
+
+        results['num_tx_ok'] = 0
+        results['num_tx_fail'] = 0
+        tx_ok_stats = []
+        tx_fail_stats = []
+        events_regex = '%s|%s' % (aware_const.SESSION_CB_ON_MESSAGE_SEND_FAILED,
+                                  aware_const.SESSION_CB_ON_MESSAGE_SENT)
+        for i in range(results['num_messages']):
+            self.msg_id = self.msg_id + 1
+            self.subscriber.droid.wifiAwareSendMessage(
+                sub_id, event_sub_match['data']['peerId'], self.msg_id,
+                "msg %s" % i, 0)
+            try:
+                events = self.subscriber.ed.pop_events(events_regex,
+                                                       EVENT_TIMEOUT)
+                for event in events:
+                    if event['name'] == aware_const.SESSION_CB_ON_MESSAGE_SENT:
+                        results['num_tx_ok'] = results['num_tx_ok'] + 1
+                        if aware_const.SESSION_CB_KEY_LATENCY_MS in event[
+                                'data']:
+                            tx_ok_stats.append(event['data'][
+                                aware_const.SESSION_CB_KEY_LATENCY_MS])
+                    if event[
+                            'name'] == aware_const.SESSION_CB_ON_MESSAGE_SEND_FAILED:
+                        results['num_tx_fail'] = results['num_tx_fail'] + 1
+                        if aware_const.SESSION_CB_KEY_LATENCY_MS in event[
+                                'data']:
+                            tx_fail_stats.append(event['data'][
+                                aware_const.SESSION_CB_KEY_LATENCY_MS])
+            except queue.Empty:
+                asserts.fail('Timed out while waiting for %s on Subscriber',
+                             events_regex,
+                             extras=results)
+        self.extract_stats(tx_ok_stats, results, 'tx_ok_latency',
+                           'Successful tx')
+        self.extract_stats(tx_fail_stats, results, 'tx_fail_latency', 'Fail tx')
+        asserts.explicit_pass(
+            'test_aware_messaging_no_queue finished successfully',
+            extras=results)
+
+    def test_aware_rtt(self):
+        """Perform Aware configuration, discovery, and RTT.
+
+        Configuration: 2 devices, one acting as Publisher (P) and the
+        other as Subscriber (S)
+
+        Logical steps:
+          * P & S initiate Aware clustering (if not already up)
+          * P & S wait for Aware connection confirmation
+          * P starts publishing
+          * S starts subscribing
+          * S waits for a match (discovery) notification
+          * S performs 3 RTT measurements with P
+        """
+        # Configure Test
+        self.publisher = self.android_devices[0]
+        self.subscriber = self.android_devices[1]
+        rtt_iterations = 10
+
+        # Start Test
+        pub_connect_id = self.exec_connect(self.publisher, "publisher")
+        sub_connect_id = self.exec_connect(self.subscriber, "subscriber")
+
+        pub_id = self.publisher.droid.wifiAwarePublish(pub_connect_id,
+                                                       self.publish_config)
+        sub_id = self.subscriber.droid.wifiAwareSubscribe(sub_connect_id,
+                                                          self.subscribe_config)
+
+        try:
+            event_sub_match = self.subscriber.ed.pop_event(
+                aware_const.SESSION_CB_ON_SERVICE_DISCOVERED, EVENT_TIMEOUT)
+            self.log.info('%s: %s',
+                          aware_const.SESSION_CB_ON_SERVICE_DISCOVERED,
+                          event_sub_match['data'])
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on Subscriber' %
+                         aware_const.SESSION_CB_ON_SERVICE_DISCOVERED)
+        self.log.debug(event_sub_match)
+
+        self.exec_rtt(device=self.subscriber,
+                      session_id=sub_id,
+                      peer_id=event_sub_match['data']['peerId'],
+                      rtt_param=self.rtt_24_20, label="2.4GHz / 20MHz BW",
+                      repeat_count=rtt_iterations)
+        self.exec_rtt(device=self.subscriber,
+                      session_id=sub_id,
+                      peer_id=event_sub_match['data']['peerId'],
+                      rtt_param=self.rtt_50_40, label="5Hz / 40MHz BW",
+                      repeat_count=rtt_iterations)
+        self.exec_rtt(device=self.subscriber,
+                      session_id=sub_id,
+                      peer_id=event_sub_match['data']['peerId'],
+                      rtt_param=self.rtt_50_80, label="5GHz / 80MHz BW",
+                      repeat_count=rtt_iterations)
+
+    def test_disable_wifi_during_connection(self):
+        """Validate behavior when Wi-Fi is disabled during an active Aware
+        connection. Expected behavior: receive an onAwareDown(1002) event.
+
+        Configuration: 1 device - the DUT.
+
+        Logical steps:
+          * DUT initiate Aware clustering (if not already up)
+          * DUT waits for Aware connection confirmation
+          * DUT starts publishing
+          * Disable Wi-Fi
+          * DUT waits for an onAwareDown(1002) event and confirms that received
+        """
+        # Configure Test
+        self.dut = self.android_devices[0]
+
+        # Start Test
+        connect_id = self.dut.droid.wifiAwareAttach()
+
+        try:
+            event = self.dut.ed.pop_event(aware_const.EVENT_CB_ON_ATTACHED,
+                                          EVENT_TIMEOUT)
+            self.log.info('%s: %s', aware_const.EVENT_CB_ON_ATTACHED,
+                          event['data'])
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on dut' %
+                         aware_const.EVENT_CB_ON_ATTACHED)
+        self.log.debug(event)
+
+        pub_id = self.dut.droid.wifiAwarePublish(connect_id,
+                                                 self.publish_config)
+
+        asserts.assert_true(
+            wutils.wifi_toggle_state(self.dut, False),
+            "Failed disabling Wi-Fi interface on dut")
+
+        try:
+            event = self.dut.ed.pop_event(
+                aware_const.BROADCAST_WIFI_AWARE_NOT_AVAILABLE, EVENT_TIMEOUT)
+            self.log.info(aware_const.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on dut' %
+                         aware_const.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+        self.log.debug(event)
+
+    def test_aware_data_path(self):
+        """Perform Aware configuration, discovery, data-path setup, and data
+        transfer.
+
+        Configuration: 2 devices, one acting as Publisher (P) and the
+        other as Subscriber (S)
+
+        Logical steps:
+          * P & S initiate Aware clustering (if not already up)
+          * P & S wait for Aware connection confirmation
+          * P starts publishing
+          * S starts subscribing
+          * S waits for a match (discovery) notification
+          * S sends a message to P
+          * P waits for message
+          * P creates an Aware network to S as RESPONDER
+          * P sends a message to S
+          * S waits for message
+          * S creates an Aware network to P as INITIATOR (order important!)
+          * Both P & S wait for events confirming network set up
+          * NSD option:
+          *     P registers NSD service
+          *     S discovers NSD service and obtains P's IPv6 address
+          * Direct config option:
+          *     No communication: script uses address read from P
+          * run iperf3 between P (server) and S (client)
+          * unregister network callback on S
+        """
+        # Configure Test
+        self.publisher = self.android_devices[0]
+        self.subscriber = self.android_devices[1]
+        results = {}
+
+        sub2pub_msg = "Get ready!"
+        pub2sub_msg = "Ready!"
+        publisher_passphrase = None
+        subscriber_passphrase = None
+        use_nsd = False
+
+        # Start Test
+        pub_connect_id = self.exec_connect(self.publisher, "publisher")
+        sub_connect_id = self.exec_connect(self.subscriber, "subscriber")
+
+        # Discovery: publish + subscribe + wait for match
+        pub_id = self.publisher.droid.wifiAwarePublish(pub_connect_id,
+                                                       self.publish_config)
+        sub_id = self.subscriber.droid.wifiAwareSubscribe(sub_connect_id,
+                                                          self.subscribe_config)
+
+        def filter_callbacks(event, key, name):
+            return event['data'][key] == name
+
+        try:
+            event_sub_match = self.subscriber.ed.pop_event(
+                aware_const.SESSION_CB_ON_SERVICE_DISCOVERED, EVENT_TIMEOUT)
+            self.log.info('%s: %s',
+                          aware_const.SESSION_CB_ON_SERVICE_DISCOVERED,
+                          event_sub_match['data'])
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on Subscriber' %
+                         aware_const.SESSION_CB_ON_SERVICE_DISCOVERED)
+        self.log.debug(event_sub_match)
+
+        # S sends message to P
+        self.reliable_tx(self.subscriber, sub_id,
+                         event_sub_match['data']['peerId'], sub2pub_msg)
+
+        try:
+            event_pub_rx = self.publisher.ed.pop_event(
+                aware_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT)
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on publisher' %
+                         aware_const.SESSION_CB_ON_MESSAGE_RECEIVED)
+        self.log.info('%s: %s', aware_const.SESSION_CB_ON_MESSAGE_RECEIVED,
+                      event_pub_rx['data'])
+        asserts.assert_equal(event_pub_rx['data']['messageAsString'],
+                             sub2pub_msg,
+                             "Subscriber -> publisher message corrupted")
+
+        # P requests an Aware network as RESPONDER
+        pub_ns = self.publisher.droid.wifiAwareCreateNetworkSpecifier(pub_id,
+            event_pub_rx['data']['peerId'], publisher_passphrase)
+        self.log.info("Publisher network specifier - '%s'", pub_ns)
+        self.network_req['NetworkSpecifier'] = pub_ns
+        pub_req_key = self.publisher.droid.connectivityRequestWifiAwareNetwork(
+            self.network_req)
+
+        # P sends message to S
+        self.reliable_tx(self.publisher, pub_id, event_pub_rx['data']['peerId'],
+                         pub2sub_msg)
+
+        try:
+            event_sub_rx = self.subscriber.ed.pop_event(
+                aware_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT)
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s on subscriber' %
+                         aware_const.SESSION_CB_ON_MESSAGE_RECEIVED)
+        self.log.info('%s: %s', aware_const.SESSION_CB_ON_MESSAGE_RECEIVED,
+                      event_sub_rx['data'])
+        asserts.assert_equal(event_sub_rx['data']['messageAsString'],
+                             pub2sub_msg,
+                             "Publisher -> subscriber message corrupted")
+
+        # S requests an Aware network as INITIATOR
+        sub_ns = self.subscriber.droid.wifiAwareCreateNetworkSpecifier(sub_id,
+            event_sub_rx['data']['peerId'], subscriber_passphrase)
+        self.log.info("Subscriber network specifier - '%s'", sub_ns)
+        self.network_req['NetworkSpecifier'] = sub_ns
+        sub_req_key = self.subscriber.droid.connectivityRequestWifiAwareNetwork(
+            self.network_req)
+
+        # Wait until both S and P get confirmation that network formed
+        try:
+            event_network = self.subscriber.ed.wait_for_event(
+                con_const.EVENT_NETWORK_CALLBACK,
+                filter_callbacks,
+                EVENT_TIMEOUT,
+                key=con_const.NETWORK_CB_KEY_EVENT,
+                name=con_const.NETWORK_CB_LINK_PROPERTIES_CHANGED)
+            self.log.info('Subscriber %s: %s', con_const.EVENT_NETWORK_CALLBACK,
+                          event_network['data'])
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s/%s on Subscriber' %
+                         (con_const.EVENT_NETWORK_CALLBACK,
+                          con_const.NETWORK_CB_LINK_PROPERTIES_CHANGED))
+        self.log.debug(event_network)
+        sub_aware_if = event_network['data']['interfaceName']
+
+        try:
+            event_network = self.publisher.ed.wait_for_event(
+                con_const.EVENT_NETWORK_CALLBACK,
+                filter_callbacks,
+                EVENT_TIMEOUT,
+                key=con_const.NETWORK_CB_KEY_EVENT,
+                name=con_const.NETWORK_CB_LINK_PROPERTIES_CHANGED)
+            self.log.info('Publisher %s: %s', con_const.EVENT_NETWORK_CALLBACK,
+                          event_network['data'])
+        except queue.Empty:
+            asserts.fail('Timed out while waiting for %s/%s on Publisher' %
+                         (con_const.EVENT_NETWORK_CALLBACK,
+                          con_const.NETWORK_CB_LINK_PROPERTIES_CHANGED))
+        self.log.debug(event_network)
+        pub_aware_if = event_network['data']['interfaceName']
+
+        pub_ipv6 = "";
+        if use_nsd:
+            try:
+                # P registers NSD service (i.e. starts publishing)
+                nsd_reg = self.publisher.droid.nsdRegisterService(
+                    self.nsd_service_info)
+                try:
+                    event_nsd = self.publisher.ed.wait_for_event(
+                        nsd_const.REG_LISTENER_EVENT,
+                        filter_callbacks,
+                        EVENT_TIMEOUT,
+                        key=nsd_const.REG_LISTENER_CALLBACK,
+                        name=nsd_const.REG_LISTENER_EVENT_ON_SERVICE_REGISTERED)
+                    self.log.info(
+                        'Publisher %s: %s',
+                        nsd_const.REG_LISTENER_EVENT_ON_SERVICE_REGISTERED,
+                        event_nsd['data'])
+                except queue.Empty:
+                    asserts.fail('Timed out while waiting for %s on Publisher' %
+                             nsd_const.REG_LISTENER_EVENT_ON_SERVICE_REGISTERED)
+
+                # S starts NSD discovery
+                nsd_discovery = self.subscriber.droid.nsdDiscoverServices(
+                    self.nsd_service_info[
+                        nsd_const.NSD_SERVICE_INFO_SERVICE_TYPE])
+                try:
+                    event_nsd = self.subscriber.ed.wait_for_event(
+                        nsd_const.DISCOVERY_LISTENER_EVENT,
+                        filter_callbacks,
+                        EVENT_TIMEOUT,
+                        key=nsd_const.DISCOVERY_LISTENER_DATA_CALLBACK,
+                       name=nsd_const.DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND)
+                    self.log.info(
+                        'Subscriber %s: %s',
+                        nsd_const.DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND,
+                        event_nsd['data'])
+                except queue.Empty:
+                    asserts.fail(
+                        'Timed out while waiting for %s on Subscriber' %
+                        nsd_const.DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND)
+
+                # S resolves IP address of P from NSD service discovery
+                self.subscriber.droid.nsdResolveService(event_nsd['data'])
+                try:
+                    event_nsd = self.subscriber.ed.wait_for_event(
+                        nsd_const.RESOLVE_LISTENER_EVENT,
+                        filter_callbacks,
+                        EVENT_TIMEOUT,
+                        key=nsd_const.RESOLVE_LISTENER_DATA_CALLBACK,
+                      name=nsd_const.RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED)
+                    self.log.info(
+                        'Subscriber %s: %s',
+                        nsd_const.RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED,
+                        event_nsd['data'])
+                except queue.Empty:
+                    asserts.fail(
+                        'Timed out while waiting for %s on Subscriber' %
+                        nsd_const.RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED)
+
+                # mDNS returns first character as '/' - strip
+                # out to get clean IPv6
+                pub_ipv6 = event_nsd['data'][
+                               nsd_const.NSD_SERVICE_INFO_HOST][1:]
+            finally:
+                # Stop NSD
+                if nsd_reg is not None:
+                    self.publisher.droid.nsdUnregisterService(nsd_reg)
+                if nsd_discovery is not None:
+                    self.subscriber.droid.nsdStopServiceDiscovery(nsd_discovery)
+        else:
+            pub_ipv6 = self.publisher.droid.connectivityGetLinkLocalIpv6Address(
+                pub_aware_if)
+            pub_ipv6 = pub_ipv6.split('%')[0] # get rid of <name> - xx:xx%<name>
+            self.log.info('Publisher IPv6: %s', pub_ipv6)
+
+        # P starts iPerf server
+        result, data = self.publisher.run_iperf_server("-D")
+        asserts.assert_true(result, "Can't start iperf3 server")
+
+        # S starts iPerf client
+        result, data = self.subscriber.run_iperf_client(
+            "%s%%%s" % (pub_ipv6, sub_aware_if), "-6 -J")
+        self.log.debug(data)
+        asserts.assert_true(result,
+                            "Failure starting/running iperf3 in client mode")
+        self.log.debug(pprint.pformat(data))
+        data_json = json.loads(''.join(data))
+        results['tx_rate'] = data_json['end']['sum_sent']['bits_per_second']
+        results['rx_rate'] = data_json['end']['sum_received']['bits_per_second']
+        self.log.info('iPerf3: Sent = %d bps Received = %d bps',
+                      results['tx_rate'], results['rx_rate'])
+        asserts.explicit_pass('Aware data-path test passes', extras=results)
diff --git a/acts/tests/google/wifi/WifiEnterpriseRoamingTest.py b/acts/tests/google/wifi/WifiEnterpriseRoamingTest.py
index 09bfed0..1321136 100644
--- a/acts/tests/google/wifi/WifiEnterpriseRoamingTest.py
+++ b/acts/tests/google/wifi/WifiEnterpriseRoamingTest.py
@@ -17,11 +17,10 @@
 import random
 import time
 
-import acts.base_test
-import acts.signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
-
 from acts import asserts
+from acts import base_test
+from acts import signals
+from acts.test_utils.wifi import wifi_test_utils as wutils
 
 WifiEnums = wutils.WifiEnums
 
@@ -32,14 +31,8 @@
 # Enterprise Config Macros
 Ent = WifiEnums.Enterprise
 
-class WifiEnterpriseRoamingTest(acts.base_test.BaseTestClass):
 
-    def __init__(self, controllers):
-        acts.base_test.BaseTestClass.__init__(self, controllers)
-        self.tests = (
-            "test_roaming_with_different_auth_method",
-        )
-
+class WifiEnterpriseRoamingTest(base_test.BaseTestClass):
     def setup_class(self):
         self.dut = self.android_devices[0]
         wutils.wifi_test_device_init(self.dut)
@@ -55,8 +48,7 @@
             "client_key",
             "eap_identity",
             "eap_password",
-            "device_password"
-        )
+            "device_password")
         self.unpack_userparams(req_params)
         self.config_peap = {
             Ent.EAP: int(EAP.PEAP),
@@ -86,6 +78,7 @@
             Ent.EAP: int(EAP.SIM),
             WifiEnums.SSID_KEY: self.ent_roaming_ssid,
         }
+        self.attenuators = wutils.group_attenuators(self.attenuators)
         self.attn_a = self.attenuators[0]
         self.attn_b = self.attenuators[1]
         # Set screen lock password so ConfigStore is unlocked.
@@ -104,7 +97,6 @@
         self.dut.droid.wakeUpNow()
         wutils.reset_wifi(self.dut)
         self.dut.ed.clear_all_events()
-        return True
 
     def teardown_test(self):
         self.dut.droid.wakeLockRelease()
@@ -121,14 +113,14 @@
         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)
+        self.log.info("Set attenuation values to %s",
+                      self.attn_vals[attn_val_name])
         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)
+            self.log.exception("Failed to set attenuation values %s.",
+                           attn_val_name)
             raise
 
     def gen_eap_configs(self):
@@ -138,7 +130,7 @@
             A list of dicts each representing an EAP configuration.
         """
         configs = [self.config_tls]
-                   # self.config_sim
+        # self.config_sim
         configs += wutils.expand_enterprise_config_by_phase2(self.config_ttls)
         configs += wutils.expand_enterprise_config_by_phase2(self.config_peap)
         return configs
@@ -153,14 +145,14 @@
                 to roam to.
         """
         self.set_attns(attn_val_name)
-        self.log.info("Wait %ss for roaming to finish." % self.roam_interval)
+        self.log.info("Wait %ss for roaming to finish.", self.roam_interval)
         time.sleep(self.roam_interval)
         try:
             self.dut.droid.wakeLockAcquireBright()
             self.dut.droid.wakeUpNow()
             wutils.verify_wifi_connection_info(self.dut, expected_con)
             expected_bssid = expected_con[WifiEnums.BSSID_KEY]
-            self.log.info("Roamed to %s successfully" % expected_bssid)
+            self.log.info("Roamed to %s successfully", expected_bssid)
         finally:
             self.dut.droid.wifiLockRelease()
             self.dut.droid.goToSleepNow()
@@ -185,29 +177,27 @@
             WifiEnums.BSSID_KEY: self.bssid_b,
         }
         self.set_attns("a_on_b_off")
-        asserts.assert_true(
-            wutils.eap_connect(config, self.dut, validate_con=False),
-            "Failed to connect to %s" % config
-            )
+        wutils.wifi_connect(self.dut, config)
         wutils.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.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.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 """
-    @acts.signals.generated_test
+
+    @signals.generated_test
     def test_roaming_with_different_auth_method(self):
         eap_configs = self.gen_eap_configs()
-        self.log.info("Testing %d different configs." % len(eap_configs))
+        self.log.info("Testing %d different configs.", len(eap_configs))
         random.shuffle(eap_configs)
         failed = self.run_generated_testcases(
             self.roaming_between_a_and_b_logic,
             eap_configs,
             name_func=wutils.generate_eap_test_name)
-        msg = ("The following configs failed enterprise roaming test: %s" %
-               pprint.pformat(failed))
-        asserts.assert_true(len(failed) == 0, msg)
+        asserts.assert_equal(
+            len(failed), 0,
+            "The following configs failed enterprise roaming test: %s" %
+            pprint.pformat(failed))
 
     """ Tests End """
diff --git a/acts/tests/google/wifi/WifiEnterpriseTest.py b/acts/tests/google/wifi/WifiEnterpriseTest.py
index 9d56d70..98caaf2 100755
--- a/acts/tests/google/wifi/WifiEnterpriseTest.py
+++ b/acts/tests/google/wifi/WifiEnterpriseTest.py
@@ -31,45 +31,29 @@
 # Enterprise Config Macros
 Ent = WifiEnums.Enterprise
 
-class WifiEnterpriseTest(base_test.BaseTestClass):
 
+class WifiEnterpriseTest(base_test.BaseTestClass):
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
-        self.tests = (
-            "test_eap_connect",
-            "test_eap_connect_negative",
-        )
+        self.tests = ("test_eap_connect", "test_eap_connect_negative",
+                      "test_eap_connect_config_store", )
 
     def setup_class(self):
         self.dut = self.android_devices[0]
         wutils.wifi_test_device_init(self.dut)
+        # If running in a setup with attenuators, set attenuation on all
+        # channels to zero.
+        if getattr(self, "attenuators", []):
+            for a in self.attenuators:
+                a.set_atten(0)
         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_peap0",
-            "ssid_peap1",
-            "ssid_tls",
-            "ssid_ttls",
-            "ssid_pwd",
-            "ssid_sim",
-            "ssid_aka",
-            "ssid_aka_prime",
-            "ssid_passpoint",
-            "device_password",
-            "ping_addr"
-        )
+            "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_peap0", "ssid_peap1", "ssid_tls", "ssid_ttls", "ssid_pwd",
+            "ssid_sim", "ssid_aka", "ssid_aka_prime", "ssid_passpoint",
+            "device_password", "ping_addr")
         self.unpack_userparams(required_userparam_names,
                                roaming_consortium_ids=None,
                                plmn=None)
@@ -129,13 +113,15 @@
         if self.plmn:
             self.config_passpoint[Ent.PLMN] = self.plmn
         if self.roaming_consortium_ids:
-            self.config_passpoint[Ent.ROAMING_IDS] = 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
+        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)
@@ -176,7 +162,7 @@
             True if connection failed as expected, False otherwise.
         """
         with asserts.assert_raises(signals.TestFailure, extras=config):
-            verdict = wutils.eap_connect(config, ad)
+            verdict = wutils.wifi_connect(ad, config)
         asserts.explicit_pass("Connection failed as expected.")
 
     def expand_config_by_phase2(self, config):
@@ -205,11 +191,8 @@
         Returns:
             A list of dicts each representing an EAP configuration.
         """
-        configs = [self.config_tls,
-                   self.config_pwd,
-                   self.config_sim,
-                   self.config_aka,
-                   self.config_aka_prime]
+        configs = [self.config_tls, self.config_pwd, self.config_sim,
+                   self.config_aka, self.config_aka_prime]
         configs += wutils.expand_enterprise_config_by_phase2(self.config_ttls)
         configs += wutils.expand_enterprise_config_by_phase2(self.config_peap0)
         configs += wutils.expand_enterprise_config_by_phase2(self.config_peap1)
@@ -224,7 +207,8 @@
             passpoint networks.
         """
         configs = [self.config_passpoint_tls]
-        configs += wutils.expand_enterprise_config_by_phase2(self.config_passpoint_ttls)
+        configs += wutils.expand_enterprise_config_by_phase2(
+            self.config_passpoint_ttls)
         return configs
 
     def gen_negative_configs(self, configs, neg_params):
@@ -300,6 +284,21 @@
         configs = self.gen_passpoint_configs()
         return self.gen_negative_configs(configs, neg_params)
 
+    def gen_eap_test_name_for_config_store(self, config, ad):
+        """Generates a test case name based on an EAP configuration for config
+        store tests.
+
+        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.
+        """
+        return wutils.generate_eap_test_name(config) + "-config_store"
+
     def gen_passpoint_test_name(self, config, ad):
         """Generates a test case name based on an EAP passpoint configuration.
 
@@ -317,7 +316,43 @@
         name = name.replace("connect", "passpoint_connect")
         return name
 
+    def gen_passpoint_test_name_for_config_store(self, config, ad):
+        """Generates a test case name based on an EAP passpoint configuration
+        for config store tests.
+
+        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.
+        """
+        return self.gen_passpoint_test_name(config, ad) + "-config_store"
+
+    def eap_connect_toggle_wifi(self,
+                                config,
+                                *args):
+        """Connects to an enterprise network, toggles wifi state and ensures
+        that the device reconnects.
+
+        This logic expect the enterprise network to have Internet access.
+
+        Args:
+            config: A dict representing a wifi enterprise configuration.
+            args: args to be passed to |wutils.eap_connect|.
+
+        Returns:
+            True if the connection is successful and Internet access works.
+        """
+        ad = args[0]
+        wutils.wifi_connect(ad, config)
+        wutils.toggle_wifi_and_wait_for_reconnection(ad, config, num_of_tries=5)
+
     """Tests"""
+
     @signals.generated_test
     def test_eap_connect(self):
         """Test connecting to enterprise networks of different authentication
@@ -338,16 +373,16 @@
             networks.
         """
         eap_configs = self.gen_eap_configs()
-        self.log.info("Testing %d different configs." % len(eap_configs))
+        self.log.info("Testing %d different configs.", len(eap_configs))
         random.shuffle(eap_configs)
-        failed = self.run_generated_testcases(
-            wutils.eap_connect,
-            eap_configs,
-            args=(self.dut,),
-            name_func=wutils.generate_eap_test_name)
-        msg = ("The following configs failed EAP connect test: %s" %
-               pprint.pformat(failed))
-        asserts.assert_equal(len(failed), 0, msg)
+        failed = self.run_generated_testcases(wutils.wifi_connect,
+                                              eap_configs,
+                                              args=(self.dut, ),
+                                              name_func=wutils.generate_eap_test_name,
+                                              format_args=True)
+        asserts.assert_equal(
+            len(failed), 0, "The following configs failed EAP connect test: %s"
+            % pprint.pformat(failed))
 
     @signals.generated_test
     def test_eap_connect_negative(self):
@@ -361,22 +396,56 @@
             Fail to establish connection.
         """
         neg_eap_configs = self.gen_negative_eap_configs()
-        self.log.info("Testing %d different configs." % len(neg_eap_configs))
+        self.log.info("Testing %d different configs.", len(neg_eap_configs))
         random.shuffle(neg_eap_configs)
+
         def name_gen(config, ad):
             name = wutils.generate_eap_test_name(config)
             name += "-with_wrong-{}".format(config["invalid_field"])
             return name
-        failed = self.run_generated_testcases(
-            self.eap_negative_connect_logic,
-            neg_eap_configs,
-            args=(self.dut,),
-            name_func=name_gen)
+
+        failed = self.run_generated_testcases(self.eap_negative_connect_logic,
+                                              neg_eap_configs,
+                                              args=(self.dut, ),
+                                              name_func=name_gen)
         msg = ("The following configs failed negative EAP connect test: %s" %
                pprint.pformat(failed))
         asserts.assert_equal(len(failed), 0, msg)
 
     @signals.generated_test
+    def test_eap_connect_config_store(self):
+        """Test connecting to enterprise networks of different authentication
+        types after wifi toggle.
+
+        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.
+            3. Toggle wifi.
+            4. Ensure that the device reconnects to the same network.
+
+        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))
+        random.shuffle(eap_configs)
+        failed = self.run_generated_testcases(
+            self.eap_connect_toggle_wifi,
+            eap_configs,
+            args=(self.dut, ),
+            name_func=wutils.generate_eap_test_name)
+        asserts.assert_equal(
+            len(failed), 0, "The following configs failed EAP connect test: %s"
+            % pprint.pformat(failed))
+
+    @signals.generated_test
     def test_passpoint_connect(self):
         """Test connecting to enterprise networks of different authentication
         types with passpoint support.
@@ -395,18 +464,21 @@
             networks with passpoint support.
         """
         asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
-            "Passpoint is not supported on device %s" % self.dut.model)
+                        "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))
+        self.log.info("Testing %d different configs.", len(passpoint_configs))
         random.shuffle(passpoint_configs)
         failed = self.run_generated_testcases(
-            wutils.eap_connect,
-            passpoint_configs,
-            args=(self.dut,),
-            name_func=self.gen_passpoint_test_name)
-        msg = ("The following configs failed passpoint connect test: %s" %
-               pprint.pformat(failed))
-        asserts.assert_equal(len(failed), 0, msg)
+                wutils.wifi_connect,
+                passpoint_configs,
+                args=(self.dut, ),
+                name_func=self.gen_passpoint_test_name,
+                format_args=True)
+        asserts.assert_equal(
+            len(failed), 0,
+            "The following configs failed passpoint connect test: %s" %
+            pprint.pformat(failed))
 
     @signals.generated_test
     def test_passpoint_connect_negative(self):
@@ -420,19 +492,59 @@
             Fail to establish connection.
         """
         asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
-            "Passpoint is not supported on device %s" % self.dut.model)
+                        "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))
+        self.log.info("Testing %d different configs.",
+                      len(neg_passpoint_configs))
         random.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,
+                                              args=(self.dut, ),
+                                              name_func=name_gen)
+        asserts.assert_equal(
+            len(failed), 0,
+            "The following configs failed negative passpoint connect test: %s"
+            % pprint.pformat(failed))
+
+    @signals.generated_test
+    def test_passpoint_connect_config_store(self):
+        """Test connecting to enterprise networks of different authentication
+        types with passpoint support after wifi toggle.
+
+        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.
+            3. Toggle wifi.
+            4. Ensure that the device reconnects to the same network.
+
+        Expect:
+            Successful connection and Internet access through the enterprise
+            networks with passpoint support.
+        """
+        asserts.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))
+        random.shuffle(passpoint_configs)
         failed = self.run_generated_testcases(
-            self.eap_negative_connect_logic,
-            neg_passpoint_configs,
-            args=(self.dut,),
-            name_func=name_gen)
-        msg = ("The following configs failed negative passpoint connect test: "
-               "%s") % pprint.pformat(failed)
-        asserts.assert_equal(len(failed), 0, msg)
+            self.eap_connect_toggle_wifi,
+            passpoint_configs,
+            args=(self.dut, ),
+            name_func=self.gen_passpoint_test_name_for_config_store)
+        asserts.assert_equal(
+            len(failed), 0,
+            "The following configs failed passpoint connect test: %s" %
+            pprint.pformat(failed))
diff --git a/acts/tests/google/wifi/WifiManagerTest.py b/acts/tests/google/wifi/WifiManagerTest.py
index 81a6303..c2feb03 100755
--- a/acts/tests/google/wifi/WifiManagerTest.py
+++ b/acts/tests/google/wifi/WifiManagerTest.py
@@ -22,48 +22,239 @@
 import acts.base_test
 import acts.signals
 import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils
 
 from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
 
 WifiEnums = wutils.WifiEnums
-WifiEventNames = wutils.WifiEventNames
+# Default timeout used for reboot, toggle WiFi and Airplane mode,
+# for the system to settle down after the operation.
+DEFAULT_TIMEOUT = 10
+BAND_2GHZ = 0
+BAND_5GHZ = 1
 
-class WifiManagerTest(acts.base_test.BaseTestClass):
+
+class WifiManagerTest(WifiBaseTest):
+    """Tests for APIs in Android's WifiManager class.
+
+    Test Bed Requirement:
+    * One Android device
+    * Several Wi-Fi networks visible to the device, including an open Wi-Fi
+      network.
+    """
+
+    def __init__(self, controllers):
+        WifiBaseTest.__init__(self, controllers)
 
     def setup_class(self):
         self.dut = self.android_devices[0]
         wutils.wifi_test_device_init(self.dut)
-        req_params = (
-            "iot_networks",
-            "open_network",
-            "iperf_server_address",
-            "tdls_models",
-            "energy_info_models"
-            )
-        self.unpack_userparams(req_params)
-        asserts.assert_true(len(self.iot_networks) > 0,
+        # If running in a setup with attenuators, set attenuation on all
+        # channels to zero.
+        if getattr(self, "attenuators", []):
+            for a in self.attenuators:
+                a.set_atten(0)
+        req_params = []
+        opt_param = [
+            "additional_energy_info_models", "additional_tdls_models",
+            "iot_networks", "open_network", "config_store_networks",
+            "reference_networks", "iperf_server_address"
+        ]
+        self.unpack_userparams(
+            req_param_names=req_params, opt_param_names=opt_param)
+
+        if "AccessPoint" in self.user_params:
+            self.legacy_configure_ap_and_start()
+
+        asserts.assert_true(
+            len(self.reference_networks) > 0,
+            "Need at least one reference network with psk.")
+        asserts.assert_true(
+            len(self.iot_networks) > 0,
             "Need at least one iot network with psk.")
-        asserts.assert_true(wutils.wifi_toggle_state(self.dut, True),
-            "Failed to turn on wifi before tests.")
+        wutils.wifi_toggle_state(self.dut, True)
         self.iot_networks = self.iot_networks + [self.open_network]
-        self.iperf_server = self.iperf_servers[0]
+        if "iperf_server_address" in self.user_params:
+            self.iperf_server = self.iperf_servers[0]
+        self.iot_test_prefix = "test_connection_to-"
+        self.wpapsk_2g = self.reference_networks[0]["2g"]
+        self.wpapsk_5g = self.reference_networks[0]["5g"]
 
     def setup_test(self):
         self.dut.droid.wakeLockAcquireBright()
         self.dut.droid.wakeUpNow()
-        self.iperf_server.start()
+        if self.iot_test_prefix in self.current_test_name:
+            if "iperf_server_address" in self.user_params:
+                self.iperf_server.start()
 
     def teardown_test(self):
         self.dut.droid.wakeLockRelease()
         self.dut.droid.goToSleepNow()
         wutils.reset_wifi(self.dut)
-        self.iperf_server.stop()
+        if self.iot_test_prefix in self.current_test_name:
+            if "iperf_server_address" in self.user_params:
+                self.iperf_server.stop()
 
     def on_fail(self, test_name, begin_time):
+        self.dut.take_bug_report(test_name, begin_time)
         self.dut.cat_adb_log(test_name, begin_time)
 
     """Helper Functions"""
-    def connect_to_wifi_network_with_password(self, params):
+
+    def connect_to_wifi_network(self, params):
+        """Connection logic for open and psk wifi networks.
+
+        Args:
+            params: A tuple of network info and AndroidDevice object.
+        """
+        network, ad = params
+        droid = ad.droid
+        ed = ad.ed
+        SSID = network[WifiEnums.SSID_KEY]
+        ed.clear_all_events()
+        wutils.start_wifi_connection_scan(ad)
+        scan_results = droid.wifiGetScanResults()
+        wutils.assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
+        wutils.wifi_connect(ad, network, num_of_tries=3)
+
+    def get_connection_data(self, dut, network):
+        """Get network id and ssid info from connection data.
+
+        Args:
+            dut: The Android device object under test.
+            network: dict representing the network to connect to.
+
+        Returns:
+            A convenience dict with the connected network's ID and SSID.
+
+        """
+        params = (network, dut)
+        self.connect_to_wifi_network(params)
+        connect_data = dut.droid.wifiGetConnectionInfo()
+        ssid_id_dict = dict()
+        ssid_id_dict[WifiEnums.NETID_KEY] = connect_data[WifiEnums.NETID_KEY]
+        ssid_id_dict[WifiEnums.SSID_KEY] = connect_data[WifiEnums.SSID_KEY]
+        return ssid_id_dict
+
+    def connect_multiple_networks(self, dut):
+        """Connect to one 2.4GHz and one 5Ghz network.
+
+        Args:
+            dut: The Android device object under test.
+
+        Returns:
+            A list with the connection details for the 2GHz and 5GHz networks.
+
+        """
+        network_list = list()
+        connect_2g_data = self.get_connection_data(dut, self.wpapsk_2g)
+        network_list.append(connect_2g_data)
+        connect_5g_data = self.get_connection_data(dut, self.wpapsk_5g)
+        network_list.append(connect_5g_data)
+        return network_list
+
+    def get_enabled_network(self, network1, network2):
+        """Check network status and return currently unconnected network.
+
+        Args:
+            network1: dict representing a network.
+            network2: dict representing a network.
+
+        Return:
+            Network dict of the unconnected network.
+
+        """
+        wifi_info = self.dut.droid.wifiGetConnectionInfo()
+        enabled = network1
+        if wifi_info[WifiEnums.SSID_KEY] == network1[WifiEnums.SSID_KEY]:
+            enabled = network2
+        return enabled
+
+    def check_configstore_networks(self, networks):
+        """Verify that all previously configured networks are presistent after
+           reboot.
+
+        Args:
+            networks: List of network dicts.
+
+        Return:
+            None. Raises TestFailure.
+
+        """
+        network_info = self.dut.droid.wifiGetConfiguredNetworks()
+        if len(network_info) != len(networks):
+            msg = (
+                "Length of configured networks before and after reboot don't"
+                " match. \nBefore reboot = %s \n After reboot = %s" %
+                (networks, network_info))
+            raise signals.TestFailure(msg)
+        current_count = 0
+        # For each network, check if it exists in configured list after reboot
+        for network in networks:
+            exists = wutils.match_networks({
+                WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY]
+            }, network_info)
+            if not len(exists):
+                raise signals.TestFailure("%s network is not present in the"
+                                          " configured list after reboot" %
+                                          network[WifiEnums.SSID_KEY])
+            # Get the new network id for each network after reboot.
+            network[WifiEnums.NETID_KEY] = exists[0]['networkId']
+            if exists[0]['status'] == 'CURRENT':
+                current_count += 1
+                # At any given point, there can only be one currently active
+                # network, defined with 'status':'CURRENT'
+                if current_count > 1:
+                    raise signals.TestFailure("More than one network showing"
+                                              "as 'CURRENT' after reboot")
+
+    def connect_to_wifi_network_with_id(self, network_id, network_ssid):
+        """Connect to the given network using network id and verify SSID.
+
+        Args:
+            network_id: int Network Id of the network.
+            network_ssid: string SSID of the network.
+
+        Returns: True if connect using network id was successful;
+                 False otherwise.
+
+        """
+        self.dut.ed.clear_all_events()
+        wutils.start_wifi_connection_scan(self.dut)
+        scan_results = self.dut.droid.wifiGetScanResults()
+        wutils.assert_network_in_list({
+            WifiEnums.SSID_KEY: network_ssid
+        }, scan_results)
+        wutils.wifi_connect_by_id(self.dut, network_id)
+        connect_data = self.dut.droid.wifiGetConnectionInfo()
+        connect_ssid = connect_data[WifiEnums.SSID_KEY]
+        self.log.debug("Expected SSID = %s Connected SSID = %s" %
+                       (network_ssid, connect_ssid))
+        if connect_ssid != network_ssid:
+            return False
+        return True
+
+    def run_iperf_client(self, params):
+        """Run iperf traffic after connection.
+
+        Args:
+            params: A tuple of network info and AndroidDevice object.
+        """
+        if "iperf_server_address" in self.user_params:
+            wait_time = 5
+            network, ad = params
+            SSID = network[WifiEnums.SSID_KEY]
+            self.log.info("Starting iperf traffic through {}".format(SSID))
+            time.sleep(wait_time)
+            port_arg = "-p {}".format(self.iperf_server.port)
+            success, data = ad.run_iperf_client(self.iperf_server_address,
+                                                port_arg)
+            self.log.debug(pprint.pformat(data))
+            asserts.assert_true(success, "Error occurred in iPerf traffic.")
+
+    def connect_to_wifi_network_and_run_iperf(self, params):
         """Connection logic for open and psk wifi networks.
 
         Logic steps are
@@ -72,67 +263,60 @@
 
         Args:
             params: A tuple of network info and AndroidDevice object.
-
-        Returns:
-            True if successful, False otherwise.
         """
-        result = False
-        wait_time = 5
+        self.connect_to_wifi_network(params)
+        self.run_iperf_client(params)
+
+    def connect_to_wifi_network_toggle_wifi_and_run_iperf(self, params):
+        """ Connect to the provided network and then toggle wifi mode and wait
+        for reconnection to the provided network.
+
+        Logic steps are
+        1. Connect to the network.
+        2. Turn wifi off.
+        3. Turn wifi on.
+        4. Wait for connection to the network.
+        5. Run iperf traffic.
+
+        Args:
+            params: A tuple of network info and AndroidDevice object.
+       """
         network, ad = params
-        droid = ad.droid
-        ed = ad.ed
-        SSID = network[WifiEnums.SSID_KEY]
-        try:
-            ed.clear_all_events()
-            wutils.start_wifi_connection_scan(ad)
-            droid.wifiStartTrackingStateChange()
-            asserts.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 queue.Empty:
-            self.log.exception("Failed to connect to {}".format(SSID))
-        finally:
-            droid.wifiStopTrackingStateChange()
-        return result
+        self.connect_to_wifi_network(params)
+        wutils.toggle_wifi_and_wait_for_reconnection(
+            ad, network, num_of_tries=5)
+        self.run_iperf_client(params)
 
     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."))
+                            "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)
+            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)
+        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"""
+
+    @test_tracker_info(uuid="525fc5e3-afba-4bfd-9a02-5834119e3c66")
     def test_toggle_state(self):
         """Test toggling wifi"""
         self.log.debug("Going from on to off.")
-        asserts.assert_true(wutils.wifi_toggle_state(self.dut, False),
-                         "Failed to turn wifi off.")
+        wutils.wifi_toggle_state(self.dut, False)
         self.log.debug("Going from off to on.")
-        asserts.assert_true(wutils.wifi_toggle_state(self.dut, True),
-                         "Failed to turn wifi on.")
+        wutils.wifi_toggle_state(self.dut, True)
 
+    @test_tracker_info(uuid="e9d11563-2bbe-4c96-87eb-ec919b51435b")
     def test_toggle_with_screen(self):
         """Test toggling wifi with screen on/off"""
         wait_time = 5
@@ -142,73 +326,296 @@
         time.sleep(wait_time)
         self.log.debug("Going from on to off.")
         try:
-            asserts.assert_true(wutils.wifi_toggle_state(self.dut, False),
-                             "Failed to turn wifi off.")
+            wutils.wifi_toggle_state(self.dut, False)
             time.sleep(wait_time)
             self.log.debug("Going from off to on.")
-            asserts.assert_true(wutils.wifi_toggle_state(self.dut, True),
-                             "Failed to turn wifi on.")
+            wutils.wifi_toggle_state(self.dut, True)
         finally:
             self.dut.droid.wakeLockRelease()
             time.sleep(wait_time)
             self.dut.droid.goToSleepNow()
 
+    @test_tracker_info(uuid="71556e06-7fb1-4e2b-9338-b01f1f8e286e")
     def test_scan(self):
         """Test wifi connection scan can start and find expected networks."""
         wutils.wifi_toggle_state(self.dut, True)
         self.log.debug("Start regular wifi scan.")
         wutils.start_wifi_connection_scan(self.dut)
         wifi_results = self.dut.droid.wifiGetScanResults()
-        self.log.debug("Scan results: %s" % wifi_results)
+        self.log.debug("Scan results: %s", wifi_results)
         ssid = self.open_network[WifiEnums.SSID_KEY]
-        condition = {WifiEnums.SSID_KEY: ssid}
-        asserts.assert_true(wutils.match_networks(condition, wifi_results),
-                         "Can not find expected network %s" % ssid)
+        wutils.assert_network_in_list({WifiEnums.SSID_KEY: ssid}, wifi_results)
 
+    @test_tracker_info(uuid="a4ad9930-a8fa-4868-81ed-a79c7483e502")
     def test_add_network(self):
         """Test wifi connection scan."""
         ssid = self.open_network[WifiEnums.SSID_KEY]
         nId = self.dut.droid.wifiAddNetwork(self.open_network)
         asserts.assert_true(nId > -1, "Failed to add network.")
         configured_networks = self.dut.droid.wifiGetConfiguredNetworks()
-        self.log.debug(("Configured networks after adding: %s" %
-                        configured_networks))
-        condition = {WifiEnums.SSID_KEY: ssid}
-        asserts.assert_true(wutils.match_networks(condition, configured_networks),
-                         ("Could not find expected network %s in configured "
-                          "networks.") % ssid)
+        self.log.debug(
+            ("Configured networks after adding: %s" % configured_networks))
+        wutils.assert_network_in_list({
+            WifiEnums.SSID_KEY: ssid
+        }, configured_networks)
 
+    @test_tracker_info(uuid="aca85551-10ba-4007-90d9-08bcdeb16a60")
     def test_forget_network(self):
         self.test_add_network()
         ssid = self.open_network[WifiEnums.SSID_KEY]
         wutils.wifi_forget_network(self.dut, ssid)
         configured_networks = self.dut.droid.wifiGetConfiguredNetworks()
         for nw in configured_networks:
-            asserts.assert_true(nw[WifiEnums.BSSID_KEY] != ssid,
+            asserts.assert_true(
+                nw[WifiEnums.BSSID_KEY] != ssid,
                 "Found forgotten network %s in configured networks." % ssid)
 
+    @test_tracker_info(uuid="b306d65c-6df3-4eb5-a178-6278bdc76c3e")
+    def test_reconnect_to_connected_network(self):
+        """Connect to a network and immediately issue reconnect.
+
+        Steps:
+        1. Connect to a 2GHz network.
+        2. Reconnect to the network using its network id.
+        3. Connect to a 5GHz network.
+        4. Reconnect to the network using its network id.
+
+        """
+        connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
+        reconnect_2g = self.connect_to_wifi_network_with_id(
+            connect_2g_data[WifiEnums.NETID_KEY],
+            connect_2g_data[WifiEnums.SSID_KEY])
+        if not reconnect_2g:
+            raise signals.TestFailure("Device did not connect to the correct"
+                                      " 2GHz network.")
+        connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
+        reconnect_5g = self.connect_to_wifi_network_with_id(
+            connect_5g_data[WifiEnums.NETID_KEY],
+            connect_5g_data[WifiEnums.SSID_KEY])
+        if not reconnect_5g:
+            raise signals.TestFailure("Device did not connect to the correct"
+                                      " 5GHz network.")
+
+    @test_tracker_info(uuid="3cff17f6-b684-4a95-a438-8272c2ad441d")
+    def test_reconnect_to_previously_connected(self):
+        """Connect to multiple networks and reconnect to the previous network.
+
+        Steps:
+        1. Connect to a 2GHz network.
+        2. Connect to a 5GHz network.
+        3. Reconnect to the 2GHz network using its network id.
+        4. Reconnect to the 5GHz network using its network id.
+
+        """
+        connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
+        connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
+        reconnect_2g = self.connect_to_wifi_network_with_id(
+            connect_2g_data[WifiEnums.NETID_KEY],
+            connect_2g_data[WifiEnums.SSID_KEY])
+        if not reconnect_2g:
+            raise signals.TestFailure("Device did not connect to the correct"
+                                      " 2GHz network.")
+        reconnect_5g = self.connect_to_wifi_network_with_id(
+            connect_5g_data[WifiEnums.NETID_KEY],
+            connect_5g_data[WifiEnums.SSID_KEY])
+        if not reconnect_5g:
+            raise signals.TestFailure("Device did not connect to the correct"
+                                      " 5GHz network.")
+
+    @test_tracker_info(uuid="334175c3-d26a-4c87-a8ab-8eb220b2d80f")
+    def test_reconnect_toggle_wifi(self):
+        """Connect to multiple networks, turn off/on wifi, then reconnect to
+           a previously connected network.
+
+        Steps:
+        1. Connect to a 2GHz network.
+        2. Connect to a 5GHz network.
+        3. Turn WiFi OFF/ON.
+        4. Reconnect to the non-current network.
+
+        """
+        connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
+        connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
+        wutils.toggle_wifi_off_and_on(self.dut)
+        reconnect_to = self.get_enabled_network(connect_2g_data,
+                                                connect_5g_data)
+        reconnect = self.connect_to_wifi_network_with_id(
+            reconnect_to[WifiEnums.NETID_KEY],
+            reconnect_to[WifiEnums.SSID_KEY])
+        if not reconnect:
+            raise signals.TestFailure("Device did not connect to the correct"
+                                      " network after toggling WiFi.")
+
+    @test_tracker_info(uuid="8e6e6c21-fefb-4fe8-9fb1-f09b1182b76d")
+    def test_reconnect_toggle_airplane(self):
+        """Connect to multiple networks, turn on/off Airplane moce, then
+           reconnect a previously connected network.
+
+        Steps:
+        1. Connect to a 2GHz network.
+        2. Connect to a 5GHz network.
+        3. Turn ON/OFF Airplane mode.
+        4. Reconnect to the non-current network.
+
+        """
+        connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
+        connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
+        wutils.toggle_airplane_mode_on_and_off(self.dut)
+        reconnect_to = self.get_enabled_network(connect_2g_data,
+                                                connect_5g_data)
+        reconnect = self.connect_to_wifi_network_with_id(
+            reconnect_to[WifiEnums.NETID_KEY],
+            reconnect_to[WifiEnums.SSID_KEY])
+        if not reconnect:
+            raise signals.TestFailure("Device did not connect to the correct"
+                                      " network after toggling Airplane mode.")
+
+    @test_tracker_info(uuid="3d041c12-05e2-46a7-ab9b-e3f60cc735db")
+    def test_reboot_configstore_reconnect(self):
+        """Connect to multiple networks, reboot then reconnect to previously
+           connected network.
+
+        Steps:
+        1. Connect to a 2GHz network.
+        2. Connect to a 5GHz network.
+        3. Reboot device.
+        4. Verify all networks are persistent after reboot.
+        5. Reconnect to the non-current network.
+
+        """
+        network_list = self.connect_multiple_networks(self.dut)
+        self.dut.reboot()
+        time.sleep(DEFAULT_TIMEOUT)
+        self.check_configstore_networks(network_list)
+
+        reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
+                                                network_list[BAND_5GHZ])
+
+        reconnect = self.connect_to_wifi_network_with_id(
+            reconnect_to[WifiEnums.NETID_KEY],
+            reconnect_to[WifiEnums.SSID_KEY])
+        if not reconnect:
+            raise signals.TestFailure(
+                "Device failed to reconnect to the correct"
+                " network after reboot.")
+
+    @test_tracker_info(uuid="26d94dfa-1349-4c8b-aea0-475eb73bb521")
+    def test_toggle_wifi_reboot_configstore_reconnect(self):
+        """Connect to multiple networks, disable WiFi, reboot, then
+           reconnect to previously connected network.
+
+        Steps:
+        1. Connect to a 2GHz network.
+        2. Connect to a 5GHz network.
+        3. Turn WiFi OFF.
+        4. Reboot device.
+        5. Turn WiFi ON.
+        4. Verify all networks are persistent after reboot.
+        5. Reconnect to the non-current network.
+
+        """
+        network_list = self.connect_multiple_networks(self.dut)
+        self.log.debug("Toggling wifi OFF")
+        wutils.wifi_toggle_state(self.dut, False)
+        time.sleep(DEFAULT_TIMEOUT)
+        self.dut.reboot()
+        time.sleep(DEFAULT_TIMEOUT)
+        self.log.debug("Toggling wifi ON")
+        wutils.wifi_toggle_state(self.dut, True)
+        time.sleep(DEFAULT_TIMEOUT)
+        self.check_configstore_networks(network_list)
+        reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
+                                                network_list[BAND_5GHZ])
+        reconnect = self.connect_to_wifi_network_with_id(
+            reconnect_to[WifiEnums.NETID_KEY],
+            reconnect_to[WifiEnums.SSID_KEY])
+        if not reconnect:
+            msg = ("Device failed to reconnect to the correct network after"
+                   " toggling WiFi and rebooting.")
+            raise signals.TestFailure(msg)
+
+    @test_tracker_info(uuid="4fce017b-b443-40dc-a598-51d59d3bb38f")
+    def test_toggle_airplane_reboot_configstore_reconnect(self):
+        """Connect to multiple networks, enable Airplane mode, reboot, then
+           reconnect to previously connected network.
+
+        Steps:
+        1. Connect to a 2GHz network.
+        2. Connect to a 5GHz network.
+        3. Toggle Airplane mode ON.
+        4. Reboot device.
+        5. Toggle Airplane mode OFF.
+        4. Verify all networks are persistent after reboot.
+        5. Reconnect to the non-current network.
+
+        """
+        network_list = self.connect_multiple_networks(self.dut)
+        self.log.debug("Toggling Airplane mode ON")
+        asserts.assert_true(
+            acts.utils.force_airplane_mode(self.dut, True),
+            "Can not turn on airplane mode on: %s" % self.dut.serial)
+        time.sleep(DEFAULT_TIMEOUT)
+        self.dut.reboot()
+        time.sleep(DEFAULT_TIMEOUT)
+        self.log.debug("Toggling Airplane mode OFF")
+        asserts.assert_true(
+            acts.utils.force_airplane_mode(self.dut, False),
+            "Can not turn on airplane mode on: %s" % self.dut.serial)
+        time.sleep(DEFAULT_TIMEOUT)
+        self.check_configstore_networks(network_list)
+        reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
+                                                network_list[BAND_5GHZ])
+        reconnect = self.connect_to_wifi_network_with_id(
+            reconnect_to[WifiEnums.NETID_KEY],
+            reconnect_to[WifiEnums.SSID_KEY])
+        if not reconnect:
+            msg = ("Device failed to reconnect to the correct network after"
+                   " toggling Airplane mode and rebooting.")
+            raise signals.TestFailure(msg)
+
+    @test_tracker_info(uuid="cfc0084d-8fe4-4d19-8af2-6c9a8d1e2b6b")
     @acts.signals.generated_test
     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]
+        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,
+            self.connect_to_wifi_network_and_run_iperf,
             params,
             name_func=name_gen)
         asserts.assert_true(not failed, "Failed ones: {}".format(failed))
 
+    @test_tracker_info(uuid="117b1d1c-963d-40f1-bf50-7cbc8b5e1c69")
+    @acts.signals.generated_test
+    def test_config_store(self):
+        params = list(
+            itertools.product(self.config_store_networks,
+                              self.android_devices))
+
+        def name_gen(p):
+            return "test_connection_to-%s-for_config_store" % p[0][
+                WifiEnums.SSID_KEY]
+
+        failed = self.run_generated_testcases(
+            self.connect_to_wifi_network_toggle_wifi_and_run_iperf,
+            params,
+            name_func=name_gen)
+        asserts.assert_false(failed, "Failed ones: {}".format(failed))
+
+    @test_tracker_info(uuid="b9fbc13a-47b4-4f64-bd2c-e5a3cb24ab2f")
     def test_tdls_supported(self):
         model = acts.utils.trim_model_name(self.dut.model)
         self.log.debug("Model is %s" % model)
         if model in self.tdls_models:
-            asserts.assert_true(self.dut.droid.wifiIsTdlsSupported(),
-                             ("TDLS should be supported on %s, but device is "
-                              "reporting not supported.") % model)
+            asserts.assert_true(self.dut.droid.wifiIsTdlsSupported(), (
+                "TDLS should be supported on %s, but device is "
+                "reporting not supported.") % model)
         else:
-            asserts.assert_true(not self.dut.droid.wifiIsTdlsSupported(),
-                             ("TDLS should not be supported on %s, but device "
-                              "is reporting supported.") % model)
+            asserts.assert_false(self.dut.droid.wifiIsTdlsSupported(), (
+                "TDLS should not be supported on %s, but device "
+                "is reporting supported.") % model)
 
+    @test_tracker_info(uuid="50637d40-ea59-4f4b-9fc1-e6641d64074c")
     def test_energy_info(self):
         """Verify the WiFi energy info reporting feature.
 
@@ -226,12 +633,11 @@
         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)
-        asserts.assert_true(actual_support == expected_support, msg)
+        asserts.assert_equal(expected_support, actual_support)
         if not actual_support:
-            asserts.skip(("Device %s does not support energy info reporting as "
-                       "expected.") % model)
+            asserts.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)
@@ -243,15 +649,16 @@
             new_energy = info["ControllerEnergyUsed"]
             new_idle_time = info["ControllerIdleTimeMillis"]
             asserts.assert_true(new_energy >= energy,
-                "Energy value decreased: previous %d, now %d" % (energy,
-                    new_energy))
+                                "Energy value decreased: previous %d, now %d" %
+                                (energy, new_energy))
             energy = new_energy
             asserts.assert_true(new_idle_time >= idle_time,
-                "Idle time decreased: previous %d, now %d" % (idle_time,
-                    new_idle_time))
+                                "Idle time decreased: previous %d, now %d" % (
+                                    idle_time, new_idle_time))
             idle_time = new_idle_time
             wutils.start_wifi_connection_scan(self.dut)
 
+    @test_tracker_info(uuid="1f1cf549-53eb-4f36-9f33-ce06c9158efc")
     def test_energy_info_connected(self):
         """Verify the WiFi energy info reporting feature when connected.
 
diff --git a/acts/tests/google/wifi/WifiNanManagerTest.py b/acts/tests/google/wifi/WifiNanManagerTest.py
deleted file mode 100644
index 004316d..0000000
--- a/acts/tests/google/wifi/WifiNanManagerTest.py
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/usr/bin/python3.4
-#
-#   Copyright 2016 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-import queue
-
-import acts.base_test as base_test
-import acts.controllers.android_device as android_device
-import acts.test_utils.wifi.wifi_test_utils as wutils
-
-from acts import asserts
-
-ON_IDENTITY_CHANGED = "WifiNanOnIdentityChanged"
-ON_MATCH = "WifiNanSessionOnMatch"
-ON_MESSAGE_RX = "WifiNanSessionOnMessageReceived"
-ON_MESSAGE_TX_FAIL = "WifiNanSessionOnMessageSendFail"
-ON_MESSAGE_TX_OK = "WifiNanSessionOnMessageSendSuccess"
-
-class WifiNanManagerTest(base_test.BaseTestClass):
-    msg_id = 10
-
-    def __init__(self, controllers):
-        base_test.BaseTestClass.__init__(self, controllers)
-        self.publisher = self.android_devices[0]
-        self.subscriber = self.android_devices[1]
-        self.tests = (
-            "test_nan_base_test",
-            )
-
-    def setup_class(self):
-        required_params = (
-            "config_request1",
-            "config_request2",
-            "publish_data",
-            "publish_settings",
-            "subscribe_data",
-            "subscribe_settings"
-        )
-        self.unpack_userparams(required_params)
-
-    def setup_test(self):
-        assert wutils.wifi_toggle_state(self.publisher, True)
-        assert wutils.wifi_toggle_state(self.subscriber, True)
-
-    def teardown_class(self):
-        assert wutils.wifi_toggle_state(self.publisher, False)
-        assert wutils.wifi_toggle_state(self.subscriber, False)
-
-    def reliable_tx(self, device, peer, msg):
-        num_tries = 0
-        max_num_tries = 10
-        events_regex = '%s|%s' % (ON_MESSAGE_TX_FAIL, ON_MESSAGE_TX_OK)
-        self.msg_id = self.msg_id + 1
-
-        while True:
-            try:
-                num_tries += 1
-                device.droid.wifiNanSendMessage(peer, msg, self.msg_id)
-                events = device.ed.pop_events(events_regex, 30)
-                for event in events:
-                    self.log.info('%s: %s' % (event['name'], event['data']))
-                    if event['data']['messageId'] != self.msg_id:
-                        continue
-                    if event['name'] == ON_MESSAGE_TX_OK:
-                        return True
-                    if num_tries == max_num_tries:
-                        self.log.info("Max number of retries reached")
-                        return False
-            except queue.Empty:
-                self.log.info('Timed out while waiting for %s' % events_regex)
-                return False
-
-    def test_nan_base_test(self):
-        """Perform NAN configuration, discovery, and message exchange.
-
-        Configuration: 2 devices, one acting as Publisher (P) and the
-        other as Subscriber (S)
-
-        Logical steps:
-          * P & S configure NAN
-          * P & S wait for NAN configuration confirmation
-          * P starts publishing
-          * S starts subscribing
-          * S waits for a match (discovery) notification
-          * S sends a message to P, confirming that sent successfully
-          * P waits for a message and confirms that received (uncorrupted)
-          * P sends a message to S, confirming that sent successfully
-          * S waits for a message and confirms that received (uncorrupted)
-        """
-        self.publisher.droid.wifiNanEnable(self.config_request1)
-        self.subscriber.droid.wifiNanEnable(self.config_request2)
-
-        sub2pub_msg = "How are you doing?"
-        pub2sub_msg = "Doing ok - thanks!"
-
-        try:
-            event = self.publisher.ed.pop_event(ON_IDENTITY_CHANGED, 30)
-            self.log.info('%s: %s' % (ON_IDENTITY_CHANGED, event['data']))
-        except queue.Empty:
-            asserts.fail('Timed out while waiting for %s on Publisher' %
-                      ON_IDENTITY_CHANGED)
-        self.log.debug(event)
-
-        try:
-            event = self.subscriber.ed.pop_event(ON_IDENTITY_CHANGED, 30)
-            self.log.info('%s: %s' % (ON_IDENTITY_CHANGED, event['data']))
-        except queue.Empty:
-            asserts.fail('Timed out while waiting for %s on Subscriber' %
-                      ON_IDENTITY_CHANGED)
-        self.log.debug(event)
-
-        self.publisher.droid.wifiNanPublish(self.publish_data,
-                                            self.publish_settings, 0)
-        self.subscriber.droid.wifiNanSubscribe(self.subscribe_data,
-                                               self.subscribe_settings, 0)
-
-        try:
-            event = self.subscriber.ed.pop_event(ON_MATCH, 30)
-            self.log.info('%s: %s' % (ON_MATCH, event['data']))
-        except queue.Empty:
-            asserts.fail('Timed out while waiting for %s on Subscriber' % ON_MATCH)
-        self.log.debug(event)
-
-        asserts.assert_true(self.reliable_tx(self.subscriber,
-                                          event['data']['peerId'],
-                                          sub2pub_msg),
-                         "Failed to transmit from subscriber")
-
-        try:
-            event = self.publisher.ed.pop_event(ON_MESSAGE_RX, 10)
-            self.log.info('%s: %s' % (ON_MESSAGE_RX, event['data']))
-            asserts.assert_true(event['data']['messageAsString'] == sub2pub_msg,
-                             "Subscriber -> publisher message corrupted")
-        except queue.Empty:
-            asserts.fail('Timed out while waiting for %s on publisher' %
-                      ON_MESSAGE_RX)
-
-        asserts.assert_true(self.reliable_tx(self.publisher,
-                                          event['data']['peerId'],
-                                          pub2sub_msg),
-                         "Failed to transmit from publisher")
-
-        try:
-            event = self.subscriber.ed.pop_event(ON_MESSAGE_RX, 10)
-            self.log.info('%s: %s' % (ON_MESSAGE_RX, event['data']))
-            asserts.assert_true(event['data']['messageAsString'] == pub2sub_msg,
-                             "Publisher -> subscriber message corrupted")
-        except queue.Empty:
-            asserts.fail('Timed out while waiting for %s on subscriber' %
-                      ON_MESSAGE_RX)
diff --git a/acts/tests/google/wifi/WifiNativeTest.py b/acts/tests/google/wifi/WifiNativeTest.py
index 9cb3a91..aa90763 100644
--- a/acts/tests/google/wifi/WifiNativeTest.py
+++ b/acts/tests/google/wifi/WifiNativeTest.py
@@ -14,29 +14,26 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-import acts.base_test as base_test
+from acts import base_test
+from acts.controllers import native_android_device
 
 class WifiNativeTest(base_test.BaseTestClass):
-    tests = None
 
-    def __init__(self, controllers):
-        base_test.BaseTestClass.__init__(self, controllers)
-        self.device = self.native_android_devices[0]
-        self.tests = (
-                "test_hal_get_features",
-        )
+    def setup_class(self):
+        self.native_devices = self.register_controller(native_android_device)
+        self.dut = self.native_devices[0]
 
     def setup_test(self):
 #        TODO: uncomment once wifi_toggle_state (or alternative)
 #              work with sl4n
 #        assert wutils.wifi_toggle_state(self.device, True)
-        return self.device.droid.WifiInit()
+        return self.dut.droid.WifiInit()
 
 #   TODO: uncomment once wifi_toggle_state (or alternative)
 #         work with sl4n
-#    def teardown_class(self):
+#    def teardown_test(self):
 #        assert wutils.wifi_toggle_state(self.device, False)
 
     def test_hal_get_features(self):
-        result = self.device.droid.WifiGetSupportedFeatureSet()
-        self.log.info("Wi-Fi feature set: {}".format(result))
+        result = self.dut.droid.WifiGetSupportedFeatureSet()
+        self.log.info("Wi-Fi feature set: %d", result)
diff --git a/acts/tests/google/wifi/WifiNetworkSelectorTest.py b/acts/tests/google/wifi/WifiNetworkSelectorTest.py
new file mode 100644
index 0000000..a1a33c0
--- /dev/null
+++ b/acts/tests/google/wifi/WifiNetworkSelectorTest.py
@@ -0,0 +1,388 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import logging
+import time
+
+from acts import asserts
+from acts import base_test
+from acts.controllers import android_device
+from acts.controllers import attenuator
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+WifiEnums = wutils.WifiEnums
+
+AP_1 = 0
+AP_2 = 1
+AP_1_2G_ATTENUATOR = 0
+AP_1_5G_ATTENUATOR = 1
+AP_2_2G_ATTENUATOR = 2
+AP_2_5G_ATTENUATOR = 3
+ATTENUATOR_INITIAL_SETTING = 60
+# WifiNetworkSelector imposes a 10 seconds gap between two selections
+NETWORK_SELECTION_TIME_GAP = 12
+
+
+class WifiNetworkSelectorTest(base_test.BaseTestClass):
+    """These tests verify the behavior of the Android Wi-Fi Network Selector
+    feature.
+    """
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wutils.wifi_test_device_init(self.dut)
+        req_params = ("reference_networks", "open_network")
+        self.unpack_userparams(req_params)
+        # self.dut.droid.wifiEnableVerboseLogging(1)
+
+    def setup_test(self):
+        #reset and clear all saved networks on the DUT
+        wutils.reset_wifi(self.dut)
+        #move the APs out of range
+        for attenuator in self.attenuators:
+            attenuator.set_atten(ATTENUATOR_INITIAL_SETTING)
+        #turn on the screen
+        self.dut.droid.wakeLockAcquireBright()
+        self.dut.droid.wakeUpNow()
+        self.dut.ed.clear_all_events()
+
+    def teardown_test(self):
+        #turn off the screen
+        self.dut.droid.wakeLockRelease()
+        self.dut.droid.goToSleepNow()
+
+    def on_fail(self, test_name, begin_time):
+        self.dut.take_bug_report(test_name, begin_time)
+        self.dut.cat_adb_log(test_name, begin_time)
+
+    """ Helper Functions """
+
+    def add_networks(self, ad, networks):
+        """Add Wi-Fi networks to an Android device and verify the networks were
+        added correctly.
+
+        Args:
+            ad: the AndroidDevice object to add networks to.
+            networks: a list of dicts, each dict represents a Wi-Fi network.
+        """
+        for network in networks:
+            ret = ad.droid.wifiAddNetwork(network)
+            asserts.assert_true(ret != -1, "Failed to add network %s" %
+                                network)
+            ad.droid.wifiEnableNetwork(ret, 0)
+        configured_networks = ad.droid.wifiGetConfiguredNetworks()
+        logging.debug("Configured networks: %s", configured_networks)
+
+    def connect_and_verify_connected_bssid(self, expected_bssid):
+        """Start a scan to get the DUT connected to an AP and verify the DUT
+        is connected to the correct BSSID.
+
+        Args:
+            expected_bssid: Network bssid to which connection.
+
+        Returns:
+            True if connection to given network happen, else return False.
+        """
+        #wait for the attenuator to stablize
+        time.sleep(10)
+        #force start a single scan so we don't have to wait for the
+        #WCM scheduled scan.
+        wutils.start_wifi_connection_scan(self.dut)
+        #wait for connection
+        time.sleep(20)
+        #verify connection
+        actual_network = self.dut.droid.wifiGetConnectionInfo()
+        logging.debug("Actual network: %s", actual_network)
+        asserts.assert_equal(expected_bssid,
+                             actual_network[WifiEnums.BSSID_KEY])
+
+    """ Tests Begin """
+
+    def test_network_selector_automatic_connection(self):
+        """
+            1. Add one saved network to DUT.
+            2. Move the DUT in range.
+            3. Verify the DUT is connected to the network.
+        """
+        #add a saved network to DUT
+        networks = [self.reference_networks[AP_1]['5g']]
+        self.add_networks(self.dut, networks)
+        #move the DUT in range
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+
+    def test_network_selector_basic_connection_prefer_5g(self):
+        """
+            1. Add one saved SSID with 2G and 5G BSSIDs of similar RSSI.
+            2. Move the DUT in range.
+            3. Verify the DUT is connected to the 5G BSSID.
+        """
+        #add a saved network with both 2G and 5G BSSIDs to DUT
+        # TODO: bmahadev Change this to a single SSID once we migrate tests to
+        # use dynamic AP.
+        networks = [self.reference_networks[AP_1]['2g'],
+                    self.reference_networks[AP_1]['5g']]
+        self.add_networks(self.dut, networks)
+        #move the DUT in range
+        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+
+    def test_network_selector_prefer_stronger_rssi(self):
+        """
+            1. Add two saved SSIDs to DUT, same band, one has stronger RSSI
+               than the other.
+            2. Move the DUT in range.
+            3. Verify the DUT is connected to the SSID with stronger RSSI.
+        """
+        #add a 2G and a 5G saved network to DUT
+        networks = [
+            self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][
+                '2g']
+        ]
+        self.add_networks(self.dut, networks)
+        #move the DUT in range
+        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(20)
+        self.attenuators[AP_2_2G_ATTENUATOR].set_atten(40)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '2g']['bssid'])
+
+    def test_network_selector_prefer_secure_over_open_network(self):
+        """
+            1. Add two saved networks to DUT, same band, similar RSSI, one uses
+               WPA2 security, the other is open.
+            2. Move the DUT in range.
+            3. Verify the DUT is connected to the secure network that uses WPA2.
+        """
+        #add a open network and a secure saved network to DUT
+        networks = [
+            self.open_network['5g'], self.reference_networks[AP_1]['5g']
+        ]
+        self.add_networks(self.dut, networks)
+        #move the DUT in range
+        #TODO: control open network attenuator
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+
+    def test_network_selector_blacklist_by_connection_failure(self):
+        """
+            1. Add two saved secured networks X and Y to DUT. X has stronger
+               RSSI than Y. X has wrong password configured.
+            2. Move the DUT in range.
+            3. Verify the DUT is connected to network Y.
+        """
+        #add two saved networks to DUT, and one of them is configured with incorrect password
+        wrong_passwd_network = self.reference_networks[AP_1]['5g'].copy()
+        wrong_passwd_network['password'] += 'haha'
+        networks = [wrong_passwd_network, self.reference_networks[AP_2]['5g']]
+        self.add_networks(self.dut, networks)
+        #make both AP_1 5G and AP_2 5G in range, and AP_1 5G has stronger RSSI than AP_2 5G
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
+        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(10)
+        #start 3 scans to get AP_1 5G blacklisted because of the incorrect password
+        count = 0
+        while count < 3:
+            wutils.start_wifi_connection_scan(self.dut)
+            time.sleep(NETWORK_SELECTION_TIME_GAP)
+            count += 1
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][
+            '5g']['bssid'])
+
+    def test_network_selector_2g_to_5g_prefer_same_SSID(self):
+        """
+            1. Add SSID_A and SSID_B to DUT. Both SSIDs have both 2G and 5G
+               BSSIDs.
+            2. Attenuate the networks so that the DUT is connected to SSID_A's
+               2G in the beginning.
+            3. Increase the RSSI of both SSID_A's 5G and SSID_B's 5G.
+            4. Verify the DUT switches to SSID_A's 5G.
+        """
+        #add two saved networks to DUT
+        networks = [
+            self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][
+                '2g']
+        ]
+        self.add_networks(self.dut, networks)
+        #make AP_1 2G in range
+        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '2g']['bssid'])
+        #make both AP_1 and AP_2 5G in range with similar RSSI
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
+        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
+        #ensure the time gap between two network selections
+        time.sleep(NETWORK_SELECTION_TIME_GAP)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+
+    def test_network_selector_2g_to_5g_different_ssid(self):
+        """
+            1. Add SSID_A and SSID_B to DUT. Both SSIDs have both 2G and 5G
+               BSSIDs.
+            2. Attenuate the networks so that the DUT is connected to SSID_A's
+               2G in the beginning.
+            3. Increase the RSSI of SSID_B's 5G while attenuate down SSID_A's
+               2G RSSI.
+            4. Verify the DUT switches to SSID_B's 5G.
+        """
+        #add two saved networks to DUT
+        networks = [
+            self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][
+                '2g']
+        ]
+        self.add_networks(self.dut, networks)
+        #make both AP_1 2G and AP_2 5G in range, and AP_1 2G
+        #has much stronger RSSI than AP_2 5G
+        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
+        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '2g']['bssid'])
+        #bump up AP_2 5G RSSI and reduce AP_1 2G RSSI
+        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(40)
+        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
+        #ensure the time gap between two network selections
+        time.sleep(NETWORK_SELECTION_TIME_GAP)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][
+            '5g']['bssid'])
+
+    def test_network_selector_5g_to_2g_same_ssid(self):
+        """
+            1. Add one SSID that has both 2G and 5G to the DUT.
+            2. Attenuate down the 2G RSSI.
+            3. Connect the DUT to the 5G BSSID.
+            4. Bring up the 2G RSSI and attenuate down the 5G RSSI.
+            5. Verify the DUT switches to the 2G BSSID.
+        """
+        #add a saved network to DUT
+        networks = [self.reference_networks[AP_1]['2g']]
+        self.add_networks(self.dut, networks)
+        #make both AP_1 2G and AP_2 5G in range, and AP_1 5G
+        #has much stronger RSSI than AP_2 2G
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
+        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(50)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+        #bump up AP_1 2G RSSI and reduce AP_1 5G RSSI
+        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(30)
+        #ensure the time gap between two network selections
+        time.sleep(NETWORK_SELECTION_TIME_GAP)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '2g']['bssid'])
+
+    def test_network_selector_stay_on_sufficient_network(self):
+        """
+            1. Add two 5G WPA2 BSSIDs X and Y to the DUT. X has higher RSSI
+               than Y.
+            2. Connect the DUT to X.
+            3. Change attenuation so that Y's RSSI goes above X's.
+            4. Verify the DUT stays on X.
+        """
+        #add two saved networks to DUT
+        networks = [
+            self.reference_networks[AP_1]['5g'], self.reference_networks[AP_2][
+                '5g']
+        ]
+        self.add_networks(self.dut, networks)
+        #make both AP_1 5G and AP_2 5G in range, and AP_1 5G
+        #has stronger RSSI than AP_2 5G
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(10)
+        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+        #bump up AP_2 5G RSSI over AP_1 5G RSSI
+        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
+        #ensure the time gap between two network selections
+        time.sleep(NETWORK_SELECTION_TIME_GAP)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+
+    def test_network_selector_stay_on_user_selected_network(self):
+        """
+            1. Connect the DUT to SSID_A with a very low RSSI via the user select code path.
+            2. Add SSID_B to the DUT as saved network. SSID_B has higher RSSI than SSID_A.
+            3. Start a scan and network selection.
+            4. Verify DUT stays on SSID_A.
+        """
+        #make AP_1 5G in range with a low RSSI
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(10)
+        #connect to AP_1 via user selection
+        wutils.wifi_connect(self.dut, self.reference_networks[AP_1]['5g'])
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+        #make AP_2 5G in range with a strong RSSI
+        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
+        #add AP_2 as a saved network to DUT
+        networks = [self.reference_networks[AP_2]['5g']]
+        self.add_networks(self.dut, networks)
+        #ensure the time gap between two network selections
+        time.sleep(NETWORK_SELECTION_TIME_GAP)
+        #verify we are still connected to AP_1 5G
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+
+    def test_network_selector_reselect_after_forget_network(self):
+        """
+            1. Add two 5G BSSIDs X and Y to the DUT. X has higher RSSI
+               than Y.
+            2. Connect the DUT to X.
+            3. Forget X.
+            5. Verify the DUT reselect and connect to Y.
+        """
+        #add two saved networks to DUT
+        networks = [
+            self.reference_networks[AP_1]['5g'], self.reference_networks[AP_2][
+                '5g']
+        ]
+        self.add_networks(self.dut, networks)
+        #make both AP_1 5G and AP_2 5G in range. AP_1 5G has stronger
+        #RSSI than AP_2 5G
+        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
+        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(10)
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+            '5g']['bssid'])
+        #forget AP_1
+        wutils.wifi_forget_network(self.dut,
+                                   self.reference_networks[AP_1]['5g']['SSID'])
+        #verify
+        self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][
+            '5g']['bssid'])
+
+    # def test_network_selector_2g_to_5g_same_SSID_roaming(self):
+    #     """ Implement this in HAL test suite when it's ready.
+    #         1. Add one SSID that has both 2G and 5G to the DUT.
+    #         2. Attenuate the networks so that the DUT is connected to 2G.
+    #         3. Increase the RSSI of 5G and lower the RSSI of 2G.
+    #         4. Verify DUT switches to 5G without waking up.
+    #     """
diff --git a/acts/tests/google/wifi/WifiNewSetupAutoJoinTest.py b/acts/tests/google/wifi/WifiNewSetupAutoJoinTest.py
index f85e024..7b735cb 100644
--- a/acts/tests/google/wifi/WifiNewSetupAutoJoinTest.py
+++ b/acts/tests/google/wifi/WifiNewSetupAutoJoinTest.py
@@ -18,6 +18,8 @@
 
 from acts import asserts
 from acts import base_test
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_constants
 from acts.test_utils.wifi import wifi_test_utils as wutils
 
 WifiEnums = wutils.WifiEnums
@@ -41,10 +43,7 @@
                       "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", )
+                      "test_autojoin_Ap1_5gto2g", )
 
     def setup_class(self):
         """It will setup the required dependencies from config file and configure
@@ -90,22 +89,22 @@
             self.dut.droid.wakeLockAcquireBright()
             self.dut.droid.wakeUpNow()
             try:
-                self.dut.droid.wifiPriorityConnect(self.reference_networks[0][
+                self.dut.droid.wifiConnectByConfig(self.reference_networks[0][
                     '2g'])
                 connect_result = self.dut.ed.pop_event(
-                    "WifiManagerPriorityConnectOnSuccess", 1)
+                    wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 1)
                 self.log.info(connect_result)
                 time.sleep(wait_time)
                 if self.ref_ssid_count == 2:  #add 5g network as well
-                    self.dut.droid.wifiPriorityConnect(self.reference_networks[
+                    self.dut.droid.wifiConnectByConfig(self.reference_networks[
                         0]['5g'])
                     connect_result = self.dut.ed.pop_event(
-                        "WifiManagerPriorityConnectOnSuccess", 1)
+                        wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 1)
                     self.log.info(connect_result)
                     time.sleep(wait_time)
-                self.dut.droid.wifiPriorityConnect(self.other_network)
+                self.dut.droid.wifiConnectByConfig(self.other_network)
                 connect_result = self.dut.ed.pop_event(
-                    "WifiManagerPriorityConnectOnSuccess")
+                    wifi_constants.CONNECT_BY_CONFIG_SUCCESS)
                 self.log.info(connect_result)
                 wutils.track_connection(self.dut, self.other_network["SSID"], 1)
                 wutils.wifi_forget_network(self.dut, self.other_network["SSID"])
@@ -157,7 +156,7 @@
                 "Device is not connected to required bssid {}".format(bssid))
             time.sleep(10)  #wait for connection to be active
             asserts.assert_true(
-                wutils.check_internet_connection(self.dut, self.ping_addr),
+                wutils.validate_connection(self.dut, self.ping_addr),
                 "Error, No Internet connection for current bssid {}".format(
                     bssid))
         finally:
@@ -172,6 +171,7 @@
 
     """ Tests Begin """
 
+    @test_tracker_info(uuid="9ea2c78d-d305-497f-87a5-f621f0a4b34e")
     def test_autojoin_Ap1_2g(self):
         """Test wifi auto join functionality move in range of AP1.
 
@@ -196,6 +196,7 @@
             failed,
             "Number of test_autojoin_Ap1_2g failed {}".format(len(failed)))
 
+    @test_tracker_info(uuid="b5eba5ec-96e5-4bd8-b483-f5b2a9504558")
     def test_autojoin_Ap1_2gto5g(self):
         """Test wifi auto join functionality move to high range.
 
@@ -219,6 +220,7 @@
             failed,
             "Number of test_autojoin_Ap1_2gto5g failed {}".format(len(failed)))
 
+    @test_tracker_info(uuid="37822578-d35c-462c-87c0-7a2d9252938c")
     def test_autojoin_in_AP1_5gto2g(self):
         """Test wifi auto join functionality move to low range toward AP2.
 
@@ -243,6 +245,7 @@
             failed, "Number of test_autojoin_in_AP1_5gto2g failed {}".format(
                 len(failed)))
 
+    @test_tracker_info(uuid="8ffdcab1-2bfb-4acd-b1e8-089ba8a4ec41")
     def test_autojoin_swtich_AP1toAp2(self):
         """Test wifi auto join functionality move from low range of AP1 to better
            range of AP2.
@@ -268,6 +271,7 @@
             failed, "Number of test_autojoin_swtich_AP1toAp2 failed {}".format(
                 len(failed)))
 
+    @test_tracker_info(uuid="7a8b9242-f93c-449a-90a6-4562274a213a")
     def test_autojoin_Ap2_2gto5g(self):
         """Test wifi auto join functionality move to high range of AP2.
 
@@ -291,6 +295,7 @@
             failed,
             "Number of test_autojoin_Ap2_2gto5g failed {}".format(len(failed)))
 
+    @test_tracker_info(uuid="009457df-f430-402c-96ab-c456b043b6f5")
     def test_autojoin_Ap2_5gto2g(self):
         """Test wifi auto join functionality move to low range of AP2.
 
@@ -313,6 +318,7 @@
             failed,
             "Number of test_autojoin_Ap2_5gto2g failed {}".format(len(failed)))
 
+    @test_tracker_info(uuid="c6d070af-b601-42f1-adec-5ac564154b29")
     def test_autojoin_out_of_range(self):
         """Test wifi auto join functionality move to low range.
 
@@ -342,6 +348,7 @@
             self.dut.droid.wifiLockRelease()
             self.dut.droid.goToSleepNow()
 
+    @test_tracker_info(uuid="15c27654-bae0-4d2d-bdc8-54fb04b901d1")
     def test_autojoin_Ap2_2g(self):
         """Test wifi auto join functionality move in low range of AP2.
 
@@ -366,6 +373,7 @@
             failed,
             "Number of test_autojoin_Ap2_2g failed {}".format(len(failed)))
 
+    @test_tracker_info(uuid="80e74c78-59e2-46db-809d-cb03bd1b6824")
     def test_autojoin_in_Ap2_5gto2g(self):
         """Test wifi auto join functionality move to medium range of Ap2 and
            low range of AP1.
@@ -390,6 +398,7 @@
             failed, "Number of test_autojoin_in_Ap2_5gto2g failed {}".format(
                 len(failed)))
 
+    @test_tracker_info(uuid="01faeba0-bd66-4d30-a3d9-b81e959025b2")
     def test_autojoin_swtich_AP2toAp1(self):
         """Test wifi auto join functionality move from low range of AP2 to better
            range of AP1.
@@ -415,6 +424,7 @@
             failed, "Number of test_autojoin_swtich_AP2toAp1 failed {}".format(
                 len(failed)))
 
+    @test_tracker_info(uuid="ec509d40-e339-47c2-995e-cc77f5a28687")
     def test_autojoin_Ap1_5gto2g(self):
         """Test wifi auto join functionality move to medium range of AP1.
 
diff --git a/acts/tests/google/wifi/WifiPasspointTest.py b/acts/tests/google/wifi/WifiPasspointTest.py
new file mode 100755
index 0000000..97bd396
--- /dev/null
+++ b/acts/tests/google/wifi/WifiPasspointTest.py
@@ -0,0 +1,260 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import itertools
+import pprint
+import queue
+import time
+
+import acts.base_test
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+import WifiManagerTest
+from acts import asserts
+from acts import signals
+from acts.utils import force_airplane_mode
+
+WifiEnums = wutils.WifiEnums
+
+DEFAULT_TIMEOUT = 10
+GLOBAL_RE = 0
+BOINGO = 1
+UNKNOWN_FQDN = "@#@@!00fffffx"
+
+class WifiPasspointTest(acts.base_test.BaseTestClass):
+    """Tests for APIs in Android's WifiManager class.
+
+    Test Bed Requirement:
+    * One Android device
+    * Several Wi-Fi networks visible to the device, including an open Wi-Fi
+      network.
+    """
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wutils.wifi_test_device_init(self.dut)
+        req_params = ("reference_networks", "passpoint_networks")
+        self.unpack_userparams(req_params)
+        asserts.assert_true(
+            len(self.passpoint_networks) > 0,
+            "Need at least one Passpoint network.")
+        wutils.wifi_toggle_state(self.dut, True)
+        self.unknown_fqdn = UNKNOWN_FQDN
+
+
+    def setup_test(self):
+        self.dut.droid.wakeLockAcquireBright()
+        self.dut.droid.wakeUpNow()
+
+
+    def teardown_test(self):
+        self.dut.droid.wakeLockRelease()
+        self.dut.droid.goToSleepNow()
+        wutils.reset_wifi(self.dut)
+
+
+    def on_fail(self, test_name, begin_time):
+        self.dut.take_bug_report(test_name, begin_time)
+
+
+    """Helper Functions"""
+
+
+    def install_passpoint_profile(self, passpoint_config):
+        """Install the Passpoint network Profile.
+
+        Args:
+            passpoint_config: A JSON dict of the Passpoint configuration.
+
+        """
+        asserts.assert_true(WifiEnums.SSID_KEY in passpoint_config,
+                "Key '%s' must be present in network definition." %
+                WifiEnums.SSID_KEY)
+        # Install the Passpoint profile.
+        self.dut.droid.addUpdatePasspointConfig(passpoint_config)
+
+
+    def check_passpoint_connection(self, passpoint_network):
+        """Verify the device is automatically able to connect to the Passpoint
+           network.
+
+           Args:
+               passpoint_network: SSID of the Passpoint network.
+
+        """
+        ad = self.dut
+        ad.ed.clear_all_events()
+        wutils.start_wifi_connection_scan(ad)
+        scan_results = ad.droid.wifiGetScanResults()
+        # Wait for scan to complete.
+        time.sleep(5)
+        ssid = passpoint_network
+        wutils.assert_network_in_list({WifiEnums.SSID_KEY: ssid}, scan_results)
+        # Passpoint network takes longer time to connect than normal networks.
+        # Every try comes with a timeout of 30s. Setting total timeout to 120s.
+        wutils.wifi_passpoint_connect(self.dut, passpoint_network, num_of_tries=4)
+        # Re-verify we are connected to the correct network.
+        network_info = self.dut.droid.wifiGetConnectionInfo()
+        if network_info[WifiEnums.SSID_KEY] != passpoint_network:
+            raise signals.TestFailure("Device did not connect to the passpoint"
+                                      " network.")
+
+
+    def get_configured_passpoint_and_delete(self):
+        """Get configured Passpoint network and delete using its FQDN."""
+        passpoint_config = self.dut.droid.getPasspointConfigs()
+        if not len(passpoint_config):
+            raise signals.TestFailure("Failed to fetch the list of configured"
+                                      "passpoint networks.")
+        if not wutils.delete_passpoint(self.dut, passpoint_config[0]):
+            raise signals.TestFailure("Failed to delete Passpoint configuration"
+                                      " with FQDN = %s" % passpoint_config[0])
+
+    """Tests"""
+
+    def test_add_passpoint_network(self):
+        """Add a Passpoint network and verify device connects to it.
+
+        Steps:
+            1. Install a Passpoint Profile.
+            2. Verify the device connects to the required Passpoint SSID.
+            3. Get the Passpoint configuration added above.
+            4. Delete Passpoint configuration using its FQDN.
+            5. Verify that we are disconnected from the Passpoint network.
+
+        """
+        passpoint_config = self.passpoint_networks[BOINGO]
+        self.install_passpoint_profile(passpoint_config)
+        ssid = passpoint_config[WifiEnums.SSID_KEY]
+        self.check_passpoint_connection(ssid)
+        self.get_configured_passpoint_and_delete()
+        wutils.wait_for_disconnect(self.dut)
+
+
+    def test_update_passpoint_network(self):
+        """Update a previous Passpoint network and verify device still connects
+           to it.
+
+        1. Install a Passpoint Profile.
+        2. Verify the device connects to the required Passpoint SSID.
+        3. Update the Passpoint Profile.
+        4. Verify device is still connected to the Passpoint SSID.
+        5. Get the Passpoint configuration added above.
+        6. Delete Passpoint configuration using its FQDN.
+
+        """
+        passpoint_config = self.passpoint_networks[BOINGO]
+        self.install_passpoint_profile(passpoint_config)
+        ssid = passpoint_config[WifiEnums.SSID_KEY]
+        self.check_passpoint_connection(ssid)
+
+        # Update passpoint configuration using the original profile because we
+        # do not have real profile with updated credentials to use.
+        self.install_passpoint_profile(passpoint_config)
+
+        # Wait for a Disconnect event from the supplicant.
+        wutils.wait_for_disconnect(self.dut)
+
+        # Now check if we are again connected with the updated profile.
+        self.check_passpoint_connection(ssid)
+
+        self.get_configured_passpoint_and_delete()
+        wutils.wait_for_disconnect(self.dut)
+
+
+    def test_add_delete_list_of_passpoint_network(self):
+        """Add multiple passpoint networks, list them and delete one by one.
+
+        1. Install Passpoint Profile A.
+        2. Install Passpoint Profile B.
+        3. Get all the Passpoint configurations added above and verify.
+        6. Ensure all Passpoint configurations can be deleted.
+
+        """
+        for passpoint_config in self.passpoint_networks:
+            self.install_passpoint_profile(passpoint_config)
+            time.sleep(DEFAULT_TIMEOUT)
+        configs = self.dut.droid.getPasspointConfigs()
+        if not len(configs) or len(configs) != len(self.passpoint_networks):
+            raise signals.TestFailure("Failed to fetch some or all of the"
+                                      " configured passpoint networks.")
+        for config in configs:
+            if not wutils.delete_passpoint(self.dut, config):
+                raise signals.TestFailure("Failed to delete Passpoint"
+                                          " configuration with FQDN = %s" %
+                                          config)
+
+
+    def test_delete_unknown_fqdn(self):
+        """Negative test to delete Passpoint profile using an unknown FQDN.
+
+        1. Pass an unknown FQDN for removal.
+        2. Verify that it was not successful.
+
+        """
+        if wutils.delete_passpoint(self.dut, self.unknown_fqdn):
+            raise signals.TestFailure("Failed because an unknown FQDN"
+                                      " was successfully deleted.")
+
+
+    def test_passpoint_failover(self):
+        """Add a pair of passpoint networks and test failover when one of the"
+           profiles is removed.
+
+        1. Install a Passpoint Profile A and B.
+        2. Verify device connects to a Passpoint network and get SSID.
+        3. Delete the current Passpoint profile using its FQDN.
+        4. Verify device fails over and connects to the other Passpoint SSID.
+        5. Delete Passpoint configuration using its FQDN.
+
+        """
+        # Install both Passpoint profiles on the device.
+        passpoint_ssid = list()
+        for passpoint_config in self.passpoint_networks:
+            passpoint_ssid.append(passpoint_config[WifiEnums.SSID_KEY])
+            self.install_passpoint_profile(passpoint_config)
+            time.sleep(DEFAULT_TIMEOUT)
+
+        # Get the current network and the failover network.
+        wutils.wait_for_connect(self.dut)
+        current_passpoint = self.dut.droid.wifiGetConnectionInfo()
+        current_ssid = current_passpoint[WifiEnums.SSID_KEY]
+        if current_ssid not in passpoint_ssid:
+           raise signals.TestFailure("Device did not connect to any of the "
+                                     "configured Passpoint networks.")
+
+        expected_ssid =  self.passpoint_networks[0][WifiEnums.SSID_KEY]
+        if current_ssid == expected_ssid:
+            expected_ssid = self.passpoint_networks[1][WifiEnums.SSID_KEY]
+
+        # Remove the current Passpoint profile.
+        for network in self.passpoint_networks:
+            if network[WifiEnums.SSID_KEY] == current_ssid:
+                if not wutils.delete_passpoint(self.dut, network["fqdn"]):
+                    raise signals.TestFailure("Failed to delete Passpoint"
+                                              " configuration with FQDN = %s" %
+                                              network["fqdn"])
+        # Verify device fails over and connects to the other passpoint network.
+        time.sleep(DEFAULT_TIMEOUT)
+
+        current_passpoint = self.dut.droid.wifiGetConnectionInfo()
+        if current_passpoint[WifiEnums.SSID_KEY] != expected_ssid:
+            raise signals.TestFailure("Device did not failover to the %s"
+                                      " passpoint network" % expected_ssid)
+
+        # Delete the remaining Passpoint profile.
+        self.get_configured_passpoint_and_delete()
+        wutils.wait_for_disconnect(self.dut)
diff --git a/acts/tests/google/wifi/WifiPnoTest.py b/acts/tests/google/wifi/WifiPnoTest.py
index 6aeda48..66cff8f 100644
--- a/acts/tests/google/wifi/WifiPnoTest.py
+++ b/acts/tests/google/wifi/WifiPnoTest.py
@@ -15,48 +15,34 @@
 
 import time
 
-import acts.base_test
+from acts import asserts
+from acts import base_test
 import acts.test_utils.wifi.wifi_test_utils as wutils
 
-from acts import asserts
-
 WifiEnums = wutils.WifiEnums
-WifiEventNames = wutils.WifiEventNames
 
-class WifiPnoTest(acts.base_test.BaseTestClass):
 
-    def __init__(self, controllers):
-        acts.base_test.BaseTestClass.__init__(self, controllers)
-        self.tests = (
-            "test_simple_pno_connection",
-            "test_pno_connection_with_multiple_saved_networks",
-        )
-
+class WifiPnoTest(base_test.BaseTestClass):
     def setup_class(self):
         self.dut = self.android_devices[0]
         wutils.wifi_test_device_init(self.dut)
-        req_params = (
-            "attn_vals",
-            "pno_network_a",
-            "pno_network_b",
-            "pno_interval"
-        )
+        req_params = ("attn_vals", "pno_network_a", "pno_network_b",
+                      "pno_interval")
         self.unpack_userparams(req_params)
+        self.attenuators = wutils.group_attenuators(self.attenuators)
         self.attn_a = self.attenuators[0]
         self.attn_b = self.attenuators[1]
         self.set_attns("default")
 
     def setup_test(self):
         self.dut.droid.wifiStartTrackingStateChange()
-        self.dut.droid.wakeLockAcquireBright()
-        self.dut.droid.wakeUpNow()
+        self.dut.droid.wakeLockRelease()
+        self.dut.droid.goToSleepNow()
         wutils.reset_wifi(self.dut)
         self.dut.ed.clear_all_events()
 
     def teardown_test(self):
         self.dut.droid.wifiStopTrackingStateChange()
-        self.dut.droid.wakeLockRelease()
-        self.dut.droid.goToSleepNow()
         wutils.reset_wifi(self.dut)
         self.dut.ed.clear_all_events()
         self.set_attns("default")
@@ -66,26 +52,26 @@
         self.dut.cat_adb_log(test_name, begin_time)
 
     """Helper Functions"""
+
     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)
+        self.log.info("Set attenuation values to %s",
+                      self.attn_vals[attn_val_name])
         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)
+            self.log.error("Failed to set attenuation values %s.",
+                           attn_val_name)
             raise
 
     def trigger_pno_and_assert_connect(self, attn_val_name, expected_con):
-        """Sets attenuators to disconnect current connection and power-off the
-        screen to trigger PNO. Validate that the DUT connected to the new SSID
-        as expected after PNO.
+        """Sets attenuators to disconnect current connection to trigger PNO.
+        Validate that the DUT connected to the new SSID as expected after PNO.
 
         Args:
             attn_val_name: Name of the attenuation value pair to use.
@@ -93,24 +79,20 @@
                 to roam to.
         """
         connection_info = self.dut.droid.wifiGetConnectionInfo()
-        self.log.info("Triggering PNO connect from %s to %s" %
-            (connection_info[WifiEnums.SSID_KEY],
-                expected_con[WifiEnums.SSID_KEY]))
-        self.dut.droid.goToSleepNow()
+        self.log.info("Triggering PNO connect from %s to %s",
+                      connection_info[WifiEnums.SSID_KEY],
+                      expected_con[WifiEnums.SSID_KEY])
         self.set_attns(attn_val_name)
-        self.log.info("Wait %ss for PNO to trigger." % self.pno_interval)
+        self.log.info("Wait %ss for PNO to trigger.", self.pno_interval)
         time.sleep(self.pno_interval)
         try:
-            self.dut.droid.wakeLockAcquireBright()
-            self.dut.droid.wakeUpNow()
             expected_ssid = expected_con[WifiEnums.SSID_KEY]
-            verify_con = { WifiEnums.SSID_KEY : expected_ssid }
+            verify_con = {WifiEnums.SSID_KEY: expected_ssid}
             wutils.verify_wifi_connection_info(self.dut, verify_con)
-            self.log.info("Connected to %s successfully after PNO" %
-                expected_ssid)
+            self.log.info("Connected to %s successfully after PNO",
+                          expected_ssid)
         finally:
-            self.dut.droid.wifiLockRelease()
-            self.dut.droid.goToSleepNow()
+            pass
 
     def add_dummy_networks(self, num_networks):
         """Add some dummy networks to the device.
@@ -119,26 +101,27 @@
             num_networks: Number of networks to add.
         """
         ssid_name_base = "pno_dummy_network_"
-        for i in range(0, num_networks) :
+        for i in range(0, num_networks):
             network = {}
             network[WifiEnums.SSID_KEY] = ssid_name_base + str(i)
-            network[WifiEnums.PWD_KEY] = "pno_dummy";
-            asserts.assert_true(self.dut.droid.wifiAddNetwork(network) != -1,
+            network[WifiEnums.PWD_KEY] = "pno_dummy"
+            asserts.assert_true(
+                self.dut.droid.wifiAddNetwork(network) != -1,
                 "Add network %r failed" % network)
 
     """ Tests Begin """
+
     def test_simple_pno_connection(self):
         """Test PNO triggered autoconnect to a network.
 
         Steps:
-        1. Save 2 valid network configurations (a & b) in the device.
-        2. Attenuate network b.
-        3. Connect the device to network a.
-        4. Switch off the screen on the device.
+        1. Switch off the screen on the device.
+        2. Save 2 valid network configurations (a & b) in the device.
+        3. Attenuate network b.
+        4. Connect the device to network a.
         5. Attenuate network a and remove attenuation on network b and wait for
            a few seconds to trigger PNO.
         6. Check the device connected to network b automatically.
-        7. Switch off the screen on the device.
         8. Attenuate network b and remove attenuation on network a and wait for
            a few seconds to trigger PNO.
         9. Check the device connected to network a automatically.
@@ -168,4 +151,5 @@
         """
         self.add_dummy_networks(16)
         self.test_simple_pno_connection()
+
     """ Tests End """
diff --git a/acts/tests/google/wifi/WifiPowerTest.py b/acts/tests/google/wifi/WifiPowerTest.py
old mode 100644
new mode 100755
index 66d3d82..ffa8dc4
--- a/acts/tests/google/wifi/WifiPowerTest.py
+++ b/acts/tests/google/wifi/WifiPowerTest.py
@@ -15,81 +15,135 @@
 #   limitations under the License.
 
 import os
+import threading
+import time
 
-import acts.base_test
+from acts import base_test
 from acts import asserts
 from acts import utils
+from acts.controllers import adb
+from acts.controllers import iperf_server as ip_server
 from acts.controllers import monsoon
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.wifi import wifi_test_utils as wutils
-from acts.test_utils.tel import tel_test_utils as tel_utils
+from acts.utils import force_airplane_mode
+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
 
 pmc_base_cmd = ("am broadcast -a com.android.pmc.action.AUTOPOWER --es"
                 " PowerAction ")
-start_pmc_cmd = ("am start -n com.android.pmc/com.android.pmc."
-    "PMCMainActivity")
-pmc_interval_cmd = ("am broadcast -a com.android.pmc.action.SETINTERVAL --es "
-                    "Value %s ")
+start_pmc_cmd = ("am start -S -n com.android.pmc/com.android.pmc."
+                 "PMCMainActivity")
+pmc_interval_cmd = ("am broadcast -a com.android.pmc.action.SETPARAMS --es "
+                    "Interval %s ")
+pmc_set_params = "am broadcast -a com.android.pmc.action.SETPARAMS --es "
+
 pmc_start_connect_scan_cmd = "%sStartConnectivityScan" % pmc_base_cmd
 pmc_stop_connect_scan_cmd = "%sStopConnectivityScan" % pmc_base_cmd
 pmc_start_gscan_no_dfs_cmd = "%sStartGScanBand" % pmc_base_cmd
 pmc_start_gscan_specific_channels_cmd = "%sStartGScanChannel" % pmc_base_cmd
 pmc_stop_gscan_cmd = "%sStopGScan" % pmc_base_cmd
-pmc_start_1MB_download_cmd = "%sDownload1MB" % pmc_base_cmd
-pmc_stop_1MB_download_cmd = "%sStopDownload" % pmc_base_cmd
+pmc_start_iperf_client = "%sStartIperfClient" % pmc_base_cmd
+pmc_stop_iperf_client = "%sStopIperfClient" % pmc_base_cmd
+pmc_turn_screen_on = "%sTurnScreenOn" % pmc_base_cmd
+pmc_turn_screen_off = "%sTurnScreenOff" % pmc_base_cmd
+# Path of the iperf json output file from an iperf run.
+pmc_iperf_json_file = "/sdcard/iperf.txt"
 
-class WifiPowerTest(acts.base_test.BaseTestClass):
 
-    def __init__(self, controllers):
-        super(WifiPowerTest, self).__init__(controllers)
-        self.tests = (
-            "test_power_wifi_off",
-            "test_power_wifi_on_idle",
-            "test_power_disconnected_connectivity_scan",
-            "test_power_connected_2g_continuous_download",
-            "test_power_connected_2g_idle",
-            "test_power_connected_5g_continuous_download",
-            "test_power_connected_5g_idle",
-            "test_power_gscan_three_2g_channels",
-            "test_power_gscan_all_channels_no_dfs",
-            "test_power_connected_2g_gscan_all_channels_no_dfs",
-            "test_power_connected_5g_gscan_all_channels_no_dfs"
-        )
-
+class WifiPowerTest(base_test.BaseTestClass):
     def setup_class(self):
-        self.hz = 10
         self.offset = 5 * 60
-        self.duration = 30 * 60 + self.offset
+        self.hz = 5000
         self.scan_interval = 15
         # Continuosly download
         self.download_interval = 0
         self.mon_data_path = os.path.join(self.log_path, "Monsoon")
-
         self.mon = self.monsoons[0]
         self.mon.set_voltage(4.2)
         self.mon.set_max_current(7.8)
         self.dut = self.android_devices[0]
         self.mon.attach_device(self.dut)
-        asserts.assert_true(self.mon.usb("auto"),
-                         "Failed to turn USB mode to auto on monsoon.")
+        asserts.assert_true(
+            self.mon.usb("auto"),
+            "Failed to turn USB mode to auto on monsoon.")
+        asserts.assert_true(
+            force_airplane_mode(self.dut, True),
+            "Can not turn on airplane mode on: %s" % self.dut.serial)
+        set_location_service(self.dut, False)
+        set_adaptive_brightness(self.dut, False)
+        set_ambient_display(self.dut, False)
+        self.dut.adb.shell("settings put system screen_brightness 0")
+        set_auto_rotate(self.dut, False)
         required_userparam_names = (
             # These two params should follow the format of
             # {"SSID": <SSID>, "password": <Password>}
             "network_2g",
-            "network_5g"
-        )
+            "network_5g",
+            "iperf_server_address")
         self.unpack_userparams(required_userparam_names, threshold=None)
         wutils.wifi_test_device_init(self.dut)
-        # Start pmc app.
-        self.dut.adb.shell(start_pmc_cmd)
-        self.dut.adb.shell("setprop log.tag.PMC VERBOSE")
+        try:
+            self.attn = self.attenuators[0]
+            self.attn.set_atten(0)
+        except AttributeError:
+            self.log.warning("No attenuator found, some tests will fail.")
+            pass
 
     def teardown_class(self):
         self.mon.usb("on")
 
     def setup_test(self):
+        # Default measurement time is 30min with an offset of 5min. Each test
+        # can overwrite this by setting self.duration and self.offset.
+        self.offset = 5 * 60
+        self.duration = 20 * 60 + self.offset
+        self.start_pmc()
         wutils.reset_wifi(self.dut)
         self.dut.ed.clear_all_events()
 
+    def on_fail(self, test_name, begin_time):
+        self.dut.take_bug_report(test_name, begin_time)
+
+    def on_pass(self, test_name, begin_time):
+        self.dut.take_bug_report(test_name, begin_time)
+
+    def start_pmc(self):
+        """Starts a new instance of PMC app on the device and initializes it.
+
+        This function performs the following:
+        1. Starts a new instance of PMC (killing any existing instances).
+        2. Turns on PMC verbose logging.
+        3. Sets up the server IP address/port for download/iperf tests.
+        4. Removes an existing iperf json output files.
+        """
+        self.dut.adb.shell(start_pmc_cmd)
+        self.dut.adb.shell("setprop log.tag.PMC VERBOSE")
+        self.iperf_server = self.iperf_servers[0]
+        # Setup iperf related params on the client side.
+        self.dut.adb.shell("%sServerIP %s" % (pmc_set_params,
+                                              self.iperf_server_address))
+        self.dut.adb.shell("%sServerPort %s" % (pmc_set_params,
+                                                self.iperf_server.port))
+        try:
+            self.dut.adb.shell("rm %s" % pmc_iperf_json_file)
+        except adb.AdbError:
+            pass
+
+    def get_iperf_result(self):
+        """Pulls the iperf json output from device.
+
+        Returns:
+            An IPerfResult object based on the iperf run output.
+        """
+        dest = os.path.join(self.iperf_server.log_path, "iperf.txt")
+        self.dut.adb.pull(pmc_iperf_json_file, " ", dest)
+        result = ip_server.IPerfResult(dest)
+        self.dut.adb.shell("rm %s" % pmc_iperf_json_file)
+        return result
+
     def measure_and_process_result(self):
         """Measure the current drawn by the device for the period of
         self.duration, at the frequency of self.hz.
@@ -102,37 +156,46 @@
                                         self.duration,
                                         tag=tag,
                                         offset=self.offset)
-        asserts.assert_true(result, "Got empty measurement data set in %s." % tag)
+        asserts.assert_true(result,
+                            "Got empty measurement data set in %s." % tag)
         self.log.info(repr(result))
         data_path = os.path.join(self.mon_data_path, "%s.txt" % tag)
         monsoon.MonsoonData.save_to_text_file([result], data_path)
         actual_current = result.average_current
         actual_current_str = "%.2fmA" % actual_current
         result_extra = {"Average Current": actual_current_str}
-        if self.threshold:
-            model = utils.trim_model_name(self.dut.model)
-            asserts.assert_true(tag in self.threshold[model],
-                             "Acceptance threshold for %s is missing" % tag,
-                             extras=result_extra)
+        if "continuous_traffic" in tag:
+            self.dut.adb.shell(pmc_stop_iperf_client)
+            iperf_result = self.get_iperf_result()
+            asserts.assert_true(iperf_result.avg_rate,
+                                "Failed to send iperf traffic",
+                                extras=result_extra)
+            rate = "%.2fMB/s" % iperf_result.avg_rate
+            result_extra["Average Rate"] = rate
+        model = utils.trim_model_name(self.dut.model)
+        if self.threshold and (model in self.threshold) and (
+                tag in self.threshold[model]):
             acceptable_threshold = self.threshold[model][tag]
-            asserts.assert_true(actual_current < acceptable_threshold,
-                             ("Measured average current for %s - %s - is "
-                              "higher than acceptable threshold %.2f.") % (
-                              tag, actual_current_str, acceptable_threshold),
-                              extras=result_extra)
+            asserts.assert_true(
+                actual_current < acceptable_threshold,
+                ("Measured average current in [%s]: %s, which is "
+                 "higher than acceptable threshold %.2fmA.") % (
+                     tag, actual_current_str, acceptable_threshold),
+                extras=result_extra)
         asserts.explicit_pass("Measurement finished for %s." % tag,
-                           extras=result_extra)
+                              extras=result_extra)
 
+    @test_tracker_info(uuid="99ed6d06-ad07-4650-8434-0ac9d856fafa")
     def test_power_wifi_off(self):
-        asserts.assert_true(wutils.wifi_toggle_state(self.dut, False),
-                         "Failed to toggle wifi off.")
+        wutils.wifi_toggle_state(self.dut, False)
         self.measure_and_process_result()
 
+    @test_tracker_info(uuid="086db8fd-4040-45ac-8934-49b4d84413fc")
     def test_power_wifi_on_idle(self):
-        asserts.assert_true(wutils.wifi_toggle_state(self.dut, True),
-                         "Failed to toggle wifi on.")
+        wutils.wifi_toggle_state(self.dut, True)
         self.measure_and_process_result()
 
+    @test_tracker_info(uuid="031516d9-b0f5-4f21-bc8b-078258852325")
     def test_power_disconnected_connectivity_scan(self):
         try:
             self.dut.adb.shell(pmc_interval_cmd % self.scan_interval)
@@ -143,36 +206,49 @@
             self.dut.adb.shell(pmc_stop_connect_scan_cmd)
             self.log.info("Stoped connectivity scan.")
 
+    @test_tracker_info(uuid="5e1f92d7-a79e-459c-aff0-d4acba3adee4")
     def test_power_connected_2g_idle(self):
+        wutils.reset_wifi(self.dut)
+        self.dut.ed.clear_all_events()
         wutils.wifi_connect(self.dut, self.network_2g)
         self.measure_and_process_result()
 
-    def test_power_connected_2g_continuous_download(self):
+    @test_tracker_info(uuid="e2b4ab89-420e-4560-a08b-d3bf4336f05d")
+    def test_power_connected_2g_continuous_traffic(self):
         try:
-            self.dut.adb.shell(pmc_interval_cmd % self.download_interval)
-            self.dut.adb.shell(pmc_start_1MB_download_cmd)
-            self.log.info("Start downloading 1MB file continuously.")
+            wutils.reset_wifi(self.dut)
+            self.dut.ed.clear_all_events()
+            wutils.wifi_connect(self.dut, self.network_2g)
+            self.iperf_server.start()
+            self.dut.adb.shell(pmc_start_iperf_client)
+            self.log.info("Started iperf traffic.")
             self.measure_and_process_result()
         finally:
-            self.dut.adb.shell(pmc_stop_1MB_download_cmd)
-            self.log.info("Stopped downloading 1MB file.")
+            self.iperf_server.stop()
+            self.log.info("Stopped iperf traffic.")
 
+    @test_tracker_info(uuid="a9517306-b967-494e-b471-84de58df8f1b")
     def test_power_connected_5g_idle(self):
         wutils.reset_wifi(self.dut)
         self.dut.ed.clear_all_events()
         wutils.wifi_connect(self.dut, self.network_5g)
         self.measure_and_process_result()
 
-    def test_power_connected_5g_continuous_download(self):
+    @test_tracker_info(uuid="816716b3-a90b-4835-84b8-d8d761ebfba9")
+    def test_power_connected_5g_continuous_traffic(self):
         try:
-            self.dut.adb.shell(pmc_interval_cmd % self.download_interval)
-            self.dut.adb.shell(pmc_start_1MB_download_cmd)
-            self.log.info("Started downloading 1MB file continuously.")
+            wutils.reset_wifi(self.dut)
+            self.dut.ed.clear_all_events()
+            wutils.wifi_connect(self.dut, self.network_5g)
+            self.iperf_server.start()
+            self.dut.adb.shell(pmc_start_iperf_client)
+            self.log.info("Started iperf traffic.")
             self.measure_and_process_result()
         finally:
-            self.dut.adb.shell(pmc_stop_1MB_download_cmd)
-            self.log.info("Stopped downloading 1MB file.")
+            self.iperf_server.stop()
+            self.log.info("Stopped iperf traffic.")
 
+    @test_tracker_info(uuid="e2d08e4e-7863-4554-af63-64d41ab0976a")
     def test_power_gscan_three_2g_channels(self):
         try:
             self.dut.adb.shell(pmc_interval_cmd % self.scan_interval)
@@ -183,6 +259,7 @@
             self.dut.adb.shell(pmc_stop_gscan_cmd)
             self.log.info("Stopped gscan.")
 
+    @test_tracker_info(uuid="0095b7e7-94b9-4cd9-912f-51971949748b")
     def test_power_gscan_all_channels_no_dfs(self):
         try:
             self.dut.adb.shell(pmc_interval_cmd % self.scan_interval)
@@ -193,6 +270,7 @@
             self.dut.adb.shell(pmc_stop_gscan_cmd)
             self.log.info("Stopped gscan.")
 
+    @test_tracker_info(uuid="263d1b68-8eb0-4e7f-99d4-3ca23ca359ce")
     def test_power_connected_2g_gscan_all_channels_no_dfs(self):
         try:
             wutils.wifi_connect(self.dut, self.network_2g)
@@ -204,6 +282,7 @@
             self.dut.adb.shell(pmc_stop_gscan_cmd)
             self.log.info("Stopped gscan.")
 
+    @test_tracker_info(uuid="aad1a39d-01f9-4fa5-a23a-b85d54210f3c")
     def test_power_connected_5g_gscan_all_channels_no_dfs(self):
         try:
             wutils.wifi_connect(self.dut, self.network_5g)
@@ -214,3 +293,55 @@
         finally:
             self.dut.adb.shell(pmc_stop_gscan_cmd)
             self.log.info("Stopped gscan.")
+
+    @test_tracker_info(uuid="8f72cd5f-1c66-4ced-92d9-b7ebadf76424")
+    def test_power_auto_reconnect(self):
+        """
+        Steps:
+            1. Connect to network, wait for three minutes.
+            2. Attenuate AP away, wait for one minute.
+            3. Make AP reappear, wait for three minutes for the device to
+               reconnect to the Wi-Fi network.
+        """
+        self.attn.set_atten(0)
+        wutils.wifi_connect(self.dut, self.network_2g)
+
+        def attn_control():
+            for i in range(7):
+                self.log.info("Iteration %s: Idle 3min after AP appeared.", i)
+                time.sleep(3 * 60)
+                self.attn.set_atten(90)
+                self.log.info("Iteration %s: Idle 1min after AP disappeared.",
+                              i)
+                time.sleep(60)
+                self.attn.set_atten(0)
+
+        t = threading.Thread(target=attn_control)
+        t.start()
+        try:
+            self.measure_and_process_result()
+        finally:
+            t.join()
+
+    @test_tracker_info(uuid="a6db5964-3c68-47fa-b4c9-49f880549031")
+    def test_power_screen_on_wifi_off(self):
+        self.duration = 10 * 60
+        self.offset = 4 * 60
+        wutils.wifi_toggle_state(self.dut, False)
+        try:
+            self.dut.adb.shell(pmc_turn_screen_on)
+            self.measure_and_process_result()
+        finally:
+            self.dut.adb.shell(pmc_turn_screen_off)
+
+    @test_tracker_info(uuid="230d667a-aa42-4123-9dae-2036429ed574")
+    def test_power_screen_on_wifi_connected_2g_idle(self):
+        self.duration = 10 * 60
+        self.offset = 4 * 60
+        wutils.wifi_toggle_state(self.dut, True)
+        wutils.wifi_connect(self.dut, self.network_2g)
+        try:
+            self.dut.adb.shell(pmc_turn_screen_on)
+            self.measure_and_process_result()
+        finally:
+            self.dut.adb.shell(pmc_turn_screen_off)
diff --git a/acts/tests/google/wifi/WifiRttManagerTest.py b/acts/tests/google/wifi/WifiRttManagerTest.py
index 713793f..a906c6b 100644
--- a/acts/tests/google/wifi/WifiRttManagerTest.py
+++ b/acts/tests/google/wifi/WifiRttManagerTest.py
@@ -21,7 +21,7 @@
 import acts.test_utils.wifi.wifi_test_utils as wutils
 import acts.utils
 from acts import asserts
-from acts.controllers.android import SL4AAPIError
+from acts.controllers import sl4a_client
 
 WifiEnums = wutils.WifiEnums
 
@@ -37,8 +37,10 @@
 ScanResult = WifiEnums.ScanResult
 RTT_MARGIN_OF_ERROR = WifiEnums.RTT_MARGIN_OF_ERROR
 
-class WifiRTTRangingError (Exception):
-     """Error in WifiScanner Rtt."""
+
+class WifiRTTRangingError(Exception):
+    """Error in WifiScanner Rtt."""
+
 
 class WifiRttManagerTest(acts.base_test.BaseTestClass):
     """Tests for wifi's RttManager APIs."""
@@ -47,30 +49,22 @@
 
     def __init__(self, controllers):
         acts.base_test.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"
-        )
+        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]
         wutils.wifi_test_device_init(self.dut)
-        required_params = (
-            "support_models",
-            "stress_num",
-            "vht80_5g",
-            "actual_distance"
-        )
+        required_params = ("support_models", "stress_num", "vht80_5g",
+                           "actual_distance")
         self.unpack_userparams(required_params)
-        asserts.assert_true(self.actual_distance >= 5,
+        asserts.assert_true(
+            self.actual_distance >= 5,
             "Actual distance should be no shorter than 5 meters.")
-        self.visible_networks = (
-            self.vht80_5g,
-        )
+        self.visible_networks = (self.vht80_5g, )
         self.default_rtt_params = {
             RttParam.request_type: RttType.TYPE_TWO_SIDED,
             RttParam.device_type: RttPeerType.PEER_TYPE_AP,
@@ -120,13 +114,15 @@
         }
 
     """Helper Functions"""
+
     def invalid_params_logic(self, rtt_params):
         try:
             self.dut.droid.wifiRttStartRanging([rtt_params])
-        except SL4AAPIError as e:
+        except sl4a_client.Sl4aApiError as e:
             e_str = str(e)
             asserts.assert_true("IllegalArgumentException" in e_str,
-                "Missing IllegalArgumentException in %s." % e_str)
+                                "Missing IllegalArgumentException in %s." %
+                                e_str)
             msg = "Got expected exception with invalid param %s." % rtt_params
             self.log.info(msg)
 
@@ -149,7 +145,8 @@
                 result_len = len(results)
                 param_len = len(rtt_params)
                 asserts.assert_true(result_len == param_len,
-                    "Expected %d results, got %d." % (param_len, result_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):
@@ -217,17 +214,20 @@
             A list of networks that have RTTResponders.
         """
         s = {
-            "reportEvents" : WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+            "reportEvents": WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
             "band": WifiEnums.WIFI_BAND_BOTH,
             "periodInMs": 10000,
             "numBssidsPerScan": 32
         }
-        idx = wutils.start_wifi_single_scan(self.android_devices[0], s)["Index"]
+        idx = wutils.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)):
@@ -284,18 +284,20 @@
                         # 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))
+                            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 * wutils.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))
+                        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.
@@ -305,26 +307,28 @@
                     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)
+                                          " acceptable range 0-200") % rssi)
                         out_of_range += 1
                         continue
-        self.log.info(("Processed %d RTT events. %d aborted, %s failed. Among"
+        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))
+            " RTT values that are out of range.") %
+                      (len(events), aborted, failure, total, invalid,
+                       out_of_range))
         asserts.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.log.info("%.2f%% of the responses are valid." %
+                      (valid_response_rate * 100))
         asserts.assert_true(valid_response_rate >= 0.9,
-                         "Valid response rate is below 90%%.")
+                            "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))
+        self.log.info("%.2f%% of valid responses have in-range RTT value" %
+                      (valid_value_rate * 100))
         msg = "In-range response rate is below 67%%."
         asserts.assert_true(valid_value_rate >= 0.67, msg)
 
@@ -399,30 +403,27 @@
         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.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: 4}, {
                 RttParam.num_retries_per_measurement_frame: 2,
                 RttParam.num_retries_per_FTMR: -1
-            },
-            {RttParam.num_retries_per_FTMR: 4}
+            }, {RttParam.num_retries_per_FTMR: 4}
         ]
         for param in param_list:
             self.invalid_params_logic(param)
@@ -433,18 +434,21 @@
         devices support device-to-ap RTT.
         """
         model = acts.utils.trim_model_name(self.dut.model)
-        asserts.assert_true(not self.dut.droid.wifiIsDeviceToDeviceRttSupported(),
-            "Device to device is not supposed to be supported.")
+        asserts.assert_true(
+            self.dut.droid.wifiIsDeviceToDeviceRttSupported(),
+            "Device to device is supposed to be supported.")
         if any([model in m for m in self.support_models]):
             asserts.assert_true(self.dut.droid.wifiIsDeviceToApRttSupported(),
-                "%s should support device-to-ap RTT." % model)
+                                "%s should support device-to-ap RTT." % model)
             self.log.info("%s supports device-to-ap RTT as expected." % model)
         else:
-            asserts.assert_true(not self.dut.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)
-            asserts.abort_class("Device %s does not support RTT, abort." % model)
+            asserts.assert_false(self.dut.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)
+            asserts.abort_class("Device %s does not support RTT, abort." %
+                                model)
         return True
 
     def test_capability_check(self):
@@ -454,12 +458,13 @@
         asserts.assert_true(caps, "Unable to get rtt capabilities.")
         self.log.debug("Got rtt capabilities %s" % caps)
         model = acts.utils.trim_model_name(self.dut.model)
-        asserts.assert_true(model in self.rtt_cap_table, "Unknown model %s" % model)
+        asserts.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():
             asserts.assert_true(k in caps, "%s missing in capabilities." % k)
-            asserts.assert_true(v == caps[k],
-                "Expected %s for %s, got %s." % (v, k, caps[k]))
+            asserts.assert_true(v == caps[k], "Expected %s for %s, got %s." %
+                                (v, k, caps[k]))
         return True
 
     def test_discovery(self):
@@ -479,7 +484,8 @@
         scan_results = self.dut.droid.wifiGetScanResults()
         self.log.debug(scan_results)
         for n in visible_networks:
-            asserts.assert_true(wutils.match_networks(n, scan_results),
+            asserts.assert_true(
+                wutils.match_networks(n, scan_results),
                 "Network %s was not discovered properly." % n)
         return True
 
@@ -489,9 +495,9 @@
         """
         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.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])
@@ -508,9 +514,9 @@
         """
         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.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]
diff --git a/acts/tests/google/wifi/WifiScannerBssidTest.py b/acts/tests/google/wifi/WifiScannerBssidTest.py
old mode 100755
new mode 100644
index 5d6f2e8..e91c449
--- a/acts/tests/google/wifi/WifiScannerBssidTest.py
+++ b/acts/tests/google/wifi/WifiScannerBssidTest.py
@@ -14,63 +14,59 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import json
-import os
 import itertools
+import queue
 
-from queue import Empty
 from acts import asserts
-from acts.base_test import BaseTestClass
-from acts.utils import load_config
-from acts.test_utils.wifi.wifi_test_utils import start_wifi_track_bssid
-from acts.test_utils.wifi.wifi_test_utils import start_wifi_background_scan
-from acts.test_utils.wifi.wifi_test_utils import wifi_test_device_init
-from acts.test_utils.wifi.wifi_test_utils import WifiChannelUS
-from acts.test_utils.wifi.wifi_test_utils import WifiEnums
-from acts.test_utils.wifi.wifi_test_utils import get_scan_time_and_channels
-
+from acts import base_test
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
 
 BSSID_EVENT_WAIT = 30
 
 BSSID_EVENT_TAG = "WifiScannerBssid"
 SCAN_EVENT_TAG = "WifiScannerScan"
-SCANTIME = 10000 #framework support only 10s as minimum scan interval
+SCANTIME = 10000  #framework support only 10s as minimum scan interval
+
 
 class WifiScannerBssidError(Exception):
     pass
 
-class WifiScannerBssidTest(BaseTestClass):
 
+class WifiScannerBssidTest(base_test.BaseTestClass):
     def __init__(self, controllers):
-        BaseTestClass.__init__(self, controllers)
+        base_test.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",)
+
+    def setup_class(self):
         self.default_scan_setting = {
-            "band": WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+            "band": wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
             "periodInMs": SCANTIME,
-            "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+            "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
             'numBssidsPerScan': 32
         }
         self.leeway = 5
-        self.stime_channel = 47 #dwell time plus 2ms
-
-    def setup_class(self):
+        self.stime_channel = 47  #dwell time plus 2ms
         self.dut = self.android_devices[0]
-        wifi_test_device_init(self.dut)
+        wutils.wifi_test_device_init(self.dut)
+        self.attenuators = wutils.group_attenuators(self.attenuators)
         asserts.assert_true(self.dut.droid.wifiIsScannerSupported(),
-            "Device %s doesn't support WifiScanner, abort." % self.dut.model)
+                            "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)
-        self.unpack_userparams(req_params)
+        self.wifi_chs = wutils.WifiChannelUS(self.dut.model)
+        self.unpack_userparams(req_params, two_ap_testbed=False)
 
     def teardown_class(self):
         BaseTestClass.teardown_test(self)
@@ -83,6 +79,7 @@
             self.max_bugreports -= 1
 
     """ Helper Functions Begin """
+
     def fetch_scan_result(self, scan_idx, scan_setting):
         """Fetch the scan result for provider listener index.
 
@@ -98,23 +95,25 @@
         """
         #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
+        scan_time, scan_channels = wutils.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"] == wutils.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))
+            time_cache = scan_setting['periodInMs'] * 10  #default cache
+            waittime = int((time_cache + scan_time) / 1000) + self.leeway
+        event_name = "%s%sonResults" % (SCAN_EVENT_TAG, scan_idx)
+        self.log.info("Waiting for the scan result event %s", event_name)
         event = self.dut.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):
+    def start_scan_and_validate_environment(self, scan_setting,
+                                            bssid_settings):
         """Validate environment for test using current scan result for provided
            settings.
 
@@ -129,22 +128,23 @@
             True, if bssid not found in scan result.
         """
         try:
-            data = start_wifi_background_scan(self.dut, scan_setting)
+            data = wutils.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))
-            asserts.assert_true(results, "Device is not able to fetch the scan results")
+            self.log.debug("scan result %s.", results)
+            asserts.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:
+                    if bssid_setting[wutils.WifiEnums.BSSID_KEY] == result[
+                            wutils.WifiEnums.BSSID_KEY]:
+                        asserts.fail(("Test environment is not valid: Bssid %s"
+                                      "already exist in current scan results")
+                                     % result[wutils.WifiEnums.BSSID_KEY])
+        except queue.Empty as error:
             self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
-            raise AssertionError("OnResult event did not triggered for scanner\n{}".
-                                 format(error))
-        return True
+            raise AssertionError(
+                "OnResult event did not triggered for scanner\n%s" % error)
 
     def check_bssid_in_found_result(self, bssid_settings, found_results):
         """look for any tracked bssid in reported result of found bssids.
@@ -158,10 +158,11 @@
         """
         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
+                if found_result[wutils.WifiEnums.BSSID_KEY] == bssid_setting[
+                        wutils.WifiEnums.BSSID_KEY]:
+                    return
+        asserts.fail("Test fail because Bssid %s is not found in event results"
+                     % bssid_settings)
 
     def track_bssid_with_vaild_scan_for_found(self, track_setting):
         """Common logic for tracking a bssid for Found event.
@@ -180,31 +181,27 @@
             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)
+        data = wutils.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"])
+        self.start_scan_and_validate_environment(self.default_scan_setting,
+                                                 track_setting["bssidInfos"])
         try:
-            asserts.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_name = "%s%sonFound" % (BSSID_EVENT_TAG, idx)
+            self.log.info("Waiting for the BSSID event %s", event_name)
             event = self.dut.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"])
-            asserts.assert_true(found,
-                             "Test fail because Bssid is not found in event results")
-        except Empty as error:
-            self.log.error("{}".format(error))
+            self.check_bssid_in_found_result(track_setting["bssidInfos"],
+                                             event["data"]["Results"])
+        except queue.Empty as error:
+            self.log.error(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))
+            self.log.debug("scan result %s", results)
+            raise AssertionError("Event %s did not triggered for %s\n%s" %
+                                 (event_name, track_setting["bssidInfos"],
+                                  error))
         finally:
             self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
             self.dut.droid.wifiScannerStopTrackingBssids(idx)
@@ -229,39 +226,35 @@
             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"])
+        self.start_scan_and_validate_environment(self.default_scan_setting,
+                                                 track_setting["bssidInfos"])
         idx = None
         found = False
         try:
-            asserts.assert_true(valid_env,
-                             "Test environment is not valid, AP is in range")
-            data = start_wifi_track_bssid(self.dut, track_setting)
+            data = wutils.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_name = "%s%sonFound" % (BSSID_EVENT_TAG, idx)
+            self.log.info("Waiting for the BSSID event %s", event_name)
             event = self.dut.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"])
-            asserts.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.dut.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))
+            self.check_bssid_in_found_result(track_setting["bssidInfos"],
+                                             event["data"]["Results"])
+            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 %s %s", i, results)
+            event_name = "%s%sonLost" % (BSSID_EVENT_TAG, idx)
+            self.log.info("Waiting for the BSSID event %s", event_name)
+            event = self.dut.ed.pop_event(event_name, BSSID_EVENT_WAIT)
+            self.log.debug(event)
+        except queue.Empty as error:
+            raise AssertionError("Event %s did not triggered for %s\n%s" %
+                                 (event_name, track_setting["bssidInfos"],
+                                  error))
         finally:
             self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
             if idx:
@@ -275,12 +268,12 @@
         """
         bssids = [[self.bssid_2g], [self.bssid_5g],
                   [self.bssid_2g, self.bssid_5g]]
-        if self.dut.model != "hammerhead":
+        if self.dut.model != "hammerhead" or not self.two_ap_testbed:
             bssids.append([self.bssid_dfs])
         if isLost:
-            apthreshold = (3,5)
+            apthreshold = (3, 5)
         else:
-            apthreshold = (1,)
+            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
@@ -296,7 +289,7 @@
         """Convert track setting to string for Bssids in that"""
         string = ""
         for bssid_setting in track_setting:
-            string += bssid_setting[WifiEnums.BSSID_KEY]
+            string += bssid_setting[wutils.WifiEnums.BSSID_KEY]
             string += "_"
         return string
 
@@ -308,8 +301,9 @@
         return bssids
 
     """ Helper Functions End """
-
     """ Tests Begin """
+
+    @test_tracker_info(uuid="599a30b8-73ad-4314-a245-7ec58fc7e74b")
     def test_wifi_track_bssid_found(self):
         """Test bssid track for event found with a list of different settings.
 
@@ -321,16 +315,15 @@
             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)
-        asserts.assert_true(not failed,
-                         "Track bssid found failed with these bssids: {}".
-                         format(failed))
+        name_func = lambda track_setting: "test_wifi_track_found_bssidInfos_%sapLostThreshold_%s" % (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)
+        asserts.assert_false(
+            failed, "Track bssid found failed with these bssids: %s" % failed)
 
+    @test_tracker_info(uuid="7ebd4b61-c408-45b3-b9b6-098753d46aa7")
     def test_wifi_track_bssid_lost(self):
         """Test bssid track for event lost with a list of different settings.
 
@@ -344,14 +337,13 @@
          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)
-        asserts.assert_true(not failed,
-                         "Track bssid lost failed with these bssids: {}".format(failed))
+        name_func = lambda track_setting: "test_wifi_track_lost_bssidInfos_%sapLostThreshold_%s" % (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)
+        asserts.assert_false(
+            failed, "Track bssid lost failed with these bssids: %s" % failed)
 
     def test_wifi_track_bssid_sanity(self):
         """Test bssid track for event found and lost with default settings.
@@ -363,11 +355,11 @@
          5. Attenuate the signal to move out of range
          6. Verify that onLost event occur.
         """
-        track_setting = {"bssidInfos":[self.bssid_2g], "apLostThreshold":3}
+        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.
+        """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.
@@ -375,36 +367,34 @@
          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:
-          asserts.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.dut.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"])
-          asserts.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.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
-          if idx:
-              self.dut.droid.wifiScannerStopTrackingBssids(idx)
+        self.attenuators[self.attenuator_id].set_atten(90)
+        scan_setting = {"band": wutils.WifiEnums.WIFI_BAND_5_GHZ,
+                        "periodInMs": SCANTIME,
+                        "reportEvents":
+                        wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                        "numBssidsPerScan": 32}
+        track_setting = {"bssidInfos": [self.bssid_2g], "apLostThreshold": 3}
+        self.start_scan_and_validate_environment(scan_setting,
+                                                 track_setting["bssidInfos"])
+        idx = None
+        try:
+            data = wutils.start_wifi_track_bssid(self.dut, track_setting)
+            idx = data["Index"]
+            self.attenuators[self.attenuator_id].set_atten(0)
+            event_name = "%s%sonFound" % (BSSID_EVENT_TAG, idx)
+            self.log.info("Waiting for the BSSID event %s", event_name)
+            #waiting for 2x time to make sure
+            event = self.dut.ed.pop_event(event_name, BSSID_EVENT_WAIT * 2)
+            self.log.debug(event)
+            self.check_bssid_in_found_result(track_setting["bssidInfos"],
+                                             event["data"]["Results"])
+        except queue.Empty as error:
+            self.log.info(
+                "As excepted event didn't occurred with different scan setting")
+        finally:
+            self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+            if idx:
+                self.dut.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.
@@ -416,30 +406,28 @@
            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)
+        scan_setting = {"band": wutils.WifiEnums.WIFI_BAND_24_GHZ,
+                        "periodInMs": SCANTIME,
+                        "reportEvents":
+                        wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                        "numBssidsPerScan": 32}
+        track_setting = {"bssidInfos": [self.bssid_5g], "apLostThreshold": 3}
+        data = wutils.start_wifi_track_bssid(self.dut, track_setting)
         idx = data["Index"]
-        valid_env = self.start_scan_and_validate_environment(scan_setting,
-                                                       track_setting["bssidInfos"])
+        self.start_scan_and_validate_environment(scan_setting,
+                                                 track_setting["bssidInfos"])
         try:
-            asserts.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_name = "%s%sonFound" % (BSSID_EVENT_TAG, idx)
+            self.log.info("Waiting for the BSSID event %s", event_name)
             #waiting for 2x time to make sure
             event = self.dut.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"])
-            asserts.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")
+            self.check_bssid_in_found_result(track_setting["bssidInfos"],
+                                             event["data"]["Results"])
+        except queue.Empty as error:
+            self.log.info(
+                "As excepted event didn't occurred with different scan setting")
         finally:
             self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
             if idx:
diff --git a/acts/tests/google/wifi/WifiScannerMultiScanTest.py b/acts/tests/google/wifi/WifiScannerMultiScanTest.py
index 771f866..481ffb0 100755
--- a/acts/tests/google/wifi/WifiScannerMultiScanTest.py
+++ b/acts/tests/google/wifi/WifiScannerMultiScanTest.py
@@ -17,16 +17,18 @@
 import queue
 import time
 
-import acts.base_test
-import acts.test_utils.wifi.wifi_test_utils as wutils
-
 from acts import asserts
+from acts import base_test
+from acts import signals
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
 
 WifiChannelUS = wutils.WifiChannelUS
 WifiEnums = wutils.WifiEnums
 
 SCAN_EVENT_TAG = "WifiScannerScan"
 
+
 class WifiScanResultEvents():
     """This class stores the setting of a scan, parameters generated
     from starting the scan, and events reported later from the scan
@@ -62,17 +64,16 @@
             scan_result_next: A dictionary representing a scan result for a
                 BSSID, whose scan happened after scan_result.
         """
-        actual_interval = scan_result_next["timestamp"] - scan_result["timestamp"]
+        actual_interval = scan_result_next["timestamp"] - scan_result[
+            "timestamp"]
         expected_interval = self.scan_setting['periodInMs'] * 1000
         delta = abs(actual_interval - expected_interval)
-        margin = expected_interval * 0.20 # 20% of the expected_interval
-        assert delta < margin, ("The difference in time between scan %s and "
-                "%s is %dms, which is out of the expected range %sms") % (
-                    scan_result,
-                    scan_result_next,
-                    delta / 1000,
-                    self.scan_setting['periodInMs']
-                )
+        margin = expected_interval * 0.25  # 25% of the expected_interval
+        asserts.assert_true(
+            delta < margin, "The difference in time between scan %s and "
+            "%s is %dms, which is out of the expected range %sms" % (
+                scan_result, scan_result_next, delta / 1000,
+                self.scan_setting['periodInMs']))
 
     def verify_one_scan_result(self, scan_result):
         """Verifies the scan result of a single BSSID.
@@ -85,9 +86,10 @@
                 BSSID.
         """
         freq = scan_result["frequency"]
-        assert freq in self.scan_channels, ("Frequency %d of "
-            "result entry %s is out of the expected range %s.") % (
-                freq, scan_result, self.scan_channels)
+        asserts.assert_true(
+            freq in self.scan_channels,
+            "Frequency %d of result entry %s is out of the expected range %s."
+            % (freq, scan_result, self.scan_channels))
         # TODO(angli): add RSSI check.
 
     def verify_one_scan_result_group(self, batch):
@@ -104,11 +106,9 @@
         scan_results = batch["ScanResults"]
         actual_num_of_results = len(scan_results)
         expected_num_of_results = self.scan_setting['numBssidsPerScan']
-        assert actual_num_of_results <= expected_num_of_results, (
-            "Expected no more than %d BSSIDs, got %d.") % (
-                expected_num_of_results,
-                actual_num_of_results
-            )
+        asserts.assert_true(actual_num_of_results <= expected_num_of_results,
+                            "Expected no more than %d BSSIDs, got %d." %
+                            (expected_num_of_results, actual_num_of_results))
         for scan_result in scan_results:
             self.verify_one_scan_result(scan_result)
 
@@ -130,32 +130,80 @@
         5. The time gap between two consecutive scan results should be
            approximately equal to the scan interval specified by the scan
            setting.
+        A scan result looks like this:
+        {
+          'data':
+           {
+             'Type': 'onResults',
+             'ResultElapsedRealtime': 4280931,
+             'Index': 10,
+             'Results': [
+                         {
+                          'Flags': 0,
+                          'Id': 4,
+                          'ScanResults':[
+                                          {
+                                           'is80211McRTTResponder': False,
+                                           'channelWidth': 0,
+                                           'numUsage': 0,
+                                           'SSID': '"wh_ap1_2g"',
+                                           'timestamp': 4280078660,
+                                           'numConnection': 0,
+                                           'BSSID': '30:b5:c2:33:f9:05',
+                                           'frequency': 2412,
+                                           'numIpConfigFailures': 0,
+                                           'distanceSdCm': 0,
+                                           'distanceCm': 0,
+                                           'centerFreq1': 0,
+                                           'centerFreq0': 0,
+                                           'blackListTimestamp': 0,
+                                           'venueName': '',
+                                           'seen': 0,
+                                           'operatorFriendlyName': '',
+                                           'level': -31,
+                                           'passpointNetwork': False,
+                                           'untrusted': False
+                                          }
+                                        ]
+                         }
+                        ]
+            },
+          'time': 1491744576383,
+          'name': 'WifiScannerScan10onResults'
+        }
         """
         num_of_events = len(self.results_events)
-        assert num_of_events >= 2, (
-                "Expected more than one scan result events, got %d."
-            ) % num_of_events
+        asserts.assert_true(
+            num_of_events >= 2,
+            "Expected more than one scan result events, got %d." %
+            num_of_events)
         for event_idx in range(num_of_events):
             batches = self.results_events[event_idx]["data"]["Results"]
             actual_num_of_batches = len(batches)
+            if not actual_num_of_batches:
+                raise signals.TestFailure("Scan returned empty Results list %s "
+                                          "% batches")
             # For batch scan results.
             report_type = self.scan_setting['reportEvents']
             if not (report_type & WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN):
                 # Verifies that the number of buffered results matches the
                 # number defined in scan settings.
                 expected_num_of_batches = self.scan_setting['maxScansToCache']
-                assert actual_num_of_batches <= expected_num_of_batches, (
-                    "Expected to get at most %d batches in event No.%d, got %d.") % (
-                        expected_num_of_batches,
-                        event_idx,
-                        actual_num_of_batches
-                    )
-                # Check the results within each event of batch scan
-                for batch_idx in range(1, actual_num_of_batches):
+                asserts.assert_true(
+                    actual_num_of_batches <= expected_num_of_batches,
+                    "Expected to get at most %d batches in event No.%d, got %d."
+                    % (expected_num_of_batches, event_idx,
+                       actual_num_of_batches))
+            # Check the results within each event of batch scan
+            for batch_idx in range(actual_num_of_batches):
+                if not len(batches[batch_idx]["ScanResults"]):
+                    raise signals.TestFailure("Scan event %d returned empty"
+                    " scan results in batch %d" % (event_idx, batch_idx))
+                # Start checking interval from the second batch.
+                if batch_idx >=1:
                     self.check_interval(
-                        batches[batch_idx-1]["ScanResults"][0],
-                        batches[batch_idx]["ScanResults"][0]
-                    )
+                        batches[batch_idx - 1]["ScanResults"][0],
+                        batches[batch_idx]["ScanResults"][0])
             for batch in batches:
                 self.verify_one_scan_result_group(batch)
 
@@ -163,13 +211,13 @@
             # the last result of its previous event
             # Skip the very first event.
             if event_idx >= 1:
-                previous_batches = self.results_events[event_idx-1]["data"]["Results"]
-                self.check_interval(
-                    previous_batches[-1]["ScanResults"][0],
-                    batches[0]["ScanResults"][0]
-                )
+                previous_batches = self.results_events[event_idx - 1]["data"][
+                    "Results"]
+                self.check_interval(previous_batches[-1]["ScanResults"][0],
+                                    batches[0]["ScanResults"][0])
 
-class WifiScannerMultiScanTest(acts.base_test.BaseTestClass):
+
+class WifiScannerMultiScanTest(base_test.BaseTestClass):
     """This class is the WiFi Scanner Multi-Scan Test suite.
     It collects a number of test cases, sets up and executes
     the tests, and validates the scan results.
@@ -183,26 +231,19 @@
         max_bugreports: Max number of bug reports allowed.
     """
 
-    def __init__(self, controllers):
-        acts.base_test.BaseTestClass.__init__(self, controllers)
-        # A list of all test cases to be executed in this class.
-        self.tests = ("test_wifi_two_scans_at_same_interval",
-                      "test_wifi_two_scans_at_different_interval",
-                      "test_wifi_scans_24GHz_and_both",
-                      "test_wifi_scans_5GHz_and_both",
-                      "test_wifi_scans_24GHz_5GHz_and_DFS",
-                      "test_wifi_scans_batch_and_24GHz",
-                      "test_wifi_scans_batch_and_5GHz",
-                      "test_wifi_scans_24GHz_5GHz_full_result",)
-        self.leeway = 5 # seconds, for event wait time computation
-        self.stime_channel = 47 #dwell time plus 2ms
-
     def setup_class(self):
+        # If running in a setup with attenuators, set attenuation on all
+        # channels to zero.
+        if getattr(self, "attenuators", []):
+            for a in self.attenuators:
+                a.set_atten(0)
+        self.leeway = 5  # seconds, for event wait time computation
+        self.stime_channel = 47  #dwell time plus 2ms
         self.dut = self.android_devices[0]
         wutils.wifi_test_device_init(self.dut)
         asserts.assert_true(self.dut.droid.wifiIsScannerSupported(),
-            "Device %s doesn't support WifiScanner, abort." % self.dut.model)
-
+                            "Device %s doesn't support WifiScanner, abort." %
+                            self.dut.model)
         """ Setup the required dependencies and fetch the user params from
         config file.
         """
@@ -217,15 +258,13 @@
         self.dut.cat_adb_log(test_name, begin_time)
 
     """ Helper Functions Begin """
+
     def start_scan(self, scan_setting):
         data = wutils.start_wifi_background_scan(self.dut, scan_setting)
         idx = data["Index"]
         # Calculate event wait time from scan setting plus leeway
         scan_time, scan_channels = wutils.get_scan_time_and_channels(
-                                        self.wifi_chs,
-                                        scan_setting,
-                                        self.stime_channel
-                                    )
+            self.wifi_chs, scan_setting, self.stime_channel)
         scan_period = scan_setting['periodInMs']
         report_type = scan_setting['reportEvents']
         if report_type & WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN:
@@ -254,27 +293,29 @@
         # results.
         max_wait_time = max(wait_time_list)
         max_end_time = time.monotonic() + max_wait_time
-        self.log.debug("Event wait time {} seconds".format(event_wait_time))
+        self.log.debug("Event wait time %s seconds", event_wait_time)
 
         try:
             # Wait for scan results on all the caller specified bands
             event_name = SCAN_EVENT_TAG
             while True:
-                self.log.debug("Waiting for events '{}' for up to {} seconds".
-                               format(event_name, event_wait_time))
+                self.log.debug("Waiting for events '%s' for up to %s seconds",
+                               event_name, event_wait_time)
                 events = self.dut.ed.pop_events(event_name, event_wait_time)
                 for event in events:
-                    self.log.debug("Event received: {}".format(event))
+                    self.log.debug("Event received: %s", event)
                     # Event name is the key to the scan results dictionary
                     actual_event_name = event["name"]
-                    asserts.assert_true(actual_event_name in scan_results_dict,
-                        ("Expected one of these event names: %s, got '%s'."
-                        ) % (scan_results_dict.keys(), actual_event_name))
+                    asserts.assert_true(
+                        actual_event_name in scan_results_dict,
+                        "Expected one of these event names: %s, got '%s'." %
+                        (scan_results_dict.keys(), actual_event_name))
 
                     # TODO validate full result callbacks also
                     if event["name"].endswith("onResults"):
                         # Append the event
-                        scan_results_dict[actual_event_name].add_results_event(event)
+                        scan_results_dict[actual_event_name].add_results_event(
+                            event)
 
                 # If we time out then stop waiting for events.
                 if time.monotonic() >= max_end_time:
@@ -286,12 +327,10 @@
                     if not scan_results_dict[key].have_enough_events():
                         have_enough_events = False
                 if have_enough_events:
-                  break
+                    break
         except queue.Empty:
-            asserts.fail("Event did not trigger for {} in {} seconds".
-                      format(event_name, event_wait_time))
-
-
+            asserts.fail("Event did not trigger for {} in {} seconds".format(
+                event_name, event_wait_time))
 
     def scan_and_validate_results(self, scan_settings):
         """Perform WifiScanner scans and check the scan results
@@ -312,25 +351,27 @@
 
         try:
             for scan_setting in scan_settings:
-                self.log.debug("Scan setting: band {}, interval {}, reportEvents "
-                               "{}, numBssidsPerScan {}".format(
-                                    scan_setting["band"],
-                                    scan_setting["periodInMs"],
-                                    scan_setting["reportEvents"],
-                                    scan_setting["numBssidsPerScan"]
-                                ))
+                self.log.debug(
+                    "Scan setting: band %s, interval %s, reportEvents "
+                    "%s, numBssidsPerScan %s", scan_setting["band"],
+                    scan_setting["periodInMs"], scan_setting["reportEvents"],
+                    scan_setting["numBssidsPerScan"])
                 idx, wait_time, scan_chan = self.start_scan(scan_setting)
-                self.log.debug(("Scan started for band {}: idx {}, wait_time {} s,"
-                                " scan_channels {}").format(
-                                scan_setting["band"], idx, wait_time, scan_chan))
+                self.log.debug(
+                    "Scan started for band %s: idx %s, wait_time %ss, scan_channels %s",
+                    scan_setting["band"], idx, wait_time, scan_chan)
                 idx_list.append(idx)
                 wait_time_list.append(wait_time)
 
                 report_type = scan_setting['reportEvents']
-                scan_results_events = WifiScanResultEvents(scan_setting, scan_chan)
-                scan_results_dict["{}{}onResults".format(SCAN_EVENT_TAG, idx)] = scan_results_events
-                if (scan_setting['reportEvents'] & WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT):
-                    scan_results_dict["{}{}onFullResult".format(SCAN_EVENT_TAG, idx)] = scan_results_events
+                scan_results_events = WifiScanResultEvents(scan_setting,
+                                                           scan_chan)
+                scan_results_dict["{}{}onResults".format(
+                    SCAN_EVENT_TAG, idx)] = scan_results_events
+                if (scan_setting['reportEvents']
+                        & WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT):
+                    scan_results_dict["{}{}onFullResult".format(
+                        SCAN_EVENT_TAG, idx)] = scan_results_events
 
             self.wait_for_scan_events(wait_time_list, scan_results_dict)
 
@@ -342,10 +383,11 @@
             for idx in idx_list:
                 self.dut.droid.wifiScannerStopBackgroundScan(idx)
             self.dut.ed.clear_all_events()
+
     """ Helper Functions End """
-
-
     """ Tests Begin """
+
+    @test_tracker_info(uuid="d490b146-5fc3-4fc3-9958-78ba0ad63211")
     def test_wifi_two_scans_at_same_interval(self):
         """Perform two WifiScanner background scans, one at 2.4GHz and the other
         at 5GHz, the same interval and number of BSSIDs per scan.
@@ -361,17 +403,20 @@
             between them approximately equals to the expected interval
           * Number of BSSIDs doesn't exceed
         """
-        scan_settings = [{ "band": WifiEnums.WIFI_BAND_24_GHZ,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24},
-                         { "band": WifiEnums.WIFI_BAND_5_GHZ,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24}]
+        scan_settings = [{"band": WifiEnums.WIFI_BAND_24_GHZ,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 24},
+                         {"band": WifiEnums.WIFI_BAND_5_GHZ,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 24}]
 
         self.scan_and_validate_results(scan_settings)
 
+    @test_tracker_info(uuid="0ec9a554-f942-41a9-8096-6b0b400f60b0")
     def test_wifi_two_scans_at_different_interval(self):
         """Perform two WifiScanner background scans, one at 2.4GHz and the other
         at 5GHz, different interval and number of BSSIDs per scan.
@@ -387,17 +432,20 @@
             between them approximately equals to the expected interval
           * Number of BSSIDs doesn't exceed
         """
-        scan_settings = [{ "band": WifiEnums.WIFI_BAND_24_GHZ,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 20},
-                         { "band": WifiEnums.WIFI_BAND_5_GHZ,
-                           "periodInMs": 20000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24}]
+        scan_settings = [{"band": WifiEnums.WIFI_BAND_24_GHZ,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 20},
+                         {"band": WifiEnums.WIFI_BAND_5_GHZ,
+                          "periodInMs": 30000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 24}]
 
         self.scan_and_validate_results(scan_settings)
 
+    @test_tracker_info(uuid="0d616591-0d32-4be6-8fd4-e4a5e9ccdce0")
     def test_wifi_scans_24GHz_and_both(self):
         """Perform two WifiScanner background scans, one at 2.4GHz and
            the other at both 2.4GHz and 5GHz
@@ -413,17 +461,20 @@
             between them approximately equals to the expected interval
           * Number of BSSIDs doesn't exceed
         """
-        scan_settings = [{ "band": WifiEnums.WIFI_BAND_BOTH,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24},
-                         { "band": WifiEnums.WIFI_BAND_24_GHZ,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24}]
+        scan_settings = [{"band": WifiEnums.WIFI_BAND_BOTH,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 24},
+                         {"band": WifiEnums.WIFI_BAND_24_GHZ,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 24}]
 
         self.scan_and_validate_results(scan_settings)
 
+    @test_tracker_info(uuid="ddcf959e-512a-4e86-b3d3-18cebd0b22a0")
     def test_wifi_scans_5GHz_and_both(self):
         """Perform two WifiScanner scans, one at 5GHz and the other at both
            2.4GHz and 5GHz
@@ -439,17 +490,20 @@
             between them approximately equals to the expected interval
           * Number of BSSIDs doesn't exceed
         """
-        scan_settings = [{ "band": WifiEnums.WIFI_BAND_BOTH,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24},
-                         { "band": WifiEnums.WIFI_BAND_5_GHZ,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24}]
+        scan_settings = [{"band": WifiEnums.WIFI_BAND_BOTH,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 24},
+                         {"band": WifiEnums.WIFI_BAND_5_GHZ,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 24}]
 
         self.scan_and_validate_results(scan_settings)
 
+    @test_tracker_info(uuid="060469f1-fc6b-4255-ab6e-b1d5b54db53d")
     def test_wifi_scans_24GHz_5GHz_and_DFS(self):
         """Perform three WifiScanner scans, one at 5GHz, one at 2.4GHz and the
         other at just 5GHz DFS channels
@@ -465,21 +519,24 @@
             between them approximately equals to the expected interval
           * Number of BSSIDs doesn't exceed
         """
-        scan_settings = [{ "band": WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY,
-                           "periodInMs": 20000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24},
-                         { "band": WifiEnums.WIFI_BAND_5_GHZ,
-                           "periodInMs": 20000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24},
-                         { "band": WifiEnums.WIFI_BAND_24_GHZ,
-                           "periodInMs": 40000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24}]
+        scan_settings = [
+            {"band": WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY,
+             "periodInMs": 10000,  # ms
+             "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+             "numBssidsPerScan": 24},
+            {"band": WifiEnums.WIFI_BAND_5_GHZ,
+             "periodInMs": 10000,  # ms
+             "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+             "numBssidsPerScan": 24},
+            {"band": WifiEnums.WIFI_BAND_24_GHZ,
+             "periodInMs": 30000,  # ms
+             "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+             "numBssidsPerScan": 24}
+        ]
 
         self.scan_and_validate_results(scan_settings)
 
+    @test_tracker_info(uuid="14104e98-27a0-43d5-9525-b36b65ac3957")
     def test_wifi_scans_batch_and_24GHz(self):
         """Perform two WifiScanner background scans, one in batch mode for both
         bands and the other in periodic mode at 2.4GHz
@@ -496,18 +553,21 @@
           * Number of results in batch mode should match the setting
           * Number of BSSIDs doesn't exceed
         """
-        scan_settings = [{ "band": WifiEnums.WIFI_BAND_BOTH,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL,
-                           "numBssidsPerScan": 24,
-                           "maxScansToCache": 2},
-                         { "band": WifiEnums.WIFI_BAND_24_GHZ,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24}]
+        scan_settings = [{"band": WifiEnums.WIFI_BAND_BOTH,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL,
+                          "numBssidsPerScan": 24,
+                          "maxScansToCache": 2},
+                         {"band": WifiEnums.WIFI_BAND_24_GHZ,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 24}]
 
         self.scan_and_validate_results(scan_settings)
 
+    @test_tracker_info(uuid="cd6064b5-840b-4334-8cd4-8320a6cda52f")
     def test_wifi_scans_batch_and_5GHz(self):
         """Perform two WifiScanner background scans, one in batch mode for both
         bands and the other in periodic mode at 5GHz
@@ -524,18 +584,21 @@
           * Number of results in batch mode should match the setting
           * Number of BSSIDs doesn't exceed
         """
-        scan_settings = [{ "band": WifiEnums.WIFI_BAND_BOTH,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL,
-                           "numBssidsPerScan": 24,
-                           "maxScansToCache": 2},
-                         { "band": WifiEnums.WIFI_BAND_5_GHZ,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24}]
+        scan_settings = [{"band": WifiEnums.WIFI_BAND_BOTH,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL,
+                          "numBssidsPerScan": 24,
+                          "maxScansToCache": 2},
+                         {"band": WifiEnums.WIFI_BAND_5_GHZ,
+                          "periodInMs": 10000,  # ms
+                          "reportEvents":
+                          WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+                          "numBssidsPerScan": 24}]
 
         self.scan_and_validate_results(scan_settings)
 
+    @test_tracker_info(uuid="9f48cb0c-de87-4cd2-9e50-857579d44079")
     def test_wifi_scans_24GHz_5GHz_full_result(self):
         """Perform two WifiScanner background scans, one at 2.4GHz and
            the other at 5GHz. Report full scan results.
@@ -551,16 +614,18 @@
             between them approximately equals to the expected interval
           * Number of BSSIDs doesn't exceed
         """
-        scan_settings = [{ "band": WifiEnums.WIFI_BAND_24_GHZ,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT
-                           | WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24},
-                         { "band": WifiEnums.WIFI_BAND_5_GHZ,
-                           "periodInMs": 10000, # ms
-                           "reportEvents": WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT
-                           | WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
-                           "numBssidsPerScan": 24}]
+        scan_settings = [
+            {"band": WifiEnums.WIFI_BAND_24_GHZ,
+             "periodInMs": 10000,  # ms
+             "reportEvents": WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT
+             | WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+             "numBssidsPerScan": 24},
+            {"band": WifiEnums.WIFI_BAND_5_GHZ,
+             "periodInMs": 10000,  # ms
+             "reportEvents": WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT
+             | WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+             "numBssidsPerScan": 24}
+        ]
 
         self.scan_and_validate_results(scan_settings)
 
diff --git a/acts/tests/google/wifi/WifiScannerScanTest.py b/acts/tests/google/wifi/WifiScannerScanTest.py
index 558b1f7..140ddd9 100755
--- a/acts/tests/google/wifi/WifiScannerScanTest.py
+++ b/acts/tests/google/wifi/WifiScannerScanTest.py
@@ -15,28 +15,22 @@
 #   limitations under the License.
 
 import itertools
-from queue import Empty
-import threading, time, traceback
+import queue
+import time
+import traceback
 
 from acts import asserts
-from acts.base_test import BaseTestClass
-from acts.utils import load_config
-from acts.test_utils.wifi.wifi_test_utils import get_scan_time_and_channels
-from acts.test_utils.wifi.wifi_test_utils import check_internet_connection
-from acts.test_utils.wifi.wifi_test_utils import start_wifi_background_scan
-from acts.test_utils.wifi.wifi_test_utils import start_wifi_single_scan
-from acts.test_utils.wifi.wifi_test_utils import track_connection
-from acts.test_utils.wifi.wifi_test_utils import wifi_test_device_init
-from acts.test_utils.wifi.wifi_test_utils import WifiEnums
-from acts.test_utils.wifi.wifi_test_utils import WifiChannelUS
-from acts.test_utils.wifi.wifi_test_utils import wifi_forget_network
-from acts.test_utils.wifi.wifi_test_utils import wifi_toggle_state
+from acts import base_test
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_constants
+from acts.test_utils.wifi import wifi_test_utils as wutils
 
-SCANTIME = 10000 #framework support only 10s as minimum scan interval
+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
+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"
@@ -44,15 +38,17 @@
 EMPTY_RESULT = "Test fail because empty scan result reported"
 KEY_RET = "ResultElapsedRealtime"
 
+
 class WifiScannerScanError(Exception):
     pass
 
-class WifiScannerScanTest(BaseTestClass):
 
+class WifiScannerScanTest(base_test.BaseTestClass):
     def __init__(self, controllers):
-        BaseTestClass.__init__(self, controllers)
-        asserts.failed_scan_settings = None
-        # A list of all test cases to be executed in this class.
+        base_test.BaseTestClass.__init__(self, controllers)
+        # TODO(angli): Remove this list.
+        # There are order dependencies among these tests so we'll have to leave
+        # it here for now. :(
         self.tests = (
             "test_available_channels_generated",
             "test_wifi_scanner_single_scan_channel_sanity",
@@ -73,36 +69,37 @@
             "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
-                        }
+            "test_wifi_scanner_with_invalid_numBssidsPerScan")
 
     def setup_class(self):
         self.dut = self.android_devices[0]
-        wifi_test_device_init(self.dut)
+        wutils.wifi_test_device_init(self.dut)
         req_params = ("connect_network", "run_extended_test", "ping_addr",
                       "max_bugreports")
         self.unpack_userparams(req_params)
+        self.leeway = 10
+        self.stime_channel = SCAN_TIME_PASSIVE
+        self.default_scan_setting = {
+            "band": wutils.WifiEnums.WIFI_BAND_BOTH,
+            "periodInMs": SCANTIME,
+            "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN
+        }
+        self.default_batch_scan_setting = {
+            "band": wutils.WifiEnums.WIFI_BAND_BOTH,
+            "periodInMs": SCANTIME,
+            "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL
+        }
         self.log.debug("Run extended test: {}".format(self.run_extended_test))
-        self.wifi_chs = WifiChannelUS(self.dut.model)
+        self.wifi_chs = wutils.WifiChannelUS(self.dut.model)
         asserts.assert_true(self.dut.droid.wifiIsScannerSupported(),
-            "Device %s doesn't support WifiScanner, abort." % self.dut.model)
+                            "Device %s doesn't support WifiScanner, abort." %
+                            self.dut.model)
+        self.attenuators = wutils.group_attenuators(self.attenuators)
         self.attenuators[0].set_atten(0)
         self.attenuators[1].set_atten(0)
 
     def teardown_test(self):
-        BaseTestClass.teardown_test(self)
+        base_test.BaseTestClass.teardown_test(self)
         self.log.debug("Shut down all wifi scanner activities.")
         self.dut.droid.wifiScannerShutdown()
 
@@ -114,7 +111,8 @@
 
     """ Helper Functions Begin """
 
-    def wifi_generate_scanner_scan_settings(self, extended, scan_type, report_result):
+    def wifi_generate_scanner_scan_settings(self, extended, scan_type,
+                                            report_result):
         """Generates all the combinations of different scan setting parameters.
 
         Args:
@@ -125,29 +123,30 @@
         Returns:
           A list of dictionaries each representing a set of scan settings.
         """
-        base_scan_time = [SCANTIME*2]
+        base_scan_time = [SCANTIME * 2]
         if scan_type == "band":
-            scan_types_setting = [WifiEnums.WIFI_BAND_BOTH]
+            scan_types_setting = [wutils.WifiEnums.WIFI_BAND_BOTH]
         else:
             scan_types_setting = [self.wifi_chs.MIX_CHANNEL_SCAN]
-        num_of_bssid = [NUMBSSIDPERSCAN*4]
+        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])
+                scan_types_setting.extend(
+                    [wutils.WifiEnums.WIFI_BAND_24_GHZ,
+                     wutils.WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS,
+                     wutils.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)
+                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}
+        if report_result == wutils.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
@@ -158,10 +157,10 @@
                 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))
+            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:
@@ -197,23 +196,26 @@
         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, scan_channels = wutils.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) )
+            asserts.assert_true(
+                batch["ScanResults"],
+                "At least one scan result is required to validate")
+            max_scan_interval = batch["ScanResults"][0][
+                "timestamp"] + scan_time_mic
+            self.log.debug("max_scan_interval: %s", 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"])))
+                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: %s",
+                                   result)
+                    validity = False
+            self.log.info("Number of scan result in batch %s: %s", i,
+                          len(batch["ScanResults"]))
             bssids += len(batch["ScanResults"])
         return bssids, validity
 
@@ -231,8 +233,8 @@
             events = self.dut.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)))
+        except queue.Empty as error:
+            self.log.debug("Number of Full scan results %s", len(results))
         return results
 
     def wifi_scanner_single_scan(self, scan_setting):
@@ -247,50 +249,49 @@
         Args:
             scan_setting: The params for the single scan.
         """
-        data = start_wifi_single_scan(self.dut, scan_setting)
+        data = wutils.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))
+        self.log.info(
+            "Wifi single shot scan started index: %s at real time: %s", 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
+        scan_time, scan_channels = wutils.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):
+            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))
+                self.log.debug("Waiting for event: %s for time %s", event_name,
+                               wait_time)
                 event = self.dut.ed.pop_event(event_name, wait_time)
-                self.log.debug("Event received: {}".format(event ))
+                self.log.debug("Event received: %s", 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)
-                asserts.assert_true(len(results) == 1,
-                                 "Test fail because number of scan result {}"
-                                 .format(len(results)))
+                    results, scan_rt, event["data"][KEY_RET], scan_setting)
+                asserts.assert_equal(
+                    len(results), 1,
+                    "Test fail because number of scan result %s" %
+                    len(results))
                 asserts.assert_true(bssids > 0, EMPTY_RESULT)
                 asserts.assert_true(validity, INVALID_RESULT)
-                self.log.info("Scan number Buckets: {}\nTotal BSSID: {}".
-                              format(len(results), bssids))
-        except Empty as error:
-            asserts.assert_true(result_received >= 1,
-                             "Event did not triggered for single shot {}".
-                             format(error))
+                self.log.info("Scan number Buckets: %s\nTotal BSSID: %s",
+                              len(results), bssids)
+        except queue.Empty as error:
+            asserts.assert_true(
+                result_received >= 1,
+                "Event did not triggered for single shot {}".format(error))
         finally:
             self.dut.droid.wifiScannerStopScan(idx)
             #For single shot number of result received and length of result should be one
-            asserts.assert_true(result_received == 1,
-                             "Test fail because received result {}".
-                             format(result_received))
+            asserts.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.
@@ -305,37 +306,35 @@
             scan_setting: The parameters for the single scan.
         """
         self.dut.ed.clear_all_events()
-        data = start_wifi_single_scan(self.dut, scan_setting)
+        data = wutils.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))
+        self.log.info("Wifi single shot scan started with index: %s", 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
+        scan_time, scan_channels = wutils.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_name = "%s%sonResults" % (EVENT_TAG, idx)
+            self.log.debug("Waiting for event: %s for time %s", event_name,
+                           wait_time)
             event = self.dut.ed.pop_event(event_name, wait_time)
-            self.log.info("Event received: {}".format(event))
+            self.log.info("Event received: %s", event)
             bssids, validity = (self.proces_and_valid_batch_scan_result(
-                                                event["data"]["Results"], scan_rt,
-                                                event["data"][KEY_RET],
-                                                scan_setting))
+                event["data"]["Results"], scan_rt, event["data"][KEY_RET],
+                scan_setting))
             asserts.assert_true(bssids > 0, EMPTY_RESULT)
             asserts.assert_true(validity, INVALID_RESULT)
             event_name = "{}{}onFullResult".format(EVENT_TAG, idx)
             results = self.pop_scan_result_events(event_name)
-            asserts.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))
+            asserts.assert_true(
+                len(results) >= bssids,
+                "Full single shot result don't match {}".format(len(results)))
+        except queue.Empty as error:
+            raise AssertionError(
+                "Event did not triggered for single shot {}".format(error))
         finally:
             self.dut.droid.wifiScannerStopScan(idx)
 
@@ -352,41 +351,39 @@
             scan_setting: The params for the batch scan.
         """
         self.dut.ed.clear_all_events()
-        data = start_wifi_background_scan(self.dut, scan_setting)
+        data = wutils.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))
+        self.log.info("Wifi batch shot scan started with index: %s", 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_channels = wutils.get_scan_time_and_channels(
+            self.wifi_chs, scan_setting, self.stime_channel)
         # multiply scan period by two to account for scheduler changing period
-        scan_time += scan_setting['periodInMs'] * 2 #add scan period delay for next cycle
+        scan_time += scan_setting[
+            'periodInMs'] * 2  #add scan period delay for next cycle
         wait_time = scan_time / 1000 + self.leeway
         validity = False
         try:
-            for snumber in range(1,3):
+            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_name = "%s%sonResults" % (EVENT_TAG, idx)
+                self.log.debug("Waiting for event: %s for time %s", event_name,
+                               wait_time)
                 event = self.dut.ed.pop_event(event_name, wait_time)
-                self.log.debug("Event received: {}".format(event))
+                self.log.debug("Event received: %s", 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)
+                    event["data"]["Results"], scan_rt, event["data"][KEY_RET],
+                    scan_setting)
+                event_name = "%s%sonFullResult" % (EVENT_TAG, idx)
                 results = self.pop_scan_result_events(event_name)
-                asserts.assert_true(len(results) >= bssids,
-                                 "Full single shot result don't match {}".
-                                 format(len(results)))
+                asserts.assert_true(
+                    len(results) >= bssids,
+                    "Full single shot result don't match %s" % len(results))
                 asserts.assert_true(bssids > 0, EMPTY_RESULT)
                 asserts.assert_true(validity, INVALID_RESULT)
-        except Empty as error:
-             raise AssertionError("Event did not triggered for batch scan {}".
-                                  format(error))
+        except queue.Empty as error:
+            raise AssertionError("Event did not triggered for batch scan %s" %
+                                 error)
         finally:
             self.dut.droid.wifiScannerStopBackgroundScan(idx)
             self.dut.ed.clear_all_events()
@@ -403,72 +400,73 @@
         Args:
             scan_setting: The parameters for the batch scan.
         """
-        data = start_wifi_background_scan(self.dut, scan_setting)
+        data = wutils.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)
+        self.log.info(
+            "Wifi background scan started with index: %s real time %s", idx,
+            scan_rt)
+        scan_time, scan_channels = wutils.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
+        number_bucket = 1  #bucket for Report result on each scan
         check_get_result = False
-        if scan_setting['reportEvents'] == WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL:
+        if scan_setting[
+                'reportEvents'] == wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL:
             check_get_result = True
             if ('maxScansToCache' in scan_setting and
-                                    scan_setting['maxScansToCache'] != 0) :
+                    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
+                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
+            time_cache = scan_setting[
+                'periodInMs'
+            ]  #need while waiting for seconds resutls
         # multiply cache time by two to account for scheduler changing period
         wait_time = (time_cache * 2 + 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))
+            for snumber in range(1, 3):
+                event_name = "%s%sonResults" % (EVENT_TAG, idx)
+                self.log.info("Waiting for event: %s for time %s", event_name,
+                              wait_time)
                 event = self.dut.ed.pop_event(event_name, wait_time)
-                self.log.debug("Event received: {}".format(event ))
+                self.log.debug("Event received: %s", 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))
-                asserts.assert_true(len(results) == number_bucket,
-                                     "Test fail because number_bucket {}".
-                                     format(len(results)))
+                    results, scan_rt, event["data"][KEY_RET], scan_setting))
+                self.log.info("Scan number: %s\n Buckets: %s\n  BSSID: %s",
+                              snumber, len(results), bssids)
+                asserts.assert_equal(
+                    len(results), number_bucket,
+                    "Test fail because number_bucket %s" % len(results))
                 asserts.assert_true(bssids >= 1, EMPTY_RESULT)
                 asserts.assert_true(validity, INVALID_RESULT)
-                if snumber%2 == 1 and check_get_result :
+                if snumber % 2 == 1 and check_get_result:
                     self.log.info("Get Scan result using GetScanResult API")
-                    time.sleep(wait_time/number_bucket)
+                    time.sleep(wait_time / number_bucket)
                     if self.dut.droid.wifiScannerGetScanResults():
                         event = self.dut.ed.pop_event(event_name, 1)
-                        self.log.debug("Event onResults: {}".format(event))
+                        self.log.debug("Event onResults: %s", 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))
+                        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: %s BSSID: %s",
+                                      snumber, bssids)
                         asserts.assert_true(bssids >= 1, EMPTY_RESULT)
                         asserts.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))
+        except queue.Empty as error:
+            raise AssertionError("Event did not triggered for batch scan %s" %
+                                 error)
         finally:
             self.dut.droid.wifiScannerStopBackgroundScan(idx)
             self.dut.ed.clear_all_events()
@@ -485,10 +483,11 @@
         """
         try:
             idx = self.dut.droid.wifiScannerStartScan(scan_setting)
-            event = self.dut.ed.pop_event("{}{}onFailure".format(EVENT_TAG, idx),
-                                      SHORT_TIMEOUT)
-        except Empty as error:
-            raise AssertionError("Did not get expected onFailure {}".format(error))
+            event = self.dut.ed.pop_event(
+                "{}{}onFailure".format(EVENT_TAG, idx), SHORT_TIMEOUT)
+        except queue.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
@@ -501,11 +500,12 @@
           scan_setting: The params for the single scan.
         """
         try:
-          idx = self.dut.droid.wifiScannerStartBackgroundScan(scan_setting)
-          event = self.dut.ed.pop_event("{}{}onFailure".format(EVENT_TAG, idx),
-                                    SHORT_TIMEOUT)
-        except Empty as error:
-          raise AssertionError("Did not get expected onFailure {}".format(error))
+            idx = self.dut.droid.wifiScannerStartBackgroundScan(scan_setting)
+            event = self.dut.ed.pop_event(
+                "{}{}onFailure".format(EVENT_TAG, idx), SHORT_TIMEOUT)
+        except queue.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.
@@ -520,47 +520,50 @@
         self.log.debug(band)
         self.log.debug(r)
         expected = self.wifi_chs.band_to_freq(band)
-        asserts.assert_true(set(r) == set(expected),
-                         "Band {} failed. Expected {}, got {}".
-                         format(band, expected, r))
+        asserts.assert_equal(set(r), set(expected), "Band %s failed." % band)
 
     def connect_to_reference_network(self):
         """Connect to reference network and make sure that connection happen"""
         self.dut.droid.wakeLockAcquireBright()
         self.dut.droid.wakeUpNow()
         try:
-            self.dut.droid.wifiPriorityConnect(self.connect_network)
-            connect_result = self.dut.ed.pop_event("WifiManagerPriorityConnectOnSuccess",
-                                               SHORT_TIMEOUT)
+            self.dut.droid.wifiConnectByConfig(self.connect_network)
+            connect_result = self.dut.ed.pop_event(
+                wifi_constants.CONNECT_BY_CONFIG_SUCCESS, SHORT_TIMEOUT)
             self.log.info(connect_result)
-            return track_connection(self.dut, self.connect_network["SSID"], 1)
+            return wutils.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))
+            self.log.error("Connection to network fail because %s", error)
             return False
         finally:
             self.dut.droid.wifiLockRelease()
             self.dut.droid.goToSleepNow()
 
     """ Helper Functions End """
-
     """ Tests Begin """
+
+    @test_tracker_info(uuid="7cca8142-529f-4951-ab6f-cd03b359b3cc")
     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)
+        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)
-        asserts.assert_true(not failed,
-                         "Number of test_get_channel_band failed {}".
-                         format(len(failed)))
+            self.check_get_available_channels_with_one_band,
+            bands,
+            name_func=name_func)
+        asserts.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_tracker_info(uuid="95069244-b76c-4834-b3a6-96b0d8da98d8")
+    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
@@ -568,25 +571,29 @@
          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"]))
+            self.run_extended_test, "channels",
+            wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+        self.log.debug("Scan settings: %s\n%s", len(scan_settings),
+                       scan_settings)
+
+        def name_func(scan_setting):
+            return (
+                "test_single_scan_report_each_scan_for_channels_%s"
+                "_numBssidsPerScan_%s_maxScansToCache_%s_period_%s") % (
+                    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)
-        asserts.assert_true(not failed,
-                         ("Number of test_single_scan_report_each_scan_for_channels"
-                          " failed {}").format(len(failed)))
+                                              name_func=name_func)
+        asserts.assert_false(
+            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_tracker_info(uuid="5595ebe5-6d91-4379-a606-be59967e5ec9")
+    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
@@ -594,25 +601,28 @@
          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"]))
+            self.run_extended_test, "band",
+            wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+        self.log.debug("Scan settings:%s\n%s", len(scan_settings),
+                       scan_settings)
+
+        def scan_setting_name_gen(scan_setting):
+            return "test_single_scan_report_each_scan_for_band_%s_numBssidsPerScan_%s_maxScansToCache_%s_period_%s" % (
+                scan_setting["band"], scan_setting["numBssidsPerScan"],
+                scan_setting["maxScansToCache"], scan_setting["periodInMs"])
+
+        name_func = scan_setting_name_gen
         failed = self.run_generated_testcases(self.wifi_scanner_single_scan,
                                               scan_settings,
-                                              name_func = name_func)
-        asserts.assert_true(not failed,
-                         ("Number of test_single_scan_report_each_scan_for_band"
-                          " failed {}").format(len(failed)))
+                                              name_func=name_func)
+        asserts.assert_true(
+            not failed,
+            "Number of test_single_scan_report_each_scan_for_band failed %s" %
+            len(failed))
 
-    def test_batch_scan_report_buffer_full_for_channels_with_enumerated_params(self):
+    @test_tracker_info(uuid="44989f93-e63b-4c2e-a90a-a483477303bb")
+    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.
 
@@ -621,25 +631,24 @@
          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"]))
+            self.run_extended_test, "channels",
+            wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL)
+        self.log.debug("Scan settings:%s\n%s", 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)
-        asserts.assert_true(not failed,
-                         ("Number of test_batch_scan_report_buffer_full_for_channels"
-                          " failed {}").format(len(failed)))
+                                              name_func=name_func)
+        asserts.assert_false(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_tracker_info(uuid="63538df6-388a-4c16-964f-e9c19b750e07")
+    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.
 
@@ -648,25 +657,24 @@
          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"]))
+            self.run_extended_test, "band",
+            wutils.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)
-        asserts.assert_true(not failed,
-                         ("Number of test_batch_scan_report_buffer_full_for_band"
-                          " failed {}").format(len(failed)))
+                                              name_func=name_func)
+        asserts.assert_false(
+            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_tracker_info(uuid="bd4e8c53-16c8-4ed6-b680-55c1ba688ad8")
+    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.
 
@@ -675,24 +683,22 @@
          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"]))
+            self.run_extended_test, "channels",
+            wutils.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)
-        asserts.assert_true(not failed,
-                         ("Number of test_batch_scan_report_each_scan_for_channels"
-                          " failed {}").format(len(failed)))
+                                              name_func=name_func)
+        asserts.assert_false(
+            failed, ("Number of test_batch_scan_report_each_scan_for_channels"
+                     " failed {}").format(len(failed)))
 
+    @test_tracker_info(uuid="d11e8c09-97d0-49c1-bf09-b9ec672c2fa6")
     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.
@@ -702,25 +708,24 @@
          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"]))
+            self.run_extended_test, "band",
+            wutils.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)
-        asserts.assert_true(not failed,
-                         ("Number of test_batch_scan_report_each_scan_for_band"
-                          " failed {}").format(len(failed)))
+                                              name_func=name_func)
+        asserts.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_tracker_info(uuid="7f967b0e-82fe-403e-9d74-0dee7f09a21d")
+    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.
 
@@ -729,21 +734,24 @@
          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)
-        asserts.assert_true(not failed,
-                         ("Number of test_single_scan_report_full_scan_for_channels"
-                          " failed {}").format(len(failed)))
+            self.run_extended_test, "channels",
+            wutils.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)
+        asserts.assert_false(
+            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_tracker_info(uuid="34d09f60-31bf-4952-8fb3-03fc93ec98fa")
+    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.
 
@@ -752,21 +760,24 @@
          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)
-        asserts.assert_true(not failed,
-                         ("Number of test_single_scan_report_full_scan_for_band"
-                          " failed {}").format(len(failed)))
+            self.run_extended_test, "band",
+            wutils.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)
+        asserts.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_tracker_info(uuid="0ddccf2e-b518-45a7-ae75-96924070b841")
+    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.
 
@@ -775,21 +786,23 @@
          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)
-        asserts.assert_true(not failed,
-                         ("Number of test_batch_scan_report_full_scan_for_channels"
-                          " failed {}").format(len(failed)))
+            self.run_extended_test, "channels",
+            wutils.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)
+        asserts.assert_false(
+            failed, ("Number of test_batch_scan_report_full_scan_for_channels"
+                     " failed {}").format(len(failed)))
 
+    @test_tracker_info(uuid="0685b667-8470-43a0-923d-dee71428f8ce")
     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.
@@ -799,21 +812,23 @@
          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)
-        asserts.assert_true(not failed,
-                         ("Number of test_batch_scan_report_full_scan_for_band"
-                          " failed {}").format(len(failed)))
+            self.run_extended_test, "band",
+            wutils.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)
+        asserts.assert_true(
+            not failed, ("Number of test_batch_scan_report_full_scan_for_band"
+                         " failed {}").format(len(failed)))
 
+    @test_tracker_info(uuid="740e1c18-911a-43d2-9317-3827ecf71d3b")
     def test_wifi_connection_while_single_scan(self):
         """Test configuring a connection parallel to wifi scanner single scan.
 
@@ -823,38 +838,41 @@
          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)
+        data = wutils.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.log.info("Wifi single shot scan started with index: {}".format(
+            idx))
         asserts.assert_true(self.connect_to_reference_network(), NETWORK_ERROR)
-        time.sleep(10) #wait for connection to be active
-        asserts.assert_true(check_internet_connection(self.dut, self.ping_addr),
-                         "Error, No internet connection for current network")
+        time.sleep(10)  #wait for connection to be active
+        asserts.assert_true(
+            wutils.validate_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
+        scan_time, scan_channels = wutils.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))
+            self.log.debug("Waiting for event: {} for time {}".format(
+                event_name, wait_time))
             event = self.dut.ed.pop_event(event_name, wait_time)
-            self.log.debug("Event received: {}".format(event ))
+            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))
-            asserts.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))
+                results, scan_rt, event["data"][KEY_RET],
+                self.default_scan_setting)
+            self.log.info("Scan number Buckets: {}\nTotal BSSID: {}".format(
+                len(results), bssids))
+            asserts.assert_true(
+                len(results) == 1 and bssids >= 1, EMPTY_RESULT)
+        except queue.Empty as error:
+            raise AssertionError(
+                "Event did not triggered for single scan {}".format(error))
 
+    @test_tracker_info(uuid="e9a7cfb5-21c4-4c40-8169-8d88b65a1dee")
     def test_single_scan_while_pno(self):
         """Test wifi scanner single scan parallel to PNO connection.
 
@@ -872,21 +890,23 @@
         asserts.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
+        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
+        time.sleep(30)  #wait for connection through PNO
         current_network = self.dut.droid.wifiGetConnectionInfo()
         self.log.info("Current network: {}".format(current_network))
         asserts.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
         asserts.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR)
-        time.sleep(10) #wait for IP to be assigned
-        asserts.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"])
+        time.sleep(10)  #wait for IP to be assigned
+        asserts.assert_true(
+            wutils.validate_connection(self.dut, self.ping_addr),
+            "Error, No internet connection for current network")
+        wutils.wifi_forget_network(self.dut, self.connect_network["SSID"])
 
+    @test_tracker_info(uuid="fc18d947-0b5a-42b4-98f3-dd1f2b52a7af")
     def test_wifi_connection_and_pno_while_batch_scan(self):
         """Test configuring a connection and PNO connection parallel to wifi
            scanner batch scan.
@@ -905,74 +925,93 @@
          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)
+        data = wutils.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))
+        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)
+        scan_time, scan_channels = wutils.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
+        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
         #multiply cache time by two to account for scheduler changing period
         wait_time = (time_cache * 2 + 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.dut.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))
-            asserts.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 ))
-                asserts.assert_true(self.connect_to_reference_network(), NETWORK_ERROR)
-                time.sleep(10) #wait for connection to be active
-                asserts.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.dut.droid.wifiGetConnectionInfo()
-                self.log.info("Current network: {}".format(current_network))
-                asserts.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
-                asserts.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.dut.droid.wifiGetConnectionInfo()
-                self.log.info("Current network: {}".format(current_network))
-                asserts.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
-                asserts.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR)
-                time.sleep(10) #wait for connection to be active
-                asserts.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))
+            for snumber in range(1, 7):
+                event_name = "{}{}onResults".format(EVENT_TAG, idx)
+                self.log.info("Waiting for event: {}".format(event_name))
+                event = self.dut.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))
+                asserts.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))
+                    asserts.assert_true(self.connect_to_reference_network(),
+                                        NETWORK_ERROR)
+                    time.sleep(10)  #wait for connection to be active
+                    asserts.assert_true(
+                        wutils.validate_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.dut.droid.wifiGetConnectionInfo()
+                    self.log.info("Current network: {}".format(
+                        current_network))
+                    asserts.assert_true('network_id' in current_network,
+                                        NETWORK_ID_ERROR)
+                    asserts.assert_true(
+                        current_network['network_id'] == -1,
+                        "Device is still connected to network  {}".format(
+                            current_network[wutils.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.dut.droid.wifiGetConnectionInfo()
+                    self.log.info("Current network: {}".format(
+                        current_network))
+                    asserts.assert_true('network_id' in current_network,
+                                        NETWORK_ID_ERROR)
+                    asserts.assert_true(current_network['network_id'] >= 0,
+                                        NETWORK_ERROR)
+                    time.sleep(10)  #wait for connection to be active
+                    asserts.assert_true(
+                        wutils.validate_connection(self.dut, self.ping_addr),
+                        "Error, No internet connection for current network")
+                    wutils.wifi_forget_network(self.dut,
+                                               self.connect_network["SSID"])
+        except queue.Empty as error:
+            raise AssertionError(
+                "Event did not triggered for batch scan {}".format(error))
         finally:
             self.dut.droid.wifiScannerStopBackgroundScan(idx)
             self.dut.ed.clear_all_events()
 
+    @test_tracker_info(uuid="7c25ce32-0fae-4a68-a7cb-fdf6d4d03caf")
     def test_wifi_scanner_single_scan_channel_sanity(self):
         """Test WiFi scanner single scan for mix channel with default setting
            parameters.
@@ -981,11 +1020,13 @@
             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 }
+        scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+                        "periodInMs": SCANTIME,
+                        "reportEvents":
+                        wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN}
         self.wifi_scanner_single_scan(scan_setting)
 
+    @test_tracker_info(uuid="e9f3aaad-4af3-4c54-9829-65dc1d6d4987")
     def test_wifi_scanner_batch_scan_channel_sanity(self):
         """Test WiFi scanner batch scan for mix channel with default setting
            parameters to report the result on buffer full.
@@ -994,21 +1035,25 @@
             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}
+        scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+                        "periodInMs": SCANTIME,
+                        "reportEvents":
+                        wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL}
         self.wifi_scanner_batch_scan(scan_setting)
 
+    @test_tracker_info(uuid="49ba245a-52e2-4c9b-90ad-a2fbc97e3d9f")
     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);
+        scan_setting = {"band": wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+                        "periodInMs": 5000,
+                        "reportEvents":
+                        wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL}
+        self.start_wifi_scanner_background_scan_expect_failure(scan_setting)
 
+    @test_tracker_info(uuid="6fe45cd7-4fac-4ddd-a950-b9431e68f735")
     def test_wifi_scanner_single_scan_in_isolated(self):
         """Test WiFi scanner in isolated environment with default scan settings.
 
@@ -1019,39 +1064,38 @@
         """
         self.attenuators[0].set_atten(90)
         self.attenuators[1].set_atten(90)
-        data = start_wifi_single_scan(self.dut, self.default_scan_setting)
+        data = wutils.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.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
+        scan_time, scan_channels = wutils.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))
+            self.log.debug("Waiting for event: {} for time {}".format(
+                event_name, wait_time))
             event = self.dut.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))
-            asserts.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))
+            for batch in results:
+                asserts.assert_false(batch["ScanResults"],
+                                     "Test fail because report scan "
+                                     "results reported are not empty")
+        except queue.Empty as error:
+            raise AssertionError(
+                "Event did not triggered for in isolated environment {}".format(
+                    error))
         finally:
             self.dut.ed.clear_all_events()
             self.attenuators[0].set_atten(0)
             self.attenuators[1].set_atten(0)
 
+    @test_tracker_info(uuid="46f817b9-97a3-455e-af2c-56f9aea64f7e")
     def test_wifi_scanner_with_wifi_off(self):
         """Test WiFi scanner single scan when wifi is off.
 
@@ -1060,11 +1104,13 @@
          3. Verify that scan is not started.
         """
         self.log.debug("Make sure wifi is off.")
-        wifi_toggle_state(self.dut, False)
-        self.start_wifi_scanner_single_scan_expect_failure(self.default_scan_setting)
+        wutils.wifi_toggle_state(self.dut, False)
+        self.start_wifi_scanner_single_scan_expect_failure(
+            self.default_scan_setting)
         self.log.debug("Turning wifi back on.")
-        wifi_toggle_state(self.dut, True)
+        wutils.wifi_toggle_state(self.dut, True)
 
+    @test_tracker_info(uuid="257ad734-c21f-49f4-b448-3986b70eba3d")
     def test_wifi_scanner_with_invalid_numBssidsPerScan(self):
         """Test WiFi scanner single scan with invalid number of bssids reported
            per scan.
@@ -1075,10 +1121,11 @@
             bssids per scan.
         """
         scan_setting = {
-            "band": WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+            "band": wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
             "periodInMs": SCANTIME,
-            "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+            "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
             'numBssidsPerScan': 33
         }
         self.wifi_scanner_single_scan(scan_setting)
+
     """ Tests End """
diff --git a/acts/tests/google/wifi/WifiServiceApiTest.py b/acts/tests/google/wifi/WifiServiceApiTest.py
new file mode 100644
index 0000000..75d15b4
--- /dev/null
+++ b/acts/tests/google/wifi/WifiServiceApiTest.py
@@ -0,0 +1,206 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import logging
+import queue
+import sys
+import time
+
+from acts import base_test
+from acts import signals
+from acts import utils
+from acts.test_utils.wifi import wifi_constants
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+
+class WifiServiceApiTest(base_test.BaseTestClass):
+    """This class tests the API surface of WifiManager in different wifi states.
+
+       Attributes:
+       The tests in this class only require one DUT.
+       The tests in this class do not require a SIM (but it is ok if one is
+           present).
+    """
+
+
+    TEST_SSID_PREFIX = "test_config_"
+    CONFIG_ELEMENT = 'config'
+    NETWORK_ID_ELEMENT = 'network_id'
+
+    def setup_class(self):
+        """ Sets up the required dependencies from the config file and
+            configures the device for WifiService API tests.
+
+            Returns:
+            True is successfully configured the requirements for testig.
+        """
+        self.dut = self.android_devices[0]
+        # Do a simple version of init - mainly just sync the time and enable
+        # verbose logging.  We would also like to test with phones in less
+        # constrained states (or add variations where we specifically
+        # constrain).
+        utils.require_sl4a((self.dut, ))
+        utils.sync_device_time(self.dut)
+
+        # Enable verbose logging on the dut
+        self.dut.droid.wifiEnableVerboseLogging(1)
+        if self.dut.droid.wifiGetVerboseLoggingLevel() != 1:
+            raise signals.TestFailure(
+                    "Failed to enable WiFi verbose logging on the dut.")
+
+    def teardown_class(self):
+        wutils.reset_wifi(self.dut)
+
+    def on_fail(self, test_name, begin_time):
+        self.dut.take_bug_report(test_name, begin_time)
+
+    def create_and_save_wifi_network_config(self):
+        """ Create a config with random SSID and password.
+
+            Returns:
+            A tuple with the config and networkId for the newly created and saved network.
+        """
+        config_ssid = self.TEST_SSID_PREFIX + utils.rand_ascii_str(8)
+        config_password = utils.rand_ascii_str(8)
+        self.dut.log.info("creating config: %s %s", config_ssid, config_password)
+        config = {wutils.WifiEnums.SSID_KEY: config_ssid}
+        config[wutils.WifiEnums.PWD_KEY] = config_password
+
+        # Now save the config.
+        network_id = self.dut.droid.wifiAddNetwork(config)
+        self.dut.log.info("saved config: network_id = %s", network_id)
+        return {self.NETWORK_ID_ELEMENT: network_id, self.CONFIG_ELEMENT: config}
+
+    def check_network_config_saved(self, config):
+        """ Get the configured networks and check of the provided config
+            is present.  This method only checks if the SSID is the same.
+            TODO: should do a deeper check to make sure this is the
+            correct config.
+
+            Args:
+                config: WifiConfig for a network.
+
+            Returns:
+                True if the WifiConfig is present.
+        """
+        networks = self.dut.droid.wifiGetConfiguredNetworks()
+        if not networks:
+            return False
+        ssid_key = wutils.WifiEnums.SSID_KEY
+        for network in networks:
+            if config[ssid_key] == network[ssid_key]:
+                return True
+        return False
+
+    def forget_network(self, network_id):
+        """ Simple method to call wifiForgetNetwork and wait for confirmation
+            callback.  The method returns False if it was not removed.
+
+            Returns:
+                True if network was successfully deleted.
+        """
+        self.dut.log.info("deleting config: networkId = %s", network_id)
+        self.dut.droid.wifiForgetNetwork(network_id)
+        try:
+            event = self.dut.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS, 10)
+            return True
+        except queue.Empty:
+            self.dut.log.error("Failed to forget network")
+            return False
+
+
+    """ Tests Begin """
+    def test_remove_config_wifi_enabled(self):
+        """ Test if config can be deleted when wifi is enabled.
+
+            1. Enable wifi, if needed
+            2. Create and save a random config.
+            3. Confirm the config is present.
+            4. Remove the config.
+            5. Confirm the config is not listed.
+        """
+        wutils.wifi_toggle_state(self.dut, True)
+        test_network = self.create_and_save_wifi_network_config()
+        if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+            raise signals.TestFailure(
+                    "Test network not found in list of configured networks.")
+        if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
+            raise signals.TestFailure(
+                    "Test network not deleted from configured networks.")
+        if self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+            raise signals.TestFailure(
+                    "Deleted network was in configured networks list.")
+
+    def test_remove_config_wifi_disabled(self):
+        """ Test if config can be deleted when wifi is disabled.
+
+            1. Enable wifi, if needed
+            2. Create and save a random config.
+            3. Confirm the config is present.
+            4. Disable wifi.
+            5. Remove the config.
+            6. Confirm the config is not listed.
+        """
+        wutils.wifi_toggle_state(self.dut, True)
+        test_network = self.create_and_save_wifi_network_config()
+        if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+            raise signals.TestFailure(
+                    "Test network not found in list of configured networks.")
+        wutils.wifi_toggle_state(self.dut, False)
+        if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
+            raise signals.TestFailure("Failed to delete network.")
+        if self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+            raise signals.TestFailure(
+                    "Test network was found in list of configured networks.")
+
+    def test_retrieve_config_wifi_enabled(self):
+        """ Test if config can be retrieved when wifi is enabled.
+
+            1. Enable wifi
+            2. Create and save a random config
+            3. Retrieve the config
+            4. Remove the config (clean up from the test)
+        """
+        wutils.wifi_toggle_state(self.dut, True)
+        test_network = self.create_and_save_wifi_network_config()
+
+        if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+            raise signals.TestFailure(
+                    "Test network not found in list of configured networks.")
+        if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
+            raise signals.TestFailure("Failed to delete network.")
+
+    def test_retrieve_config_wifi_disabled(self):
+        """ Test if config can be retrieved when wifi is disabled.
+
+            1. Disable wifi
+            2. Create and save a random config
+            3. Retrieve the config
+            4. Remove the config (clean up from the test)
+        """
+        wutils.wifi_toggle_state(self.dut, False)
+        test_network = self.create_and_save_wifi_network_config()
+        if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+            raise signals.TestFailure(
+                    "Test network not found in list of configured networks.")
+        if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
+            raise signals.TestFailure("Failed to delete network.")
+
+    """ Tests End """
+
+
+if __name__ == "__main__":
+      pass
diff --git a/acts/tests/google/wifi/WifiSoftApTest.py b/acts/tests/google/wifi/WifiSoftApTest.py
index 02ceb3e..e38a58a 100644
--- a/acts/tests/google/wifi/WifiSoftApTest.py
+++ b/acts/tests/google/wifi/WifiSoftApTest.py
@@ -18,16 +18,16 @@
 import queue
 import time
 
-import acts.test_utils.wifi.wifi_test_utils as wutils
-
 from acts import asserts
-from acts import signals
 from acts import utils
-from acts.base_test import BaseTestClass
+from acts import base_test
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.tel import tel_defines
-from acts.test_utils.tel.tel_test_utils import is_sim_ready
+from acts.test_utils.tel import tel_test_utils as tel_utils
+from acts.test_utils.wifi import wifi_test_utils as wutils
 
-class WifiSoftApTest(BaseTestClass):
+
+class WifiSoftApTest(base_test.BaseTestClass):
 
     def setup_class(self):
         """It will setup the required dependencies from config file and configure
@@ -49,16 +49,19 @@
 
         # Enable verbose logging on the duts
         self.dut.droid.wifiEnableVerboseLogging(1)
-        msg = "Failed to enable WiFi verbose logging on the softap dut."
-        asserts.assert_true(self.dut.droid.wifiGetVerboseLoggingLevel() == 1, msg)
+        asserts.assert_equal(self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
+            "Failed to enable WiFi verbose logging on the softap dut.")
         self.dut_client.droid.wifiEnableVerboseLogging(1)
-        msg = "Failed to enable WiFi verbose logging on the client dut."
-        asserts.assert_true(self.dut_client.droid.wifiGetVerboseLoggingLevel() == 1, msg)
+        asserts.assert_equal(self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
+            "Failed to enable WiFi verbose logging on the client dut.")
 
     def teardown_class(self):
         wutils.reset_wifi(self.dut)
         wutils.reset_wifi(self.dut_client)
 
+    def on_fail(self, test_name, begin_time):
+        self.dut.take_bug_report(test_name, begin_time)
+
     """ Helper Functions """
     def verify_return_to_wifi_enabled(self):
         """Verifies that wifi is enabled
@@ -101,7 +104,7 @@
         """Create a softap config with ssid and password."""
         ap_ssid = "softap_" + utils.rand_ascii_str(8)
         ap_password = utils.rand_ascii_str(8)
-        self.log.info("softap setup: %s %s", ap_ssid, ap_password)
+        self.dut.log.info("softap setup: %s %s", ap_ssid, ap_password)
         config = {wutils.WifiEnums.SSID_KEY: ap_ssid}
         config[wutils.WifiEnums.PWD_KEY] = ap_password
         return config
@@ -118,11 +121,9 @@
         wutils.start_wifi_connection_scan(self.dut_client)
         client_scan_results = self.dut_client.droid.wifiGetScanResults()
         for result in client_scan_results:
-            self.log.debug("scan found: %s", result[wutils.WifiEnums.SSID_KEY])
-
-        asserts.assert_true(wutils.match_networks(
-                {wutils.WifiEnums.SSID_KEY: ap_ssid}, client_scan_results),
-                "Did not find SSID in scan results")
+            self.dut.log.debug("scan found: %s", result[wutils.WifiEnums.SSID_KEY])
+        wutils.assert_network_in_list({wutils.WifiEnums.SSID_KEY: ap_ssid},
+                                      client_scan_results)
 
     def check_cell_data_and_enable(self):
         """Make sure that cell data is enabled if there is a sim present.
@@ -134,13 +135,14 @@
         """
         # We do have a sim.  Make sure data is enabled so we can tether.
         if not self.dut.droid.telephonyIsDataEnabled():
-            self.log.info("need to enable data")
+            self.dut.log.info("need to enable data")
             self.dut.droid.telephonyToggleDataConnection(True)
             asserts.assert_true(self.dut.droid.telephonyIsDataEnabled(),
                                 "Failed to enable cell data for softap dut.")
 
 
     """ Tests Begin """
+    @test_tracker_info(uuid="6437727d-7db1-4f69-963e-f26a7797e47f")
     def test_switch_to_softap_mode(self):
         """Test switch to softap mode.
 
@@ -150,11 +152,11 @@
         """
         success = True
         initial_wifi_state = self.dut.droid.wifiCheckState()
-        initial_cell_state = is_sim_ready(self.log, self.dut)
-        self.log.info("current state: %s", initial_wifi_state)
-        self.log.info("is sim ready? %s", initial_cell_state)
+        initial_cell_state = tel_utils.is_sim_ready(self.log, self.dut)
+        self.dut.log.info("current state: %s", initial_wifi_state)
+        self.dut.log.info("is sim ready? %s", initial_cell_state)
         if initial_cell_state:
-            self.log.info("cell is on")
+            self.dut.log.info("cell is on")
             self.check_cell_data_and_enable()
 
         config = self.create_softap_config()
@@ -167,13 +169,13 @@
             self.confirm_softap_in_scan_results(config[wutils.WifiEnums.SSID_KEY])
             success = self.dut.droid.wifiSetApEnabled(False, None)
             asserts.assert_true(success, "turn off softap mode failed")
-            asserts.assert_true(not self.dut.droid.wifiIsApEnabled(),
-                                "SoftAp is still reported as running")
+            asserts.assert_false(self.dut.droid.wifiIsApEnabled(),
+                                 "SoftAp is still reported as running")
             curr_state = "waiting for WifiManagerApDisabled"
             event = self.dut.ed.pop_event("WifiManagerApDisabled", 5)
             if initial_wifi_state:
                 self.verify_return_to_wifi_enabled()
-            elif not self.dut.droid.wifiCheckState():
+            elif self.dut.droid.wifiCheckState():
                 # really need to verify that wifi did not come back up, and will not
                 # TODO(silberst): look at alternatives to this simple check
                 asserts.fail("Wifi was disabled before softap and now it is enabled")
@@ -185,6 +187,7 @@
             self.dut.droid.wifiStopTrackingTetherStateChange()
             self.dut_client.droid.wifiStopTrackingStateChange()
 
+    @test_tracker_info(uuid="495f1252-e440-461c-87a7-2c45f369e129")
     def test_check_wifi_tethering_supported(self):
         """Test check for wifi tethering support.
 
@@ -192,17 +195,17 @@
         """
         # TODO(silberst): wifiIsPortableHotspotSupported() is currently failing.
         # Remove the extra check and logging when b/30800811 is resolved
-        portable_hotspot_supported = self.dut.droid.wifiIsPortableHotspotSupported()
+        hotspot_supported = self.dut.droid.wifiIsPortableHotspotSupported()
         tethering_supported = self.dut.droid.connectivityIsTetheringSupported()
-        if not portable_hotspot_supported:
-            self.log.info("DUT should support wifi tethering but is reporting false.")
-        if not tethering_supported:
-            self.log.info("DUT should also support wifi tethering when called from ConnectivityManager")
-        asserts.assert_true(self.dut.droid.wifiIsPortableHotspotSupported(),
+        self.log.info(
+            "IsPortableHotspotSupported: %s, IsTetheringSupported %s." % (
+            hotspot_supported, tethering_supported))
+        asserts.assert_true(hotspot_supported,
                             "DUT should support wifi tethering but is reporting false.")
-        asserts.assert_true(self.dut.droid.connectivityIsTetheringSupported(),
+        asserts.assert_true(tethering_supported,
                             "DUT should also support wifi tethering when called from ConnectivityManager")
 
+    @test_tracker_info(uuid="09c19c35-c708-48a5-939b-ac2bbb403d54")
     def test_full_tether_startup(self):
         """Test full startup of wifi tethering
 
@@ -212,30 +215,27 @@
         4. Shutdown wifi tethering.
         5. verify back to previous mode.
         """
-        success = True
         initial_wifi_state = self.dut.droid.wifiCheckState()
-        initial_cell_state = is_sim_ready(self.log, self.dut)
-        self.log.info("current state: %s", initial_wifi_state)
-        self.log.info("is sim ready? %s", initial_cell_state)
-
+        initial_cell_state = tel_utils.is_sim_ready(self.log, self.dut)
+        self.dut.log.info("current state: %s", initial_wifi_state)
+        self.dut.log.info("is sim ready? %s", initial_cell_state)
         if initial_cell_state:
             self.check_cell_data_and_enable()
-
         config = self.create_softap_config()
-
-        success = wutils.start_wifi_tethering(self.dut,
-                                              config[wutils.WifiEnums.SSID_KEY],
-                                              config[wutils.WifiEnums.PWD_KEY])
-        asserts.assert_true(success, "call to start_wifi_tethering returned false.  Check config")
+        wutils.start_wifi_tethering(self.dut,
+                                    config[wutils.WifiEnums.SSID_KEY],
+                                    config[wutils.WifiEnums.PWD_KEY])
         self.confirm_softap_in_scan_results(config[wutils.WifiEnums.SSID_KEY])
         wutils.stop_wifi_tethering(self.dut)
-        asserts.assert_true(not self.dut.droid.wifiIsApEnabled(),
-                            "SoftAp is still reported as running")
+        asserts.assert_false(self.dut.droid.wifiIsApEnabled(),
+                             "SoftAp is still reported as running")
         if initial_wifi_state:
             self.verify_return_to_wifi_enabled()
-        elif not self.dut.droid.wifiCheckState():
+        elif self.dut.droid.wifiCheckState():
             asserts.fail("Wifi was disabled before softap and now it is enabled")
 
     """ Tests End """
+
+
 if __name__ == "__main__":
     pass
diff --git a/acts/tests/google/wifi/WifiTeleCoexTest.py b/acts/tests/google/wifi/WifiTeleCoexTest.py
new file mode 100644
index 0000000..6ec1ef8
--- /dev/null
+++ b/acts/tests/google/wifi/WifiTeleCoexTest.py
@@ -0,0 +1,289 @@
+#!/usr/bin/env python3.4
+
+import queue
+import time
+
+import acts.base_test
+import acts.test_utils.wifi.wifi_test_utils as wifi_utils
+import acts.test_utils.tel.tel_test_utils as tele_utils
+import acts.utils
+
+from acts import asserts
+from acts import signals
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+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_short_seq
+
+WifiEnums = wifi_utils.WifiEnums
+
+ATTENUATORS = "attenuators"
+WIFI_SSID = "wifi_network_ssid"
+WIFI_PWD = "wifi_network_pass"
+STRESS_COUNT = "stress_iteration"
+
+class WifiTeleCoexTest(TelephonyBaseTest):
+    """Tests for WiFi, Celular Co-existance."""
+
+
+    def __init__(self, controllers):
+        TelephonyBaseTest.__init__(self, controllers)
+
+
+    def setup_class(self):
+        TelephonyBaseTest.setup_class(self)
+        self.dut = self.android_devices[0]
+        wifi_utils.wifi_test_device_init(self.dut)
+        # Set attenuation to 0 on all channels.
+        if getattr(self, ATTENUATORS, []):
+            for a in self.attenuators:
+                a.set_atten(0)
+        self.ads = self.android_devices
+        self.dut = self.android_devices[0]
+        self.wifi_network_ssid = self.user_params.get(WIFI_SSID)
+        self.wifi_network_pass = self.user_params.get(WIFI_PWD)
+        self.network = { WifiEnums.SSID_KEY : self.wifi_network_ssid,
+                         WifiEnums.PWD_KEY : self.wifi_network_pass
+                       }
+        self.stress_count = self.user_params.get(STRESS_COUNT)
+
+
+    def setup_test(self):
+        wifi_utils.wifi_toggle_state(self.dut, True)
+
+
+    def teardown_test(self):
+        wifi_utils.reset_wifi(self.dut)
+
+
+    """Helper Functions"""
+
+
+    def connect_to_wifi(self, ad, network):
+        """Connection logic for open and psk wifi networks.
+
+        Args:
+            ad: Android device object.
+            network: A JSON dict of the WiFi network configuration.
+
+        """
+        ad.ed.clear_all_events()
+        wifi_utils.start_wifi_connection_scan(ad)
+        scan_results = ad.droid.wifiGetScanResults()
+        wifi_utils.assert_network_in_list({WifiEnums.SSID_KEY:
+                self.wifi_network_ssid}, scan_results)
+        wifi_utils.wifi_connect(ad, network)
+        self.log.debug("Connected to %s network on %s device" % (
+                network[WifiEnums.SSID_KEY], ad.serial))
+
+
+    def stress_toggle_wifi(self, stress_count):
+        """Toggle WiFi in a loop.
+
+        Args:
+            stress_count: Number of times to toggle WiFi OFF and ON.
+
+        """
+        for count in range(stress_count):
+            self.log.debug("stress_toggle_wifi: Iteration %d" % count)
+            wifi_utils.toggle_wifi_off_and_on(self.dut)
+
+        if not self.dut.droid.wifiGetisWifiEnabled():
+            raise signals.TestFailure("WiFi did not turn on after toggling it"
+                                      " %d times" % self.stress_count)
+
+
+    def stress_toggle_airplane(self, stress_count):
+        """Toggle Airplane mode in a loop.
+
+        Args:
+            stress_count: Number of times to toggle Airplane mode OFF and ON.
+
+        """
+        for count in range(stress_count):
+            self.log.debug("stress_toggle_airplane: Iteration %d" % count)
+            wifi_utils.toggle_airplane_mode_on_and_off(self.dut)
+
+        if not self.dut.droid.wifiGetisWifiEnabled():
+            raise signals.TestFailure("WiFi did not turn on after toggling it"
+                                      " %d times" % self.stress_count)
+
+
+    def stress_toggle_airplane_and_wifi(self, stress_count):
+        """Toggle Airplane and WiFi modes in a loop.
+
+        Args:
+            stress_count: Number of times to perform Airplane mode ON, WiFi ON,
+                          Airplane mode OFF, in a sequence.
+
+        """
+        self.log.debug("Toggling Airplane mode ON")
+        asserts.assert_true(
+            acts.utils.force_airplane_mode(self.dut, True),
+            "Can not turn on airplane mode on: %s" % self.dut.serial)
+        self.log.debug("Toggling wifi ON")
+        wifi_utils.wifi_toggle_state(self.dut, True)
+        asserts.assert_true(
+            acts.utils.force_airplane_mode(self.dut, False),
+            "Can not turn on airplane mode on: %s" % self.dut.serial)
+
+        if not self.dut.droid.wifiGetisWifiEnabled():
+            raise signals.TestFailure("WiFi did not turn on after toggling it"
+                                      " %d times" % self.stress_count)
+
+
+    def setup_cellular_voice_calling(self):
+        """Setup phone for voice general calling and make sure phone is
+           attached to voice."""
+        # Make sure Phone A and B are attached to voice network.
+        for ad in self.ads:
+            if not phone_setup_voice_general(self.log, ad):
+                raise signals.TestFailure("Phone failed to setup for voice"
+                                          " calling serial:%s" % ad.serial)
+        self.log.debug("Finished setting up phones for voice calling")
+
+
+    def validate_cellular_and_wifi(self):
+        """Validate WiFi, make some cellular calls.
+
+        Steps:
+            1. Check if device is still connected to the WiFi network.
+            2. If WiFi looks good, check if deivce is attached to voice.
+            3. Make a short sequence voice call between Phone A and B.
+
+        """
+        wifi_info = self.dut.droid.wifiGetConnectionInfo()
+        if wifi_info[WifiEnums.SSID_KEY] != self.wifi_network_ssid:
+            raise signals.TestFailure("Phone failed to connect to %s network on"
+                                      " %s" % (self.wifi_network_ssid,
+                                      self.dut.serial))
+
+        # Make short call sequence between Phone A and Phone B.
+        two_phone_call_short_seq(self.log, self.ads[0], None, None, self.ads[1],
+                                 None, None)
+
+    """Tests"""
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_toggle_wifi_call(self):
+        """Test to toggle WiFi and then perform WiFi connection and
+           cellular calls.
+
+        Steps:
+            1. Attach device to voice subscription network.
+            2. Connect to a WiFi network.
+            3. Toggle WiFi OFF and ON.
+            4. Verify device auto-connects to the WiFi network.
+            5. Verify device is attached to voice network.
+            6. Make short sequence voice calls.
+
+        """
+        self.setup_cellular_voice_calling()
+        self.connect_to_wifi(self.dut, self.network)
+        wifi_utils.toggle_wifi_off_and_on(self.dut)
+        self.validate_cellular_and_wifi()
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_toggle_airplane_call(self):
+        """Test to toggle Airplane mode and perform WiFi connection and
+           cellular calls.
+
+        Steps:
+            1. Attach device to voice subscription network.
+            2. Connect to a WiFi network.
+            3. Toggle Airplane mode OFF and ON.
+            4. Verify device auto-connects to the WiFi network.
+            5. Verify device is attached to voice network.
+            6. Make short sequence voice calls.
+
+        """
+        self.setup_cellular_voice_calling()
+        self.connect_to_wifi(self.dut, self.network)
+        wifi_utils.toggle_airplane_mode_on_and_off(self.dut)
+        self.validate_cellular_and_wifi()
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_toggle_airplane_and_wifi_call(self):
+        """Test to toggle WiFi in a loop and perform WiFi connection and
+           cellular calls.
+
+        Steps:
+            1. Attach device to voice subscription network.
+            2. Connect to a WiFi network.
+            3. Toggle Airplane mode ON.
+            4. Turn WiFi ON.
+            5. Toggle Airplane mode OFF.
+            3. Verify device auto-connects to the WiFi network.
+            4. Verify device is attached to voice network.
+            5. Make short sequence voice calls.
+
+        """
+        self.setup_cellular_voice_calling()
+        self.connect_to_wifi(self.dut, self.network)
+        self.stress_toggle_airplane_and_wifi(1)
+        self.validate_cellular_and_wifi()
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_stress_toggle_wifi_call(self):
+        """Stress test to toggle WiFi in a loop, then perform WiFi connection
+           and cellular calls.
+
+        Steps:
+            1. Attach device to voice subscription network.
+            2. Connect to a WiFi network.
+            3. Toggle WiFi OFF and ON in a loop.
+            4. Verify device auto-connects to the WiFi network.
+            5. Verify device is attached to voice network.
+            6. Make short sequence voice calls.
+
+        """
+        self.setup_cellular_voice_calling()
+        self.connect_to_wifi(self.dut, self.network)
+        self.stress_toggle_wifi(self.stress_count)
+        self.validate_cellular_and_wifi()
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_stress_toggle_airplane_call(self):
+        """Stress test to toggle Airplane mode in a loop, then perform WiFi and
+           cellular calls.
+
+        Steps:
+            1. Attach device to voice subscription network.
+            2. Connect to a WiFi network.
+            3. Toggle Airplane mode OFF and ON in a loop.
+            4. Verify device auto-connects to the WiFi network.
+            5. Verify device is attached to voice network.
+            6. Make short sequence voice calls.
+
+        """
+        self.setup_cellular_voice_calling()
+        self.connect_to_wifi(self.dut, self.network)
+        self.stress_toggle_airplane(self.stress_count)
+        self.validate_cellular_and_wifi()
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_stress_toggle_airplane_and_wifi_call(self):
+        """Stress test to toggle Airplane and WiFi mode in a loop, then perform
+           WiFi connection and cellular calls.
+
+        Steps:
+            1. Attach device to voice subscription network.
+            2. Connect to a WiFi network.
+            3. Toggle Airplane mode ON.
+            4. Turn WiFi ON.
+            5. Toggle Airplane mode OFF.
+            6. Repeat 3, 4 & 5, in a loop.
+            7. Verify device auto-connects to the WiFi network.
+            8. Verify device is attached to voice network.
+            9. Make short sequence voice calls.
+
+        """
+        self.setup_cellular_voice_calling()
+        self.connect_to_wifi(self.dut, self.network)
+        stress_toggle_airplane_and_wifi(self.stress_count)
+        self.validate_cellular_and_wifi()
diff --git a/acts/tests/google/wifi/WifiTetheringTest.py b/acts/tests/google/wifi/WifiTetheringTest.py
new file mode 100644
index 0000000..c1e0b23
--- /dev/null
+++ b/acts/tests/google/wifi/WifiTetheringTest.py
@@ -0,0 +1,180 @@
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import logging
+import time
+import socket
+
+from acts import asserts
+from acts import base_test
+from acts import test_runner
+from acts.controllers import adb
+from acts.test_utils.tel import tel_data_utils
+from acts.test_utils.tel import tel_test_utils
+from acts.test_utils.tel import tel_defines
+from acts.test_utils.wifi import wifi_test_utils
+
+
+class WifiTetheringTest(base_test.BaseTestClass):
+    """ Tests for Wifi Tethering """
+
+    def setup_class(self):
+        """ Setup devices for tethering and unpack params """
+        self.hotspot_device, self.tethered_device = self.android_devices[:2]
+        req_params = ("ssid", "password", "url")
+        self.unpack_userparams(req_params)
+        asserts.assert_true(
+            tel_data_utils.toggle_airplane_mode(self.log, self.hotspot_device, False),
+            "Could not disable airplane mode")
+        wifi_test_utils.wifi_toggle_state(self.hotspot_device, False)
+        self.hotspot_device.droid.telephonyToggleDataConnection(True)
+        tel_data_utils.wait_for_cell_data_connection(self.log, self.hotspot_device, True)
+        asserts.assert_true(
+            tel_data_utils.verify_http_connection(self.log, self.hotspot_device),
+            "HTTP verification failed on cell data connection")
+        asserts.assert_true(
+            self.hotspot_device.droid.connectivityIsTetheringSupported(),
+            "Tethering is not supported for the provider")
+        wifi_test_utils.wifi_test_device_init(self.tethered_device)
+        self.tethered_device.droid.telephonyToggleDataConnection(False)
+
+    def teardown_class(self):
+        """ Reset devices """
+        wifi_test_utils.wifi_toggle_state(self.hotspot_device, True)
+        self.hotspot_device.droid.telephonyToggleDataConnection(True)
+        self.tethered_device.droid.telephonyToggleDataConnection(True)
+
+    """ Helper functions """
+
+    def _is_ipaddress_ipv6(self,ip_address):
+        """ Verify if the given string is a valid IPv6 address
+
+        Args:
+            1. string which contains the IP address
+
+        Returns:
+            True: if valid ipv6 address
+            False: if not
+        """
+        try:
+            socket.inet_pton(socket.AF_INET6, ip_address)
+            return True
+        except socket.error:
+            return False
+
+    def _supports_ipv6_tethering(self, dut):
+        """ Check if provider supports IPv6 tethering.
+            Currently, only Verizon supports IPv6 tethering
+
+        Returns:
+            True: if provider supports IPv6 tethering
+            False: if not
+        """
+        # Currently only Verizon support IPv6 tethering
+        carrier_supports_tethering = ["vzw"]
+        operator = tel_test_utils.get_operator_name(self.log, dut)
+        return operator in carrier_supports_tethering
+
+    def _find_ipv6_default_route(self, dut):
+        """ Checks if IPv6 default route exists in the link properites
+
+        Returns:
+            True: if default route found
+            False: if not
+        """
+        default_route_substr = "::/0 -> "
+        link_properties = dut.droid.connectivityGetActiveLinkProperties()
+        return link_properties and default_route_substr in link_properties
+
+    def _verify_ipv6_tethering(self,dut):
+        """ Verify IPv6 tethering """
+        http_response = dut.droid.httpRequestString(self.url)
+        link_properties = dut.droid.connectivityGetActiveLinkProperties()
+        self.log.info("IP address %s " % http_response)
+        if dut==self.hotspot_device or self._supports_ipv6_tethering(self.hotspot_device):
+            asserts.assert_true(self._is_ipaddress_ipv6(http_response),
+                                "The http response did not return IPv6 address")
+            asserts.assert_true(link_properties and http_response in link_properties,
+                                "Could not find IPv6 address in link properties")
+            asserts.assert_true(self._find_ipv6_default_route(dut),
+                                "Could not find IPv6 default route in link properties")
+        else:
+            asserts.assert_true(not self._find_ipv6_default_route(dut),
+                                "Found IPv6 default route in link properties")
+
+
+    """ Test Cases """
+
+    def test_ipv6_tethering(self):
+        """ IPv6 tethering test
+
+        Steps:
+            1. Start wifi tethering on provider
+            2. Client connects to wifi tethering SSID
+            3. Verify IPv6 address on the client's link properties
+            4. Verify ping on client using ping6 which should pass
+            5. Disable mobile data on provider and verify that link properties
+               does not have IPv6 address and default route
+        """
+        # Start wifi tethering on the hotspot device
+        self.log.info("Start tethering on provider: {}"
+                      .format(self.hotspot_device.serial))
+        wifi_test_utils.start_wifi_tethering(self.hotspot_device,
+                                             self.ssid,
+                                             self.password)
+        time.sleep(tel_defines.WAIT_TIME_ANDROID_STATE_SETTLING)
+        asserts.assert_true(
+            tel_data_utils.verify_http_connection(self.log,self.hotspot_device),
+            "Could not verify http connection on the provider")
+
+        # Verify link properties on hotspot device
+        self.log.info("Check IPv6 properties on the hotspot device")
+        self._verify_ipv6_tethering(self.hotspot_device)
+
+        # Connect the client to the SSID
+        asserts.assert_true(
+            tel_test_utils.WifiUtils.wifi_connect(self.log,
+                                                  self.tethered_device,
+                                                  self.ssid,
+                                                  self.password),
+            "Unable to connect to the hotspot SSID")
+
+        # Need to wait atleast 2 seconds for IPv6 address to
+        # show up in the link properties
+        time.sleep(2)
+
+        # Verify link properties on tethered device
+        self.log.info("Check IPv6 properties on the tethered device")
+        self._verify_ipv6_tethering(self.tethered_device)
+
+        # Verify ping6 on tethered device
+        ping_result = self.tethered_device.droid.pingHost("www.google.com",
+                                                          5,
+                                                          "ping6")
+        if self._supports_ipv6_tethering(self.hotspot_device):
+            asserts.assert_true(ping_result, "Ping6 failed on the client")
+        else:
+            asserts.assert_true(not ping_result, "Ping6 failed as expected")
+
+        # Disable mobile data on hotspot device
+        # and verify the link properties on tethered device
+        self.log.info("Disabling mobile data on hotspot device")
+        self.hotspot_device.droid.telephonyToggleDataConnection(False)
+        asserts.assert_equal(self.hotspot_device.droid.telephonyGetDataConnectionState(),
+                             tel_defines.DATA_STATE_CONNECTED,
+                             "Could not disable cell data")
+        time.sleep(2) # wait until the IPv6 is removed from link properties
+        asserts.assert_true(not self._find_ipv6_default_route(self.tethered_device),
+                            "Found IPv6 default route in link properties - Data off")
diff --git a/acts/tests/sample/ConfigurableAccessPointTest.py b/acts/tests/sample/ConfigurableAccessPointTest.py
new file mode 100644
index 0000000..ccef716
--- /dev/null
+++ b/acts/tests/sample/ConfigurableAccessPointTest.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+
+import logging
+
+from acts import base_test
+from acts.controllers.ap_lib import hostapd_config
+
+
+class ConfigurableAccessPointTest(base_test.BaseTestClass):
+    """ Demonstrates example usage of a configurable access point."""
+
+    def setup_class(self):
+        self.ap = self.access_points[0]
+
+    def setup_test(self):
+        self.ap.stop_all_aps()
+
+    def teardown_test(self):
+        self.ap.stop_all_aps()
+
+    def test_setup_access_point(self):
+        config = hostapd_config.HostapdConfig(
+            channel=6,
+            ssid='ImagineYourNetworkHere')
+        self.ap.start_ap(config)
+
+        logging.info('Your test logic should go here!')
diff --git a/tools/yapf_checker.py b/tools/yapf_checker.py
new file mode 100755
index 0000000..c9bf2c8
--- /dev/null
+++ b/tools/yapf_checker.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2016 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+import logging
+import os
+import sys
+
+from acts.libs.proc import job
+
+COMMIT_ID_ENV_KEY = 'PREUPLOAD_COMMIT'
+REPO_PATH_KEY = 'REPO_PATH'
+GIT_COMMAND = 'git diff-tree --no-commit-id --name-only -r %s'
+YAPF_COMMAND = 'yapf -d -p %s'
+YAPF_OLD_COMMAND = 'yapf -d %s'
+YAPF_INPLACE_FORMAT = 'yapf -p -i %s'
+YAPF_INPLACE_FORMAT_OLD = 'yapf -i %s'
+
+
+def main(argv):
+    if COMMIT_ID_ENV_KEY not in os.environ:
+        logging.error('Missing commit id in environment.')
+        exit(1)
+
+    if REPO_PATH_KEY not in os.environ:
+        logging.error('Missing repo path in environment.')
+        exit(1)
+
+    commit_id = os.environ[COMMIT_ID_ENV_KEY]
+    full_git_command = GIT_COMMAND % commit_id
+
+    files = job.run(full_git_command).stdout.splitlines()
+    full_files = [os.path.abspath(f) for f in files if f.endswith('.py')]
+    if not full_files:
+        return
+
+    files_param_string = ' '.join(full_files)
+
+    result = job.run(YAPF_COMMAND % files_param_string, ignore_status=True)
+    yapf_inplace_format = YAPF_INPLACE_FORMAT
+    if result.exit_status:
+        logging.warning('Using an old version of yapf. Please update soon.')
+        result = job.run(YAPF_OLD_COMMAND % files_param_string)
+        yapf_inplace_format = YAPF_INPLACE_FORMAT_OLD
+
+    if result.stdout:
+        logging.error(result.stdout)
+        logging.error('INVALID FORMATTING.')
+        logging.error('Consider run:')
+        logging.error(yapf_inplace_format % files_param_string)
+        exit(1)
+
+
+if __name__ == '__main__':
+    main(sys.argv[1:])