Merge "le advertisement address type settings api SL4A tests"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/acts/framework/acts/controllers/OWNERS b/acts/framework/acts/controllers/OWNERS
index 64922ab..c4ba954 100644
--- a/acts/framework/acts/controllers/OWNERS
+++ b/acts/framework/acts/controllers/OWNERS
@@ -1,5 +1,5 @@
 per-file asus_axe11000_ap.py = martschneider@google.com
-per-file fuchsia_device.py = chcl@google.com, dhobsd@google.com, haydennix@google.com, jmbrenna@google.com, mnck@google.com, silberst@google.com, tturney@google.com
+per-file fuchsia_device.py = chcl@google.com, dhobsd@google.com, haydennix@google.com, jmbrenna@google.com, mnck@google.com, silberst@google.com, tturney@google.com, sbalana@google.com
 per-file bluetooth_pts_device.py = tturney@google.com
 per-file cellular_simulator.py = iguarna@google.com, chaoyangf@google.com, codycaldwell@google.com, yixiang@google.com
 per-file openwrt_ap.py = jerrypcchen@google.com, martschneider@google.com, gmoturu@google.com, sishichen@google.com
diff --git a/acts/framework/acts/controllers/access_point.py b/acts/framework/acts/controllers/access_point.py
index 21e26bb..a2f2840 100755
--- a/acts/framework/acts/controllers/access_point.py
+++ b/acts/framework/acts/controllers/access_point.py
@@ -185,15 +185,14 @@
         ssh_settings: The ssh settings being used by the ssh connection.
         dhcp_settings: The dhcp server settings being used.
     """
-
     def __init__(self, configs):
         """
         Args:
             configs: configs for the access point from config file.
         """
         self.ssh_settings = settings.from_config(configs['ssh_config'])
-        self.log = logger.create_logger(lambda msg: '[Access Point|%s] %s' % (
-            self.ssh_settings.hostname, msg))
+        self.log = logger.create_logger(lambda msg: '[Access Point|%s] %s' %
+                                        (self.ssh_settings.hostname, msg))
         self.device_pdu_config = configs.get('PduDevice', None)
         self.identifier = self.ssh_settings.hostname
 
@@ -429,11 +428,25 @@
     def get_dhcp_logs(self):
         """Get DHCP logs for this AP object.
 
-        This allows consumers of the access point objects validate DHCP
+        This allows consumers of the access point objects to validate DHCP
         behavior.
         """
         return self._dhcp.get_logs()
 
+    def get_hostapd_logs(self):
+        """Get hostapd logs for all interfaces on AP object.
+
+        This allows consumers of the access point objects to validate hostapd
+        behavior.
+
+        Returns: A dict with {interface: log} from hostapd instances.
+        """
+        hostapd_logs = dict()
+        for identifier in self._aps:
+            hostapd_logs[identifier] = self._aps.get(
+                identifier).hostapd.pull_logs()
+        return hostapd_logs
+
     def start_nat(self):
         """Start NAT on the AP.
 
diff --git a/acts/framework/acts/controllers/android_device.py b/acts/framework/acts/controllers/android_device.py
index 7060b86..793cb23 100755
--- a/acts/framework/acts/controllers/android_device.py
+++ b/acts/framework/acts/controllers/android_device.py
@@ -48,6 +48,10 @@
 ACTS_CONTROLLER_REFERENCE_NAME = "android_devices"
 
 ANDROID_DEVICE_PICK_ALL_TOKEN = "*"
+# Key name for SL4A extra params in config file
+ANDROID_DEVICE_SL4A_CLIENT_PORT_KEY = "sl4a_client_port"
+ANDROID_DEVICE_SL4A_FORWARDED_PORT_KEY = "sl4a_forwarded_port"
+ANDROID_DEVICE_SL4A_SERVER_PORT_KEY = "sl4a_server_port"
 # Key name for adb logcat extra params in config file.
 ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param"
 ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
@@ -235,12 +239,41 @@
             raise errors.AndroidDeviceConfigError(
                 "Required value 'serial' is missing in AndroidDevice config %s."
                 % c)
+        client_port = 0
+        if ANDROID_DEVICE_SL4A_CLIENT_PORT_KEY in c:
+            try:
+                client_port = int(c.pop(ANDROID_DEVICE_SL4A_CLIENT_PORT_KEY))
+            except ValueError:
+                raise errors.AndroidDeviceConfigError(
+                    "'%s' is not a valid number for config %s" %
+                    (ANDROID_DEVICE_SL4A_CLIENT_PORT_KEY, c))
+        server_port = None
+        if ANDROID_DEVICE_SL4A_SERVER_PORT_KEY in c:
+            try:
+                server_port = int(c.pop(ANDROID_DEVICE_SL4A_SERVER_PORT_KEY))
+            except ValueError:
+                raise errors.AndroidDeviceConfigError(
+                    "'%s' is not a valid number for config %s" %
+                    (ANDROID_DEVICE_SL4A_SERVER_PORT_KEY, c))
+        forwarded_port = 0
+        if ANDROID_DEVICE_SL4A_FORWARDED_PORT_KEY in c:
+            try:
+                forwarded_port = int(
+                    c.pop(ANDROID_DEVICE_SL4A_FORWARDED_PORT_KEY))
+            except ValueError:
+                raise errors.AndroidDeviceConfigError(
+                    "'%s' is not a valid number for config %s" %
+                    (ANDROID_DEVICE_SL4A_FORWARDED_PORT_KEY, 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 = AndroidDevice(serial,
+                           ssh_connection=ssh_connection,
+                           client_port=client_port,
+                           forwarded_port=forwarded_port,
+                           server_port=server_port)
         ad.load_config(c)
         results.append(ad)
     return results
@@ -360,14 +393,27 @@
         adb: An AdbProxy object used for interacting with the device via adb.
         fastboot: A FastbootProxy object used for interacting with the device
                   via fastboot.
+        client_port: Preferred client port number on the PC host side for SL4A
+        forwarded_port: Preferred server port number forwarded from Android
+                        to the host PC via adb for SL4A connections
+        server_port: Preferred server port used by SL4A on Android device
+
     """
 
-    def __init__(self, serial='', ssh_connection=None):
+    def __init__(self,
+                 serial='',
+                 ssh_connection=None,
+                 client_port=0,
+                 forwarded_port=0,
+                 server_port=None):
         self.serial = 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_dir = 'AndroidDevice%s' % serial
         self.log_path = os.path.join(log_path_base, self.log_dir)
+        self.client_port = client_port
+        self.forwarded_port = forwarded_port
+        self.server_port = server_port
         self.log = tracelogger.TraceLogger(
             AndroidDeviceLoggerAdapter(logging.getLogger(),
                                        {'serial': serial}))
@@ -377,8 +423,8 @@
         self.register_service(services.Sl4aService(self))
         self.adb_logcat_process = None
         self.adb = adb.AdbProxy(serial, ssh_connection=ssh_connection)
-        self.fastboot = fastboot.FastbootProxy(
-            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
@@ -402,6 +448,34 @@
         if self._ssh_connection:
             self._ssh_connection.close()
 
+    def recreate_services(self, serial):
+        """Clean up the AndroidDevice object and re-create adb/sl4a services.
+
+        Unregister the existing services and re-create adb and sl4a services,
+        call this method when the connection break after certain API call
+        (e.g., enable USB tethering by #startTethering)
+
+        Args:
+            serial: the serial number of the AndroidDevice
+        """
+        # Clean the old services
+        for service in self._services:
+            service.unregister()
+        self._services.clear()
+        if self._ssh_connection:
+            self._ssh_connection.close()
+        self._sl4a_manager.stop_service()
+
+        # Wait for old services to stop
+        time.sleep(5)
+
+        # Re-create the new adb and sl4a services
+        self.register_service(services.AdbLogcatService(self))
+        self.register_service(services.Sl4aService(self))
+        self.adb.wait_for_device()
+        self.terminate_all_sessions()
+        self.start_services()
+
     def register_service(self, service):
         """Registers the service on the device. """
         service.register()
@@ -428,8 +502,8 @@
 
         Stop adb logcat and terminate sl4a sessions if exist.
         """
-        event_bus.post(
-            android_events.AndroidStopServicesEvent(self), ignore_errors=True)
+        event_bus.post(android_events.AndroidStopServicesEvent(self),
+                       ignore_errors=True)
 
     def is_connected(self):
         out = self.adb.devices()
@@ -654,7 +728,13 @@
             >>> ad = AndroidDevice()
             >>> droid, ed = ad.get_droid()
         """
-        session = self._sl4a_manager.create_session()
+        self.log.debug(
+            "Creating RPC client_port={}, forwarded_port={}, server_port={}".
+            format(self.client_port, self.forwarded_port, self.server_port))
+        session = self._sl4a_manager.create_session(
+            client_port=self.client_port,
+            forwarded_port=self.forwarded_port,
+            server_port=self.server_port)
         droid = session.rpc_client
         if handle_event:
             ed = session.get_event_dispatcher()
@@ -674,9 +754,8 @@
         """
         for cmd in ("ps -A", "ps"):
             try:
-                out = self.adb.shell(
-                    '%s | grep "S %s"' % (cmd, package_name),
-                    ignore_status=True)
+                out = self.adb.shell('%s | grep "S %s"' % (cmd, package_name),
+                                     ignore_status=True)
                 if package_name not in out:
                     continue
                 try:
@@ -768,10 +847,10 @@
         return adb_excerpt_path
 
     def search_logcat(self,
-                    matching_string,
-                    begin_time=None,
-                    end_time=None,
-                    logcat_path=None):
+                      matching_string,
+                      begin_time=None,
+                      end_time=None,
+                      logcat_path=None):
         """Search logcat message with given string.
 
         Args:
@@ -801,13 +880,12 @@
         """
         if not logcat_path:
             logcat_path = os.path.join(self.device_log_path,
-                                    'adblog_%s_debug.txt' % self.serial)
+                                       'adblog_%s_debug.txt' % self.serial)
         if not os.path.exists(logcat_path):
             self.log.warning("Logcat file %s does not exist." % logcat_path)
             return
-        output = job.run(
-            "grep '%s' %s" % (matching_string, logcat_path),
-            ignore_status=True)
+        output = job.run("grep '%s' %s" % (matching_string, logcat_path),
+                         ignore_status=True)
         if not output.stdout or output.exit_status != 0:
             return []
         if begin_time:
@@ -815,13 +893,13 @@
                 log_begin_time = acts_logger.epoch_to_log_line_timestamp(
                     begin_time)
                 begin_time = datetime.strptime(log_begin_time,
-                                            "%Y-%m-%d %H:%M:%S.%f")
+                                               "%Y-%m-%d %H:%M:%S.%f")
         if end_time:
             if not isinstance(end_time, datetime):
                 log_end_time = acts_logger.epoch_to_log_line_timestamp(
                     end_time)
                 end_time = datetime.strptime(log_end_time,
-                                            "%Y-%m-%d %H:%M:%S.%f")
+                                             "%Y-%m-%d %H:%M:%S.%f")
         result = []
         logs = re.findall(r'(\S+\s\S+)(.*)', output.stdout)
         for log in logs:
@@ -893,8 +971,8 @@
         Returns:
         Linux UID for the apk.
         """
-        output = self.adb.shell(
-            "dumpsys package %s | grep userId=" % apk_name, ignore_status=True)
+        output = self.adb.shell("dumpsys package %s | grep userId=" % apk_name,
+                                ignore_status=True)
         result = re.search(r"userId=(\d+)", output)
         if result:
             return result.group(1)
@@ -960,9 +1038,8 @@
         """
         for cmd in ("ps -A", "ps"):
             try:
-                out = self.adb.shell(
-                    '%s | grep "S %s"' % (cmd, package_name),
-                    ignore_status=True)
+                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
@@ -987,8 +1064,8 @@
         True if package is installed. False otherwise.
         """
         try:
-            self.adb.shell(
-                'am force-stop %s' % package_name, ignore_status=True)
+            self.adb.shell('am force-stop %s' % package_name,
+                           ignore_status=True)
         except Exception as e:
             self.log.warning("Fail to stop package %s: %s", package_name, e)
 
@@ -1029,8 +1106,8 @@
             br_out_path = out.split(':')[1].strip().split()[0]
             self.adb.pull("%s %s" % (br_out_path, full_out_path))
         else:
-            self.adb.bugreport(
-                " > {}".format(full_out_path), timeout=BUG_REPORT_TIMEOUT)
+            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)
         self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT)
@@ -1092,10 +1169,10 @@
         if not host_path:
             host_path = self.log_path
         for device_path in device_paths:
-            self.log.info(
-                'Pull from device: %s -> %s' % (device_path, host_path))
-            self.adb.pull(
-                "%s %s" % (device_path, host_path), timeout=PULL_TIMEOUT)
+            self.log.info('Pull from device: %s -> %s' %
+                          (device_path, host_path))
+            self.adb.pull("%s %s" % (device_path, host_path),
+                          timeout=PULL_TIMEOUT)
 
     def check_crash_report(self,
                            test_name=None,
@@ -1110,10 +1187,9 @@
             except Exception as e:
                 self.log.debug("received exception %s", e)
                 continue
-            crashes = self.get_file_names(
-                crash_path,
-                skip_files=CRASH_REPORT_SKIPS,
-                begin_time=begin_time)
+            crashes = self.get_file_names(crash_path,
+                                          skip_files=CRASH_REPORT_SKIPS,
+                                          begin_time=begin_time)
             if crash_path == "/data/tombstones/" and crashes:
                 tombstones = crashes[:]
                 for tombstone in tombstones:
@@ -1136,8 +1212,9 @@
         # Sleep 10 seconds for the buffered log to be written in qxdm log file
         time.sleep(10)
         log_path = getattr(self, "qxdm_log_path", DEFAULT_QXDM_LOG_PATH)
-        qxdm_logs = self.get_file_names(
-            log_path, begin_time=begin_time, match_string="*.qmdl")
+        qxdm_logs = self.get_file_names(log_path,
+                                        begin_time=begin_time,
+                                        match_string="*.qmdl")
         if qxdm_logs:
             qxdm_log_path = os.path.join(self.device_log_path,
                                          "QXDM_%s" % self.serial)
@@ -1146,10 +1223,9 @@
             self.log.info("Pull QXDM Log %s to %s", qxdm_logs, qxdm_log_path)
             self.pull_files(qxdm_logs, qxdm_log_path)
 
-            self.adb.pull(
-                "/firmware/image/qdsp6m.qdb %s" % qxdm_log_path,
-                timeout=PULL_TIMEOUT,
-                ignore_status=True)
+            self.adb.pull("/firmware/image/qdsp6m.qdb %s" % qxdm_log_path,
+                          timeout=PULL_TIMEOUT,
+                          ignore_status=True)
             # Zip Folder
             utils.zip_directory('%s.zip' % qxdm_log_path, qxdm_log_path)
             shutil.rmtree(qxdm_log_path)
@@ -1176,8 +1252,9 @@
         ]
         sdm_logs = []
         for path in log_paths:
-            sdm_logs += self.get_file_names(
-                path, begin_time=begin_time, match_string="*.sdm*")
+            sdm_logs += self.get_file_names(path,
+                                            begin_time=begin_time,
+                                            match_string="*.sdm*")
         if sdm_logs:
             sdm_log_path = os.path.join(self.device_log_path,
                                         "SDM_%s" % self.serial)
@@ -1263,8 +1340,8 @@
             status: true if iperf client start successfully.
             results: results have data flow information
         """
-        out = self.adb.shell(
-            "iperf3 -c {} {}".format(server_host, extra_args), timeout=timeout)
+        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
@@ -1315,7 +1392,9 @@
             'Device %s booting process timed out.' % self.serial,
             serial=self.serial)
 
-    def reboot(self, stop_at_lock_screen=False, timeout=180,
+    def reboot(self,
+               stop_at_lock_screen=False,
+               timeout=180,
                wait_after_reboot_complete=1):
         """Reboots the device.
 
@@ -1352,8 +1431,8 @@
                 # want the device to be missing to prove the device has kicked
                 # off the reboot.
                 break
-        self.wait_for_boot_completion(
-            timeout=(timeout - time.time() + timeout_start))
+        self.wait_for_boot_completion(timeout=(timeout - time.time() +
+                                               timeout_start))
 
         self.log.debug('Wait for a while after boot completion.')
         time.sleep(wait_after_reboot_complete)
@@ -1388,8 +1467,8 @@
                 break
             except adb.AdbError as e:
                 if timer + 1 == timeout:
-                    self.log.warning(
-                        'Unable to find IP address for %s.' % interface)
+                    self.log.warning('Unable to find IP address for %s.' %
+                                     interface)
                     return None
                 else:
                     time.sleep(1)
@@ -1454,7 +1533,7 @@
         for cmd in dumpsys_cmd:
             output = self.adb.shell(cmd, ignore_status=True)
             if not output or "not found" in output or "Can't find" in output or (
-                "mFocusedApp=null" in output):
+                    "mFocusedApp=null" in output):
                 result = ''
             else:
                 result = output.split(' ')[-2]
@@ -1603,11 +1682,11 @@
             return
         if not self.is_user_setup_complete() or self.is_setupwizard_on():
             # b/116709539 need this to prevent reboot after skip setup wizard
-            self.adb.shell(
-                "am start -a com.android.setupwizard.EXIT", ignore_status=True)
-            self.adb.shell(
-                "pm disable %s" % self.get_setupwizard_package_name(),
-                ignore_status=True)
+            self.adb.shell("am start -a com.android.setupwizard.EXIT",
+                           ignore_status=True)
+            self.adb.shell("pm disable %s" %
+                           self.get_setupwizard_package_name(),
+                           ignore_status=True)
         # Wait up to 5 seconds for user_setup_complete to be updated
         end_time = time.time() + 5
         while time.time() < end_time:
@@ -1657,8 +1736,8 @@
         try:
             self.ensure_verity_disabled()
             self.adb.remount()
-            out = self.adb.push(
-                '%s %s' % (src_file_path, dst_file_path), timeout=push_timeout)
+            out = self.adb.push('%s %s' % (src_file_path, dst_file_path),
+                                timeout=push_timeout)
             if 'error' in out:
                 self.log.error('Unable to push system file %s to %s due to %s',
                                src_file_path, dst_file_path, out)
diff --git a/acts/framework/acts/controllers/asus_axe11000_ap.py b/acts/framework/acts/controllers/asus_axe11000_ap.py
index d19af3b..c549d6e 100644
--- a/acts/framework/acts/controllers/asus_axe11000_ap.py
+++ b/acts/framework/acts/controllers/asus_axe11000_ap.py
@@ -246,7 +246,7 @@
           BAND_2G_RAD_PORT).get_attribute("value")
       dict_2g["radius_secret"] = self.driver.find_element_by_name(
           BAND_2G_RAD_KEY).get_attribute("value")
-    channel_field = self._get_webdriver_elements_for_channels(band)
+    channel_field = self._get_webdriver_elements_for_channels("2g")
     ch_val = self.driver.find_element_by_name(channel_field).get_attribute(
         "value")
     channel = 0
@@ -282,9 +282,9 @@
           BAND_5G_RAD_IP).get_attribute("value")
       dict_5g["radius_port"] = self.driver.find_element_by_name(
           BAND_5G_RAD_PORT).get_attribute("value")
-      dict_2g["radius_secret"] = self.driver.find_element_by_name(
+      dict_5g["radius_secret"] = self.driver.find_element_by_name(
           BAND_5G_RAD_KEY).get_attribute("value")
-    channel_field = self._get_webdriver_elements_for_channels(band)
+    channel_field = self._get_webdriver_elements_for_channels("5g")
     ch_val = self.driver.find_element_by_name(channel_field).get_attribute(
         "value")
     channel = 0
@@ -312,7 +312,7 @@
     if dict_6g["security"] == "sae":
       dict_6g["password"] = self.driver.find_element_by_name(
           BAND_6G_PSK).get_attribute("value")
-    channel_field = self._get_webdriver_elements_for_channels(band)
+    channel_field = self._get_webdriver_elements_for_channels("6g")
     ch_val = self.driver.find_element_by_name(channel_field).get_attribute(
         "value")
     channel = 0
diff --git a/acts/framework/acts/controllers/fuchsia_device.py b/acts/framework/acts/controllers/fuchsia_device.py
index 86b8423..59abec3 100644
--- a/acts/framework/acts/controllers/fuchsia_device.py
+++ b/acts/framework/acts/controllers/fuchsia_device.py
@@ -55,6 +55,7 @@
 from acts.controllers.fuchsia_lib.logging_lib import FuchsiaLoggingLib
 from acts.controllers.fuchsia_lib.netstack.netstack_lib import FuchsiaNetstackLib
 from acts.controllers.fuchsia_lib.ram_lib import FuchsiaRamLib
+from acts.controllers.fuchsia_lib.session_manager_lib import FuchsiaSessionManagerLib
 from acts.controllers.fuchsia_lib.syslog_lib import FuchsiaSyslogError
 from acts.controllers.fuchsia_lib.syslog_lib import start_syslog
 from acts.controllers.fuchsia_lib.sysinfo_lib import FuchsiaSysInfoLib
@@ -219,6 +220,7 @@
         self.sl4f_port = fd_conf_data.get("sl4f_port", 80)
         self.ssh_port = fd_conf_data.get("ssh_port", 22)
         self.ssh_config = fd_conf_data.get("ssh_config", None)
+        self.ssh_priv_key = fd_conf_data.get("ssh_priv_key", None)
         self.authorized_file = fd_conf_data.get("authorized_file_loc", None)
         self.serial_number = fd_conf_data.get("serial_number", None)
         self.device_type = fd_conf_data.get("device_type", None)
@@ -228,6 +230,8 @@
         self.build_type = fd_conf_data.get("build_type", None)
         self.server_path = fd_conf_data.get("server_path", None)
         self.specific_image = fd_conf_data.get("specific_image", None)
+        self.ffx_binary_path = fd_conf_data.get("ffx_binary_path", None)
+        self.mdns_name = fd_conf_data.get("mdns_name", None)
 
         # Instead of the input ssh_config, a new config is generated with proper
         # ControlPath to the test output directory.
@@ -281,6 +285,9 @@
                 else:
                     time.sleep(1)
             if mdns_ip and utils.is_valid_ipv6_address(mdns_ip):
+                # self.ip was actually an mdns name. Use it for self.mdns_name
+                # unless one was explicitly provided.
+                self.mdns_name = self.mdns_name or self.ip
                 self.ip = mdns_ip
                 self.address = "http://[{}]:{}".format(self.ip, self.sl4f_port)
             else:
@@ -318,11 +325,12 @@
         self.teardown_commands = fd_conf_data.get('teardown_commands', [])
 
         try:
+            self.init_ffx_connection()
             self.run_commands_from_config(self.setup_commands)
-        except FuchsiaDeviceError:
+        except Exception as e:
             # Prevent a threading error, since controller isn't fully up yet.
             self.clean_up()
-            raise FuchsiaDeviceError('Failed to run setup commands.')
+            raise e
 
     def _set_control_path_config(self, old_config, new_config):
         """Given an input ssh_config, write to a new config with proper
@@ -438,6 +446,9 @@
         self.sysinfo_lib = FuchsiaSysInfoLib(self.address, self.test_counter,
                                              self.client_id)
 
+        # Grab commands from FuchsiaSessionManagerLib
+        self.session_manager_lib = FuchsiaSessionManagerLib(self)
+
         # Grabs command from FuchsiaWlanDeprecatedConfigurationLib
         self.wlan_deprecated_configuration_lib = (
             FuchsiaWlanDeprecatedConfigurationLib(self.address,
@@ -486,6 +497,58 @@
         requests.get(url=self.init_address, data=init_data)
         self.test_counter += 1
 
+    def init_ffx_connection(self):
+        """Initializes ffx's connection to the device."""
+        self.log.debug("Initializing ffx connection")
+
+        # ffx looks for the private key in several default locations. For testbeds
+        # which have the private key in another location, set it now.
+        if self.ssh_priv_key:
+            self.ffx_command(f"config set ssh.priv {self.ssh_priv_key}")
+
+        # Wait for the device to be available. If the device isn't available within
+        # a short time (e.g. 5 seconds), log a warning before waiting longer.
+        try:
+            self.ffx_command("target wait", timeout_sec=5)
+        except job.TimeoutError as e:
+            longer_wait_sec = 60
+            self.log.info(
+                "Device is not immediately available via ffx." +
+                f" Waiting up to {longer_wait_sec} seconds for device to be reachable."
+            )
+            self.ffx_command("target wait", timeout_sec=longer_wait_sec)
+
+        # Test actual connectivity to the device by getting device information.
+        # Use a shorter timeout than default because this command can hang for
+        # a long time if the device is not actually connectable.
+        try:
+            result = self.ffx_command("target show --json", timeout_sec=15)
+        except Exception as e:
+            self.log.error(
+                f'Failed to reach target device. Try running "{self.ffx_binary_path}'
+                + ' doctor" to diagnose issues.')
+            raise e
+
+        # Compare the device's version to the ffx version
+        result_json = json.loads(result.stdout)
+        build_info = next(
+            filter(lambda s: s.get('label') == 'build', result_json))
+        version_info = next(
+            filter(lambda s: s.get('label') == 'version', build_info['child']))
+        device_version = version_info.get('value')
+        ffx_version = self.ffx_command("version").stdout
+
+        if not getattr(self, '_have_logged_ffx_version', False):
+            self._have_logged_ffx_version = True
+            self.log.info(
+                f"Device version: {device_version}, ffx version: {ffx_version}"
+            )
+            if device_version != ffx_version:
+                self.log.warning(
+                    "ffx versions that differ from device versions may" +
+                    " have compatibility issues. It is recommended to" +
+                    " use versions within 6 weeks of each other.")
+
     def run_commands_from_config(self, cmd_dicts):
         """Runs commands on the Fuchsia device from the config file. Useful for
         device and/or Fuchsia specific configuration.
@@ -701,6 +764,7 @@
                               'SL4F.')
                 self.start_services()
                 self.init_server_connection()
+                self.init_ffx_connection()
                 raise ConnectionError('Device never went down.')
             self.log.info('Device is unreachable as expected.')
         if reboot_type == FUCHSIA_REBOOT_TYPE_HARD:
@@ -746,6 +810,7 @@
             'Initiating connection to SL4F and verifying commands can run.')
         try:
             self.init_server_connection()
+            self.init_ffx_connection()
             self.hwinfo_lib.getDeviceInfo()
         except Exception as err:
             raise ConnectionError(
@@ -1285,6 +1350,56 @@
         bt_snoop_file.write(bt_snoop_data)
         bt_snoop_file.close()
 
+    def ffx_command(self,
+                    command,
+                    timeout_sec=FUCHSIA_DEFAULT_COMMAND_TIMEOUT,
+                    skip_status_code_check=False):
+        """Runs an ffx command.
+
+        Args:
+            command: string, command to run with ffx.
+            timeout_sec: Seconds to wait for a command to complete.
+            skip_status_code_check: Whether to check for the status code.
+
+        Raises:
+            ValueError: if necessary attributes are not set.
+            job.TimeoutError: when the command times out.
+            Error: when the command returns non-zero and skip_status_code_check is False.
+            FuchsiaDeviceError: when stderr has contents and skip_status_code_check is False.
+
+        Returns:
+            A job.Result object containing the results of the command.
+        """
+        if not self.ffx_binary_path:
+            raise ValueError(
+                'Must provide "ffx_binary_path: path_to_ffx" in the device config'
+            )
+        if not self.mdns_name:
+            raise ValueError(
+                'Must provide "mdns_name: device_MDNS_name" in the device config'
+            )
+
+        self.log.debug(f'Running "ffx {command}".')
+
+        full_command = f'{self.ffx_binary_path} --target {self.mdns_name} {command}'
+        result = job.run(command=full_command,
+                         timeout=timeout_sec,
+                         ignore_status=skip_status_code_check)
+
+        if isinstance(result, Exception):
+            raise result
+
+        elif not skip_status_code_check and result.stderr:
+            self.log.warning(
+                f'Ran "{full_command}", exit status {result.exit_status}')
+            self.log.warning(f'stdout: {result.stdout}')
+            self.log.warning(f'stderr: {result.stderr}')
+
+            raise FuchsiaDeviceError(
+                f'Error when running "{full_command}": {result.stderr}')
+
+        return result
+
 
 class FuchsiaDeviceLoggerAdapter(logging.LoggerAdapter):
     def process(self, msg, kwargs):
diff --git a/acts/framework/acts/controllers/fuchsia_lib/OWNERS b/acts/framework/acts/controllers/fuchsia_lib/OWNERS
index 0bc23b3..febd4ad 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/OWNERS
+++ b/acts/framework/acts/controllers/fuchsia_lib/OWNERS
@@ -5,3 +5,4 @@
 mnck@google.com
 silberst@google.com
 tturney@google.com
+sbalana@google.com
diff --git a/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/wlan_controller.py b/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/wlan_controller.py
index 1d202c6..d50f726 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/wlan_controller.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/wlan_controller.py
@@ -111,7 +111,12 @@
         net_ifaces = self.device.netstack_controller.list_interfaces()
         wlan_ifaces_by_role = {'client': {}, 'ap': {}}
         for iface in net_ifaces:
-            iface_mac = utils.mac_address_list_to_str(iface['mac'])
+            try:
+                # Some interfaces might not have a MAC
+                iface_mac = utils.mac_address_list_to_str(iface['mac'])
+            except Exception as e:
+                self.log.debug(f'Error {e} getting MAC for iface {iface}')
+                continue
             if iface_mac in wlan_ifaces_by_mac:
                 wlan_ifaces_by_mac[iface_mac]['netstack_id'] = iface['id']
 
diff --git a/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/wlan_policy_controller.py b/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/wlan_policy_controller.py
index ab2773d..7fe7aa3 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/wlan_policy_controller.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/wlan_policy_controller.py
@@ -20,6 +20,10 @@
 from acts import logger
 from acts import signals
 
+import typing
+if typing.TYPE_CHECKING:
+    from acts.controllers.fuchsia_device import FuchsiaDevice
+
 SAVED_NETWORKS = "saved_networks"
 CLIENT_STATE = "client_connections_state"
 CONNECTIONS_ENABLED = "ConnectionsEnabled"
@@ -41,12 +45,13 @@
     """
 
     def __init__(self, fuchsia_device):
-        self.device = fuchsia_device
+        self.device: FuchsiaDevice = fuchsia_device
         self.log = logger.create_tagged_trace_logger(
             'WlanPolicyController for FuchsiaDevice | %s' % self.device.ip)
         self.client_controller = False
         self.preserved_networks_and_client_state = None
         self.policy_configured = False
+        self._paused_session = False
 
     def _configure_wlan(self, preserve_saved_networks, timeout=15):
         """Sets up wlan policy layer.
@@ -57,7 +62,7 @@
         """
         end_time = time.time() + timeout
 
-        # Kill basemgr
+        # Kill basemgr (Component v1 version of session manager)
         while time.time() < end_time:
             response = self.device.basemgr_lib.killBasemgr()
             if not response.get('error'):
@@ -69,6 +74,16 @@
             raise WlanPolicyControllerError(
                 'Failed to issue successful basemgr kill call.')
 
+        # Stop the session manager, which also holds the Policy controller.
+        response = self.device.session_manager_lib.pauseSession()
+        if response.get('error'):
+            self.log.error('Failed to stop the session.')
+            raise WlanPolicyControllerError(response['error'])
+        else:
+            if response.get('result') == 'Success':
+                self._paused_session = True
+            self.log.debug(f"Paused session: {response.get('result')}")
+
         # Acquire control of policy layer
         while time.time() < end_time:
             # Create a client controller
@@ -114,6 +129,13 @@
             if not self.policy_configured:
                 self._configure_wlan()
             self.restore_preserved_networks_and_client_state()
+        if self._paused_session:
+            response = self.device.session_manager_lib.resumeSession()
+            if response.get('error'):
+                self.log.warning('Failed to resume the session.')
+                self.log.warning(response['error'])
+            else:
+                self.log.debug(f"Resumed session: {response.get('result')}")
 
     def start_client_connections(self):
         """Allow device to connect to networks via policy layer (including
diff --git a/acts/framework/acts/controllers/fuchsia_lib/session_manager_lib.py b/acts/framework/acts/controllers/fuchsia_lib/session_manager_lib.py
new file mode 100644
index 0000000..2593dde
--- /dev/null
+++ b/acts/framework/acts/controllers/fuchsia_lib/session_manager_lib.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2021 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 typing
+if typing.TYPE_CHECKING:
+    from acts.controllers.fuchsia_device import FuchsiaDevice
+
+
+class FuchsiaSessionManagerLib():
+    def __init__(self, fuchsia_device):
+        self.device: FuchsiaDevice = fuchsia_device
+
+    def resumeSession(self):
+        """Resumes a previously paused session
+
+        Returns:
+            Dictionary:
+                error: None, unless an error occurs
+                result: 'Success' or None if error
+        """
+        try:
+            self.device.ffx_command(
+                "component start /core/session-manager/session:session")
+            return {'error': None, 'result': 'Success'}
+        except Exception as e:
+            return {'error': e, 'result': None}
+
+    def pauseSession(self):
+        """Pause the session, allowing for later resumption
+
+        Returns:
+            Dictionary:
+                error: None, unless an error occurs
+                result: 'Success', 'NoSessionToPause', or None if error
+        """
+        result = self.device.ffx_command(
+            "component stop -r /core/session-manager/session:session",
+            skip_status_code_check=True)
+
+        if result.exit_status == 0:
+            return {'error': None, 'result': 'Success'}
+        else:
+            if "InstanceNotFound" in result.stderr:
+                return {'error': None, 'result': 'NoSessionToPause'}
+            else:
+                return {'error': result, 'result': None}
diff --git a/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py b/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py
index 6ea9589..3fc1813 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py
@@ -267,7 +267,6 @@
         image_tgz = os.path.basename(fuchsia_device.specific_image)
 
     job.run('tar xfvz %s/%s -C %s' % (tmp_path, image_tgz, tmp_path))
-    os.chdir(tmp_path)
     all_files = []
     for root, _dirs, files in itertools.islice(os.walk(tmp_path), 1, None):
         for filename in files:
@@ -328,13 +327,9 @@
                 flash_process_found = True
         if not flash_process_found:
             break
-    logging.info('Flashing fuchsia_device(%s) with %s/%s.' %
-                 (fuchsia_device.orig_ip, tmp_path, image_tgz))
+    logging.info(f'Flashing {fuchsia_device.orig_ip} with {tmp_path}/{image_tgz} using authorized keys "{fuchsia_device.authorized_file}".')
     try:
-        flash_output = job.run(
-            'bash flash.sh --ssh-key=%s -s %s' %
-            (fuchsia_device.authorized_file, fuchsia_device.serial_number),
-            timeout=120)
+        flash_output = job.run(f'bash {tmp_path}/flash.sh --ssh-key={fuchsia_device.authorized_file} -s {fuchsia_device.serial_number}', timeout=120)
         logging.debug(flash_output.stderr)
     except job.TimeoutError as err:
         raise TimeoutError(err)
diff --git a/acts/framework/acts/controllers/openwrt_ap.py b/acts/framework/acts/controllers/openwrt_ap.py
index 22221ef..ac0712d 100644
--- a/acts/framework/acts/controllers/openwrt_ap.py
+++ b/acts/framework/acts/controllers/openwrt_ap.py
@@ -1,14 +1,17 @@
 """Controller for Open WRT access point."""
 
+import random
 import re
 import time
 from acts import logger
+from acts import signals
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.openwrt_lib import network_settings
 from acts.controllers.openwrt_lib import wireless_config
 from acts.controllers.openwrt_lib import wireless_settings_applier
 from acts.controllers.utils_lib.ssh import connection
 from acts.controllers.utils_lib.ssh import settings
+from acts.controllers.openwrt_lib.openwrt_constants import OpenWrtWifiSetting
 import yaml
 
 MOBLY_CONTROLLER_CONFIG_NAME = "OpenWrtAP"
@@ -20,6 +23,7 @@
 ENT_SECURITY = "wpa2"
 OWE_SECURITY = "owe"
 SAE_SECURITY = "sae"
+SAEMIXED_SECURITY = "sae-mixed"
 ENABLE_RADIO = "0"
 PMF_ENABLED = 2
 WIFI_2G = "wifi2g"
@@ -293,7 +297,7 @@
       else:
         self.ssh.run(
             'uci set wireless.@wifi-iface[{}].key={}'.format(3, pwd_5g))
-        self.log.info("Set 5G password to :{}".format(pwd_2g))
+        self.log.info("Set 5G password to :{}".format(pwd_5g))
 
     if pwd_2g:
       if len(pwd_2g) < 8 or len(pwd_2g) > 63:
@@ -309,6 +313,89 @@
     self.ssh.run("uci commit wireless")
     self.ssh.run("wifi")
 
+  def set_ssid(self, ssid_5g=None, ssid_2g=None):
+    """Set SSID for individual interface.
+
+    Args:
+        ssid_5g: 8 ~ 63 chars for 5g network.
+        ssid_2g: 8 ~ 63 chars for 2g network.
+    """
+    if ssid_5g:
+      if len(ssid_5g) < 8 or len(ssid_5g) > 63:
+        self.log.error("SSID must be 8~63 characters long")
+      # Only accept ascii letters and digits
+      else:
+        self.ssh.run(
+            'uci set wireless.@wifi-iface[{}].ssid={}'.format(3, ssid_5g))
+        self.log.info("Set 5G SSID to :{}".format(ssid_5g))
+
+    if ssid_2g:
+      if len(ssid_2g) < 8 or len(ssid_2g) > 63:
+        self.log.error("SSID must be 8~63 characters long")
+      # Only accept ascii letters and digits
+      else:
+        self.ssh.run(
+            'uci set wireless.@wifi-iface[{}].ssid={}'.format(2, ssid_2g))
+        self.log.info("Set 2G SSID to :{}".format(ssid_2g))
+
+    self.ssh.run("uci commit wireless")
+    self.ssh.run("wifi")
+
+  def generate_mobility_domain(self):
+      """Generate 4-character hexadecimal ID
+
+      Returns: String; a 4-character hexadecimal ID.
+      """
+      md = "{:04x}".format(random.getrandbits(16))
+      self.log.info("Mobility Domain ID: {}".format(md))
+      return md
+
+  def enable_80211r(self, iface, md):
+    """Enable 802.11r for one single radio.
+
+     Args:
+       iface: index number of wifi-iface.
+              2: radio1
+              3: radio0
+       md: mobility domain. a 4-character hexadecimal ID.
+    Raises: TestSkip if 2g or 5g radio is not up or 802.11r is not enabled.
+     """
+    str_output = self.ssh.run("wifi status").stdout
+    wifi_status = yaml.load(str_output.replace("\t", "").replace("\n", ""),
+                            Loader=yaml.FullLoader)
+    # Check if the radio is up.
+    if iface == OpenWrtWifiSetting.IFACE_2G:
+      if wifi_status['radio1']['up']:
+        self.log.info("2g network is ENABLED")
+      else:
+        raise signals.TestSkip("2g network is NOT ENABLED")
+    elif iface == OpenWrtWifiSetting.IFACE_5G:
+      if wifi_status['radio0']['up']:
+        self.log.info("5g network is ENABLED")
+      else:
+        raise signals.TestSkip("5g network is NOT ENABLED")
+
+    # Setup 802.11r.
+    self.ssh.run(
+        "uci set wireless.@wifi-iface[{}].ieee80211r='1'".format(iface))
+    self.ssh.run(
+        "uci set wireless.@wifi-iface[{}].ft_psk_generate_local='1'"
+          .format(iface))
+    self.ssh.run(
+        "uci set wireless.@wifi-iface[{}].mobility_domain='{}'"
+          .format(iface, md))
+    self.ssh.run(
+        "uci commit wireless")
+    self.ssh.run("wifi")
+
+    # Check if 802.11r is enabled.
+    result = self.ssh.run(
+        "uci get wireless.@wifi-iface[{}].ieee80211r".format(iface)).stdout
+    if result == '1':
+      self.log.info("802.11r is ENABLED")
+    else:
+      raise signals.TestSkip("802.11r is NOT ENABLED")
+
   def generate_wireless_configs(self, wifi_configs):
     """Generate wireless configs to configure.
 
@@ -335,7 +422,8 @@
                                              config["security"],
                                              hostapd_constants.BAND_2G,
                                              password=config["password"],
-                                             hidden=config["hiddenSSID"]))
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=config["ieee80211w"]))
         elif config["security"] == PSK1_SECURITY:
           wireless_configs.append(
               wireless_config.WirelessConfig("%s%s" % (WIFI_2G, num_2g),
@@ -343,7 +431,8 @@
                                              config["security"],
                                              hostapd_constants.BAND_2G,
                                              password=config["password"],
-                                             hidden=config["hiddenSSID"]))
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=config["ieee80211w"]))
         elif config["security"] == WEP_SECURITY:
           wireless_configs.append(
               wireless_config.WirelessConfig("%s%s" % (WIFI_2G, num_2g),
@@ -376,6 +465,15 @@
                                              password=config["password"],
                                              hidden=config["hiddenSSID"],
                                              ieee80211w=PMF_ENABLED))
+        elif config["security"] == SAEMIXED_SECURITY:
+          wireless_configs.append(
+              wireless_config.WirelessConfig("%s%s" % (WIFI_2G, num_2g),
+                                             config["SSID"],
+                                             config["security"],
+                                             hostapd_constants.BAND_2G,
+                                             password=config["password"],
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=config["ieee80211w"]))
         elif config["security"] == ENT_SECURITY:
           wireless_configs.append(
               wireless_config.WirelessConfig(
@@ -397,7 +495,8 @@
                                              config["security"],
                                              hostapd_constants.BAND_5G,
                                              password=config["password"],
-                                             hidden=config["hiddenSSID"]))
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=config["ieee80211w"]))
         elif config["security"] == PSK1_SECURITY:
           wireless_configs.append(
               wireless_config.WirelessConfig("%s%s" % (WIFI_5G, num_5g),
@@ -405,7 +504,8 @@
                                              config["security"],
                                              hostapd_constants.BAND_5G,
                                              password=config["password"],
-                                             hidden=config["hiddenSSID"]))
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=config["ieee80211w"]))
         elif config["security"] == WEP_SECURITY:
           wireless_configs.append(
               wireless_config.WirelessConfig("%s%s" % (WIFI_5G, num_5g),
@@ -438,6 +538,15 @@
                                              password=config["password"],
                                              hidden=config["hiddenSSID"],
                                              ieee80211w=PMF_ENABLED))
+        elif config["security"] == SAEMIXED_SECURITY:
+          wireless_configs.append(
+              wireless_config.WirelessConfig("%s%s" % (WIFI_5G, num_5g),
+                                             config["SSID"],
+                                             config["security"],
+                                             hostapd_constants.BAND_5G,
+                                             password=config["password"],
+                                             hidden=config["hiddenSSID"],
+                                             ieee80211w=config["ieee80211w"]))
         elif config["security"] == ENT_SECURITY:
           wireless_configs.append(
               wireless_config.WirelessConfig(
diff --git a/acts/framework/acts/controllers/openwrt_lib/network_const.py b/acts/framework/acts/controllers/openwrt_lib/network_const.py
index 0a3fb42..3aba0de 100644
--- a/acts/framework/acts/controllers/openwrt_lib/network_const.py
+++ b/acts/framework/acts/controllers/openwrt_lib/network_const.py
@@ -1,3 +1,5 @@
+LOCALHOST = "192.168.1.1"
+
 # params for ipsec.conf
 IPSEC_CONF = {
     "config setup": {
@@ -15,7 +17,7 @@
     "conn L2TP_PSK": {
         "keyexchange": "ikev1",
         "type": "transport",
-        "left": "192.168.1.1",
+        "left": LOCALHOST,
         "leftprotoport": "17/1701",
         "leftauth": "psk",
         "right": "%any",
@@ -30,7 +32,7 @@
     "conn L2TP_RSA": {
         "keyexchange": "ikev1",
         "type": "transport",
-        "left": "192.168.1.1",
+        "left": LOCALHOST,
         "leftprotoport": "17/1701",
         "leftauth": "pubkey",
         "leftcert": "serverCert.der",
@@ -45,7 +47,7 @@
 IPSEC_HYBRID_RSA = {
     "conn HYBRID_RSA": {
         "keyexchange": "ikev1",
-        "left": "192.168.1.1",
+        "left": LOCALHOST,
         "leftsubnet": "0.0.0.0/0",
         "leftauth": "pubkey",
         "leftcert": "serverCert.der",
@@ -62,7 +64,7 @@
 IPSEC_XAUTH_PSK = {
     "conn XAUTH_PSK": {
         "keyexchange": "ikev1",
-        "left": "192.168.1.1",
+        "left": LOCALHOST,
         "leftsubnet": "0.0.0.0/0",
         "leftauth": "psk",
         "right": "%any",
@@ -76,7 +78,7 @@
 IPSEC_XAUTH_RSA = {
     "conn XAUTH_RSA": {
         "keyexchange": "ikev1",
-        "left": "192.168.1.1",
+        "left": LOCALHOST,
         "leftsubnet": "0.0.0.0/0",
         "leftcert": "serverCert.der",
         "leftsendcert": "always",
@@ -88,6 +90,100 @@
     }
 }
 
+IPSEC_IKEV2_MSCHAPV2 = {
+    "conn IKEV2_MSCHAPV2": {
+        "keyexchange": "ikev2",
+        "left": LOCALHOST,
+        "leftid": LOCALHOST,
+        "leftcert": "serverCert.der",
+        "leftsubnet": "0.0.0.0/0",
+        "leftauth": "pubkey",
+        "leftsendcert": "always",
+        "right": "%any",
+        "rightid": "vpntest",
+        "rightauth": "eap-mschapv2",
+        "auto": "add"
+    }
+}
+
+IPSEC_IKEV2_PSK = {
+    "conn IKEV2_PSK": {
+        "keyexchange": "ikev2",
+        "left": LOCALHOST,
+        "leftid": LOCALHOST,
+        "leftauth": "psk",
+        "leftsubnet": "0.0.0.0/0",
+        "right": "%any",
+        "rightid": "vpntest",
+        "rightauth": "psk",
+        "auto": "add"
+    }
+}
+
+IPSEC_IKEV2_RSA = {
+    "conn IKEV2_RSA": {
+        "keyexchange": "ikev2",
+        "left": LOCALHOST,
+        "leftid": LOCALHOST,
+        "leftcert": "serverCert.der",
+        "leftsubnet": "0.0.0.0/0",
+        "leftauth": "pubkey",
+        "leftsendcert": "always",
+        "right": "%any",
+        "rightid": "vpntest@%s" % LOCALHOST,
+        "rightauth": "pubkey",
+        "rightcert": "clientCert.pem",
+        "auto": "add"
+    }
+}
+
+IPSEC_IKEV2_MSCHAPV2_HOSTNAME = {
+    "conn IKEV2_MSCHAPV2_HOSTNAME": {
+        "keyexchange": "ikev2",
+        "left": LOCALHOST,
+        "leftid": "strongswan-vpn-server.android-iperf.com",
+        "leftcert": "serverCert.der",
+        "leftsubnet": "0.0.0.0/0",
+        "leftauth": "pubkey",
+        "leftsendcert": "always",
+        "right": "%any",
+        "rightid": "vpntest",
+        "rightauth": "eap-mschapv2",
+        "auto": "add"
+    }
+}
+
+IPSEC_IKEV2_PSK_HOSTNAME = {
+    "conn IKEV2_PSK_HOSTNAME": {
+        "keyexchange": "ikev2",
+        "left": LOCALHOST,
+        "leftid": "strongswan-vpn-server.android-iperf.com",
+        "leftauth": "psk",
+        "leftsubnet": "0.0.0.0/0",
+        "right": "%any",
+        "rightid": "vpntest",
+        "rightauth": "psk",
+        "auto": "add"
+    }
+}
+
+IPSEC_IKEV2_RSA_HOSTNAME = {
+    "conn IKEV2_RSA_HOSTNAME": {
+        "keyexchange": "ikev2",
+        "left": LOCALHOST,
+        "leftid": "strongswan-vpn-server.android-iperf.com",
+        "leftcert": "serverCert.der",
+        "leftsubnet": "0.0.0.0/0",
+        "leftauth": "pubkey",
+        "leftsendcert": "always",
+        "right": "%any",
+        "rightid": "vpntest@strongswan-vpn-server.android-iperf.com",
+        "rightauth": "pubkey",
+        "rightcert": "clientCert.pem",
+        "auto": "add"
+    }
+}
+
 # parmas for lx2tpd
 
 XL2TPD_CONF_GLOBAL = (
@@ -149,7 +245,6 @@
     "iptables -I FORWARD  -m policy --dir out --pol ipsec --proto esp -j ACCEPT",
     "iptables -I OUTPUT   -m policy --dir out --pol ipsec --proto esp -j ACCEPT",
     "iptables -t nat -I POSTROUTING -m policy --pol ipsec --dir out -j ACCEPT",
-    "iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT",
     "iptables -A INPUT -p esp -j ACCEPT",
     "iptables -A INPUT -i eth0.2 -p udp --dport 500 -j ACCEPT",
     "iptables -A INPUT -i eth0.2 -p tcp --dport 500 -j ACCEPT",
diff --git a/acts/framework/acts/controllers/openwrt_lib/network_settings.py b/acts/framework/acts/controllers/openwrt_lib/network_settings.py
index ca1b212..b3c1e4e 100644
--- a/acts/framework/acts/controllers/openwrt_lib/network_settings.py
+++ b/acts/framework/acts/controllers/openwrt_lib/network_settings.py
@@ -42,6 +42,7 @@
 XL2TPD_OPTION_CONFIG_PATH = "/etc/ppp/options.xl2tpd"
 FIREWALL_CUSTOM_OPTION_PATH = "/etc/firewall.user"
 PPP_CHAP_SECRET_PATH = "/etc/ppp/chap-secrets"
+IKEV2_VPN_CERT_KEYS_PATH = "/var/ikev2_cert.sh"
 TCPDUMP_DIR = "/tmp/tcpdump/"
 LOCALHOST = "192.168.1.1"
 DEFAULT_PACKAGE_INSTALL_TIMEOUT = 200
@@ -90,6 +91,7 @@
             "ipv6_prefer_option": self.remove_ipv6_prefer_option,
             "block_dns_response": self.unblock_dns_response,
             "setup_mdns": self.remove_mdns,
+            "add_dhcp_rapid_commit": self.remove_dhcp_rapid_commit,
             "setup_captive_portal": self.remove_cpative_portal
         }
         # This map contains cleanup functions to restore the configuration to
@@ -462,7 +464,11 @@
         # setup vpn server local ip
         self.setup_vpn_local_ip()
         # generate cert and key for rsa
-        self.generate_vpn_cert_keys(country, org)
+        if self.l2tp.name == "ikev2-server":
+            self.generate_ikev2_vpn_cert_keys(country, org)
+            self.add_resource_record(self.l2tp.hostname, LOCALHOST)
+        else:
+            self.generate_vpn_cert_keys(country, org)
         # restart service
         self.service_manager.need_restart(SERVICE_IPSEC)
         self.service_manager.need_restart(SERVICE_XL2TPD)
@@ -474,6 +480,8 @@
         self.config.discard("setup_vpn_l2tp_server")
         self.restore_firewall_rules_for_l2tp()
         self.remove_vpn_local_ip()
+        if self.l2tp.name == "ikev2-server":
+            self.clear_resource_record()
         self.service_manager.need_restart(SERVICE_IPSEC)
         self.service_manager.need_restart(SERVICE_XL2TPD)
         self.service_manager.need_restart(SERVICE_FIREWALL)
@@ -507,6 +515,12 @@
                 config.append("")
 
         config = []
+        load_ipsec_config(network_const.IPSEC_IKEV2_MSCHAPV2, True)
+        load_ipsec_config(network_const.IPSEC_IKEV2_PSK, True)
+        load_ipsec_config(network_const.IPSEC_IKEV2_RSA, True)
+        load_ipsec_config(network_const.IPSEC_IKEV2_MSCHAPV2_HOSTNAME, True)
+        load_ipsec_config(network_const.IPSEC_IKEV2_PSK_HOSTNAME, True)
+        load_ipsec_config(network_const.IPSEC_IKEV2_RSA_HOSTNAME, True)
         load_ipsec_config(network_const.IPSEC_CONF)
         load_ipsec_config(network_const.IPSEC_L2TP_PSK)
         load_ipsec_config(network_const.IPSEC_L2TP_RSA)
@@ -600,6 +614,54 @@
         self.ssh.run("mv clientPkcs.p12 /www/downloads/")
         self.ssh.run("chmod 664 /www/downloads/clientPkcs.p12")
 
+    def generate_ikev2_vpn_cert_keys(self, country, org):
+        rsa = "--type rsa"
+        lifetime = "--lifetime 365"
+        size = "--size 4096"
+
+        if not self.path_exists("/www/downloads/"):
+            self.ssh.run("mkdir /www/downloads/")
+
+        ikev2_vpn_cert_keys = [
+            "ipsec pki --gen %s %s --outform der > caKey.der" % (rsa, size),
+            "ipsec pki --self --ca %s --in caKey.der %s --dn "
+            "\"C=%s, O=%s, CN=%s\" --outform der > caCert.der" %
+            (lifetime, rsa, country, org, self.l2tp.hostname),
+            "ipsec pki --gen %s %s --outform der > serverKey.der" % (size, rsa),
+            "ipsec pki --pub --in serverKey.der %s | ipsec pki --issue %s "
+            r"--cacert caCert.der --cakey caKey.der --dn \"C=%s, O=%s, CN=%s\" "
+            "--san %s --san %s --flag serverAuth --flag ikeIntermediate "
+            "--outform der > serverCert.der" % (rsa, lifetime, country, org,
+                                                self.l2tp.hostname, LOCALHOST,
+                                                self.l2tp.hostname),
+            "ipsec pki --gen %s %s --outform der > clientKey.der" % (size, rsa),
+            "ipsec pki --pub --in clientKey.der %s | ipsec pki --issue %s "
+            r"--cacert caCert.der --cakey caKey.der --dn \"C=%s, O=%s, CN=%s@%s\" "
+            r"--san \"%s\" --san \"%s@%s\" --san \"%s@%s\" --outform der "
+            "> clientCert.der" % (rsa, lifetime, country, org, self.l2tp.username,
+                                  self.l2tp.hostname, self.l2tp.username,
+                                  self.l2tp.username, LOCALHOST,
+                                  self.l2tp.username, self.l2tp.hostname),
+            "openssl rsa -inform DER -in clientKey.der "
+            "-out clientKey.pem -outform PEM",
+            "openssl x509 -inform DER -in clientCert.der "
+            "-out clientCert.pem -outform PEM",
+            "openssl x509 -inform DER -in caCert.der "
+            "-out caCert.pem -outform PEM",
+            "openssl pkcs12 -in clientCert.pem -inkey  clientKey.pem "
+            "-certfile caCert.pem -export -out clientPkcs.p12 -passout pass:",
+            "mv caCert.pem /etc/ipsec.d/cacerts/",
+            "mv *Cert* /etc/ipsec.d/certs/",
+            "mv *Key* /etc/ipsec.d/private/",
+            "mv clientPkcs.p12 /www/downloads/",
+            "chmod 664 /www/downloads/clientPkcs.p12",
+        ]
+        file_string = "\n".join(ikev2_vpn_cert_keys)
+        self.create_config_file(file_string, IKEV2_VPN_CERT_KEYS_PATH)
+
+        self.ssh.run("chmod +x %s" % IKEV2_VPN_CERT_KEYS_PATH)
+        self.ssh.run("%s" % IKEV2_VPN_CERT_KEYS_PATH)
+
     def update_firewall_rules_list(self):
         """Update rule list in /etc/config/firewall."""
         new_rules_list = []
@@ -854,6 +916,18 @@
         self.service_manager.need_restart(SERVICE_DNSMASQ)
         self.commit_changes()
 
+    def add_dhcp_rapid_commit(self):
+        self.create_config_file("dhcp-rapid-commit\n","/etc/dnsmasq.conf")
+        self.config.add("add_dhcp_rapid_commit")
+        self.service_manager.need_restart(SERVICE_DNSMASQ)
+        self.commit_changes()
+
+    def remove_dhcp_rapid_commit(self):
+        self.create_config_file("","/etc/dnsmasq.conf")
+        self.config.discard("add_dhcp_rapid_commit")
+        self.service_manager.need_restart(SERVICE_DNSMASQ)
+        self.commit_changes()
+
     def start_tcpdump(self, test_name, args="", interface="br-lan"):
         """"Start tcpdump on OpenWrt.
 
@@ -906,9 +980,11 @@
         return tcpdump_remote_path if pull_dir else None
 
     def clear_tcpdump(self):
-        self.ssh.run("killall tpcdump", ignore_status=True)
-        if self.ssh.run("pgrep tpcdump", ignore_status=True).stdout:
+        self.ssh.run("killall tcpdump", ignore_status=True)
+        if self.ssh.run("pgrep tcpdump", ignore_status=True).stdout:
             raise signals.TestFailure("Failed to clean up tcpdump process.")
+        if self.path_exists(TCPDUMP_DIR):
+            self.ssh.run("rm -f  %s/*" % TCPDUMP_DIR)
 
     def _get_tcpdump_pid(self, tcpdump_file_name):
         """Check tcpdump process on OpenWrt."""
diff --git a/acts/framework/acts/controllers/openwrt_lib/openwrt_constants.py b/acts/framework/acts/controllers/openwrt_lib/openwrt_constants.py
index 5848b5b..5ab4124 100644
--- a/acts/framework/acts/controllers/openwrt_lib/openwrt_constants.py
+++ b/acts/framework/acts/controllers/openwrt_lib/openwrt_constants.py
@@ -23,4 +23,8 @@
   WPA2_PSK_DEFAULT = "psk2"
   WPA2_PSK_CCMP = "psk2+ccmp"
   WPA2_PSK_TKIP = "psk2+tkip"
-  WPA2_PSK_TKIP_AND_CCMP = "psk2+tkip+ccmp"
\ No newline at end of file
+  WPA2_PSK_TKIP_AND_CCMP = "psk2+tkip+ccmp"
+
+class OpenWrtWifiSetting:
+  IFACE_2G = 2
+  IFACE_5G = 3
\ No newline at end of file
diff --git a/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py b/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py
index ec0bbf1..a366c02 100644
--- a/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py
+++ b/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py
@@ -15,6 +15,7 @@
 ENT_SECURITY = "wpa2"
 OWE_SECURITY = "owe"
 SAE_SECURITY = "sae"
+SAEMIXED_SECURITY = "sae-mixed"
 ENABLE_RADIO = "0"
 DISABLE_RADIO = "1"
 ENABLE_HIDDEN = "1"
@@ -108,7 +109,8 @@
       self.ssh.run("uci set wireless.%s.encryption='%s'" %
                    (config.name, config.security))
       if config.security == PSK_SECURITY or config.security == SAE_SECURITY\
-              or config.security == PSK1_SECURITY:
+              or config.security == PSK1_SECURITY\
+              or config.security == SAEMIXED_SECURITY:
         self.ssh.run("uci set wireless.%s.key='%s'" %
                      (config.name, config.password))
       elif config.security == WEP_SECURITY:
diff --git a/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py b/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py
index dbc59e7..959274d 100644
--- a/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py
+++ b/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py
@@ -111,12 +111,12 @@
         self._listen_for_port_lock = threading.Lock()
         self._sl4a_ports = set()
         self.adb = adb
-        self.log = logger.create_logger(
-            lambda msg: '[SL4A Manager|%s] %s' % (adb.serial, msg))
+        self.log = logger.create_logger(lambda msg: '[SL4A Manager|%s] %s' % (
+            adb.serial, msg))
         self.sessions = {}
         self._started = False
-        self.error_reporter = error_reporter.ErrorReporter(
-            'SL4A %s' % adb.serial)
+        self.error_reporter = error_reporter.ErrorReporter('SL4A %s' %
+                                                           adb.serial)
 
     @property
     def sl4a_ports_in_use(self):
@@ -163,8 +163,8 @@
 
         raise rpc_client.Sl4aConnectionError(
             'Unable to find a valid open port for a new server connection. '
-            'Expected port: %s. Open ports: %s' % (device_port,
-                                                   self._sl4a_ports))
+            'Expected port: %s. Open ports: %s' %
+            (device_port, self._sl4a_ports))
 
     def _get_all_ports_command(self):
         """Returns the list of all ports from the command to get ports."""
@@ -242,6 +242,7 @@
     def create_session(self,
                        max_connections=None,
                        client_port=0,
+                       forwarded_port=0,
                        server_port=None):
         """Creates an SL4A server with the given ports if possible.
 
@@ -250,7 +251,9 @@
         be randomized.
 
         Args:
-            client_port: The port on the host machine
+            client_port: The client port on the host machine
+            forwarded_port: The server port on the host machine forwarded
+                            by adb from the Android device
             server_port: The port on the Android device.
             max_connections: The max number of client connections for the
                 session.
@@ -266,22 +269,25 @@
             # Otherwise, open a new server on a random port.
             else:
                 server_port = 0
+        self.log.debug(
+            "Creating SL4A session client_port={}, forwarded_port={}, server_port={}"
+            .format(client_port, forwarded_port, server_port))
         self.start_sl4a_service()
-        session = sl4a_session.Sl4aSession(
-            self.adb,
-            client_port,
-            server_port,
-            self.obtain_sl4a_server,
-            self.diagnose_failure,
-            max_connections=max_connections)
+        session = sl4a_session.Sl4aSession(self.adb,
+                                           client_port,
+                                           server_port,
+                                           self.obtain_sl4a_server,
+                                           self.diagnose_failure,
+                                           forwarded_port,
+                                           max_connections=max_connections)
         self.sessions[session.uid] = session
         return session
 
     def stop_service(self):
         """Stops The SL4A Service. Force-stops the SL4A apk."""
         try:
-            self.adb.shell(
-                'am force-stop %s' % SL4A_PKG_NAME, ignore_status=True)
+            self.adb.shell('am force-stop %s' % SL4A_PKG_NAME,
+                           ignore_status=True)
         except Exception as e:
             self.log.warning("Fail to stop package %s: %s", SL4A_PKG_NAME, e)
         self._started = False
diff --git a/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py b/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py
index c0a4e1d..04fd787 100644
--- a/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py
+++ b/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py
@@ -55,6 +55,7 @@
                  device_port,
                  get_server_port_func,
                  on_error_callback,
+                 forwarded_port=0,
                  max_connections=None):
         """Creates an SL4A Session.
 
@@ -67,6 +68,8 @@
                 server for its first connection.
             device_port: The SL4A server port to be used as a hint for which
                 SL4A server to connect to.
+            forwarded_port: The server port on host machine forwarded by adb
+                            from Android device to accept SL4A connection
         """
         self._event_dispatcher = None
         self._terminate_lock = threading.Lock()
@@ -79,24 +82,24 @@
 
         self.log = logger.create_logger(_log_formatter)
 
+        self.forwarded_port = forwarded_port
         self.server_port = device_port
         self.uid = UNKNOWN_UID
         self.obtain_server_port = get_server_port_func
         self._on_error_callback = on_error_callback
 
         connection_creator = self._rpc_connection_creator(host_port)
-        self.rpc_client = rpc_client.RpcClient(
-            self.uid,
-            self.adb.serial,
-            self.diagnose_failure,
-            connection_creator,
-            max_connections=max_connections)
+        self.rpc_client = rpc_client.RpcClient(self.uid,
+                                               self.adb.serial,
+                                               self.diagnose_failure,
+                                               connection_creator,
+                                               max_connections=max_connections)
 
     def _rpc_connection_creator(self, host_port):
         def create_client(uid):
-            return self._create_rpc_connection(
-                ports=sl4a_ports.Sl4aPorts(host_port, 0, self.server_port),
-                uid=uid)
+            return self._create_rpc_connection(ports=sl4a_ports.Sl4aPorts(
+                host_port, self.forwarded_port, self.server_port),
+                                               uid=uid)
 
         return create_client
 
@@ -156,10 +159,14 @@
         ports.server_port = self.obtain_server_port(ports.server_port)
         self.server_port = ports.server_port
         # Forward the device port to the host.
-        ports.forwarded_port = self._create_forwarded_port(ports.server_port)
+        ports.forwarded_port = self._create_forwarded_port(
+            ports.server_port, hinted_port=ports.forwarded_port)
         client_socket, fd = self._create_client_side_connection(ports)
-        client = rpc_connection.RpcConnection(
-            self.adb, ports, client_socket, fd, uid=uid)
+        client = rpc_connection.RpcConnection(self.adb,
+                                              ports,
+                                              client_socket,
+                                              fd,
+                                              uid=uid)
         client.open()
         if uid == UNKNOWN_UID:
             self.uid = client.uid
@@ -195,9 +202,9 @@
             except OSError as e:
                 # If the port is in use, log and ask for any open port.
                 if e.errno == errno.EADDRINUSE:
-                    self.log.warning(
-                        'Port %s is already in use on the host. '
-                        'Generating a random port.' % ports.client_port)
+                    self.log.warning('Port %s is already in use on the host. '
+                                     'Generating a random port.' %
+                                     ports.client_port)
                     ports.client_port = 0
                     return self._create_client_side_connection(ports)
                 raise
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
index cb4f330..98a8fb0 100755
--- a/acts/framework/setup.py
+++ b/acts/framework/setup.py
@@ -61,8 +61,8 @@
     install_requires.append('scipy<1.6')
     install_requires.append('numpy<1.20')
 elif sys.version_info < (3, 8):
-    # Python 3.7 uses latest scipy and numpy up to 1.21.x
-    install_requires.append('scipy')
+    # Python 3.7 uses latest scipy up to 1.7.x and numpy up to 1.21.x
+    install_requires.append('scipy<1.8')
     install_requires.append('numpy<1.22')
 else:
     # Python 3.8+ is supported by latest scipy and numpy
diff --git a/acts/framework/tests/acts_utils_test.py b/acts/framework/tests/acts_utils_test.py
index 742e07f..e662db8 100755
--- a/acts/framework/tests/acts_utils_test.py
+++ b/acts/framework/tests/acts_utils_test.py
@@ -72,74 +72,68 @@
 FUCHSIA_INTERFACES = {
     'id':
     '1',
-    'result': [{
-        'features':
-        4,
-        'filepath':
-        '[none]',
-        'id':
-        1,
-        'ipv4_addresses': [[127, 0, 0, 1]],
-        'ipv6_addresses': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]],
-        'is_administrative_status_enabled':
-        True,
-        'is_physical_status_up':
-        True,
-        'mac': [0, 0, 0, 0, 0, 0],
-        'mtu':
-        65536,
-        'name':
-        'lo',
-        'topopath':
-        'loopback'
-    }, {
-        'features':
-        0,
-        'filepath':
-        '/dev/class/ethernet/000',
-        'id':
-        2,
-        'ipv4_addresses': [[100, 127, 110, 79]],
-        'ipv6_addresses':
-        [[254, 128, 0, 0, 0, 0, 0, 0, 198, 109, 60, 117, 44, 236, 29, 114],
-         [36, 1, 250, 0, 4, 128, 122, 0, 141, 79, 133, 255, 204, 92, 120, 126],
-         [36, 1, 250, 0, 4, 128, 122, 0, 4, 89, 185, 147, 252, 191, 20, 25]],
-        'is_administrative_status_enabled':
-        True,
-        'is_physical_status_up':
-        True,
-        'mac': [0, 224, 76, 5, 76, 229],
-        'mtu':
-        1514,
-        'name':
-        'eno1',
-        'topopath':
-        '@/dev/xhci/xhci/usb-bus/001/001/ifc-000/usb-cdc-ecm/ethernet'
-    }, {
-        'features':
-        1,
-        'filepath':
-        '/dev/class/ethernet/001',
-        'id':
-        3,
-        'ipv4_addresses': [],
-        'ipv6_addresses':
-        [[254, 128, 0, 0, 0, 0, 0, 0, 96, 255, 93, 96, 52, 253, 253, 243],
-         [254, 128, 0, 0, 0, 0, 0, 0, 70, 7, 11, 255, 254, 118, 126, 192]],
-        'is_administrative_status_enabled':
-        False,
-        'is_physical_status_up':
-        False,
-        'mac': [68, 7, 11, 118, 126, 192],
-        'mtu':
-        1500,
-        'name':
-        'wlanxc0',
-        'topopath':
-        '@/dev/wifi/wlanphy/wlanif-client/wlan-ethernet/ethernet'
-    }],
+    'result': [
+        {
+            'id': 1,
+            'name': 'lo',
+            'ipv4_addresses': [
+                [127, 0, 0, 1],
+            ],
+            'ipv6_addresses': [
+                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
+            ],
+            'online': True,
+            'mac': [0, 0, 0, 0, 0, 0],
+        },
+        {
+            'id':
+            2,
+            'name':
+            'eno1',
+            'ipv4_addresses': [
+                [100, 127, 110, 79],
+            ],
+            'ipv6_addresses': [
+                [
+                    254, 128, 0, 0, 0, 0, 0, 0, 198, 109, 60, 117, 44, 236, 29,
+                    114
+                ],
+                [
+                    36, 1, 250, 0, 4, 128, 122, 0, 141, 79, 133, 255, 204, 92,
+                    120, 126
+                ],
+                [
+                    36, 1, 250, 0, 4, 128, 122, 0, 4, 89, 185, 147, 252, 191,
+                    20, 25
+                ],
+            ],
+            'online':
+            True,
+            'mac': [0, 224, 76, 5, 76, 229],
+        },
+        {
+            'id':
+            3,
+            'name':
+            'wlanxc0',
+            'ipv4_addresses': [],
+            'ipv6_addresses': [
+                [
+                    254, 128, 0, 0, 0, 0, 0, 0, 96, 255, 93, 96, 52, 253, 253,
+                    243
+                ],
+                [
+                    254, 128, 0, 0, 0, 0, 0, 0, 70, 7, 11, 255, 254, 118, 126,
+                    192
+                ],
+            ],
+            'online':
+            False,
+            'mac': [68, 7, 11, 118, 126, 192],
+        },
+    ],
     'error':
-    None
+    None,
 }
 
 CORRECT_FULL_IP_LIST = {
@@ -163,6 +157,8 @@
 
 FUCHSIA_INIT_SERVER = ('acts.controllers.fuchsia_device.FuchsiaDevice.'
                        'init_server_connection')
+FUCHSIA_INIT_FFX = ('acts.controllers.fuchsia_device.FuchsiaDevice.'
+                    'init_ffx_connection')
 FUCHSIA_SET_CONTROL_PATH_CONFIG = ('acts.controllers.fuchsia_device.'
                                    'FuchsiaDevice._set_control_path_config')
 FUCHSIA_START_SERVICES = ('acts.controllers.fuchsia_device.FuchsiaDevice.'
@@ -531,15 +527,14 @@
             CORRECT_EMPTY_IP_LIST)
 
     @mock.patch(FUCHSIA_INIT_SERVER)
+    @mock.patch(FUCHSIA_INIT_FFX)
     @mock.patch(FUCHSIA_SET_CONTROL_PATH_CONFIG)
     @mock.patch(FUCHSIA_START_SERVICES)
     @mock.patch(FUCHSIA_NETSTACK_LIST_INTERFACES)
     @mock.patch(FUCHSIA_INIT_NETSTACK)
-    def test_fuchsia_get_interface_ip_addresses_full(self, init_mock,
-                                                     list_interfaces_mock,
-                                                     start_services_mock,
-                                                     control_path_mock,
-                                                     fuchsia_device_mock):
+    def test_fuchsia_get_interface_ip_addresses_full(
+            self, init_mock, list_interfaces_mock, start_services_mock,
+            control_path_mock, ffx_mock, fuchsia_device_mock):
         # Will never actually be created/used.
         logging.log_path = '/tmp/unit_test_garbage'
 
@@ -552,15 +547,14 @@
             CORRECT_FULL_IP_LIST)
 
     @mock.patch(FUCHSIA_INIT_SERVER)
+    @mock.patch(FUCHSIA_INIT_FFX)
     @mock.patch(FUCHSIA_SET_CONTROL_PATH_CONFIG)
     @mock.patch(FUCHSIA_START_SERVICES)
     @mock.patch(FUCHSIA_NETSTACK_LIST_INTERFACES)
     @mock.patch(FUCHSIA_INIT_NETSTACK)
-    def test_fuchsia_get_interface_ip_addresses_empty(self, init_mock,
-                                                      list_interfaces_mock,
-                                                      start_services_mock,
-                                                      control_path_mock,
-                                                      fuchsia_device_mock):
+    def test_fuchsia_get_interface_ip_addresses_empty(
+            self, init_mock, list_interfaces_mock, start_services_mock,
+            control_path_mock, ffx_mock, fuchsia_device_mock):
         # Will never actually be created/used.
         logging.log_path = '/tmp/unit_test_garbage'
 
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py b/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py
index e90c527..ba05a53 100644
--- a/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py
@@ -13,6 +13,9 @@
 #   WITHOUT 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 import context
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 
 from mobly import utils
@@ -32,18 +35,46 @@
             device.take_bug_report(STAGE_NAME_TEARDOWN_CLASS, begin_time)
 
     def on_fail(self, test_name, begin_time):
-        try:
-            self.dut.get_log(test_name, begin_time)
-            if (not hasattr(self.dut.device, "take_bug_report_on_fail")
-                    or self.dut.device.take_bug_report_on_fail):
-                # Take a bug report if device does not have a take bug report flag,
-                # or if the flag is true
-                self.dut.take_bug_report(test_name, begin_time)
-        except Exception:
-            pass
+        """Gets a wlan_device log and calls the generic device fail on DUT."""
+        self.dut.get_log(test_name, begin_time)
+        self.on_device_fail(self.dut.device, test_name, begin_time)
 
-        try:
-            if self.dut.device.hard_reboot_on_fail:
-                self.dut.hard_power_cycle(self.pdu_devices)
-        except AttributeError:
-            pass
+    def on_device_fail(self, device, test_name, begin_time):
+        """Gets a generic device DUT bug report.
+
+        This method takes a bug report if the generic device does not have a
+        'take_bug_report_on_fail', or if the flag is true. This method also
+        power cycles if 'hard_reboot_on_fail' is True.
+
+        Args:
+            device: Generic device to gather logs from.
+            test_name: Name of the test that triggered this function.
+            begin_time: Logline format timestamp taken when the test started.
+        """
+        if (not hasattr(device, "take_bug_report_on_fail")
+                or device.take_bug_report_on_fail):
+            device.take_bug_report(test_name, begin_time)
+
+        if device.hard_reboot_on_fail:
+            device.reboot(reboot_type='hard', testbed_pdus=self.pdu_devices)
+
+    def download_ap_logs(self):
+        """Downloads the DHCP and hostapad logs from the access_point.
+
+        Using the current TestClassContext and TestCaseContext this method pulls
+        the DHCP and hostapd logs and outputs them to the correct path.
+        """
+        current_path = context.get_current_context().get_full_output_path()
+        dhcp_full_out_path = os.path.join(current_path, "dhcp_log.txt")
+
+        dhcp_log_file = open(dhcp_full_out_path, 'w')
+        dhcp_log_file.write(self.access_point.get_dhcp_logs())
+        dhcp_log_file.close()
+
+        hostapd_logs = self.access_point.get_hostapd_logs()
+        for interface in hostapd_logs:
+            out_name = interface + "_hostapd_log.txt"
+            hostapd_full_out_path = os.path.join(current_path, out_name)
+            hostapd_log_file = open(hostapd_full_out_path, 'w')
+            hostapd_log_file.write(hostapd_logs[interface])
+            hostapd_log_file.close()
diff --git a/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py
index 303b961..1c20301 100644
--- a/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py
@@ -25,7 +25,7 @@
 from acts.keys import Config
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_default_state
 from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
 from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
 
diff --git a/acts_tests/acts_contrib/test_utils/bt/bt_carkit_lib.py b/acts_tests/acts_contrib/test_utils/bt/bt_carkit_lib.py
index 959e65a..ce206da 100644
--- a/acts_tests/acts_contrib/test_utils/bt/bt_carkit_lib.py
+++ b/acts_tests/acts_contrib/test_utils/bt/bt_carkit_lib.py
@@ -26,17 +26,17 @@
 from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_EARPIECE
 from acts_contrib.test_utils.tel.tel_defines import AUDIO_ROUTE_SPEAKER
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
-from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
 from acts_contrib.test_utils.tel.tel_message_utils import sms_send_receive_verify
-from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
+from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
 from acts_contrib.test_utils.tel.tel_voice_utils import get_audio_route
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
 from acts_contrib.test_utils.tel.tel_voice_utils import set_audio_route
 from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_and_answer_call
 from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
 from acts.utils import exe_cmd
 from acts.utils import get_current_epoch_time
 
diff --git a/acts_tests/acts_contrib/test_utils/coex/coex_test_utils.py b/acts_tests/acts_contrib/test_utils/coex/coex_test_utils.py
index 4975757..a335f09 100644
--- a/acts_tests/acts_contrib/test_utils/coex/coex_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/coex/coex_test_utils.py
@@ -50,10 +50,10 @@
 from acts_contrib.test_utils.car.car_telecom_utils import wait_for_not_in_call
 from acts_contrib.test_utils.car.car_telecom_utils import wait_for_ringing
 from acts_contrib.test_utils.tel.tel_test_utils import get_phone_number
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
 from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
-from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_and_answer_call
 from acts_contrib.test_utils.wifi.wifi_power_test_utils import get_phone_ip
 from acts_contrib.test_utils.wifi.wifi_test_utils import reset_wifi
 from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_connect
diff --git a/acts_tests/acts_contrib/test_utils/net/net_test_utils.py b/acts_tests/acts_contrib/test_utils/net/net_test_utils.py
index ba1f513d..81416c8 100644
--- a/acts_tests/acts_contrib/test_utils/net/net_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/net/net_test_utils.py
@@ -24,15 +24,18 @@
 from acts import utils
 from acts.controllers import adb
 from acts.controllers.adb_lib.error import AdbError
+from acts.libs.proc import job
 from acts.utils import start_standing_subprocess
 from acts.utils import stop_standing_subprocess
 from acts_contrib.test_utils.net import connectivity_const as cconst
+from acts_contrib.test_utils.tel import tel_defines
 from acts_contrib.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
 from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
 from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
-from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
-from scapy.all import get_if_list
 
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from scapy.config import conf
+from scapy.compat import plain_str
 
 VPN_CONST = cconst.VpnProfile
 VPN_TYPE = cconst.VpnProfileType
@@ -287,7 +290,7 @@
     return vpn_profile
 
 
-def start_tcpdump(ad, test_name):
+def start_tcpdump(ad, test_name, interface="any"):
     """Start tcpdump on all interfaces.
 
     Args:
@@ -301,8 +304,8 @@
 
     file_name = "%s/tcpdump_%s_%s.pcap" % (TCPDUMP_PATH, ad.serial, test_name)
     ad.log.info("tcpdump file is %s", file_name)
-    cmd = "adb -s {} shell tcpdump -i any -s0 -w {}".format(ad.serial,
-                                                            file_name)
+    cmd = "adb -s {} shell tcpdump -i {} -s0 -w {}".format(ad.serial,
+                                                           interface, file_name)
     try:
         return start_standing_subprocess(cmd, 5)
     except Exception:
@@ -462,6 +465,34 @@
     return operator in carrier_supports_ipv6
 
 
+def is_carrier_supports_entitlement(dut):
+    """Verify if carrier supports entitlement
+
+    Args:
+        dut: Android device
+
+    Return:
+        True if carrier supports entitlement, otherwise false
+    """
+
+    carriers_supports_entitlement = [
+        tel_defines.CARRIER_VZW,
+        tel_defines.CARRIER_ATT,
+        tel_defines.CARRIER_TMO,
+        tel_defines.CARRIER_SBM]
+    operator = get_operator_name("log", dut)
+    return operator in carriers_supports_entitlement
+
+
+def set_cap_net_raw_capability():
+    """Set the CAP_NET_RAW capability
+
+    To send the Scapy packets, we need to get the CAP_NET_RAW capability first.
+    """
+    cap_net_raw = "sudo setcap cap_net_raw=eip $(readlink -f $(which act.py))"
+    utils.exe_cmd(cap_net_raw)
+
+
 def supports_ipv6_tethering(self, dut):
     """Check if provider supports IPv6 tethering.
 
@@ -478,19 +509,15 @@
 
 
 def start_usb_tethering(ad):
-    """Start USB tethering.
+    """Start USB tethering using #startTethering API.
+
+    Enable USB tethering by #startTethering API will break the RPC session,
+    Make sure you call #ad.recreate_services after call this API - b/149116235
 
     Args:
-        ad: android device object
+        ad: AndroidDevice object
     """
-    # TODO: test USB tethering by #startTethering API - b/149116235
-    ad.log.info("Starting USB Tethering")
-    ad.stop_services()
-    ad.adb.shell(USB_TETHERING_MODE, ignore_status=True)
-    ad.adb.wait_for_device()
-    ad.start_services()
-    if "rndis" not in ad.adb.shell(DEVICE_IP_ADDRESS):
-        raise signals.TestFailure("Unable to enable USB tethering.")
+    ad.droid.connectivityStartTethering(tel_defines.TETHERING_USB, False)
 
 
 def stop_usb_tethering(ad):
@@ -524,3 +551,29 @@
             return new_ifaces.pop()
         time.sleep(1)
     asserts.fail("Timeout waiting for tethering interface on host")
+
+
+def get_if_list():
+    """Returns a list containing all network interfaces.
+
+    The newest version of Scapy.get_if_list() returns the cached interfaces,
+    which might be out-dated, and unable to perceive the interface changes.
+    Use this method when need to monitoring the network interfaces changes.
+    Reference: https://github.com/secdev/scapy/pull/2707
+
+    Returns:
+        A list of the latest network interfaces. For example:
+        ['cvd-ebr', ..., 'eno1', 'enx4afa19a8dde1', 'lo', 'wlxd03745d68d88']
+    """
+
+    # Get ifconfig output
+    result = job.run([conf.prog.ifconfig])
+    if result.exit_status:
+        raise asserts.fail(
+            "Failed to execute ifconfig: {}".format(plain_str(result.stderr)))
+
+    interfaces = [
+        line[:line.find(':')] for line in plain_str(result.stdout).splitlines()
+        if ": flags" in line.lower()
+    ]
+    return interfaces
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_hotspot_traffic_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_hotspot_traffic_power_test.py
index a4c2df6..755f511 100644
--- a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_hotspot_traffic_power_test.py
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_hotspot_traffic_power_test.py
@@ -15,8 +15,8 @@
 #   limitations under the License.
 
 import time
-from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.tel.tel_wifi_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_wifi_utils import WIFI_CONFIG_APBAND_5G
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 import acts_contrib.test_utils.power.cellular.cellular_traffic_power_test as ctpt
 
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_voice_call_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_voice_call_power_test.py
index 802d8cc..f64a1e4 100644
--- a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_voice_call_power_test.py
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_voice_call_power_test.py
@@ -19,7 +19,8 @@
 from acts.controllers.anritsu_lib.md8475a import VirtualPhoneAutoAnswer
 
 import acts_contrib.test_utils.power.cellular.cellular_power_base_test as PWCEL
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call, hangup_call, set_phone_silent_mode
+from acts_contrib.test_utils.tel.tel_test_utils import set_phone_silent_mode
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call, hangup_call
 
 
 class PowerTelVoiceCallTest(PWCEL.PowerCellularLabBaseTest):
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_volte_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_volte_power_test.py
index 986878d..e3b9531 100644
--- a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_volte_power_test.py
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_volte_power_test.py
@@ -20,7 +20,8 @@
 import acts.controllers.anritsu_lib.md8475a as md8475a
 
 import acts_contrib.test_utils.power.cellular.cellular_power_base_test as PWCEL
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call, hangup_call, set_phone_silent_mode
+from acts_contrib.test_utils.tel.tel_test_utils import set_phone_silent_mode
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call, hangup_call
 
 
 class PowerTelVoLTECallTest(PWCEL.PowerCellularLabBaseTest):
diff --git a/acts_tests/acts_contrib/test_utils/tel/GFTInOutBaseTest.py b/acts_tests/acts_contrib/test_utils/tel/GFTInOutBaseTest.py
index 9a55400..9ff86be 100644
--- a/acts_tests/acts_contrib/test_utils/tel/GFTInOutBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/tel/GFTInOutBaseTest.py
@@ -13,16 +13,8 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-import sys
-import collections
-import random
+
 import time
-import datetime
-import os
-import logging
-import subprocess
-import math
-import re
 from acts import asserts
 from acts.test_decorators import test_info
 from acts.test_decorators import test_tracker_info
@@ -31,12 +23,12 @@
 from acts.libs.utils.multithread import multithread_func
 from acts.base_test import BaseTestClass
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_logging_utils import get_screen_shot_log
+from acts_contrib.test_utils.tel.tel_logging_utils import get_screen_shot_logs
+from acts_contrib.test_utils.tel.tel_logging_utils import log_screen_shot
 from acts_contrib.test_utils.tel.tel_test_utils import get_service_state_by_adb
-from acts_contrib.test_utils.tel.tel_test_utils import get_screen_shot_log
-from acts_contrib.test_utils.tel.tel_test_utils import get_screen_shot_logs
-from acts_contrib.test_utils.tel.tel_test_utils import log_screen_shot
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call
 from acts_contrib.test_utils.tel.gft_inout_utils import mo_voice_call
 from acts_contrib.test_utils.tel.gft_inout_utils import get_voice_call_type
 from acts_contrib.test_utils.tel.gft_inout_utils import verify_data_connection
@@ -61,6 +53,7 @@
 
     def setup_test(self):
         TelephonyBaseTest.setup_test(self)
+        self.user_params["telephony_auto_rerun"] = 0
         self.my_error_msg = ""
 
     def teardown_test(self):
@@ -82,8 +75,8 @@
             network_type_data = ad.droid.telephonyGetCurrentDataNetworkType()
             data_state = ad.droid.telephonyGetDataConnectionState()
             service_state = get_service_state_by_adb(ad.log,ad)
-            sim_state = ad.droid.telephonyGetSimState()
             wifi_info = ad.droid.wifiGetConnectionInfo()
+            sim_state = ad.droid.telephonyGetSimState()
             if ad.droid.wifiCheckState():
                 if wifi_info["supplicant_state"] == "completed":
                     ad.log.info("Wifi is connected=%s" %(wifi_info["SSID"]))
@@ -100,7 +93,7 @@
                 return False
         return True
 
-    def adjust_cellular_signal(self, power):
+    def adjust_cellular_signal(self, power, adjust_gradually=False):
         """Sets the attenuation value of cellular port
         Args:
              power: power level for attenuator to be set
@@ -108,14 +101,20 @@
             return True if ceullular port is set
         """
         if self.user_params.get("Attenuator"):
-            self.log.info("adjust cellular signal set mini-circuits to %s" %(power))
-            self.attenuators[CELLULAR_PORT].set_atten(power)
+            if 'adjust_gradually' in self.user_params:
+                adjust_gradually = self.user_params.get('adjust_gradually')
+            if adjust_gradually:
+                self.log.info("adjust cellular signal gradually to mini-circuits to %s" %(power))
+                self.adjust_atten_slowly(10, NO_SERVICE_AREA)
+            else:
+                self.log.info("adjust cellular signal set mini-circuits to %s" %(power))
+                self.attenuators[CELLULAR_PORT].set_atten(power)
             return True
         else:
             self.log.info("Attenuator is set to False in config file")
             return False
 
-    def adjust_wifi_signal(self, power):
+    def adjust_wifi_signal(self, power, adjust_gradually=False):
         """Sets the attenuation value of wifi port
         Args:
              power: power level for attenuator to be set
@@ -123,10 +122,16 @@
             return True if wifi port is set
         """
         if self.user_params.get("Attenuator"):
-            self.log.info("adjust wifi signal set mini-circuits to %s" %(power))
-            self.attenuators[WIFI_PORT].set_atten(power)
-            self.attenuators[2].set_atten(power)
-            self.attenuators[3].set_atten(power)
+            if 'adjust_gradually' in self.user_params:
+                adjust_gradually = self.user_params.get('adjust_gradually')
+            if adjust_gradually:
+                self.log.info("adjust wifi signal set mini-circuits to %s" %(power))
+                self.adjust_atten_slowly(10, NO_WIFI_AREA)
+            else:
+                self.log.info("adjust wifi signal and set mini-circuits to %s" %(power))
+                self.attenuators[WIFI_PORT].set_atten(power)
+                self.attenuators[2].set_atten(power)
+                self.attenuators[3].set_atten(power)
         else:
             self.log.info("Attenuator is set to False in config file")
             return False
@@ -169,8 +174,10 @@
     def adjust_atten_slowly(self, adjust_level, move_to, step=9 , step_time=5):
         """adjust attenuator slowly
         Args:
-            port: port of attenuator
-            power: power level for attenuator to be set
+            adjust_level: adjust power level for each cycle
+            move_to: NO_SERVICE_AREA, IN_SERVICE_AREA , WIFI_AREA, NO_WIFI_AREA
+            step: adjust attenuator how many time
+            step_time: wait for how many sec for each loop
         Returns:
             return True if given port is set
         """
@@ -217,38 +224,61 @@
                 self.check_network()
         return True
 
-    def verify_device_status(self, ad, call_type=None, end_call=True, talk_time=30):
+    def verify_device_status(self, ad, call_type=None, end_call=True,
+        talk_time=30, verify_data=True, verify_voice=True, data_retries=2, voice_retries=2):
         """verfiy device status includes network service, data connection and voice call
         Args:
             ad: android device
             call_type: WFC call, VOLTE call. CSFB call, voice call
             end_call: hangup call after voice call flag
             talk_time: in call duration in sec
+            verify_data: flag to check data connection
+            verify_voice: flag to check voice
+            data_retries: retry times for data verification
+            voice_retris:retry times for voice call
         Returns:
-            return True if given port is set
+            return True if pass
         """
-        test_result = True
         tasks = [(check_network_service, (ad, )) for ad in self.android_devices]
         if not multithread_func(self.log, tasks):
             log_screen_shot(ad, "check_network_service_fail")
-            test_result = False
-        tasks = [(verify_data_connection, (ad, )) for ad in self.android_devices]
-        if not multithread_func(self.log, tasks):
-            log_screen_shot(ad, "verify_data_connection_fail")
-            test_result = False
-        if call_type != None:
-            tasks = [(mo_voice_call, (self.log, ad, call_type, end_call, talk_time))
+            ad.log.info("check_network_service fail")
+            return False
+        else:
+            self.log.info("check_network_service pass")
+        if verify_data:
+            tasks = [(verify_data_connection, (ad, data_retries ))
                 for ad in self.android_devices]
-        if not multithread_func(self.log, tasks):
-            log_screen_shot(ad, "verify_voice_call_fail")
-            test_result = False
-        return test_result
+            if not multithread_func(self.log, tasks):
+                log_screen_shot(ad, "verify_data_connection_fail")
+                ad.log.info("verify_data_connection_fail")
+                return False
+            else:
+                self.log.info("verify_data_connection pass")
+        if verify_voice:
+            if call_type:
+                tasks = [(mo_voice_call, (self.log, ad, call_type, end_call, talk_time,
+                    voice_retries)) for ad in self.android_devices]
+            if not multithread_func(self.log, tasks):
+                log_screen_shot(ad, "verify_voice_call_fail")
+                ad.log.info("verify_voice_call_fail")
+                return False
+            else:
+                self.log.info("verify_voice_call pass")
+                return True
+        return True
 
-    def _on_fail(self, error_msg=""):
+    def _on_failure(self, error_msg="", assert_on_fail=True, test_result=False):
         """ operation on fail
 
         Args:
             error_msg: error message to be written to log
 
         """
+        if assert_on_fail:
+            asserts.assert_true(False, "assert_on_fail: %s."
+                %(error_msg),extras={"failure_cause": error_msg})
+        for ad in self.android_devices:
+            log_screen_shot(ad, error_msg)
         self.log.info(error_msg)
+
diff --git a/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py b/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py
index 67019c5..ca65cb8 100644
--- a/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py
@@ -32,10 +32,19 @@
 from acts import utils
 from acts.libs.utils.multithread import multithread_func
 from acts.libs.utils.multithread import run_multithread_func
-
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
+from acts_contrib.test_utils.tel.tel_defines import SINGLE_SIM_CONFIG, MULTI_SIM_CONFIG
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
+from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_ABSENT
+from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
+from acts_contrib.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import CHIPSET_MODELS_LIST
 from acts_contrib.test_utils.tel.tel_bootloader_utils import flash_radio
 from acts_contrib.test_utils.tel.tel_ims_utils import activate_wfc_on_device
 from acts_contrib.test_utils.tel.tel_logging_utils import disable_qxdm_logger
+from acts_contrib.test_utils.tel.tel_logging_utils import get_screen_shot_log
 from acts_contrib.test_utils.tel.tel_logging_utils import set_qxdm_logger_command
 from acts_contrib.test_utils.tel.tel_logging_utils import start_qxdm_logger
 from acts_contrib.test_utils.tel.tel_logging_utils import start_qxdm_loggers
@@ -46,17 +55,15 @@
 from acts_contrib.test_utils.tel.tel_logging_utils import start_tcpdumps
 from acts_contrib.test_utils.tel.tel_logging_utils import stop_tcpdumps
 from acts_contrib.test_utils.tel.tel_logging_utils import get_tcpdump_log
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phone_default_state
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phone_idle
 from acts_contrib.test_utils.tel.tel_subscription_utils import initial_set_up_for_subid_information
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_default_sub_for_all_services
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
 from acts_contrib.test_utils.tel.tel_test_utils import build_id_override
 from acts_contrib.test_utils.tel.tel_test_utils import enable_connectivity_metrics
 from acts_contrib.test_utils.tel.tel_test_utils import enable_radio_log_on
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_default_state
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_idle
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
 from acts_contrib.test_utils.tel.tel_test_utils import force_connectivity_metrics_upload
-from acts_contrib.test_utils.tel.tel_test_utils import get_screen_shot_log
 from acts_contrib.test_utils.tel.tel_test_utils import get_sim_state
 from acts_contrib.test_utils.tel.tel_test_utils import install_apk
 from acts_contrib.test_utils.tel.tel_test_utils import print_radio_info
@@ -76,15 +83,7 @@
 from acts_contrib.test_utils.tel.tel_test_utils import check_google_fi_activated
 from acts_contrib.test_utils.tel.tel_test_utils import phone_switch_to_msim_mode
 from acts_contrib.test_utils.tel.tel_test_utils import activate_esim_using_suw
-from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
-from acts_contrib.test_utils.tel.tel_defines import SINGLE_SIM_CONFIG, MULTI_SIM_CONFIG
-from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
-from acts_contrib.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
-from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_ABSENT
-from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
-from acts_contrib.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_DISABLED
-from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
-from acts_contrib.test_utils.tel.tel_defines import CHIPSET_MODELS_LIST
+from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected
 
 
 class TelephonyBaseTest(BaseTestClass):
@@ -504,6 +503,11 @@
         stop_tcpdumps(self.android_devices)
 
     def on_fail(self, test_name, begin_time):
+        for ad in self.android_devices:
+            # open Phone information page
+            ad.adb.shell("am start -n com.android.phone/.settings.RadioInfo")
+            time.sleep(3)
+            ad.screenshot(f"{ad.serial}_last_screen")
         self._take_bug_report(test_name, begin_time)
 
     def on_pass(self, test_name, begin_time):
diff --git a/acts_tests/acts_contrib/test_utils/tel/anritsu_utils.py b/acts_tests/acts_contrib/test_utils/tel/anritsu_utils.py
index 04a9e35..2e04dac 100644
--- a/acts_tests/acts_contrib/test_utils/tel/anritsu_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/anritsu_utils.py
@@ -47,11 +47,11 @@
 from acts_contrib.test_utils.tel.tel_defines import EventSmsDeliverSuccess
 from acts_contrib.test_utils.tel.tel_defines import EventSmsSentSuccess
 from acts_contrib.test_utils.tel.tel_defines import EventSmsReceived
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_idle
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
-from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_droid_not_in_call
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phone_idle
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_droid_not_in_call
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_and_answer_call
 
 # Timers
 # Time to wait after registration before sending a command to Anritsu
@@ -480,7 +480,7 @@
         None
     """
     # Setting IP address for internet connection sharing
-    # Google Fi _init_PDN 
+    # Google Fi _init_PDN
     if sim_card == FiTMO or sim_card == FiSPR or sim_card == FiUSCC:
         pdn.ue_address_ipv4 = ipv4
         pdn.ue_address_ipv6 = ipv6
@@ -498,9 +498,9 @@
             pdn.secondary_dns_address_ipv4 = Fi_DNS_IPV4_ADDR_Sec
             pdn.dns_address_ipv6 = Fi_DNS_IPV6_ADDR
             pdn.cscf_address_ipv4 = Fi_CSCF_IPV4_ADDR_Data
-            pdn.cscf_address_ipv6 = Fi_CSCF_IPV6_ADDR_Data    
+            pdn.cscf_address_ipv6 = Fi_CSCF_IPV6_ADDR_Data
     # Pixel Lab _init_PDN_
-    else:  
+    else:
         anritsu_handle.gateway_ipv4addr = GATEWAY_IPV4_ADDR
         pdn.ue_address_ipv4 = ipv4
         pdn.ue_address_ipv6 = ipv6
@@ -550,7 +550,7 @@
             vnid.cscf_monitoring_ua = CSCF_Monitoring_UA_URI
         vnid.psap = Switch.ENABLE
         vnid.psap_auto_answer = Switch.ENABLE
-    else:   
+    else:
         vnid.cscf_address_ipv4 = CSCF_IPV4_ADDR
         vnid.cscf_address_ipv6 = ipv6_address
         vnid.imscscf_iptype = ip_type
diff --git a/acts_tests/acts_contrib/test_utils/tel/gft_inout_utils.py b/acts_tests/acts_contrib/test_utils/tel/gft_inout_utils.py
index b93baed..2e4cfd4 100644
--- a/acts_tests/acts_contrib/test_utils/tel/gft_inout_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/gft_inout_utils.py
@@ -15,27 +15,27 @@
 #   limitations under the License.
 
 import time
-
-from acts_contrib.test_utils.tel.tel_test_utils import get_telephony_signal_strength
-from acts_contrib.test_utils.tel.tel_test_utils import get_service_state_by_adb
-from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
-from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
-from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
-from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
-from acts_contrib.test_utils.tel.tel_test_utils import log_screen_shot
-from acts_contrib.test_utils.tel.tel_ims_utils import is_ims_registered
-from acts_contrib.test_utils.tel.tel_defines  import DATA_STATE_CONNECTED
-from acts_contrib.test_utils.tel.tel_defines  import SERVICE_STATE_IN_SERVICE
-from acts_contrib.test_utils.tel.tel_defines  import SERVICE_STATE_OUT_OF_SERVICE
 from acts_contrib.test_utils.tel.gft_inout_defines import VOLTE_CALL
 from acts_contrib.test_utils.tel.gft_inout_defines import CSFB_CALL
 from acts_contrib.test_utils.tel.gft_inout_defines import WFC_CALL
+from acts_contrib.test_utils.tel.tel_defines  import DATA_STATE_CONNECTED
+from acts_contrib.test_utils.tel.tel_defines  import SERVICE_STATE_IN_SERVICE
+from acts_contrib.test_utils.tel.tel_defines  import SERVICE_STATE_OUT_OF_SERVICE
+from acts_contrib.test_utils.tel.tel_logging_utils import log_screen_shot
+from acts_contrib.test_utils.tel.tel_ims_utils import is_ims_registered
+from acts_contrib.test_utils.tel.tel_test_utils import get_telephony_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import get_service_state_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts_contrib.test_utils.tel.tel_data_utils import browsing_test
 
 
-def check_no_service_time(ad, timeout=60):
+def check_no_service_time(ad, timeout=120):
     """ check device is no service or not
 
         Args:
@@ -60,7 +60,8 @@
         %(timeout, service_state))
     return False
 
-def check_back_to_service_time(ad, timeout=30):
+
+def check_back_to_service_time(ad, timeout=120):
     """ check device is back to service or not
 
         Args:
@@ -75,7 +76,8 @@
         if service_state == SERVICE_STATE_IN_SERVICE:
             if i==0:
                 check_network_service(ad)
-                ad.log.info("Skip check_back_to_service_time. Device is in-service and service_state=%s" %(service_state))
+                ad.log.info("Skip check_back_to_service_time. Service_state=%s"
+                    %(service_state))
                 return True
             else:
                 ad.log.info("device is back to service in %s sec and service_state=%s"
@@ -88,6 +90,7 @@
         %(timeout, service_state))
     return False
 
+
 def check_network_service(ad):
     """ check network service
 
@@ -106,11 +109,13 @@
     ad.log.info("networkType_data=%s" %(network_type_data))
     ad.log.info("service_state=%s" %(service_state))
     if service_state == SERVICE_STATE_OUT_OF_SERVICE:
+        log_screen_shot(ad, "device_out_of_service")
         return False
     return True
 
 
-def mo_voice_call(log, ad, call_type, end_call=True, talk_time=15):
+def mo_voice_call(log, ad, call_type, end_call=True, talk_time=15,
+    retries=1, retry_time=30):
     """ MO voice call and check call type.
         End call if necessary.
 
@@ -120,24 +125,32 @@
             call_type: WFC call, VOLTE call. CSFB call, voice call
             end_call: hangup call after voice call flag
             talk_time: in call duration in sec
+            retries: retry times
+            retry_time: wait for how many sec before next retry
 
         Returns:
             True if pass; False if fail.
     """
     callee_number = ad.mt_phone_number
-    ad.log.info("MO voice call")
+    ad.log.info("MO voice call. call_type=%s" %(call_type))
     if is_phone_in_call(log, ad):
         ad.log.info("%s is in call. hangup_call before initiate call" %(callee_number))
         hangup_call(log, ad)
         time.sleep(1)
 
-    if initiate_call(log, ad, callee_number):
-        time.sleep(5)
-        check_voice_call_type(ad,call_type)
-        get_voice_call_type(ad)
-    else:
-        ad.log.error("initiate_call fail")
-        return False
+    for i in range(retries):
+        ad.log.info("mo_voice_call attempt %d", i + 1)
+        if initiate_call(log, ad, callee_number):
+            time.sleep(5)
+            check_voice_call_type(ad,call_type)
+            get_voice_call_type(ad)
+            break
+        else:
+            ad.log.error("initiate_call fail attempt %d", i + 1)
+            time.sleep(retry_time)
+            if i+1 == retries:
+                ad.log.error("mo_voice_call retry failure")
+                return False
 
     time.sleep(10)
     if end_call:
@@ -146,10 +159,14 @@
             ad.log.info("end voice call")
             if not hangup_call(log, ad):
                 ad.log.error("end call fail")
+                ad.droid.telecomShowInCallScreen()
+                log_screen_shot(ad, "end_call_fail")
                 return False
         else:
             #Call drop is unexpected
             ad.log.error("%s Unexpected call drop" %(call_type))
+            ad.droid.telecomShowInCallScreen()
+            log_screen_shot(ad, "call_drop")
             return False
         ad.log.info("%s successful" %(call_type))
     return True
@@ -166,6 +183,7 @@
             True if pass; False if fail.
     """
     if is_phone_in_call(ad.log, ad):
+        ad.droid.telecomShowInCallScreen()
         log_screen_shot(ad, "expected_call_type_%s" %call_type)
         if call_type == CSFB_CALL:
             if not is_phone_in_call_csfb(ad.log, ad):
@@ -214,31 +232,36 @@
         ad.log.error("device is not in call")
     return "UNKNOWN"
 
-def verify_data_connection(ad):
+
+def verify_data_connection(ad, retries=3, retry_time=30):
     """ verify data connection
 
         Args:
             ad: android device
+            retries: retry times
+            retry_time: wait for how many sec before next retry
 
         Returns:
             True if pass; False if fail.
     """
-    data_state = ad.droid.telephonyGetDataConnectionState()
-    wifi_info = ad.droid.wifiGetConnectionInfo()
-    if wifi_info["supplicant_state"] == "completed":
-        ad.log.info("Wifi is connected=%s" %(wifi_info["SSID"]))
-    if data_state != DATA_STATE_CONNECTED:
-        ad.log.info("data is not connected. data_state=%s" %(data_state))
-        return False
-    else:
+    for i in range(retries):
+        data_state = ad.droid.telephonyGetDataConnectionState()
+        wifi_info = ad.droid.wifiGetConnectionInfo()
+        if wifi_info["supplicant_state"] == "completed":
+            ad.log.info("Wifi is connected=%s" %(wifi_info["SSID"]))
+        ad.log.info("verify_data_connection attempt %d", i + 1)
         if not verify_internet_connection(ad.log, ad, retries=3):
             data_state = ad.droid.telephonyGetDataConnectionState()
             network_type_data = ad.droid.telephonyGetCurrentDataNetworkType()
             ad.log.error("verify_internet fail. data_state=%s, network_type_data=%s"
-            %(data_state, network_type_data))
-            return False
-    ad.log.info("verify_data_connection pass")
-    return True
+                %(data_state, network_type_data))
+            ad.log.info("verify_data_connection fail attempt %d", i + 1)
+            log_screen_shot(ad, "verify_internet")
+            time.sleep(retry_time)
+        else:
+            ad.log.info("verify_data_connection pass")
+            return True
+    return False
 
 
 def check_ims_state(ad):
@@ -261,6 +284,22 @@
     return r1
 
 
+def browsing_test_ping_retry(ad):
+    """ If browse test fails, use ping to test data connection
 
+        Args:
+            ad: android device
 
-
+        Returns:
+            True if pass; False if fail.
+    """
+    if not browsing_test(ad.log, ad):
+        ad.log.error("Failed to browse websites!")
+        if verify_data_connection(ad):
+            ad.log.info("Ping success!")
+            return True
+        else:
+            ad.log.info("Ping fail!")
+            return False
+    else:
+        ad.log.info("Successful to browse websites!")
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_5g_test_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_5g_test_utils.py
index 9635738..6789647 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_5g_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_5g_test_utils.py
@@ -15,13 +15,8 @@
 #   limitations under the License.
 
 import time
-import random
-import re
 
-from queue import Empty
-from acts.utils import rand_ascii_str
 from acts.libs.utils.multithread import multithread_func
-from acts_contrib.test_utils.tel.tel_defines import GEN_5G
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_NR_LTE_GSM_WCDMA
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_NR_ONLY
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
@@ -30,59 +25,70 @@
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_WCDMA_ONLY
-from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_generation
-from acts_contrib.test_utils.tel.tel_test_utils import get_current_override_network_type
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_volte
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_csfb
 from acts_contrib.test_utils.tel.tel_5g_utils import is_current_network_5g_nsa
 from acts_contrib.test_utils.tel.tel_5g_utils import is_current_network_5g_sa
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_csfb
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import get_current_override_network_type
+from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_wifi_utils import wifi_toggle_state
 
 
-def provision_device_for_5g(log, ads, nr_type=None):
+def provision_device_for_5g(log, ads, nr_type = None, sub_id = None,
+                            mmwave = None):
     """Provision Devices for 5G
 
     Args:
         log: Log object.
         ads: android device object(s).
-        nr_type: NR network type
+        nr_type: NR network type.
+        sub_id: The target SIM for querying.
+        mmwave: True to detect 5G millimeter wave, False to detect sub-6,
+            None to detect both.
 
     Returns:
         True: Device(s) are provisioned on 5G
         False: Device(s) are not provisioned on 5G
     """
     if nr_type == 'sa':
-        if not provision_device_for_5g_sa(log, ads):
+        if not provision_device_for_5g_sa(
+            log, ads, sub_id=sub_id, mmwave=mmwave):
             return False
     elif nr_type == 'nsa':
-        if not provision_device_for_5g_nsa(log, ads, nr_type='nsa'):
+        if not provision_device_for_5g_nsa(
+            log, ads, sub_id=sub_id, mmwave=mmwave):
             return False
     elif nr_type == 'mmwave':
-        if not provision_device_for_5g_nsa(log, ads, nr_type='mmwave'):
+        if not provision_device_for_5g_nsa(
+            log, ads, sub_id=sub_id, mmwave=mmwave):
             return False
     else:
-        if not provision_device_for_5g_nsa(log, ads, nr_type='nsa'):
+        if not provision_device_for_5g_nsa(
+            log, ads, sub_id=sub_id, mmwave=mmwave):
             return False
     return True
 
 
-def provision_device_for_5g_nsa(log, ads, nr_type=None):
+def provision_device_for_5g_nsa(log, ads, sub_id = None, mmwave = None):
     """Provision Devices for 5G NSA
 
     Args:
         log: Log object.
         ads: android device object(s).
-        nr_type: 'sa' for 5G standalone, 'nsa' for 5G non-standalone, 'mmwave' for 5G millimeter
-                wave
+        sub_id: The target SIM for querying.
+        mmwave: True to detect 5G millimeter wave, False to detect sub-6,
+            None to detect both.
 
     Returns:
         True: Device(s) are provisioned on 5G NSA
         False: Device(s) are not provisioned on 5G NSA
     """
+    sub_id = sub_id if sub_id else ad.droid.subscriptionGetDefaultDataSubId()
+
     if isinstance(ads, list):
         # Mode Pref
         tasks = [(set_preferred_mode_for_5g, [ad]) for ad in ads]
@@ -90,7 +96,7 @@
             log.error("failed to set preferred network mode on 5g")
             return False
         # Attach
-        tasks = [(is_current_network_5g_nsa, [ad, nr_type]) for ad in ads]
+        tasks = [(is_current_network_5g_nsa, [ad, sub_id, mmwave]) for ad in ads]
         if not multithread_func(log, tasks):
             log.error("phone not on 5g")
             return False
@@ -100,7 +106,7 @@
         set_preferred_mode_for_5g(ads)
 
         # Attach nsa5g
-        if not is_current_network_5g_nsa(ads, nr_type):
+        if not is_current_network_5g_nsa(ads, sub_id, mmwave):
             ads.log.error("Phone not attached on 5g")
             return False
         return True
@@ -182,29 +188,35 @@
     return True
 
 
-def verify_5g_attach_for_both_devices(log, ads, nr_type=None):
+def verify_5g_attach_for_both_devices(log, ads, sub_id = None, nr_type = None,
+                                      mmwave = None):
     """Verify the network is attached
 
     Args:
         log: Log object.
         ads: android device object(s).
-        nr_type: 'sa' for 5G standalone, 'nsa' for 5G non-standalone, 'mmwave' for 5G millimeter
-                wave
+        sub_id: The target SIM for querying.
+        nr_type: 'sa' for 5G standalone, 'nsa' for 5G non-standalone,
+            'mmwave' for 5G millimeter wave.
+        mmwave: True to detect 5G millimeter wave, False to detect sub-6,
+            None to detect both.
 
     Returns:
         True: Device(s) are attached on 5G
         False: Device(s) are not attached on 5G NSA
     """
+    sub_id = sub_id if sub_id else ad.droid.subscriptionGetDefaultDataSubId()
+
     if nr_type=='sa':
         # Attach
-        tasks = [(is_current_network_5g_sa, [ad]) for ad in ads]
+        tasks = [(is_current_network_5g_sa, [ad, sub_id, mmwave]) for ad in ads]
         if not multithread_func(log, tasks):
             log.error("phone not on 5g sa")
             return False
         return True
     else:
         # Attach
-        tasks = [(is_current_network_5g_nsa, [ad, nr_type]) for ad in ads]
+        tasks = [(is_current_network_5g_nsa, [ad, sub_id, mmwave]) for ad in ads]
         if not multithread_func(log, tasks):
             log.error("phone not on 5g nsa")
             return False
@@ -225,17 +237,22 @@
     return set_preferred_network_mode_pref(ad.log, ad, sub_id, mode)
 
 
-def provision_device_for_5g_sa(log, ads):
+def provision_device_for_5g_sa(log, ads, sub_id = None, mmwave = None):
     """Provision Devices for 5G SA
 
     Args:
         log: Log object.
         ads: android device object(s).
+        sub_id: The target SIM for querying.
+        mmwave: True to detect 5G millimeter wave, False to detect sub-6,
+            None to detect both.
 
     Returns:
         True: Device(s) are provisioned on 5G SA
         False: Device(s) are not provisioned on 5G SA
     """
+    sub_id = sub_id if sub_id else ad.droid.subscriptionGetDefaultDataSubId()
+
     if isinstance(ads, list):
         # Mode Pref
         tasks = [(set_preferred_mode_for_5g, [ad, None, NETWORK_MODE_NR_ONLY]) for ad in ads]
@@ -243,7 +260,7 @@
             log.error("failed to set preferred network mode on 5g SA")
             return False
 
-        tasks = [(is_current_network_5g_sa, [ad]) for ad in ads]
+        tasks = [(is_current_network_5g_sa, [ad, sub_id, mmwave]) for ad in ads]
         if not multithread_func(log, tasks):
             log.error("phone not on 5g SA")
             return False
@@ -252,46 +269,59 @@
         # Mode Pref
         set_preferred_mode_for_5g(ads, None, NETWORK_MODE_NR_ONLY)
 
-        if not is_current_network_5g_sa(ads):
+        if not is_current_network_5g_sa(ads, sub_id, mmwave):
             ads.log.error("Phone not attached on SA 5g")
             return False
         return True
 
 
-def check_current_network_5g(ad, timeout=30, nr_type=None):
+def check_current_network_5g(
+    ad, sub_id = None, nr_type = None, mmwave = None, timeout = 30):
     """Verifies data network type is on 5G
 
     Args:
         ad: android device object.
+        sub_id: The target SIM for querying.
         timeout: max time to wait for event
         nr_type: 'sa' for 5G standalone, 'nsa' for 5G non-standalone, 'mmwave' for 5G millimeter
-                wave
+                wave.
+        mmwave: True to detect 5G millimeter wave, False to detect sub-6,
+            None to detect both.
 
     Returns:
         True: if data is on 5g
         False: if data is not on 5g
     """
+    sub_id = sub_id if sub_id else ad.droid.subscriptionGetDefaultDataSubId()
+
     if nr_type == 'sa':
-        if not is_current_network_5g_sa(ad):
+        if not is_current_network_5g_sa(ad, sub_id, mmwave=mmwave):
             return False
     else:
-        if not is_current_network_5g_nsa(ad, nr_type= nr_type, timeout=timeout):
+        if not is_current_network_5g_nsa(ad, sub_id, mmwave=mmwave,
+                                         timeout=timeout):
             return False
     return True
 
 
-def test_activation_by_condition(ad, from_3g=False, nr_type=None, precond_func=None):
+def test_activation_by_condition(ad, sub_id=None, from_3g=False, nr_type=None,
+                                 precond_func=None, mmwave=None):
     """Test 5G activation based on various pre-conditions.
 
     Args:
         ad: android device object.
+        sub_id: The target SIM for querying.
         from_3g: If true, test 5G activation from 3G attaching. Otherwise, starting from 5G attaching.
         nr_type: check the band of NR network. Default is to check sub-6.
         precond_func: A function to execute pre conditions before testing 5G activation.
+        mmwave: True to detect 5G millimeter wave, False to detect sub-6,
+            None to detect both.
 
     Returns:
         If success, return true. Otherwise, return false.
     """
+    sub_id = sub_id if sub_id else ad.droid.subscriptionGetDefaultDataSubId()
+
     wifi_toggle_state(ad.log, ad, False)
     toggle_airplane_mode(ad.log, ad, False)
     if not from_3g:
@@ -318,7 +348,7 @@
             ad.log.error("Fail to ensure initial data in 4G")
         # 5G attach
         ad.log.info("Waiting for 5g NSA attach for 60 secs")
-        if is_current_network_5g_nsa(ad, nr_type= nr_type, timeout=60):
+        if is_current_network_5g_nsa(ad, sub_id, mmwave=mmwave, timeout=60):
             ad.log.info("Success! attached on 5g NSA")
             return True
         else:
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_5g_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_5g_utils.py
index 58ce9ee..f35272e 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_5g_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_5g_utils.py
@@ -19,140 +19,121 @@
 from acts_contrib.test_utils.tel.tel_defines import DisplayInfoContainer
 from acts_contrib.test_utils.tel.tel_defines import EventDisplayInfoChanged
 
-def is_current_network_5g_nsa(ad, nr_type=None, timeout=30):
+def is_current_network_5g_nsa(ad, sub_id = None, mmwave = None, timeout=30):
     """Verifies 5G NSA override network type
     Args:
         ad: android device object.
-        nr_type: 'nsa' for 5G non-standalone, 'mmwave' for 5G millimeter
-                wave
+        sub_id: The target SIM for querying.
+        mmwave: True to detect 5G millimeter wave, False to detect sub-6,
+            None to detect both.
         timeout: max time to wait for event.
 
     Returns:
         True: if data is on nsa5g NSA
         False: if data is not on nsa5g NSA
     """
-    ad.ed.clear_events(EventDisplayInfoChanged)
-    ad.droid.telephonyStartTrackingDisplayInfoChange()
-    if nr_type == 'mmwave':
-        nsa_band = OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_MMWAVE
-    elif nr_type == 'nsa':
-        nsa_band = OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_NSA
+    sub_id = sub_id if sub_id else ad.droid.subscriptionGetDefaultDataSubId()
+
+    def _nsa_display_monitor(ad, sub_id, mmwave, timeout):
+        ad.ed.clear_events(EventDisplayInfoChanged)
+        ad.droid.telephonyStartTrackingDisplayInfoChangeForSubscription(sub_id)
+        if mmwave:
+            nsa_band = OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_MMWAVE
+        else:
+            nsa_band = OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_NSA
+        try:
+            event = ad.ed.wait_for_event(
+                    EventDisplayInfoChanged,
+                    ad.ed.is_event_match,
+                    timeout=timeout,
+                    field=DisplayInfoContainer.OVERRIDE,
+                    value=nsa_band)
+            ad.log.info("Got expected event %s", event)
+            return True
+        except Empty:
+            ad.log.info("No event for display info change with <%s>", nsa_band)
+            ad.screenshot("5g_nsa_icon_checking")
+            return False
+        finally:
+            ad.droid.telephonyStopTrackingServiceStateChangeForSubscription(
+                sub_id)
+
+    if mmwave is None:
+        return _nsa_display_monitor(
+            ad, sub_id, mmwave=False, timeout=timeout) or _nsa_display_monitor(
+            ad, sub_id, mmwave=True, timeout=timeout)
     else:
-        nsa_band = OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_NSA
-    try:
-        event = ad.ed.wait_for_event(
-                EventDisplayInfoChanged,
-                ad.ed.is_event_match,
-                timeout=timeout,
-                field=DisplayInfoContainer.OVERRIDE,
-                value=nsa_band)
-        ad.log.info("Got expected event %s", event)
-        return True
-    except Empty:
-        ad.log.info("No event for display info change")
-        ad.screenshot("5g_nsa_icon_checking")
-        return False
-    finally:
-        ad.droid.telephonyStopTrackingDisplayInfoChange()
-    return None
+        return _nsa_display_monitor(ad, sub_id, mmwave, timeout)
 
 
-def is_current_network_5g_nsa_for_subscription(ad, nr_type=None, timeout=30, sub_id=None):
-    """Verifies 5G NSA override network type for subscription id.
-    Args:
-        ad: android device object.
-        nr_type: 'nsa' for 5G non-standalone, 'mmwave' for 5G millimeter wave
-        timeout: max time to wait for event.
-        sub_id: subscription id.
-
-    Returns:
-        True: if data is on nsa5g NSA
-        False: if data is not on nsa5g NSA
-    """
-    if not sub_id:
-        return is_current_network_5g_nsa(ad, nr_type=nr_type)
-
-    voice_sub_id_changed = False
-    current_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
-    if current_sub_id != sub_id:
-        ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
-        voice_sub_id_changed = True
-
-    result = is_current_network_5g_nsa(ad, nr_type=nr_type)
-
-    if voice_sub_id_changed:
-        ad.droid.subscriptionSetDefaultVoiceSubId(current_sub_id)
-
-    return result
-
-
-def is_current_network_5g_sa(ad):
+def is_current_network_5g_sa(ad, sub_id = None, mmwave = None):
     """Verifies 5G SA override network type
 
     Args:
         ad: android device object.
+        sub_id: The target SIM for querying.
+        mmwave: True to detect 5G millimeter wave, False to detect sub-6,
+            None to detect both.
 
     Returns:
         True: if data is on 5g SA
         False: if data is not on 5g SA
     """
-    network_connected = ad.droid.telephonyGetCurrentDataNetworkType()
-    if network_connected == 'NR':
-        ad.log.debug("Network is currently connected to %s", network_connected)
-        return True
-    else:
-        ad.log.error("Network is currently connected to %s, Expected on NR", network_connected)
-        ad.screenshot("5g_sa_icon_checking")
+    sub_id = sub_id if sub_id else ad.droid.subscriptionGetDefaultDataSubId()
+    current_rat = ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
+        sub_id)
+    # TODO(richardwychang): check SA MMWAVE when function ready.
+    sa_type = ['NR',]
+    if mmwave is None:
+        if current_rat in sa_type:
+            ad.log.debug("Network is currently connected to %s", current_rat)
+            return True
+        else:
+            ad.log.error(
+                "Network is currently connected to %s, Expected on %s",
+                current_rat, sa_type)
+            ad.screenshot("5g_sa_icon_checking")
+            return False
+    elif mmwave:
+        ad.log.error("SA MMWAVE currently not support.")
         return False
+    else:
+        if current_rat == 'NR':
+            ad.log.debug("Network is currently connected to %s", current_rat)
+            return True
+        else:
+            ad.log.error(
+                "Network is currently connected to %s, Expected on NR",
+                current_rat)
+            ad.screenshot("5g_sa_icon_checking")
+            return False
 
 
-def is_current_network_5g(ad, nr_type=None, timeout=30):
+def is_current_network_5g(ad, sub_id = None, nr_type = None, mmwave = None,
+                          timeout = 30):
     """Verifies 5G override network type
 
     Args:
         ad: android device object
-        nr_type: 'sa' for 5G standalone, 'nsa' for 5G non-standalone, 'mmwave' for 5G millimeter
-                wave
+        sub_id: The target SIM for querying.
+        nr_type: 'sa' for 5G standalone, 'nsa' for 5G non-standalone.
+        mmwave: True to detect 5G millimeter wave, False to detect sub-6,
+            None to detect both.
         timeout: max time to wait for event.
 
     Returns:
         True: if data is on 5G regardless of SA or NSA
         False: if data is not on 5G refardless of SA or NSA
     """
+    sub_id = sub_id if sub_id else ad.droid.subscriptionGetDefaultDataSubId()
+
     if nr_type == 'nsa':
-        return is_current_network_5g_nsa(ad, nr_type = 'nsa', timeout=timeout)
+        return is_current_network_5g_nsa(
+            ad, sub_id=sub_id, mmwave=mmwave, timeout=timeout)
     elif nr_type == 'sa':
-        return is_current_network_5g_sa(ad)
-    elif nr_type == 'mmwave':
-        return is_current_network_5g_nsa(ad, nr_type = 'mmwave', timeout=timeout)
+        return is_current_network_5g_sa(ad, sub_id=sub_id, mmwave=mmwave)
     else:
-        return is_current_network_5g_nsa(ad, nr_type=None) or is_current_network_5g_sa(ad)
-
-
-def is_current_network_5g_for_subscription(ad, sub_id=None, nr_type=None):
-    """Verifies 5G override network type for subscription id.
-    Args:
-        ad: android device object
-        sub_id: subscription id
-        nr_type: 'sa' for 5G standalone, 'nsa' for 5G non-standalone, 'mmwave' for 5G millimeter
-                wave
-
-    Returns:
-        True: if data is on 5G regardless of NSA or SA.
-        False: if data is not on 5G regardless of NSA or SA.
-    """
-    if not sub_id:
-        return is_current_network_5g(ad, nr_type=nr_type)
-
-    voice_sub_id_changed = False
-    current_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
-    if current_sub_id != sub_id:
-        ad.droid.subscriptionSetDefaultVoiceSubId(sub_id)
-        voice_sub_id_changed = True
-
-    result = is_current_network_5g(ad, nr_type=nr_type)
-
-    if voice_sub_id_changed:
-        ad.droid.subscriptionSetDefaultVoiceSubId(current_sub_id)
-
-    return result
+        return is_current_network_5g_nsa(
+            ad, sub_id=sub_id, mmwave=mmwave,
+            timeout=timeout) or is_current_network_5g_sa(
+                ad, sub_id=sub_id, mmwave=mmwave)
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_bt_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_bt_utils.py
new file mode 100644
index 0000000..8f754ed
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_bt_utils.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import time
+
+from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
+from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_data_utils import test_internet_connection
+from acts_contrib.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+
+
+def enable_bluetooth_tethering_connection(log, provider, clients):
+    for ad in [provider] + clients:
+        if not bluetooth_enabled_check(ad):
+            ad.log.info("Bluetooth is not enabled")
+            return False
+        else:
+            ad.log.info("Bluetooth is enabled")
+    time.sleep(5)
+    provider.log.info("Provider enabling bluetooth tethering")
+    try:
+        provider.droid.bluetoothPanSetBluetoothTethering(True)
+    except Exception as e:
+        provider.log.warning(
+            "Failed to enable provider Bluetooth tethering with %s", e)
+        provider.droid.bluetoothPanSetBluetoothTethering(True)
+
+    if wait_for_state(provider.droid.bluetoothPanIsTetheringOn, True):
+        provider.log.info("Provider Bluetooth tethering is enabled.")
+    else:
+        provider.log.error(
+            "Failed to enable provider Bluetooth tethering.")
+        provider.log.error("bluetoothPanIsTetheringOn = %s",
+                           provider.droid.bluetoothPanIsTetheringOn())
+        return False
+    for client in clients:
+        if not (pair_pri_to_sec(provider, client)):
+            client.log.error("Client failed to pair with provider")
+            return False
+        else:
+            client.log.info("Client paired with provider")
+
+    time.sleep(5)
+    for client in clients:
+        client.droid.bluetoothConnectBonded(provider.droid.bluetoothGetLocalAddress())
+
+    time.sleep(20)
+    return True
+
+
+def verify_bluetooth_tethering_connection(log, provider, clients,
+                                           change_rat=None,
+                                           toggle_data=False,
+                                           toggle_tethering=False,
+                                           voice_call=False,
+                                           toggle_bluetooth=True):
+    """Setups up a bluetooth tethering connection between two android devices.
+
+    Returns:
+        True if PAN connection and verification is successful,
+        false if unsuccessful.
+    """
+
+
+    if not enable_bluetooth_tethering_connection(log, provider, clients):
+        return False
+
+    if not test_internet_connection(log, provider, clients):
+        log.error("Internet connection check failed")
+        return False
+    if voice_call:
+        log.info("====== Voice call test =====")
+        for caller, callee in [(provider, clients[0]),
+                               (clients[0], provider)]:
+            if not call_setup_teardown(
+                    log, caller, callee, ad_hangup=None):
+                log.error("Setup Call Failed.")
+                hangup_call(log, caller)
+                return False
+            log.info("Verify data.")
+            if not verify_internet_connection(
+                    log, clients[0], retries=1):
+                clients[0].log.warning(
+                    "client internet connection state is not on")
+            else:
+                clients[0].log.info(
+                    "client internet connection state is on")
+            hangup_call(log, caller)
+            if not verify_internet_connection(
+                    log, clients[0], retries=1):
+                clients[0].log.warning(
+                    "client internet connection state is not on")
+                return False
+            else:
+                clients[0].log.info(
+                    "client internet connection state is on")
+    if toggle_tethering:
+        log.info("====== Toggling provider bluetooth tethering =====")
+        provider.log.info("Disable bluetooth tethering")
+        provider.droid.bluetoothPanSetBluetoothTethering(False)
+        if not test_internet_connection(log, provider, clients, False, True):
+            log.error(
+                "Internet connection check failed after disable tethering")
+            return False
+        provider.log.info("Enable bluetooth tethering")
+        if not enable_bluetooth_tethering_connection(log,
+                provider, clients):
+            provider.log.error(
+                "Fail to re-enable bluetooth tethering")
+            return False
+        if not test_internet_connection(log, provider, clients, True, True):
+            log.error(
+                "Internet connection check failed after enable tethering")
+            return False
+    if toggle_bluetooth:
+        log.info("====== Toggling provider bluetooth =====")
+        provider.log.info("Disable provider bluetooth")
+        disable_bluetooth(provider.droid)
+        time.sleep(10)
+        if not test_internet_connection(log, provider, clients, False, True):
+            log.error(
+                "Internet connection check failed after disable bluetooth")
+            return False
+        if not enable_bluetooth_tethering_connection(log,
+                provider, clients):
+            provider.log.error(
+                "Fail to re-enable bluetooth tethering")
+            return False
+        if not test_internet_connection(log, provider, clients, True, True):
+            log.error(
+                "Internet connection check failed after enable bluetooth")
+            return False
+    if toggle_data:
+        log.info("===== Toggling provider data connection =====")
+        provider.log.info("Disable provider data connection")
+        provider.droid.telephonyToggleDataConnection(False)
+        time.sleep(10)
+        if not test_internet_connection(log, provider, clients, False, False):
+            return False
+        provider.log.info("Enable provider data connection")
+        provider.droid.telephonyToggleDataConnection(True)
+        if not wait_for_cell_data_connection(log, provider,
+                                             True):
+            provider.log.error(
+                "Provider failed to enable data connection.")
+            return False
+        if not test_internet_connection(log, provider, clients, True, True):
+            log.error(
+                "Internet connection check failed after enable data")
+            return False
+    if change_rat:
+        log.info("===== Change provider RAT to %s =====", change_rat)
+        if not ensure_network_generation(
+                log,
+                provider,
+                change_rat,
+                voice_or_data=NETWORK_SERVICE_DATA,
+                toggle_apm_after_setting=False):
+            provider.log.error("Provider failed to reselect to %s.",
+                                    change_rat)
+            return False
+        if not test_internet_connection(log, provider, clients, True, True):
+            log.error(
+                "Internet connection check failed after RAT change to %s",
+                change_rat)
+            return False
+    return True
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_data_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_data_utils.py
index 27572fe..05808f9 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_data_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_data_utils.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-#   Copyright 2021 - Google
+#   Copyright 2022 - Google
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
@@ -14,88 +14,102 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+
+import logging
+import os
 import time
 import random
 from queue import Empty
 
+from acts.controllers.android_device import SL4A_APK_NAME
 from acts.utils import adb_shell_ping
 from acts.utils import rand_ascii_str
 from acts.utils import disable_doze
 from acts.utils import enable_doze
 from acts.libs.utils.multithread import multithread_func
 from acts.libs.utils.multithread import run_multithread_func
-from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
-from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
-from acts_contrib.test_utils.bt.bt_test_utils import pair_pri_to_sec
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_data
+from acts_contrib.test_utils.tel.tel_defines import DATA_STATE_CONNECTED
+from acts_contrib.test_utils.tel.tel_defines import DATA_STATE_DISCONNECTED
 from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts_contrib.test_utils.tel.tel_defines import EventConnectivityChanged
 from acts_contrib.test_utils.tel.tel_defines import EventNetworkCallback
 from acts_contrib.test_utils.tel.tel_defines import GEN_5G
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_USER_PLANE_DATA
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WIFI_CONNECTION
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_CONNECTION_TYPE_CELL
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_CONNECTION_TYPE_WIFI
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
 from acts_contrib.test_utils.tel.tel_defines import RAT_5G
+from acts_contrib.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
+from acts_contrib.test_utils.tel.tel_defines import TYPE_MOBILE
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING
-from acts_contrib.test_utils.tel.tel_defines import TETHERING_MODE_WIFI
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_CHECK
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_DATA_STALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_NW_VALID_FAIL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_DATA_STALL_RECOVERY
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL_FOR_IMS
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_TETHERING_AFTER_REBOOT
+from acts_contrib.test_utils.tel.tel_defines import DataConnectionStateContainer
+from acts_contrib.test_utils.tel.tel_defines import EventDataConnectionStateChanged
+from acts_contrib.test_utils.tel.tel_5g_test_utils import check_current_network_5g
+from acts_contrib.test_utils.tel.tel_5g_test_utils import provision_device_for_5g
+from acts_contrib.test_utils.tel.tel_5g_test_utils import verify_5g_attach_for_both_devices
 from acts_contrib.test_utils.tel.tel_ims_utils import is_ims_registered
 from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_ims_registered
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts_contrib.test_utils.tel.tel_test_utils import check_is_wifi_connected
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_idle
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts_contrib.test_utils.tel.tel_test_utils import get_mobile_data_usage
+from acts_contrib.test_utils.tel.tel_lookup_tables import connection_type_from_type_string
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_network_generation_for_subscription
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phone_idle
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_default_state
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_idle_iwlan
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_iwlan
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_voice_attach_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import _check_file_existence
+from acts_contrib.test_utils.tel.tel_test_utils import _generate_file_directory_and_file_name
+from acts_contrib.test_utils.tel.tel_test_utils import _wait_for_droid_in_state
+from acts_contrib.test_utils.tel.tel_test_utils import get_internet_connection_type
 from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat_for_subscription
 from acts_contrib.test_utils.tel.tel_test_utils import get_service_state_by_adb
-from acts_contrib.test_utils.tel.tel_test_utils import get_wifi_usage
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
 from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_network_generation_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import is_event_match
 from acts_contrib.test_utils.tel.tel_test_utils import rat_generation_from_rat
-from acts_contrib.test_utils.tel.tel_test_utils import set_wifi_to_default
-from acts_contrib.test_utils.tel.tel_test_utils import start_youtube_video
-from acts_contrib.test_utils.tel.tel_test_utils import start_wifi_tethering
-from acts_contrib.test_utils.tel.tel_test_utils import stop_wifi_tethering
 from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
 from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
 from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_cell_data_connection
 from acts_contrib.test_utils.tel.tel_test_utils import wait_for_data_attach_for_subscription
 from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_voice_attach_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_wifi_data_connection
-from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
-from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
-from acts_contrib.test_utils.tel.tel_test_utils import wifi_reset
-from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts_contrib.test_utils.tel.tel_test_utils import active_file_download_task
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_default_state
-from acts_contrib.test_utils.tel.tel_test_utils import WIFI_SSID_KEY
-from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call_active
-from acts_contrib.test_utils.tel.tel_5g_test_utils import check_current_network_5g
-from acts_contrib.test_utils.tel.tel_5g_test_utils import provision_device_for_5g
-from acts_contrib.test_utils.tel.tel_5g_test_utils import verify_5g_attach_for_both_devices
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan
-from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_short_seq
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_active
 from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_short_seq
+from acts_contrib.test_utils.tel.tel_wifi_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.tel.tel_wifi_utils import WIFI_CONFIG_APBAND_5G
+from acts_contrib.test_utils.tel.tel_wifi_utils import WIFI_SSID_KEY
+from acts_contrib.test_utils.tel.tel_wifi_utils import check_is_wifi_connected
+from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_wifi_utils import get_wifi_usage
+from acts_contrib.test_utils.tel.tel_wifi_utils import set_wifi_to_default
+from acts_contrib.test_utils.tel.tel_wifi_utils import start_wifi_tethering
+from acts_contrib.test_utils.tel.tel_wifi_utils import stop_wifi_tethering
+from acts_contrib.test_utils.tel.tel_wifi_utils import wifi_reset
+from acts_contrib.test_utils.tel.tel_wifi_utils import wifi_toggle_state
 
 
 def wifi_tethering_cleanup(log, provider, client_list):
@@ -513,7 +527,7 @@
             return False
 
         if nw_gen == GEN_5G:
-            if not check_current_network_5g(ad, timeout=60, nr_type=nr_type):
+            if not check_current_network_5g(ad, nr_type=nr_type, timeout=60):
                 return False
         else:
             if not is_droid_in_network_generation_for_subscription(
@@ -862,7 +876,7 @@
     ensure_phones_idle(log, ad_list)
 
     if nw_gen == GEN_5G:
-        if not provision_device_for_5g(log, android_devices, nr_type= nr_type):
+        if not provision_device_for_5g(log, android_devices, nr_type=nr_type):
             return False
     elif nw_gen:
         if not ensure_network_generation_for_subscription(
@@ -1018,7 +1032,7 @@
     ensure_phones_idle(log, clients)
     wifi_toggle_state(log, provider, False)
     if network_generation == RAT_5G:
-        if not provision_device_for_5g(log, provider, nr_type= nr_type):
+        if not provision_device_for_5g(log, provider, nr_type=nr_type):
             return False
     elif network_generation:
         if not ensure_network_generation(
@@ -1042,7 +1056,7 @@
         provider.log.info("Disable provider wifi tethering")
         stop_wifi_tethering(log, provider)
     provider.log.info("Provider disable bluetooth")
-    disable_bluetooth(provider.droid)
+    provider.droid.bluetoothToggleState(False)
     time.sleep(10)
 
     for ad in clients:
@@ -1052,7 +1066,7 @@
         ad.log.info("Client disable data")
         ad.droid.telephonyToggleDataConnection(False)
         ad.log.info("Client disable bluetooth")
-        disable_bluetooth(ad.droid)
+        ad.droid.bluetoothToggleState(False)
         ad.log.info("Client disable wifi")
         wifi_toggle_state(log, ad, False)
 
@@ -1071,164 +1085,6 @@
     return True
 
 
-def enable_bluetooth_tethering_connection(log, provider, clients):
-    for ad in [provider] + clients:
-        if not bluetooth_enabled_check(ad):
-            ad.log.info("Bluetooth is not enabled")
-            return False
-        else:
-            ad.log.info("Bluetooth is enabled")
-    time.sleep(5)
-    provider.log.info("Provider enabling bluetooth tethering")
-    try:
-        provider.droid.bluetoothPanSetBluetoothTethering(True)
-    except Exception as e:
-        provider.log.warning(
-            "Failed to enable provider Bluetooth tethering with %s", e)
-        provider.droid.bluetoothPanSetBluetoothTethering(True)
-
-    if wait_for_state(provider.droid.bluetoothPanIsTetheringOn, True):
-        provider.log.info("Provider Bluetooth tethering is enabled.")
-    else:
-        provider.log.error(
-            "Failed to enable provider Bluetooth tethering.")
-        provider.log.error("bluetoothPanIsTetheringOn = %s",
-                           provider.droid.bluetoothPanIsTetheringOn())
-        return False
-    for client in clients:
-        if not (pair_pri_to_sec(provider, client)):
-            client.log.error("Client failed to pair with provider")
-            return False
-        else:
-            client.log.info("Client paired with provider")
-
-    time.sleep(5)
-    for client in clients:
-        client.droid.bluetoothConnectBonded(provider.droid.bluetoothGetLocalAddress())
-
-    time.sleep(20)
-    return True
-
-
-def verify_bluetooth_tethering_connection(log, provider, clients,
-                                           change_rat=None,
-                                           toggle_data=False,
-                                           toggle_tethering=False,
-                                           voice_call=False,
-                                           toggle_bluetooth=True):
-    """Setups up a bluetooth tethering connection between two android devices.
-
-    Returns:
-        True if PAN connection and verification is successful,
-        false if unsuccessful.
-    """
-
-
-    if not enable_bluetooth_tethering_connection(log, provider, clients):
-        return False
-
-    if not test_internet_connection(log, provider, clients):
-        log.error("Internet connection check failed")
-        return False
-    if voice_call:
-        log.info("====== Voice call test =====")
-        for caller, callee in [(provider, clients[0]),
-                               (clients[0], provider)]:
-            if not call_setup_teardown(
-                    log, caller, callee, ad_hangup=None):
-                log.error("Setup Call Failed.")
-                hangup_call(log, caller)
-                return False
-            log.info("Verify data.")
-            if not verify_internet_connection(
-                    log, clients[0], retries=1):
-                clients[0].log.warning(
-                    "client internet connection state is not on")
-            else:
-                clients[0].log.info(
-                    "client internet connection state is on")
-            hangup_call(log, caller)
-            if not verify_internet_connection(
-                    log, clients[0], retries=1):
-                clients[0].log.warning(
-                    "client internet connection state is not on")
-                return False
-            else:
-                clients[0].log.info(
-                    "client internet connection state is on")
-    if toggle_tethering:
-        log.info("====== Toggling provider bluetooth tethering =====")
-        provider.log.info("Disable bluetooth tethering")
-        provider.droid.bluetoothPanSetBluetoothTethering(False)
-        if not test_internet_connection(log, provider, clients, False, True):
-            log.error(
-                "Internet connection check failed after disable tethering")
-            return False
-        provider.log.info("Enable bluetooth tethering")
-        if not enable_bluetooth_tethering_connection(log,
-                provider, clients):
-            provider.log.error(
-                "Fail to re-enable bluetooth tethering")
-            return False
-        if not test_internet_connection(log, provider, clients, True, True):
-            log.error(
-                "Internet connection check failed after enable tethering")
-            return False
-    if toggle_bluetooth:
-        log.info("====== Toggling provider bluetooth =====")
-        provider.log.info("Disable provider bluetooth")
-        disable_bluetooth(provider.droid)
-        time.sleep(10)
-        if not test_internet_connection(log, provider, clients, False, True):
-            log.error(
-                "Internet connection check failed after disable bluetooth")
-            return False
-        if not enable_bluetooth_tethering_connection(log,
-                provider, clients):
-            provider.log.error(
-                "Fail to re-enable bluetooth tethering")
-            return False
-        if not test_internet_connection(log, provider, clients, True, True):
-            log.error(
-                "Internet connection check failed after enable bluetooth")
-            return False
-    if toggle_data:
-        log.info("===== Toggling provider data connection =====")
-        provider.log.info("Disable provider data connection")
-        provider.droid.telephonyToggleDataConnection(False)
-        time.sleep(10)
-        if not test_internet_connection(log, provider, clients, False, False):
-            return False
-        provider.log.info("Enable provider data connection")
-        provider.droid.telephonyToggleDataConnection(True)
-        if not wait_for_cell_data_connection(log, provider,
-                                             True):
-            provider.log.error(
-                "Provider failed to enable data connection.")
-            return False
-        if not test_internet_connection(log, provider, clients, True, True):
-            log.error(
-                "Internet connection check failed after enable data")
-            return False
-    if change_rat:
-        log.info("===== Change provider RAT to %s =====", change_rat)
-        if not ensure_network_generation(
-                log,
-                provider,
-                change_rat,
-                voice_or_data=NETWORK_SERVICE_DATA,
-                toggle_apm_after_setting=False):
-            provider.log.error("Provider failed to reselect to %s.",
-                                    change_rat)
-            return False
-        if not test_internet_connection(log, provider, clients, True, True):
-            log.error(
-                "Internet connection check failed after RAT change to %s",
-                change_rat)
-            return False
-    return True
-
-
 def test_tethering_wifi_and_voice_call(log, provider, clients,
                                         provider_data_rat,
                                         provider_setup_func,
@@ -1246,7 +1102,7 @@
         return False
 
     if provider_setup_func == RAT_5G:
-        if not provision_device_for_5g(log, provider, nr_type= nr_type):
+        if not provision_device_for_5g(log, provider, nr_type=nr_type):
             return False
     try:
         log.info("1. Setup WiFi Tethering.")
@@ -1320,36 +1176,38 @@
         True, False, True, False, False, True, False, False, False, False,
         True, False, False, False, False, False, False, False, False
     ]
+    try:
+        for toggle in wifi_toggles:
 
-    for toggle in wifi_toggles:
+            wifi_reset(log, ad, toggle)
 
-        wifi_reset(log, ad, toggle)
+            if not wait_for_cell_data_connection(
+                    log, ad, True, MAX_WAIT_TIME_WIFI_CONNECTION):
+                log.error("Failed data connection, aborting!")
+                return False
 
-        if not wait_for_cell_data_connection(
-                log, ad, True, MAX_WAIT_TIME_WIFI_CONNECTION):
-            log.error("Failed wifi connection, aborting!")
-            return False
+            if not verify_internet_connection(log, ad):
+                log.error("Failed to get user-plane traffic, aborting!")
+                return False
 
-        if not verify_internet_connection(log, ad):
-            log.error("Failed to get user-plane traffic, aborting!")
-            return False
+            if toggle:
+                wifi_toggle_state(log, ad, True)
 
-        if toggle:
-            wifi_toggle_state(log, ad, True)
+            ensure_wifi_connected(log, ad, wifi_network_ssid,
+                        wifi_network_pass)
 
-        ensure_wifi_connected(log, ad, wifi_network_ssid,
-                     wifi_network_pass)
+            if not wait_for_wifi_data_connection(
+                    log, ad, True, MAX_WAIT_TIME_WIFI_CONNECTION):
+                log.error("Failed wifi connection, aborting!")
+                return False
 
-        if not wait_for_wifi_data_connection(
-                log, ad, True, MAX_WAIT_TIME_WIFI_CONNECTION):
-            log.error("Failed wifi connection, aborting!")
-            return False
-
-        if not verify_http_connection(
-                log, ad, 'http://www.google.com', 100, .1):
-            log.error("Failed to get user-plane traffic, aborting!")
-            return False
-    return True
+            if not verify_http_connection(
+                    log, ad, 'http://www.google.com', 100, .1):
+                log.error("Failed to get user-plane traffic, aborting!")
+                return False
+        return True
+    finally:
+        wifi_toggle_state(log, ad, False)
 
 
 def test_call_setup_in_active_data_transfer(
@@ -1391,7 +1249,7 @@
                                    wait_time_in_call)
 
     if nw_gen == GEN_5G:
-        if not provision_device_for_5g(log, ads[0], nr_type= nr_type):
+        if not provision_device_for_5g(log, ads[0], nr_type=nr_type):
             return False
     elif nw_gen:
         if not ensure_network_generation(log, ads[0], nw_gen,
@@ -1446,7 +1304,8 @@
             return False
     # Disable airplane mode if test under apm on.
     toggle_airplane_mode(log, ads[0], False)
-    if nw_gen == GEN_5G and not check_current_network_5g(ads[0], nr_type= nr_type):
+    if nw_gen == GEN_5G and not check_current_network_5g(ads[0],
+                                                         nr_type=nr_type):
         ads[0].log.error("Phone not attached on 5G after call.")
         return False
     return True
@@ -1477,7 +1336,7 @@
         False if failed.
     """
     if nw_gen == GEN_5G:
-        if not provision_device_for_5g(log, ads[0], nr_type= nr_type):
+        if not provision_device_for_5g(log, ads[0], nr_type=nr_type):
             return False
     elif nw_gen:
         if not ensure_network_generation(log, ads[0], nw_gen,
@@ -1524,7 +1383,8 @@
     ad_download.force_stop_apk("com.google.android.youtube")
     # Disable airplane mode if test under apm on.
     toggle_airplane_mode(log, ads[0], False)
-    if nw_gen == GEN_5G and not check_current_network_5g(ads[0], nr_type= nr_type):
+    if nw_gen == GEN_5G and not check_current_network_5g(ads[0],
+                                                         nr_type=nr_type):
         ads[0].log.error("Phone not attached on 5G after call.")
         result = False
     return result
@@ -1594,7 +1454,8 @@
 
     time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
 
-    if nw_gen == GEN_5G and not verify_5g_attach_for_both_devices(log, ads, nr_type= nr_type):
+    if nw_gen == GEN_5G and not verify_5g_attach_for_both_devices(
+        log, ads, nr_type=nr_type):
         log.error("Phone not attached on 5G after epdg call.")
         return False
 
@@ -1695,7 +1556,7 @@
                         ssid=None,
                         password=None,
                         pre_teardown_func=None,
-                        nr_type= None):
+                        nr_type=None):
     """WiFi Tethering test
     Args:
         log: log object.
@@ -1721,7 +1582,7 @@
         nr_type: NR network type e.g. NSA, SA, MMWAVE.
 
     """
-    if not test_setup_tethering(log, provider, clients, nw_gen, nr_type= nr_type):
+    if not test_setup_tethering(log, provider, clients, nw_gen, nr_type=nr_type):
         log.error("Verify %s Internet access failed.", nw_gen)
         return False
 
@@ -2022,7 +1883,7 @@
                                              provider,
                                              clients,
                                              new_gen=None,
-                                             nr_type= None):
+                                             nr_type=None):
     """Verify toggle Data network during WiFi Tethering.
     Args:
         log: log object.
@@ -2047,7 +1908,7 @@
                                    check_iteration=2,
                                    do_cleanup=False,
                                    ssid=ssid,
-                                   nr_type= nr_type):
+                                   nr_type=nr_type):
             log.error("WiFi Tethering failed.")
             return False
         if not provider.droid.wifiIsApEnabled():
@@ -2239,3 +2100,769 @@
         else:
             return False
     return False
+
+
+def wait_for_cell_data_connection(
+        log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+    """Wait for data connection status to be expected value for default
+       data subscription.
+
+    Wait for the data connection status to be DATA_STATE_CONNECTED
+        or DATA_STATE_DISCONNECTED.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        state: Expected status: True or False.
+            If True, it will wait for status to be DATA_STATE_CONNECTED.
+            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+        timeout_value: wait for cell data timeout value.
+            This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    sub_id = get_default_data_sub_id(ad)
+    return wait_for_cell_data_connection_for_subscription(
+        log, ad, sub_id, state, timeout_value)
+
+
+def _is_data_connection_state_match(log, ad, expected_data_connection_state):
+    return (expected_data_connection_state ==
+            ad.droid.telephonyGetDataConnectionState())
+
+
+def _is_network_connected_state_match(log, ad,
+                                      expected_network_connected_state):
+    return (expected_network_connected_state ==
+            ad.droid.connectivityNetworkIsConnected())
+
+
+def wait_for_cell_data_connection_for_subscription(
+        log,
+        ad,
+        sub_id,
+        state,
+        timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+    """Wait for data connection status to be expected value for specified
+       subscrption id.
+
+    Wait for the data connection status to be DATA_STATE_CONNECTED
+        or DATA_STATE_DISCONNECTED.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        sub_id: subscription Id
+        state: Expected status: True or False.
+            If True, it will wait for status to be DATA_STATE_CONNECTED.
+            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+        timeout_value: wait for cell data timeout value.
+            This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    state_str = {
+        True: DATA_STATE_CONNECTED,
+        False: DATA_STATE_DISCONNECTED
+    }[state]
+
+    data_state = ad.droid.telephonyGetDataConnectionState()
+    if not state and ad.droid.telephonyGetDataConnectionState() == state_str:
+        return True
+
+    ad.ed.clear_events(EventDataConnectionStateChanged)
+    ad.droid.telephonyStartTrackingDataConnectionStateChangeForSubscription(
+        sub_id)
+    ad.droid.connectivityStartTrackingConnectivityStateChange()
+    try:
+        ad.log.info("User data enabled for sub_id %s: %s", sub_id,
+                    ad.droid.telephonyIsDataEnabledForSubscription(sub_id))
+        data_state = ad.droid.telephonyGetDataConnectionState()
+        ad.log.info("Data connection state is %s", data_state)
+        ad.log.info("Network is connected: %s",
+                    ad.droid.connectivityNetworkIsConnected())
+        if data_state == state_str:
+            return _wait_for_nw_data_connection(
+                log, ad, state, NETWORK_CONNECTION_TYPE_CELL, timeout_value)
+
+        try:
+            ad.ed.wait_for_event(
+                EventDataConnectionStateChanged,
+                is_event_match,
+                timeout=timeout_value,
+                field=DataConnectionStateContainer.DATA_CONNECTION_STATE,
+                value=state_str)
+        except Empty:
+            ad.log.info("No expected event EventDataConnectionStateChanged %s",
+                        state_str)
+
+        # TODO: Wait for <MAX_WAIT_TIME_CONNECTION_STATE_UPDATE> seconds for
+        # data connection state.
+        # Otherwise, the network state will not be correct.
+        # The bug is tracked here: b/20921915
+
+        # Previously we use _is_data_connection_state_match,
+        # but telephonyGetDataConnectionState sometimes return wrong value.
+        # The bug is tracked here: b/22612607
+        # So we use _is_network_connected_state_match.
+
+        if _wait_for_droid_in_state(log, ad, timeout_value,
+                                    _is_network_connected_state_match, state):
+            return _wait_for_nw_data_connection(
+                log, ad, state, NETWORK_CONNECTION_TYPE_CELL, timeout_value)
+        else:
+            return False
+
+    finally:
+        ad.droid.telephonyStopTrackingDataConnectionStateChangeForSubscription(
+            sub_id)
+
+
+def wait_for_data_connection(
+        log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+    """Wait for data connection status to be expected value.
+
+    Wait for the data connection status to be DATA_STATE_CONNECTED
+        or DATA_STATE_DISCONNECTED.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        state: Expected status: True or False.
+            If True, it will wait for status to be DATA_STATE_CONNECTED.
+            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+        timeout_value: wait for network data timeout value.
+            This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    return _wait_for_nw_data_connection(log, ad, state, None, timeout_value)
+
+
+def wait_for_wifi_data_connection(
+        log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+    """Wait for data connection status to be expected value and connection is by WiFi.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        state: Expected status: True or False.
+            If True, it will wait for status to be DATA_STATE_CONNECTED.
+            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+        timeout_value: wait for network data timeout value.
+            This is optional, default value is MAX_WAIT_TIME_NW_SELECTION
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    ad.log.info("wait_for_wifi_data_connection")
+    return _wait_for_nw_data_connection(
+        log, ad, state, NETWORK_CONNECTION_TYPE_WIFI, timeout_value)
+
+
+def _connection_state_change(_event, target_state, connection_type):
+    if connection_type:
+        if 'TypeName' not in _event['data']:
+            return False
+        connection_type_string_in_event = _event['data']['TypeName']
+        cur_type = connection_type_from_type_string(
+            connection_type_string_in_event)
+        if cur_type != connection_type:
+            logging.info(
+                "_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']['isConnected'] == target_state:
+        return True
+    return False
+
+
+def _wait_for_nw_data_connection(
+        log,
+        ad,
+        is_connected,
+        connection_type=None,
+        timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
+    """Wait for data connection status to be expected value.
+
+    Wait for the data connection status to be DATA_STATE_CONNECTED
+        or DATA_STATE_DISCONNECTED.
+
+    Args:
+        log: Log object.
+        ad: Android Device Object.
+        is_connected: Expected connection status: True or False.
+            If True, it will wait for status to be DATA_STATE_CONNECTED.
+            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
+        connection_type: expected connection type.
+            This is optional, if it is None, then any connection type will return True.
+        timeout_value: wait for network data timeout value.
+            This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
+
+    Returns:
+        True if success.
+        False if failed.
+    """
+    ad.ed.clear_events(EventConnectivityChanged)
+    ad.droid.connectivityStartTrackingConnectivityStateChange()
+    try:
+        cur_data_connection_state = ad.droid.connectivityNetworkIsConnected()
+        if is_connected == cur_data_connection_state:
+            current_type = get_internet_connection_type(log, ad)
+            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("data connection not on %s!", connection_type)
+                    return True
+                elif is_connected and current_type == connection_type:
+                    ad.log.info("data connection on %s as expected",
+                                connection_type)
+                    return True
+        else:
+            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)
+            ad.log.info("Got event: %s", event)
+        except Empty:
+            pass
+
+        log.info(
+            "_wait_for_nw_data_connection: check connection after wait event.")
+        # TODO: Wait for <MAX_WAIT_TIME_CONNECTION_STATE_UPDATE> seconds for
+        # data connection state.
+        # Otherwise, the network state will not be correct.
+        # The bug is tracked here: b/20921915
+        if _wait_for_droid_in_state(log, ad, timeout_value,
+                                    _is_network_connected_state_match,
+                                    is_connected):
+            current_type = get_internet_connection_type(log, ad)
+            ad.log.info("current data connection type: %s", current_type)
+            if not connection_type:
+                return True
+            else:
+                if not is_connected and current_type != 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("after event wait, data connection on %s",
+                                connection_type)
+                    return True
+                else:
+                    return False
+        else:
+            return False
+    except Exception as e:
+        ad.log.error("Exception error %s", str(e))
+        return False
+    finally:
+        ad.droid.connectivityStopTrackingConnectivityStateChange()
+
+
+def check_curl_availability(ad):
+    if not hasattr(ad, "curl_capable"):
+        try:
+            out = ad.adb.shell("/data/curl --version")
+            if not out or "not found" in out:
+                setattr(ad, "curl_capable", False)
+                ad.log.info("curl is unavailable, use chrome to download file")
+            else:
+                setattr(ad, "curl_capable", True)
+        except Exception:
+            setattr(ad, "curl_capable", False)
+            ad.log.info("curl is unavailable, use chrome to download file")
+    return ad.curl_capable
+
+
+def start_youtube_video(ad, url="vnd.youtube:watch?v=pSJoP0LR8CQ"):
+    ad.log.info("Open an youtube video")
+    for _ in range(3):
+        ad.ensure_screen_on()
+        ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' % url)
+        if wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
+            ad.log.info("Started a video in youtube, audio is in MUSIC state")
+            return True
+        ad.log.info("Audio is not in MUSIC state. Quit Youtube.")
+        for _ in range(3):
+            ad.send_keycode("BACK")
+            time.sleep(1)
+        time.sleep(3)
+    return False
+
+
+def http_file_download_by_sl4a(ad,
+                               url,
+                               out_path=None,
+                               expected_file_size=None,
+                               remove_file_after_check=True,
+                               timeout=300):
+    """Download http file by sl4a RPC call.
+
+    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.
+    """
+    file_folder, file_name = _generate_file_directory_and_file_name(
+        url, out_path)
+    file_path = os.path.join(file_folder, file_name)
+    ad.adb.shell("rm -f %s" % file_path)
+    accounting_apk = SL4A_APK_NAME
+    result = True
+    try:
+        if not getattr(ad, "data_droid", None):
+            ad.data_droid, ad.data_ed = ad.get_droid()
+            ad.data_ed.start()
+        else:
+            try:
+                if not ad.data_droid.is_live:
+                    ad.data_droid, ad.data_ed = ad.get_droid()
+                    ad.data_ed.start()
+            except Exception:
+                ad.log.info("Start new sl4a session for file download")
+                ad.data_droid, ad.data_ed = ad.get_droid()
+                ad.data_ed.start()
+        data_accounting = {
+            "mobile_rx_bytes":
+            ad.droid.getMobileRxBytes(),
+            "subscriber_mobile_data_usage":
+            get_mobile_data_usage(ad, None, None),
+            "sl4a_mobile_data_usage":
+            get_mobile_data_usage(ad, None, accounting_apk)
+        }
+        ad.log.debug("Before downloading: %s", data_accounting)
+        ad.log.info("Download file from %s to %s by sl4a RPC call", url,
+                    file_path)
+        try:
+            ad.data_droid.httpDownloadFile(url, file_path, timeout=timeout)
+        except Exception as e:
+            ad.log.warning("SL4A file download error: %s", e)
+            ad.data_droid.terminate()
+            return False
+        if _check_file_existence(ad, file_path, expected_file_size):
+            ad.log.info("%s is downloaded successfully", url)
+            new_data_accounting = {
+                "mobile_rx_bytes":
+                ad.droid.getMobileRxBytes(),
+                "subscriber_mobile_data_usage":
+                get_mobile_data_usage(ad, None, None),
+                "sl4a_mobile_data_usage":
+                get_mobile_data_usage(ad, None, accounting_apk)
+            }
+            ad.log.debug("After downloading: %s", new_data_accounting)
+            accounting_diff = {
+                key: value - data_accounting[key]
+                for key, value in new_data_accounting.items()
+            }
+            ad.log.debug("Data accounting difference: %s", accounting_diff)
+            if getattr(ad, "on_mobile_data", False):
+                for key, value in accounting_diff.items():
+                    if value < expected_file_size:
+                        ad.log.debug("%s diff is %s less than %s", key,
+                                       value, expected_file_size)
+                        ad.data_accounting["%s_failure"] += 1
+            else:
+                for key, value in accounting_diff.items():
+                    if value >= expected_file_size:
+                        ad.log.error("%s diff is %s. File download is "
+                                     "consuming mobile data", key, value)
+                        result = False
+            return result
+        else:
+            ad.log.warning("Fail to download %s", url)
+            return False
+    except Exception as e:
+        ad.log.error("Download %s failed with exception %s", url, e)
+        raise
+    finally:
+        if remove_file_after_check:
+            ad.log.info("Remove the downloaded file %s", file_path)
+            ad.adb.shell("rm %s" % file_path, ignore_status=True)
+
+
+def http_file_download_by_curl(ad,
+                               url,
+                               out_path=None,
+                               expected_file_size=None,
+                               remove_file_after_check=True,
+                               timeout=3600,
+                               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_directory, file_name = _generate_file_directory_and_file_name(
+        url, out_path)
+    file_path = os.path.join(file_directory, file_name)
+    curl_cmd = "/data/curl"
+    if limit_rate:
+        curl_cmd += " --limit-rate %s" % limit_rate
+    if retry:
+        curl_cmd += " --retry %s" % retry
+    curl_cmd += " --url %s > %s" % (url, file_path)
+    try:
+        ad.log.info("Download %s to %s by adb shell command %s", url,
+                    file_path, curl_cmd)
+
+        ad.adb.shell(curl_cmd, timeout=timeout)
+        if _check_file_existence(ad, file_path, expected_file_size):
+            ad.log.info("%s is downloaded to %s successfully", url, file_path)
+            return True
+        else:
+            ad.log.warning("Fail to download %s", url)
+            return False
+    except Exception as e:
+        ad.log.warning("Download %s failed with exception %s", url, e)
+        return False
+    finally:
+        if remove_file_after_check:
+            ad.log.info("Remove the downloaded file %s", file_path)
+            ad.adb.shell("rm %s" % file_path, ignore_status=True)
+
+
+def open_url_by_adb(ad, url):
+    ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' % url)
+
+
+def http_file_download_by_chrome(ad,
+                                 url,
+                                 expected_file_size=None,
+                                 remove_file_after_check=True,
+                                 timeout=3600):
+    """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.
+    """
+    chrome_apk = "com.android.chrome"
+    file_directory, file_name = _generate_file_directory_and_file_name(
+        url, "/sdcard/Download/")
+    file_path = os.path.join(file_directory, file_name)
+    # Remove pre-existing file
+    ad.force_stop_apk(chrome_apk)
+    file_to_be_delete = os.path.join(file_directory, "*%s*" % file_name)
+    ad.adb.shell("rm -f %s" % file_to_be_delete)
+    ad.adb.shell("rm -rf /sdcard/Download/.*")
+    ad.adb.shell("rm -f /sdcard/Download/.*")
+    data_accounting = {
+        "total_rx_bytes": ad.droid.getTotalRxBytes(),
+        "mobile_rx_bytes": ad.droid.getMobileRxBytes(),
+        "subscriber_mobile_data_usage": get_mobile_data_usage(ad, None, None),
+        "chrome_mobile_data_usage": get_mobile_data_usage(
+            ad, None, chrome_apk)
+    }
+    ad.log.debug("Before downloading: %s", data_accounting)
+    ad.log.info("Download %s with timeout %s", url, timeout)
+    ad.ensure_screen_on()
+    open_url_by_adb(ad, url)
+    elapse_time = 0
+    result = True
+    while elapse_time < timeout:
+        time.sleep(30)
+        if _check_file_existence(ad, file_path, expected_file_size):
+            ad.log.info("%s is downloaded successfully", url)
+            if remove_file_after_check:
+                ad.log.info("Remove the downloaded file %s", file_path)
+                ad.adb.shell("rm -f %s" % file_to_be_delete)
+                ad.adb.shell("rm -rf /sdcard/Download/.*")
+                ad.adb.shell("rm -f /sdcard/Download/.*")
+            #time.sleep(30)
+            new_data_accounting = {
+                "mobile_rx_bytes":
+                ad.droid.getMobileRxBytes(),
+                "subscriber_mobile_data_usage":
+                get_mobile_data_usage(ad, None, None),
+                "chrome_mobile_data_usage":
+                get_mobile_data_usage(ad, None, chrome_apk)
+            }
+            ad.log.info("After downloading: %s", new_data_accounting)
+            accounting_diff = {
+                key: value - data_accounting[key]
+                for key, value in new_data_accounting.items()
+            }
+            ad.log.debug("Data accounting difference: %s", accounting_diff)
+            if getattr(ad, "on_mobile_data", False):
+                for key, value in accounting_diff.items():
+                    if value < expected_file_size:
+                        ad.log.warning("%s diff is %s less than %s", key,
+                                       value, expected_file_size)
+                        ad.data_accounting["%s_failure" % key] += 1
+            else:
+                for key, value in accounting_diff.items():
+                    if value >= expected_file_size:
+                        ad.log.error("%s diff is %s. File download is "
+                                     "consuming mobile data", key, value)
+                        result = False
+            return result
+        elif _check_file_existence(ad, "%s.crdownload" % file_path):
+            ad.log.info("Chrome is downloading %s", url)
+        elif elapse_time < 60:
+            # download not started, retry download wit chrome again
+            open_url_by_adb(ad, url)
+        else:
+            ad.log.error("Unable to download file from %s", url)
+            break
+        elapse_time += 30
+    ad.log.warning("Fail to download file from %s", url)
+    ad.force_stop_apk("com.android.chrome")
+    ad.adb.shell("rm -f %s" % file_to_be_delete)
+    ad.adb.shell("rm -rf /sdcard/Download/.*")
+    ad.adb.shell("rm -f /sdcard/Download/.*")
+    return False
+
+
+def get_mobile_data_usage(ad, sid=None, apk=None):
+    if not sid:
+        sid = ad.droid.subscriptionGetDefaultDataSubId()
+    current_time = int(time.time() * 1000)
+    begin_time = current_time - 10 * 24 * 60 * 60 * 1000
+    end_time = current_time + 10 * 24 * 60 * 60 * 1000
+
+    if apk:
+        uid = ad.get_apk_uid(apk)
+        ad.log.debug("apk %s uid = %s", apk, uid)
+        try:
+            usage_info = ad.droid.getMobileDataUsageInfoForUid(uid, sid)
+            ad.log.debug("Mobile data usage info for uid %s = %s", uid,
+                        usage_info)
+            return usage_info["UsageLevel"]
+        except:
+            try:
+                return ad.droid.connectivityQueryDetailsForUid(
+                    TYPE_MOBILE,
+                    ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+                    begin_time, end_time, uid)
+            except:
+                return ad.droid.connectivityQueryDetailsForUid(
+                    ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+                    begin_time, end_time, uid)
+    else:
+        try:
+            usage_info = ad.droid.getMobileDataUsageInfo(sid)
+            ad.log.debug("Mobile data usage info = %s", usage_info)
+            return usage_info["UsageLevel"]
+        except:
+            try:
+                return ad.droid.connectivityQuerySummaryForDevice(
+                    TYPE_MOBILE,
+                    ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+                    begin_time, end_time)
+            except:
+                return ad.droid.connectivityQuerySummaryForDevice(
+                    ad.droid.telephonyGetSubscriberIdForSubscription(sid),
+                    begin_time, end_time)
+
+
+def set_mobile_data_usage_limit(ad, limit, subscriber_id=None):
+    if not subscriber_id:
+        subscriber_id = ad.droid.telephonyGetSubscriberId()
+    ad.log.debug("Set subscriber mobile data usage limit to %s", limit)
+    ad.droid.logV("Setting subscriber mobile data usage limit to %s" % limit)
+    try:
+        ad.droid.connectivitySetDataUsageLimit(subscriber_id, str(limit))
+    except:
+        ad.droid.connectivitySetDataUsageLimit(subscriber_id, limit)
+
+
+def remove_mobile_data_usage_limit(ad, subscriber_id=None):
+    if not subscriber_id:
+        subscriber_id = ad.droid.telephonyGetSubscriberId()
+    ad.log.debug("Remove subscriber mobile data usage limit")
+    ad.droid.logV(
+        "Setting subscriber mobile data usage limit to -1, unlimited")
+    try:
+        ad.droid.connectivitySetDataUsageLimit(subscriber_id, "-1")
+    except:
+        ad.droid.connectivitySetDataUsageLimit(subscriber_id, -1)
+
+
+def active_file_download_task(log, ad, file_name="5MB", method="curl"):
+    # 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
+    file_size_map = {
+        '1MB': 1000000,
+        '5MB': 5000000,
+        '10MB': 10000000,
+        '20MB': 20000000,
+        '50MB': 50000000,
+        '100MB': 100000000,
+        '200MB': 200000000,
+        '512MB': 512000000
+    }
+    url_map = {
+        "1MB": [
+            "http://146.148.91.8/download/1MB.zip",
+            "http://ipv4.download.thinkbroadband.com/1MB.zip"
+        ],
+        "5MB": [
+            "http://146.148.91.8/download/5MB.zip",
+            "http://212.183.159.230/5MB.zip",
+            "http://ipv4.download.thinkbroadband.com/5MB.zip"
+        ],
+        "10MB": [
+            "http://146.148.91.8/download/10MB.zip",
+            "http://212.183.159.230/10MB.zip",
+            "http://ipv4.download.thinkbroadband.com/10MB.zip",
+            "http://lax.futurehosting.com/test.zip",
+            "http://ovh.net/files/10Mio.dat"
+        ],
+        "20MB": [
+            "http://146.148.91.8/download/20MB.zip",
+            "http://212.183.159.230/20MB.zip",
+            "http://ipv4.download.thinkbroadband.com/20MB.zip"
+        ],
+        "50MB": [
+            "http://146.148.91.8/download/50MB.zip",
+            "http://212.183.159.230/50MB.zip",
+            "http://ipv4.download.thinkbroadband.com/50MB.zip"
+        ],
+        "100MB": [
+            "http://146.148.91.8/download/100MB.zip",
+            "http://212.183.159.230/100MB.zip",
+            "http://ipv4.download.thinkbroadband.com/100MB.zip",
+            "http://speedtest-ca.turnkeyinternet.net/100mb.bin",
+            "http://ovh.net/files/100Mio.dat",
+            "http://lax.futurehosting.com/test100.zip"
+        ],
+        "200MB": [
+            "http://146.148.91.8/download/200MB.zip",
+            "http://212.183.159.230/200MB.zip",
+            "http://ipv4.download.thinkbroadband.com/200MB.zip"
+        ],
+        "512MB": [
+            "http://146.148.91.8/download/512MB.zip",
+            "http://212.183.159.230/512MB.zip",
+            "http://ipv4.download.thinkbroadband.com/512MB.zip"
+        ]
+    }
+
+    file_size = file_size_map.get(file_name)
+    file_urls = url_map.get(file_name)
+    file_url = None
+    for url in file_urls:
+        url_splits = url.split("/")
+        if verify_http_connection(log, ad, url=url, retry=1):
+            output_path = "/sdcard/Download/%s" % url_splits[-1]
+            file_url = url
+            break
+    if not file_url:
+        ad.log.error("No url is available to download %s", file_name)
+        return False
+    timeout = min(max(file_size / 100000, 600), 3600)
+    if method == "sl4a":
+        return (http_file_download_by_sl4a, (ad, file_url, output_path,
+                                             file_size, True, timeout))
+    if method == "curl" and check_curl_availability(ad):
+        return (http_file_download_by_curl, (ad, file_url, output_path,
+                                             file_size, True, timeout))
+    elif method == "sl4a" or method == "curl":
+        return (http_file_download_by_sl4a, (ad, file_url, output_path,
+                                             file_size, True, timeout))
+    else:
+        return (http_file_download_by_chrome, (ad, file_url, file_size, True,
+                                               timeout))
+
+
+def active_file_download_test(log, ad, file_name="5MB", method="sl4a"):
+    task = active_file_download_task(log, ad, file_name, method=method)
+    if not task:
+        return False
+    return task[0](*task[1])
+
+
+def check_data_stall_detection(ad, wait_time=WAIT_TIME_FOR_DATA_STALL):
+    data_stall_detected = False
+    time_var = 1
+    try:
+        while (time_var < wait_time):
+            out = ad.adb.shell("dumpsys network_stack " \
+                              "| grep \"Suspecting data stall\"",
+                            ignore_status=True)
+            ad.log.debug("Output is %s", out)
+            if out:
+                ad.log.info("NetworkMonitor detected - %s", out)
+                data_stall_detected = True
+                break
+            time.sleep(30)
+            time_var += 30
+    except Exception as e:
+        ad.log.error(e)
+    return data_stall_detected
+
+
+def check_network_validation_fail(ad, begin_time=None,
+                                  wait_time=WAIT_TIME_FOR_NW_VALID_FAIL):
+    network_validation_fail = False
+    time_var = 1
+    try:
+        while (time_var < wait_time):
+            time_var += 30
+            nw_valid = ad.search_logcat("validation failed",
+                                         begin_time)
+            if nw_valid:
+                ad.log.info("Validation Failed received here - %s",
+                            nw_valid[0]["log_message"])
+                network_validation_fail = True
+                break
+            time.sleep(30)
+    except Exception as e:
+        ad.log.error(e)
+    return network_validation_fail
+
+
+def check_data_stall_recovery(ad, begin_time=None,
+                              wait_time=WAIT_TIME_FOR_DATA_STALL_RECOVERY):
+    data_stall_recovery = False
+    time_var = 1
+    try:
+        while (time_var < wait_time):
+            time_var += 30
+            recovery = ad.search_logcat("doRecovery() cleanup all connections",
+                                         begin_time)
+            if recovery:
+                ad.log.info("Recovery Performed here - %s",
+                            recovery[-1]["log_message"])
+                data_stall_recovery = True
+                break
+            time.sleep(30)
+    except Exception as e:
+        ad.log.error(e)
+    return data_stall_recovery
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_defines.py b/acts_tests/acts_contrib/test_utils/tel/tel_defines.py
index 94d03f1..f3fc75c 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_defines.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_defines.py
@@ -354,7 +354,6 @@
 CARRIER_KDDI = 'kddi'
 CARRIER_RAKUTEN = 'rakuten'
 CARRIER_SBM = 'sbm'
-CARRIER_VZW = "Verizon"
 CARRIER_SKT = 'skt'
 CARRIER_KT = 'kt'
 CARRIER_LG_UPLUS = 'lg_uplus'
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_dsds_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_dsds_utils.py
index 4459221..6b15886 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_dsds_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_dsds_utils.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-#   Copyright 2021 - Google
+#   Copyright 2022 - Google
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
@@ -14,19 +14,25 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+from datetime import datetime, timedelta
 import re
 import time
-from datetime import datetime, timedelta
+from typing import Optional, Sequence
 
 from acts import signals
+from acts import tracelogger
+from acts.controllers.android_device import AndroidDevice
 from acts.utils import rand_ascii_str
 from acts.libs.utils.multithread import multithread_func
 from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
+from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
 from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
 from acts_contrib.test_utils.tel.tel_defines import YOUTUBE_PACKAGE_NAME
+from acts_contrib.test_utils.tel.tel_data_utils import active_file_download_test
+from acts_contrib.test_utils.tel.tel_data_utils import start_youtube_video
 from acts_contrib.test_utils.tel.tel_message_utils import log_messaging_screen_shot
 from acts_contrib.test_utils.tel.tel_message_utils import mms_send_receive_verify
 from acts_contrib.test_utils.tel.tel_message_utils import sms_send_receive_verify_for_subscription
@@ -35,46 +41,276 @@
 from acts_contrib.test_utils.tel.tel_ss_utils import set_call_waiting
 from acts_contrib.test_utils.tel.tel_ims_utils import toggle_wfc_for_subscription
 from acts_contrib.test_utils.tel.tel_ims_utils import set_wfc_mode_for_subscription
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_voice_general
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_on_rat
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_network_idle
+from acts_contrib.test_utils.tel.tel_ss_utils import three_phone_call_forwarding_short_seq
+from acts_contrib.test_utils.tel.tel_ss_utils import three_phone_call_waiting_short_seq
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_slot_index_from_subid
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_on_same_network_of_host_ad
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_message_subid
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_data
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_voice_sub_id
-from acts_contrib.test_utils.tel.tel_test_utils import active_file_download_test
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
 from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
-from acts_contrib.test_utils.tel.tel_test_utils import get_slot_index_from_subid
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
 from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
 from acts_contrib.test_utils.tel.tel_test_utils import power_off_sim
 from acts_contrib.test_utils.tel.tel_test_utils import power_on_sim
-from acts_contrib.test_utils.tel.tel_test_utils import start_youtube_video
 from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
 from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
-from acts_contrib.test_utils.tel.tel_test_utils import wait_and_reject_call_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_general
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_on_rat
-from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
-from acts_contrib.test_utils.tel.tel_voice_utils import three_phone_call_forwarding_short_seq
-from acts_contrib.test_utils.tel.tel_voice_utils import three_phone_call_waiting_short_seq
-from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_msim_for_slot
-from acts_contrib.test_utils.tel.tel_voice_utils import wait_for_network_idle
 from acts_contrib.test_utils.tel.tel_voice_conf_utils import _test_ims_conference_merge_drop_second_call_from_participant
 from acts_contrib.test_utils.tel.tel_voice_conf_utils import _test_wcdma_conference_merge_drop
 from acts_contrib.test_utils.tel.tel_voice_conf_utils import _three_phone_call_mo_add_mt
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
+from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_msim_for_slot
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_and_reject_call_for_subscription
+from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_wifi_utils import wifi_toggle_state
 
 CallResult = TelephonyVoiceTestResult.CallResult.Value
 
+
+def dsds_long_call_streaming_test(
+    log: tracelogger.TraceLogger,
+    tel_logger: TelephonyMetricLogger.for_test_case,
+    ads: Sequence[AndroidDevice],
+    test_rat: list,
+    test_slot: int,
+    dds_slot: int,
+    direction: str = "mo",
+    duration: int = 360,
+    streaming: bool = True,
+    is_airplane_mode = False,
+    wfc_mode: list[str, str] = [
+        WFC_MODE_CELLULAR_PREFERRED,
+        WFC_MODE_CELLULAR_PREFERRED],
+    wifi_network_ssid: Optional[str] = None,
+    wifi_network_pass: Optional[str] = None,
+    turn_off_wifi_in_the_end: bool = False,
+    turn_off_airplane_mode_in_the_end: bool = False) -> bool:
+    """Make MO/MT call at specific slot in specific RAT with DDS at specific
+    slot for the given time.
+
+    Args:
+        log: Logger object.
+        tel_logger: Logger object for telephony proto.
+        ads: A list of Android device objects.
+        test_rat: RAT for both slots of primary device.
+        test_slot: The slot which make/receive MO/MT call of primary device.
+        dds_slot: Preferred data slot of primary device.
+        direction: The direction of call("mo" or "mt").
+        streaming: True for playing Youtube and False on the contrary.
+        is_airplane_mode: True or False for WFC setup
+        wfc_mode: Cellular preferred or Wi-Fi preferred.
+        wifi_network_ssid: SSID of Wi-Fi AP.
+        wifi_network_pass: Password of Wi-Fi AP SSID.
+        turn_off_wifi_in_the_end: True to turn off Wi-Fi and False not to turn
+            off Wi-Fi in the end of the function.
+        turn_off_airplane_mode_in_the_end: True to turn off airplane mode and
+            False not to turn off airplane mode in the end of the function.
+
+    Returns:
+        TestFailure if failed.
+    """
+    log.info("Step 1: Switch DDS.")
+    if not set_dds_on_slot(ads[0], dds_slot):
+        ads[0].log.error(
+            "Failed to set DDS at slot %s on %s",(dds_slot, ads[0].serial))
+        return False
+
+    log.info("Step 2: Check HTTP connection after DDS switch.")
+    if not verify_http_connection(log, ads[0]):
+        ads[0].log.error("Failed to verify http connection.")
+        return False
+    else:
+        ads[0].log.info("Verify http connection successfully.")
+
+    log.info("Step 3: Set up phones in desired RAT.")
+    if direction == "mo":
+        # setup voice subid on primary device.
+        ad_mo = ads[0]
+        mo_sub_id = get_subid_from_slot_index(log, ad_mo, test_slot)
+        if mo_sub_id == INVALID_SUB_ID:
+            ad_mo.log.warning("Failed to get sub ID at slot %s.", test_slot)
+            return False
+        mo_other_sub_id = get_subid_from_slot_index(
+            log, ad_mo, 1-test_slot)
+        sub_id_list = [mo_sub_id, mo_other_sub_id]
+        set_voice_sub_id(ad_mo, mo_sub_id)
+        ad_mo.log.info("Sub ID for outgoing call at slot %s: %s", test_slot,
+        get_outgoing_voice_sub_id(ad_mo))
+
+        # setup voice subid on secondary device.
+        ad_mt = ads[1]
+        _, mt_sub_id, _ = get_subid_on_same_network_of_host_ad(ads)
+        if mt_sub_id == INVALID_SUB_ID:
+            ad_mt.log.warning("Failed to get sub ID at default voice slot.")
+            return False
+        mt_slot = get_slot_index_from_subid(ad_mt, mt_sub_id)
+        set_voice_sub_id(ad_mt, mt_sub_id)
+        ad_mt.log.info("Sub ID for incoming call at slot %s: %s", mt_slot,
+        get_outgoing_voice_sub_id(ad_mt))
+
+        # setup the rat on non-test slot(primary device).
+        phone_setup_on_rat(
+            log,
+            ad_mo,
+            test_rat[1-test_slot],
+            mo_other_sub_id,
+            is_airplane_mode,
+            wfc_mode[1-test_slot],
+            wifi_network_ssid,
+            wifi_network_pass)
+        # assign phone setup argv for test slot.
+        mo_phone_setup_func_argv = (
+            log,
+            ad_mo,
+            test_rat[test_slot],
+            mo_sub_id,
+            is_airplane_mode,
+            wfc_mode[test_slot],
+            wifi_network_ssid,
+            wifi_network_pass)
+        verify_caller_func = is_phone_in_call_on_rat(
+            log, ad_mo, test_rat[test_slot], only_return_fn=True)
+        mt_phone_setup_func_argv = (log, ad_mt, 'general')
+        verify_callee_func = is_phone_in_call_on_rat(
+            log, ad_mt, 'general', only_return_fn=True)
+    else:
+        # setup voice subid on primary device.
+        ad_mt = ads[0]
+        mt_sub_id = get_subid_from_slot_index(log, ad_mt, test_slot)
+        if mt_sub_id == INVALID_SUB_ID:
+            ad_mt.log.warning("Failed to get sub ID at slot %s.", test_slot)
+            return False
+        mt_other_sub_id = get_subid_from_slot_index(
+            log, ad_mt, 1-test_slot)
+        sub_id_list = [mt_sub_id, mt_other_sub_id]
+        set_voice_sub_id(ad_mt, mt_sub_id)
+        ad_mt.log.info("Sub ID for incoming call at slot %s: %s", test_slot,
+        get_outgoing_voice_sub_id(ad_mt))
+
+        # setup voice subid on secondary device.
+        ad_mo = ads[1]
+        _, mo_sub_id, _ = get_subid_on_same_network_of_host_ad(ads)
+        if mo_sub_id == INVALID_SUB_ID:
+            ad_mo.log.warning("Failed to get sub ID at default voice slot.")
+            return False
+        mo_slot = get_slot_index_from_subid(ad_mo, mo_sub_id)
+        set_voice_sub_id(ad_mo, mo_sub_id)
+        ad_mo.log.info("Sub ID for outgoing call at slot %s: %s", mo_slot,
+        get_outgoing_voice_sub_id(ad_mo))
+
+        # setup the rat on non-test slot(primary device).
+        phone_setup_on_rat(
+            log,
+            ad_mt,
+            test_rat[1-test_slot],
+            mt_other_sub_id,
+            is_airplane_mode,
+            wfc_mode[1-test_slot],
+            wifi_network_ssid,
+            wifi_network_pass)
+        # assign phone setup argv for test slot.
+        mt_phone_setup_func_argv = (
+            log,
+            ad_mt,
+            test_rat[test_slot],
+            mt_sub_id,
+            is_airplane_mode,
+            wfc_mode[test_slot],
+            wifi_network_ssid,
+            wifi_network_pass)
+        verify_callee_func = is_phone_in_call_on_rat(
+            log, ad_mt, test_rat[test_slot], only_return_fn=True)
+        mo_phone_setup_func_argv = (log, ad_mo, 'general')
+        verify_caller_func = is_phone_in_call_on_rat(
+            log, ad_mo, 'general', only_return_fn=True)
+
+    tasks = [(phone_setup_on_rat, mo_phone_setup_func_argv),
+             (phone_setup_on_rat, mt_phone_setup_func_argv)]
+    if not multithread_func(log, tasks):
+        log.error("Phone Failed to Set Up Properly.")
+        tel_logger.set_result(CallResult("CALL_SETUP_FAILURE"))
+        raise signals.TestFailure("Failed",
+            extras={"fail_reason": "Phone Failed to Set Up Properly."})
+    if streaming:
+        log.info("Step 4-0: Start Youtube streaming.")
+        if not start_youtube_video(ads[0]):
+            raise signals.TestFailure("Failed",
+                extras={"fail_reason": "Fail to bring up youtube video."})
+        time.sleep(10)
+
+    log.info("Step 4: Make voice call.")
+    result = call_setup_teardown(log,
+                                 ad_mo,
+                                 ad_mt,
+                                 ad_hangup=ad_mo,
+                                 verify_caller_func=verify_caller_func,
+                                 verify_callee_func=verify_callee_func,
+                                 wait_time_in_call=duration)
+    tel_logger.set_result(result.result_value)
+
+    if not result:
+        log.error(
+            "Failed to make %s call from %s slot %s to %s slot %s",
+                direction, ad_mo.serial, mo_slot, ad_mt.serial, mt_slot)
+        raise signals.TestFailure("Failed",
+            extras={"fail_reason": str(result.result_value)})
+
+    log.info("Step 5: Verify RAT and HTTP connection.")
+    # For the tese cases related to WFC in which airplane mode will be turned
+    # off in the end.
+    if turn_off_airplane_mode_in_the_end:
+        log.info("Step 5-1: Turning off airplane mode......")
+        if not toggle_airplane_mode(log, ads[0], False):
+            ads[0].log.error('Failed to toggle off airplane mode.')
+
+    # For the tese cases related to WFC in which Wi-Fi will be turned off in the
+    # end.
+
+    rat_list = [test_rat[test_slot], test_rat[1-test_slot]]
+
+    if turn_off_wifi_in_the_end:
+        log.info("Step 5-2: Turning off Wi-Fi......")
+        if not wifi_toggle_state(log, ads[0], False):
+            ads[0].log.error('Failed to toggle off Wi-Fi.')
+            return False
+
+        for index, value in enumerate(rat_list):
+            if value == '5g_wfc':
+                rat_list[index] = '5g'
+            elif value == 'wfc':
+                rat_list[index] = '4g'
+
+    for rat, sub_id in zip(rat_list, sub_id_list):
+        if not wait_for_network_idle(log, ads[0], rat, sub_id):
+            raise signals.TestFailure(
+                "Failed",
+                extras={
+                    "fail_reason": "Idle state of sub ID %s does not match the "
+                    "given RAT %s." % (sub_id, rat)})
+
+    if not verify_http_connection(log, ads[0]):
+        ads[0].log.error("Failed to verify http connection.")
+        return False
+    else:
+        ads[0].log.info("Verify http connection successfully.")
+
+    if streaming:
+        ads[0].force_stop_apk(YOUTUBE_PACKAGE_NAME)
+
+
 def dsds_voice_call_test(
         log,
         tel_logger,
@@ -257,11 +493,11 @@
     result = two_phone_call_msim_for_slot(
         log,
         ad_mo,
-        get_slot_index_from_subid(log, ad_mo, mo_sub_id),
+        get_slot_index_from_subid(ad_mo, mo_sub_id),
         None,
         is_mo_in_call,
         ad_mt,
-        get_slot_index_from_subid(log, ad_mt, mt_sub_id),
+        get_slot_index_from_subid(ad_mt, mt_sub_id),
         None,
         is_mt_in_call)
 
@@ -662,10 +898,8 @@
 
             if call_or_sms_or_mms == "call":
                 log.info("Step 4: Make voice call.")
-                mo_slot = get_slot_index_from_subid(
-                    log, ad_mo, mo_sub_id)
-                mt_slot = get_slot_index_from_subid(
-                    log, ad_mt, mt_sub_id)
+                mo_slot = get_slot_index_from_subid(ad_mo, mo_sub_id)
+                mt_slot = get_slot_index_from_subid(ad_mt, mt_sub_id)
                 result = two_phone_call_msim_for_slot(
                     log,
                     ad_mo,
@@ -883,11 +1117,11 @@
     result = two_phone_call_msim_for_slot(
         log,
         ad_mo,
-        get_slot_index_from_subid(log, ad_mo, mo_sub_id),
+        get_slot_index_from_subid(ad_mo, mo_sub_id),
         None,
         is_mo_in_call,
         ad_mt,
-        get_slot_index_from_subid(log, ad_mt, mt_sub_id),
+        get_slot_index_from_subid(ad_mt, mt_sub_id),
         None,
         is_mt_in_call)
 
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_logging_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_logging_utils.py
index 499d7ee..1cb6bb5 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_logging_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_logging_utils.py
@@ -20,6 +20,8 @@
 import shutil
 import time
 
+from acts import utils
+from acts.libs.proc import job
 from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
 from acts.controllers.android_device import DEFAULT_SDM_LOG_PATH
 from acts.libs.utils.multithread import run_multithread_func
@@ -27,6 +29,21 @@
 from acts.utils import start_standing_subprocess
 
 
+_LS_MASK_NAME = "Lassen default + TCP"
+
+_LS_ENABLE_LOG_SHELL = f"""\
+am broadcast -n com.android.pixellogger/.receiver.AlwaysOnLoggingReceiver \
+    -a com.android.pixellogger.service.logging.LoggingService.ACTION_CONFIGURE_ALWAYS_ON_LOGGING \
+    -e intent_key_enable "true" -e intent_key_config "{_LS_MASK_NAME}" \
+    --ei intent_key_max_log_size_mb 100 --ei intent_key_max_number_of_files 100
+"""
+_LS_DISABLE_LOG_SHELL = """\
+am broadcast -n com.android.pixellogger/.receiver.AlwaysOnLoggingReceiver \
+    -a com.android.pixellogger.service.logging.LoggingService.ACTION_CONFIGURE_ALWAYS_ON_LOGGING \
+    -e intent_key_enable "false"
+"""
+
+
 def check_if_tensor_platform(ad):
     """Check if current platform belongs to the Tensor platform
 
@@ -72,32 +89,51 @@
 def start_sdm_logger(ad):
     """Start SDM logger."""
     if not getattr(ad, "sdm_log", True): return
+
     # Delete existing SDM logs which were created 15 mins prior
     ad.sdm_log_path = DEFAULT_SDM_LOG_PATH
     file_count = ad.adb.shell(
-        "find %s -type f -iname sbuff_[0-9]*.sdm* | wc -l" % ad.sdm_log_path)
+        f"find {ad.sdm_log_path} -type f -iname sbuff_[0-9]*.sdm* | wc -l")
     if int(file_count) > 3:
         seconds = 15 * 60
         # Remove sdm logs modified more than specified seconds ago
         ad.adb.shell(
-            "find %s -type f -iname sbuff_[0-9]*.sdm* -not -mtime -%ss -delete" %
-            (ad.sdm_log_path, seconds))
-    # Disable any modem logging already running
-    if not getattr(ad, "enable_always_on_modem_logger", False):
-        ad.adb.shell("setprop persist.vendor.sys.modem.logging.enable false")
+            f"find {ad.sdm_log_path} -type f -iname sbuff_[0-9]*.sdm* "
+            f"-not -mtime -{seconds}s -delete")
+
+    # Disable modem logging already running
+    stop_sdm_logger(ad)
+
     # start logging
-    cmd = "setprop vendor.sys.modem.logging.enable true"
     ad.log.debug("start sdm logging")
-    ad.adb.shell(cmd, ignore_status=True)
-    time.sleep(5)
+    while int(
+        ad.adb.shell(f"find {ad.sdm_log_path} -type f "
+                     "-iname sbuff_profile.sdm | wc -l") == 0 or
+        int(
+            ad.adb.shell(f"find {ad.sdm_log_path} -type f "
+                         "-iname sbuff_[0-9]*.sdm* | wc -l")) == 0):
+        ad.adb.shell(_LS_ENABLE_LOG_SHELL, ignore_status=True)
+        time.sleep(5)
 
 
 def stop_sdm_logger(ad):
     """Stop SDM logger."""
-    cmd = "setprop vendor.sys.modem.logging.enable false"
+    ad.sdm_log_path = DEFAULT_SDM_LOG_PATH
+    cycle = 1
+
     ad.log.debug("stop sdm logging")
-    ad.adb.shell(cmd, ignore_status=True)
-    time.sleep(5)
+    while int(
+        ad.adb.shell(
+            f"find {ad.sdm_log_path} -type f -iname sbuff_profile.sdm -o "
+            "-iname sbuff_[0-9]*.sdm* | wc -l")) != 0:
+        if cycle == 1 and int(
+            ad.adb.shell(f"find {ad.sdm_log_path} -type f "
+                         "-iname sbuff_profile.sdm | wc -l")) == 0:
+            ad.adb.shell(_LS_ENABLE_LOG_SHELL, ignore_status=True)
+            time.sleep(5)
+        ad.adb.shell(_LS_DISABLE_LOG_SHELL, ignore_status=True)
+        cycle += 1
+        time.sleep(15)
 
 
 def start_sdm_loggers(log, ads):
@@ -492,4 +528,51 @@
         stop_time = datetime.now()
         passed_time = (stop_time - start_time).total_seconds()
         if passed_time > max_wait_time:
-            return
\ No newline at end of file
+            return
+
+
+def extract_test_log(log, src_file, dst_file, test_tag):
+    os.makedirs(os.path.dirname(dst_file), exist_ok=True)
+    cmd = "grep -n '%s' %s" % (test_tag, src_file)
+    result = job.run(cmd, ignore_status=True)
+    if not result.stdout or result.exit_status == 1:
+        log.warning("Command %s returns %s", cmd, result)
+        return
+    line_nums = re.findall(r"(\d+).*", result.stdout)
+    if line_nums:
+        begin_line = int(line_nums[0])
+        end_line = int(line_nums[-1])
+        if end_line - begin_line <= 5:
+            result = job.run("wc -l < %s" % src_file)
+            if result.stdout:
+                end_line = int(result.stdout)
+        log.info("Extract %s from line %s to line %s to %s", src_file,
+                 begin_line, end_line, dst_file)
+        job.run("awk 'NR >= %s && NR <= %s' %s > %s" % (begin_line, end_line,
+                                                        src_file, dst_file))
+
+
+def log_screen_shot(ad, test_name=""):
+    file_name = "/sdcard/Pictures/screencap"
+    if test_name:
+        file_name = "%s_%s" % (file_name, test_name)
+    file_name = "%s_%s.png" % (file_name, utils.get_current_epoch_time())
+    try:
+        ad.adb.shell("screencap -p %s" % file_name)
+    except:
+        ad.log.error("Fail to log screen shot to %s", file_name)
+
+
+def get_screen_shot_log(ad, test_name="", begin_time=None):
+    logs = ad.get_file_names("/sdcard/Pictures", begin_time=begin_time)
+    if logs:
+        ad.log.info("Pulling %s", logs)
+        log_path = os.path.join(ad.device_log_path, "Screenshot_%s" % ad.serial)
+        os.makedirs(log_path, exist_ok=True)
+        ad.pull_files(logs, log_path)
+    ad.adb.shell("rm -rf /sdcard/Pictures/screencap_*", ignore_status=True)
+
+
+def get_screen_shot_logs(ads, test_name="", begin_time=None):
+    for ad in ads:
+        get_screen_shot_log(ad, test_name=test_name, begin_time=begin_time)
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_lookup_tables.py b/acts_tests/acts_contrib/test_utils/tel/tel_lookup_tables.py
index 8304cdd..df3336b 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_lookup_tables.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_lookup_tables.py
@@ -287,6 +287,28 @@
         '45004': tel_defines.CARRIER_KT,
         '45008': tel_defines.CARRIER_KT,
 
+        #Softbank (Japan)
+        '44004': tel_defines.CARRIER_SBM,
+        '44006': tel_defines.CARRIER_SBM,
+        '44020': tel_defines.CARRIER_SBM,
+        '44040': tel_defines.CARRIER_SBM,
+        '44041': tel_defines.CARRIER_SBM,
+        '44042': tel_defines.CARRIER_SBM,
+        '44043': tel_defines.CARRIER_SBM,
+        '44044': tel_defines.CARRIER_SBM,
+        '44045': tel_defines.CARRIER_SBM,
+        '44046': tel_defines.CARRIER_SBM,
+        '44047': tel_defines.CARRIER_SBM,
+        '44048': tel_defines.CARRIER_SBM,
+        '44090': tel_defines.CARRIER_SBM,
+        '44092': tel_defines.CARRIER_SBM,
+        '44093': tel_defines.CARRIER_SBM,
+        '44094': tel_defines.CARRIER_SBM,
+        '44095': tel_defines.CARRIER_SBM,
+        '44096': tel_defines.CARRIER_SBM,
+        '44097': tel_defines.CARRIER_SBM,
+        '44098': tel_defines.CARRIER_SBM,
+
         #SK Telecom (South Korea)
         '45005': tel_defines.CARRIER_SKT,
 
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_message_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_message_utils.py
index 9a8e3ad..21c418f 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_message_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_message_utils.py
@@ -40,30 +40,31 @@
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
 from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts_contrib.test_utils.tel.tel_test_utils import CallResult
-from acts_contrib.test_utils.tel.tel_test_utils import TelResultWrapper
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts_contrib.test_utils.tel.tel_test_utils import check_phone_number_match
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts_contrib.test_utils.tel.tel_test_utils import last_call_drop_reason
-from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
-from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_in_call_active
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_call_end
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_call_offhook_for_subscription
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_on_rat
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_message_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_on_same_network_of_host_ad
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_message
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_on_rat
+from acts_contrib.test_utils.tel.tel_test_utils import CallResult
+from acts_contrib.test_utils.tel.tel_test_utils import TelResultWrapper
+from acts_contrib.test_utils.tel.tel_test_utils import check_phone_number_match
+from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_voice_utils import last_call_drop_reason
 from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_on_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_and_answer_call_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_for_in_call_active
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_for_call_end
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_for_call_offhook_for_subscription
 from acts_contrib.test_utils.tel.tel_video_utils import is_phone_in_call_video_bidirectional
 from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup_teardown
 from acts_contrib.test_utils.tel.tel_video_utils import phone_idle_video
+from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected
+
 
 def send_message_with_random_message_body(
     log, ad_mo, ad_mt, msg_type='sms', long_msg=False, mms_expected_result=True):
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_mms_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_mms_utils.py
index cd3f74e..d9d85a7 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_mms_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_mms_utils.py
@@ -18,12 +18,13 @@
 from acts.utils import rand_ascii_str
 from acts_contrib.test_utils.tel.tel_message_utils import mms_send_receive_verify
 from acts_contrib.test_utils.tel.tel_message_utils import mms_receive_verify_after_call_hangup
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
 from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
 
 message_lengths = (50, 160, 180)
 long_message_lengths = (800, 1600)
 
+
 def _mms_test_mo(log, ads, expected_result=True):
     return _mms_test(log,
         [ads[0], ads[1]], expected_result=expected_result)
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_parse_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_parse_utils.py
index e4366cd..118f912 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_parse_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_parse_utils.py
@@ -63,6 +63,7 @@
 ON_IMS_MM_TEL_CONNECTED_IWLAN_SLOT0 = r'ImsPhone: \[0\].*onImsMmTelConnected imsRadioTech=WLAN'
 ON_IMS_MM_TEL_CONNECTED_IWLAN_SLOT1 = r'ImsPhone: \[1\].*onImsMmTelConnected imsRadioTech=WLAN'
 
+
 def print_nested_dict(ad, d):
     divider = "------"
     for k, v in d.items():
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_phone_setup_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_phone_setup_utils.py
new file mode 100644
index 0000000..6077d9c
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_phone_setup_utils.py
@@ -0,0 +1,1758 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2021 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import time
+from acts import signals
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
+from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_FRE
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_TMO
+from acts_contrib.test_utils.tel.tel_defines import GEN_2G
+from acts_contrib.test_utils.tel.tel_defines import GEN_3G
+from acts_contrib.test_utils.tel.tel_defines import GEN_4G
+from acts_contrib.test_utils.tel.tel_defines import GEN_5G
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WFC_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_ONLY
+from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
+from acts_contrib.test_utils.tel.tel_defines import RAT_5G
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WCDMA
+from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
+from acts_contrib.test_utils.tel.tel_defines import RAT_UNKNOWN
+from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_IDLE
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_1XRTT_VOICE_ATTACH
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
+from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_5g_utils import is_current_network_5g
+from acts_contrib.test_utils.tel.tel_ims_utils import toggle_volte
+from acts_contrib.test_utils.tel.tel_ims_utils import toggle_volte_for_subscription
+from acts_contrib.test_utils.tel.tel_ims_utils import set_wfc_mode
+from acts_contrib.test_utils.tel.tel_ims_utils import set_wfc_mode_for_subscription
+from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_enhanced_4g_lte_setting
+from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_volte_enabled
+from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_wfc_disabled
+from acts_contrib.test_utils.tel.tel_lookup_tables import network_preference_for_generation
+from acts_contrib.test_utils.tel.tel_lookup_tables import rat_families_for_network_preference
+from acts_contrib.test_utils.tel.tel_lookup_tables import rat_family_for_generation
+from acts_contrib.test_utils.tel.tel_lookup_tables import rat_family_from_rat
+from acts_contrib.test_utils.tel.tel_lookup_tables import rat_generation_from_rat
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
+from acts_contrib.test_utils.tel.tel_test_utils import _is_attached
+from acts_contrib.test_utils.tel.tel_test_utils import _is_attached_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import _wait_for_droid_in_state
+from acts_contrib.test_utils.tel.tel_test_utils import _wait_for_droid_in_state_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import get_capability_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import get_cell_data_roaming_state_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
+from acts_contrib.test_utils.tel.tel_test_utils import get_telephony_signal_strength
+from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_network_generation_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_rat_family_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_rat_family_list_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import reset_preferred_network_type_to_allowable_range
+from acts_contrib.test_utils.tel.tel_test_utils import set_cell_data_roaming_state_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import wait_for_data_attach_for_subscription
+from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_wifi_utils import set_wifi_to_default
+from acts.libs.utils.multithread import multithread_func
+
+
+def phone_setup_iwlan(log,
+                      ad,
+                      is_airplane_mode,
+                      wfc_mode,
+                      wifi_ssid=None,
+                      wifi_pwd=None,
+                      nw_gen=None):
+    """Phone setup function for epdg call test.
+    Set WFC mode according to wfc_mode.
+    Set airplane mode according to is_airplane_mode.
+    Make sure phone connect to WiFi. (If wifi_ssid is not None.)
+    Wait for phone to be in iwlan data network type.
+    Wait for phone to report wfc enabled flag to be true.
+    Args:
+        log: Log object.
+        ad: Android device object.
+        is_airplane_mode: True to turn on airplane mode. False to turn off airplane mode.
+        wfc_mode: WFC mode to set to.
+        wifi_ssid: WiFi network SSID. This is optional.
+            If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
+        wifi_pwd: WiFi network password. This is optional.
+        nw_gen: network type selection. This is optional.
+            GEN_4G for 4G, GEN_5G for 5G or None for doing nothing.
+    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, nw_gen)
+
+
+def phone_setup_iwlan_for_subscription(log,
+                                       ad,
+                                       sub_id,
+                                       is_airplane_mode,
+                                       wfc_mode,
+                                       wifi_ssid=None,
+                                       wifi_pwd=None,
+                                       nw_gen=None,
+                                       nr_type=None):
+    """Phone setup function for epdg call test for subscription id.
+    Set WFC mode according to wfc_mode.
+    Set airplane mode according to is_airplane_mode.
+    Make sure phone connect to WiFi. (If wifi_ssid is not None.)
+    Wait for phone to be in iwlan data network type.
+    Wait for phone to report wfc enabled flag to be true.
+    Args:
+        log: Log object.
+        ad: Android device object.
+        sub_id: subscription id.
+        is_airplane_mode: True to turn on airplane mode. False to turn off airplane mode.
+        wfc_mode: WFC mode to set to.
+        wifi_ssid: WiFi network SSID. This is optional.
+            If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
+        wifi_pwd: WiFi network password. This is optional.
+        nw_gen: network type selection. This is optional.
+            GEN_4G for 4G, GEN_5G for 5G or None for doing nothing.
+        nr_type: NR network type
+    Returns:
+        True if success. False if fail.
+    """
+    if not get_capability_for_subscription(ad, CAPABILITY_WFC, sub_id):
+        ad.log.error("WFC is not supported, abort test.")
+        raise signals.TestSkip("WFC is not supported, abort test.")
+
+    if nw_gen:
+        if not ensure_network_generation_for_subscription(
+                log, ad, sub_id, nw_gen, voice_or_data=NETWORK_SERVICE_DATA,
+                nr_type=nr_type):
+            ad.log.error("Failed to set to %s data.", nw_gen)
+            return False
+    toggle_airplane_mode(log, ad, is_airplane_mode, strict_checking=False)
+
+    # Pause at least for 4 seconds is necessary after airplane mode was turned
+    # on due to the mechanism of deferring Wi-Fi (b/191481736)
+    if is_airplane_mode:
+        time.sleep(5)
+
+    # 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, apm=is_airplane_mode):
+            ad.log.error("Fail to bring up WiFi connection on %s.", wifi_ssid)
+            return False
+    else:
+        ad.log.info("WiFi network SSID not specified, available user "
+                    "parameters are: wifi_network_ssid, wifi_network_ssid_2g, "
+                    "wifi_network_ssid_5g")
+    if not set_wfc_mode_for_subscription(ad, wfc_mode, sub_id):
+        ad.log.error("Unable to set WFC mode to %s.", wfc_mode)
+        return False
+
+    if wfc_mode != WFC_MODE_DISABLED:
+        if not wait_for_wfc_enabled(log, ad, max_time=MAX_WAIT_TIME_WFC_ENABLED):
+            ad.log.error("WFC is not enabled")
+            return False
+
+    return True
+
+
+def phone_setup_iwlan_cellular_preferred(log,
+                                         ad,
+                                         wifi_ssid=None,
+                                         wifi_pwd=None):
+    """Phone setup function for iwlan Non-APM CELLULAR_PREFERRED test.
+    Set WFC mode according to CELLULAR_PREFERRED.
+    Set airplane mode according to False.
+    Make sure phone connect to WiFi. (If wifi_ssid is not None.)
+    Make sure phone don't report iwlan data network type.
+    Make sure phone don't report wfc enabled flag to be true.
+
+    Args:
+        log: Log object.
+        ad: Android device object.
+        wifi_ssid: WiFi network SSID. This is optional.
+            If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
+        wifi_pwd: WiFi network password. This is optional.
+
+    Returns:
+        True if success. False if fail.
+    """
+    toggle_airplane_mode(log, ad, False, 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 wifi_ssid is not None:
+        if not ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd):
+            ad.log.error("Connect to WiFi failed.")
+            return False
+    if not set_wfc_mode(log, ad, WFC_MODE_CELLULAR_PREFERRED):
+        ad.log.error("Set WFC mode failed.")
+        return False
+    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):
+        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,
+                                        nr_type=None):
+    """Setup Phone <sub_id> Data to <network_generation>
+
+    Args:
+        log: log object
+        ad: android device object
+        sub_id: subscription id
+        network_generation: network generation, e.g. GEN_2G, GEN_3G, GEN_4G, GEN_5G
+        nr_type: NR network type e.g. NSA, SA, MMWAVE
+
+    Returns:
+        True if success, False if fail.
+    """
+    toggle_airplane_mode(log, ad, False, strict_checking=False)
+    set_wifi_to_default(log, ad)
+    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+        ad.log.error("Disable WFC failed.")
+        return False
+    if not ensure_network_generation_for_subscription(
+            log,
+            ad,
+            sub_id,
+            network_generation,
+            voice_or_data=NETWORK_SERVICE_DATA,
+            nr_type=nr_type):
+        get_telephony_signal_strength(ad)
+        return False
+    return True
+
+
+def phone_setup_5g(log, ad, nr_type=None):
+    """Setup Phone default data sub_id data to 5G.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    return phone_setup_5g_for_subscription(log, ad,
+                                           get_default_data_sub_id(ad), nr_type=nr_type)
+
+
+def phone_setup_5g_for_subscription(log, ad, sub_id, nr_type=None):
+    """Setup Phone <sub_id> Data to 5G.
+
+    Args:
+        log: log object
+        ad: android device object
+        sub_id: subscription id
+        nr_type: NR network type e.g. NSA, SA, MMWAVE
+
+    Returns:
+        True if success, False if fail.
+    """
+    return phone_setup_data_for_subscription(log, ad, sub_id, GEN_5G,
+                                        nr_type=nr_type)
+
+
+def phone_setup_4g(log, ad):
+    """Setup Phone default data sub_id data to 4G.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    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.
+
+    Args:
+        log: log object
+        ad: android device object
+        sub_id: subscription id
+
+    Returns:
+        True if success, False if fail.
+    """
+    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.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    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.
+
+    Args:
+        log: log object
+        ad: android device object
+        sub_id: subscription id
+
+    Returns:
+        True if success, False if fail.
+    """
+    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.
+
+    Args:
+        log: log object
+        ad: android device object
+
+    Returns:
+        True if success, False if fail.
+    """
+    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.
+
+    Args:
+        log: log object
+        ad: android device object
+        sub_id: subscription id
+
+    Returns:
+        True if success, False if fail.
+    """
+    return phone_setup_data_for_subscription(log, ad, sub_id, GEN_2G)
+
+
+def phone_setup_csfb(log, ad, nw_gen=GEN_4G, nr_type=None):
+    """Setup phone for CSFB call test.
+
+    Setup Phone to be in 4G mode.
+    Disabled VoLTE.
+
+    Args:
+        log: log object
+        ad: Android device object.
+        nw_gen: GEN_4G or GEN_5G
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    return phone_setup_csfb_for_subscription(log, ad,
+                                        get_outgoing_voice_sub_id(ad), nw_gen, nr_type=nr_type)
+
+
+def phone_setup_csfb_for_subscription(log, ad, sub_id, nw_gen=GEN_4G, nr_type=None):
+    """Setup phone for CSFB call test for subscription id.
+
+    Setup Phone to be in 4G mode.
+    Disabled VoLTE.
+
+    Args:
+        log: log object
+        ad: Android device object.
+        sub_id: subscription id.
+        nw_gen: GEN_4G or GEN_5G
+        nr_type: NR network type e.g. NSA, SA, MMWAVE
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    capabilities = ad.telephony["subscription"][sub_id].get("capabilities", [])
+    if capabilities:
+        if "hide_enhanced_4g_lte" in capabilities:
+            show_enhanced_4g_lte_mode = getattr(ad, "show_enhanced_4g_lte_mode", False)
+            if show_enhanced_4g_lte_mode in ["false", "False", False]:
+                ad.log.warning("'VoLTE' option is hidden. Test will be skipped.")
+                raise signals.TestSkip("'VoLTE' option is hidden. Test will be skipped.")
+
+    if nw_gen == GEN_4G:
+        if not phone_setup_4g_for_subscription(log, ad, sub_id):
+            ad.log.error("Failed to set to 4G data.")
+            return False
+    elif nw_gen == GEN_5G:
+        if not phone_setup_5g_for_subscription(log, ad, sub_id, nr_type=nr_type):
+            ad.log.error("Failed to set to 5G data.")
+            return False
+
+    if not toggle_volte_for_subscription(log, ad, sub_id, False):
+        return False
+
+    if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
+                                                  MAX_WAIT_TIME_NW_SELECTION):
+        return False
+
+    return phone_idle_csfb_for_subscription(log, ad, sub_id, nw_gen)
+
+
+def phone_setup_volte(log, ad, nw_gen=GEN_4G, nr_type=None):
+    """Setup VoLTE enable.
+
+    Args:
+        log: log object
+        ad: android device object.
+        nw_gen: GEN_4G or GEN_5G
+
+    Returns:
+        True: if VoLTE is enabled successfully.
+        False: for errors
+    """
+    if not get_capability_for_subscription(ad, CAPABILITY_VOLTE,
+        get_outgoing_voice_sub_id(ad)):
+        ad.log.error("VoLTE is not supported, abort test.")
+        raise signals.TestSkip("VoLTE is not supported, abort test.")
+    return phone_setup_volte_for_subscription(log, ad,
+                        get_outgoing_voice_sub_id(ad), nw_gen, nr_type= nr_type)
+
+
+def phone_setup_volte_for_subscription(log, ad, sub_id, nw_gen=GEN_4G,
+                                        nr_type=None):
+    """Setup VoLTE enable for subscription id.
+    Args:
+        log: log object
+        ad: android device object.
+        sub_id: subscription id.
+        nw_gen: GEN_4G or GEN_5G.
+        nr_type: NR network type.
+
+    Returns:
+        True: if VoLTE is enabled successfully.
+        False: for errors
+    """
+    if not get_capability_for_subscription(ad, CAPABILITY_VOLTE,
+        get_outgoing_voice_sub_id(ad)):
+        ad.log.error("VoLTE is not supported, abort test.")
+        raise signals.TestSkip("VoLTE is not supported, abort test.")
+
+    if nw_gen == GEN_4G:
+        if not phone_setup_4g_for_subscription(log, ad, sub_id):
+            ad.log.error("Failed to set to 4G data.")
+            return False
+    elif nw_gen == GEN_5G:
+        if not phone_setup_5g_for_subscription(log, ad, sub_id,
+                                        nr_type=nr_type):
+            ad.log.error("Failed to set to 5G data.")
+            return False
+    operator_name = get_operator_name(log, ad, sub_id)
+    if operator_name == CARRIER_TMO:
+        return True
+    else:
+        if not wait_for_enhanced_4g_lte_setting(log, ad, sub_id):
+            ad.log.error("Enhanced 4G LTE setting is not available")
+            return False
+        toggle_volte_for_subscription(log, ad, sub_id, True)
+    return phone_idle_volte_for_subscription(log, ad, sub_id, nw_gen,
+                                        nr_type=nr_type)
+
+
+def phone_setup_voice_3g(log, ad):
+    """Setup phone voice to 3G.
+
+    Args:
+        log: log object
+        ad: Android device object.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    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.
+
+    Args:
+        log: log object
+        ad: Android device object.
+        sub_id: subscription id.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    if not phone_setup_3g_for_subscription(log, ad, sub_id):
+        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.
+
+    Args:
+        log: log object
+        ad: Android device object.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    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):
+    """Setup phone voice to 2G for subscription id.
+
+    Args:
+        log: log object
+        ad: Android device object.
+        sub_id: subscription id.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    if not phone_setup_2g_for_subscription(log, ad, sub_id):
+        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.
+
+    Make sure phone attached to voice.
+    Make necessary delay.
+
+    Args:
+        ad: Android device object.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    return phone_setup_voice_general_for_subscription(
+        log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def phone_setup_voice_general_for_slot(log,ad,slot_id):
+    return phone_setup_voice_general_for_subscription(
+        log, ad, get_subid_from_slot_index(log,ad,slot_id))
+
+
+def phone_setup_voice_general_for_subscription(log, ad, sub_id):
+    """Setup phone for voice general call test for subscription id.
+
+    Make sure phone attached to voice.
+    Make necessary delay.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    toggle_airplane_mode(log, ad, False, 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.
+
+    Make sure phone attached to data.
+    Make necessary delay.
+
+    Args:
+        ad: Android device object.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    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.
+
+    Make sure phone attached to data.
+    Make necessary delay.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+
+    Returns:
+        True if setup successfully.
+        False for errors.
+    """
+    toggle_airplane_mode(log, ad, False, 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
+        reset_preferred_network_type_to_allowable_range(log, ad)
+
+    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, strict_checking=False)
+    set_wifi_to_default(log, ad)
+    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
+        ad.log.error("Disable WFC failed.")
+        return False
+    return ensure_network_rat_for_subscription(log, ad, sub_id,
+                                               network_preference, rat_family)
+
+
+def phone_setup_lte_gsm_wcdma(log, ad):
+    return phone_setup_lte_gsm_wcdma_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_lte_gsm_wcdma_for_subscription(log, ad, sub_id):
+    return phone_setup_rat_for_subscription(
+        log, ad, sub_id, NETWORK_MODE_LTE_GSM_WCDMA, RAT_FAMILY_LTE)
+
+
+def phone_setup_gsm_umts(log, ad):
+    return phone_setup_gsm_umts_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_gsm_umts_for_subscription(log, ad, sub_id):
+    return phone_setup_rat_for_subscription(
+        log, ad, sub_id, NETWORK_MODE_GSM_UMTS, RAT_FAMILY_WCDMA)
+
+
+def phone_setup_gsm_only(log, ad):
+    return phone_setup_gsm_only_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_gsm_only_for_subscription(log, ad, sub_id):
+    return phone_setup_rat_for_subscription(
+        log, ad, sub_id, NETWORK_MODE_GSM_ONLY, RAT_FAMILY_GSM)
+
+
+def phone_setup_lte_cdma_evdo(log, ad):
+    return phone_setup_lte_cdma_evdo_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_lte_cdma_evdo_for_subscription(log, ad, sub_id):
+    return phone_setup_rat_for_subscription(
+        log, ad, sub_id, NETWORK_MODE_LTE_CDMA_EVDO, RAT_FAMILY_LTE)
+
+
+def phone_setup_cdma(log, ad):
+    return phone_setup_cdma_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId())
+
+
+def phone_setup_cdma_for_subscription(log, ad, sub_id):
+    return phone_setup_rat_for_subscription(log, ad, sub_id, NETWORK_MODE_CDMA,
+                                            RAT_FAMILY_CDMA2000)
+
+
+def phone_idle_volte(log, ad):
+    """Return if phone is idle for VoLTE call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_volte_for_subscription(log, ad,
+                                             get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_volte_for_subscription(log, ad, sub_id, nw_gen=GEN_4G,
+                                    nr_type=None):
+    """Return if phone is idle for VoLTE call test for subscription id.
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+        nw_gen: GEN_4G or GEN_5G.
+        nr_type: NR network type e.g. NSA, SA, MMWAVE
+    """
+    if nw_gen == GEN_5G:
+        if not is_current_network_5g(ad, sub_id=sub_id, nr_type=nr_type):
+            ad.log.error("Not in 5G coverage.")
+            return False
+    else:
+        if not wait_for_network_rat_for_subscription(
+                log, ad, sub_id, RAT_FAMILY_LTE,
+                voice_or_data=NETWORK_SERVICE_VOICE):
+            ad.log.error("Voice rat not in LTE mode.")
+            return False
+    if not wait_for_volte_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED, sub_id):
+        ad.log.error(
+            "Failed to <report volte enabled true> within %s seconds.",
+            MAX_WAIT_TIME_VOLTE_ENABLED)
+        return False
+    return True
+
+
+def phone_idle_iwlan(log, ad):
+    """Return if phone is idle for WiFi calling call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_iwlan_for_subscription(log, ad,
+                                             get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_iwlan_for_subscription(log, ad, sub_id):
+    """Return if phone is idle for WiFi calling call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    if not wait_for_wfc_enabled(log, ad, 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
+
+
+def phone_idle_csfb(log, ad):
+    """Return if phone is idle for CSFB call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_csfb_for_subscription(log, ad,
+                                            get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_csfb_for_subscription(log, ad, sub_id, nw_gen=GEN_4G, nr_type=None):
+    """Return if phone is idle for CSFB call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+        nw_gen: GEN_4G or GEN_5G
+    """
+    if nw_gen == GEN_5G:
+        if not is_current_network_5g(ad, sub_id=sub_id, nr_type=nr_type):
+            ad.log.error("Not in 5G coverage.")
+            return False
+    else:
+        if not wait_for_network_rat_for_subscription(
+                log, ad, sub_id, RAT_FAMILY_LTE,
+                voice_or_data=NETWORK_SERVICE_DATA):
+            ad.log.error("Data rat not in lte mode.")
+            return False
+    return True
+
+
+def phone_idle_3g(log, ad):
+    """Return if phone is idle for 3G call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_3g_for_subscription(log, ad,
+                                          get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_3g_for_subscription(log, ad, sub_id):
+    """Return if phone is idle for 3G call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    return wait_for_network_generation_for_subscription(
+        log, ad, sub_id, GEN_3G, voice_or_data=NETWORK_SERVICE_VOICE)
+
+
+def phone_idle_2g(log, ad):
+    """Return if phone is idle for 2G call test.
+
+    Args:
+        ad: Android device object.
+    """
+    return phone_idle_2g_for_subscription(log, ad,
+                                          get_outgoing_voice_sub_id(ad))
+
+
+def phone_idle_2g_for_subscription(log, ad, sub_id):
+    """Return if phone is idle for 2G call test for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    return wait_for_network_generation_for_subscription(
+        log, ad, sub_id, GEN_2G, voice_or_data=NETWORK_SERVICE_VOICE)
+
+
+def phone_setup_on_rat(
+    log,
+    ad,
+    rat='volte',
+    sub_id=None,
+    is_airplane_mode=False,
+    wfc_mode=None,
+    wifi_ssid=None,
+    wifi_pwd=None,
+    only_return_fn=None,
+    sub_id_type='voice',
+    nr_type='nsa'):
+
+    if sub_id is None:
+        if sub_id_type == 'sms':
+            sub_id = get_outgoing_message_sub_id(ad)
+        else:
+            sub_id = get_outgoing_voice_sub_id(ad)
+
+    if get_default_data_sub_id(ad) != sub_id and '5g' in rat.lower():
+        ad.log.warning('Default data sub ID is NOT given sub ID %s.', sub_id)
+        network_preference = network_preference_for_generation(
+            GEN_5G,
+            ad.telephony["subscription"][sub_id]["operator"],
+            ad.telephony["subscription"][sub_id]["phone_type"])
+
+        ad.log.info("Network preference for %s is %s", GEN_5G,
+                    network_preference)
+
+        if not set_preferred_network_mode_pref(log, ad, sub_id,
+            network_preference):
+            return False
+
+        if not wait_for_network_generation_for_subscription(
+            log,
+            ad,
+            sub_id,
+            GEN_5G,
+            max_wait_time=30,
+            voice_or_data=NETWORK_SERVICE_DATA,
+            nr_type=nr_type):
+
+            ad.log.warning('Non-DDS slot (sub ID: %s) cannot attach 5G network.', sub_id)
+            ad.log.info('Check if sub ID %s can attach LTE network.', sub_id)
+
+            if not wait_for_network_generation_for_subscription(
+                log,
+                ad,
+                sub_id,
+                GEN_4G,
+                voice_or_data=NETWORK_SERVICE_DATA):
+                return False
+
+            if "volte" in rat.lower():
+                phone_setup_volte_for_subscription(log, ad, sub_id, None)
+            elif "wfc" in rat.lower():
+                return phone_setup_iwlan_for_subscription(
+                    log,
+                    ad,
+                    sub_id,
+                    is_airplane_mode,
+                    wfc_mode,
+                    wifi_ssid,
+                    wifi_pwd)
+            elif "csfb" in rat.lower():
+                return phone_setup_csfb_for_subscription(log, ad, sub_id, None)
+            return True
+
+    if rat.lower() == '5g_volte':
+        if only_return_fn:
+            return phone_setup_volte_for_subscription
+        else:
+            return phone_setup_volte_for_subscription(log, ad, sub_id, GEN_5G, nr_type='nsa')
+
+    elif rat.lower() == '5g_nsa_mmw_volte':
+        if only_return_fn:
+            return phone_setup_volte_for_subscription
+        else:
+            return phone_setup_volte_for_subscription(log, ad, sub_id, GEN_5G,
+                                                    nr_type='mmwave')
+
+    elif rat.lower() == '5g_csfb':
+        if only_return_fn:
+            return phone_setup_csfb_for_subscription
+        else:
+            return phone_setup_csfb_for_subscription(log, ad, sub_id, GEN_5G, nr_type='nsa')
+
+    elif rat.lower() == '5g_wfc':
+        if only_return_fn:
+            return phone_setup_iwlan_for_subscription
+        else:
+            return phone_setup_iwlan_for_subscription(
+                log,
+                ad,
+                sub_id,
+                is_airplane_mode,
+                wfc_mode,
+                wifi_ssid,
+                wifi_pwd,
+                GEN_5G,
+                nr_type='nsa')
+
+    elif rat.lower() == '5g_nsa_mmw_wfc':
+        if only_return_fn:
+            return phone_setup_iwlan_for_subscription
+        else:
+            return phone_setup_iwlan_for_subscription(
+                log,
+                ad,
+                sub_id,
+                is_airplane_mode,
+                wfc_mode,
+                wifi_ssid,
+                wifi_pwd,
+                GEN_5G,
+                nr_type='mmwave')
+
+    elif rat.lower() == 'volte':
+        if only_return_fn:
+            return phone_setup_volte_for_subscription
+        else:
+            return phone_setup_volte_for_subscription(log, ad, sub_id)
+
+    elif rat.lower() == 'csfb':
+        if only_return_fn:
+            return phone_setup_csfb_for_subscription
+        else:
+            return phone_setup_csfb_for_subscription(log, ad, sub_id)
+
+    elif rat.lower() == '5g':
+        if only_return_fn:
+            return phone_setup_5g_for_subscription
+        else:
+            return phone_setup_5g_for_subscription(log, ad, sub_id, nr_type='nsa')
+
+    elif rat.lower() == '5g_nsa_mmwave':
+        if only_return_fn:
+            return phone_setup_5g_for_subscription
+        else:
+            return phone_setup_5g_for_subscription(log, ad, sub_id,
+                                            nr_type='mmwave')
+
+    elif rat.lower() == '3g':
+        if only_return_fn:
+            return phone_setup_voice_3g_for_subscription
+        else:
+            return phone_setup_voice_3g_for_subscription(log, ad, sub_id)
+
+    elif rat.lower() == '2g':
+        if only_return_fn:
+            return phone_setup_voice_2g_for_subscription
+        else:
+            return phone_setup_voice_2g_for_subscription(log, ad, sub_id)
+
+    elif rat.lower() == 'wfc':
+        if only_return_fn:
+            return phone_setup_iwlan_for_subscription
+        else:
+            return phone_setup_iwlan_for_subscription(
+                log,
+                ad,
+                sub_id,
+                is_airplane_mode,
+                wfc_mode,
+                wifi_ssid,
+                wifi_pwd)
+    elif rat.lower() == 'default':
+        if only_return_fn:
+            return ensure_phone_default_state
+        else:
+            return ensure_phone_default_state(log, ad)
+    else:
+        if only_return_fn:
+            return phone_setup_voice_general_for_subscription
+        else:
+            return phone_setup_voice_general_for_subscription(log, ad, sub_id)
+
+
+def wait_for_network_idle(
+    log,
+    ad,
+    rat,
+    sub_id,
+    nr_type='nsa'):
+    """Wait for attaching to network with assigned RAT and IMS/WFC registration
+
+    This function can be used right after network service recovery after turning
+    off airplane mode or switching DDS. It will ensure DUT has attached to the
+    network with assigned RAT, and VoLTE/WFC has been ready.
+
+    Args:
+        log: log object
+        ad: Android object
+        rat: following RAT are supported:
+            - 5g
+            - 5g_volte
+            - 5g_csfb
+            - 5g_wfc
+            - 4g (LTE)
+            - volte (LTE)
+            - csfb (LTE)
+            - wfc (LTE)
+
+    Returns:
+        True or False
+    """
+    if get_default_data_sub_id(ad) != sub_id and '5g' in rat.lower():
+        ad.log.warning('Default data sub ID is NOT given sub ID %s.', sub_id)
+        network_preference = network_preference_for_generation(
+            GEN_5G,
+            ad.telephony["subscription"][sub_id]["operator"],
+            ad.telephony["subscription"][sub_id]["phone_type"])
+
+        ad.log.info("Network preference for %s is %s", GEN_5G,
+                    network_preference)
+
+        if not set_preferred_network_mode_pref(log, ad, sub_id,
+            network_preference):
+            return False
+
+        if not wait_for_network_generation_for_subscription(
+            log,
+            ad,
+            sub_id,
+            GEN_5G,
+            max_wait_time=30,
+            voice_or_data=NETWORK_SERVICE_DATA,
+            nr_type=nr_type):
+
+            ad.log.warning('Non-DDS slot (sub ID: %s) cannot attach 5G network.', sub_id)
+            ad.log.info('Check if sub ID %s can attach LTE network.', sub_id)
+
+            if not wait_for_network_generation_for_subscription(
+                log,
+                ad,
+                sub_id,
+                GEN_4G,
+                voice_or_data=NETWORK_SERVICE_DATA):
+                return False
+
+            if rat.lower() == '5g':
+                rat = '4g'
+            elif rat.lower() == '5g_volte':
+                rat = 'volte'
+            elif rat.lower() == '5g_wfc':
+                rat = 'wfc'
+            elif rat.lower() == '5g_csfb':
+                rat = 'csfb'
+
+    if rat.lower() == '5g_volte':
+        if not phone_idle_volte_for_subscription(log, ad, sub_id, GEN_5G, nr_type=nr_type):
+            return False
+    elif rat.lower() == '5g_csfb':
+        if not phone_idle_csfb_for_subscription(log, ad, sub_id, GEN_5G, nr_type=nr_type):
+            return False
+    elif rat.lower() == '5g_wfc':
+        if not wait_for_network_generation_for_subscription(
+            log,
+            ad,
+            sub_id,
+            GEN_5G,
+            voice_or_data=NETWORK_SERVICE_DATA,
+            nr_type=nr_type):
+            return False
+        if not wait_for_wfc_enabled(log, ad):
+            return False
+    elif rat.lower() == '5g':
+        if not wait_for_network_generation_for_subscription(
+            log,
+            ad,
+            sub_id,
+            GEN_5G,
+            voice_or_data=NETWORK_SERVICE_DATA,
+            nr_type=nr_type):
+            return False
+    elif rat.lower() == 'volte':
+        if not phone_idle_volte_for_subscription(log, ad, sub_id, GEN_4G):
+            return False
+    elif rat.lower() == 'csfb':
+        if not phone_idle_csfb_for_subscription(log, ad, sub_id, GEN_4G):
+            return False
+    elif rat.lower() == 'wfc':
+        if not wait_for_network_generation_for_subscription(
+            log,
+            ad,
+            sub_id,
+            GEN_4G,
+            voice_or_data=NETWORK_SERVICE_DATA):
+            return False
+        if not wait_for_wfc_enabled(log, ad):
+            return False
+    elif rat.lower() == '4g':
+        if not wait_for_network_generation_for_subscription(
+            log,
+            ad,
+            sub_id,
+            GEN_4G,
+            voice_or_data=NETWORK_SERVICE_DATA):
+            return False
+    return True
+
+
+def ensure_preferred_network_type_for_subscription(
+        ad,
+        network_preference
+        ):
+    sub_id = ad.droid.subscriptionGetDefaultSubId()
+    if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+            network_preference, sub_id):
+        ad.log.error("Set sub_id %s Preferred Networks Type %s failed.",
+                     sub_id, network_preference)
+    return True
+
+
+def ensure_network_rat(log,
+                       ad,
+                       network_preference,
+                       rat_family,
+                       voice_or_data=None,
+                       max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+                       toggle_apm_after_setting=False):
+    """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)
+
+
+def ensure_network_rat_for_subscription(
+        log,
+        ad,
+        sub_id,
+        network_preference,
+        rat_family,
+        voice_or_data=None,
+        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+        toggle_apm_after_setting=False):
+    """Ensure ad's current network is in expected rat_family.
+    """
+    if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+            network_preference, sub_id):
+        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, new_state=True, strict_checking=False)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        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 %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)))
+    return result
+
+
+def ensure_network_preference(log,
+                              ad,
+                              network_preference,
+                              voice_or_data=None,
+                              max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+                              toggle_apm_after_setting=False):
+    """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,
+        voice_or_data, max_wait_time, toggle_apm_after_setting)
+
+
+def ensure_network_preference_for_subscription(
+        log,
+        ad,
+        sub_id,
+        network_preference,
+        voice_or_data=None,
+        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+        toggle_apm_after_setting=False):
+    """Ensure ad's network preference is <network_preference> for sub_id.
+    """
+    rat_family_list = rat_families_for_network_preference(network_preference)
+    if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
+            network_preference, sub_id):
+        log.error("Set Preferred Networks failed.")
+        return False
+    if is_droid_in_rat_family_list_for_subscription(
+            log, ad, sub_id, rat_family_list, voice_or_data):
+        return True
+
+    if toggle_apm_after_setting:
+        toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        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)
+
+    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)))
+    return result
+
+
+def ensure_network_generation(log,
+                              ad,
+                              generation,
+                              max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+                              voice_or_data=None,
+                              toggle_apm_after_setting=False,
+                              nr_type=None):
+    """Ensure ad's network is <network generation> for default subscription ID.
+
+    Set preferred network generation to <generation>.
+    Toggle ON/OFF airplane mode if necessary.
+    Wait for ad in expected network type.
+    """
+    return ensure_network_generation_for_subscription(
+        log, ad, ad.droid.subscriptionGetDefaultSubId(), generation,
+        max_wait_time, voice_or_data, toggle_apm_after_setting, nr_type=nr_type)
+
+
+def ensure_network_generation_for_subscription(
+        log,
+        ad,
+        sub_id,
+        generation,
+        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+        voice_or_data=None,
+        toggle_apm_after_setting=False,
+        nr_type=None):
+    """Ensure ad's network is <network generation> for specified subscription ID.
+
+        Set preferred network generation to <generation>.
+        Toggle ON/OFF airplane mode if necessary.
+        Wait for ad in expected network type.
+
+    Args:
+        log: log object.
+        ad: android device object.
+        sub_id: subscription id.
+        generation: network generation, e.g. GEN_2G, GEN_3G, GEN_4G, GEN_5G.
+        max_wait_time: the time to wait for NW selection.
+        voice_or_data: check voice network generation or data network generation
+            This parameter is optional. If voice_or_data is None, then if
+            either voice or data in expected generation, function will return True.
+        toggle_apm_after_setting: Cycle airplane mode if True, otherwise do nothing.
+
+    Returns:
+        True if success, False if fail.
+    """
+    ad.log.info(
+        "RAT network type voice: %s, data: %s",
+        ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
+        ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id))
+
+    try:
+        ad.log.info("Finding the network preference for generation %s for "
+                    "operator %s phone type %s", generation,
+                    ad.telephony["subscription"][sub_id]["operator"],
+                    ad.telephony["subscription"][sub_id]["phone_type"])
+        network_preference = network_preference_for_generation(
+            generation, ad.telephony["subscription"][sub_id]["operator"],
+            ad.telephony["subscription"][sub_id]["phone_type"])
+        if ad.telephony["subscription"][sub_id]["operator"] == CARRIER_FRE \
+            and generation == GEN_4G:
+            network_preference = NETWORK_MODE_LTE_ONLY
+        ad.log.info("Network preference for %s is %s", generation,
+                    network_preference)
+        rat_family = rat_family_for_generation(
+            generation, ad.telephony["subscription"][sub_id]["operator"],
+            ad.telephony["subscription"][sub_id]["phone_type"])
+    except KeyError as e:
+        ad.log.error("Failed to find a rat_family entry for generation %s"
+                     " for subscriber id %s with error %s", generation,
+                     sub_id, e)
+        return False
+
+    if not set_preferred_network_mode_pref(log, ad, sub_id,
+                                           network_preference):
+        return False
+
+    if hasattr(ad, "dsds") and voice_or_data == "data" and sub_id != get_default_data_sub_id(ad):
+        ad.log.info("MSIM - Non DDS, ignore data RAT")
+        return True
+
+    if (generation == GEN_5G) or (generation == RAT_5G):
+        if is_current_network_5g(ad, sub_id=sub_id, nr_type=nr_type):
+            ad.log.info("Current network type is 5G.")
+            return True
+        else:
+            ad.log.error("Not in 5G coverage for Sub %s.", sub_id)
+            return False
+
+    if is_droid_in_network_generation_for_subscription(
+            log, ad, sub_id, generation, voice_or_data):
+        return True
+
+    if toggle_apm_after_setting:
+        toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        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)
+
+    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)))
+    if not result:
+        get_telephony_signal_strength(ad)
+    return result
+
+
+def wait_for_network_rat(log,
+                         ad,
+                         rat_family,
+                         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)
+
+
+def wait_for_network_rat_for_subscription(
+        log,
+        ad,
+        sub_id,
+        rat_family,
+        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+        voice_or_data=None):
+    return _wait_for_droid_in_state_for_subscription(
+        log, ad, sub_id, max_wait_time,
+        is_droid_in_rat_family_for_subscription, rat_family, voice_or_data)
+
+
+def wait_for_not_network_rat(log,
+                             ad,
+                             rat_family,
+                             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)
+
+
+def wait_for_not_network_rat_for_subscription(
+        log,
+        ad,
+        sub_id,
+        rat_family,
+        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+        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)
+    )
+
+
+def wait_for_preferred_network(log,
+                               ad,
+                               network_preference,
+                               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,
+        max_wait_time, voice_or_data)
+
+
+def wait_for_preferred_network_for_subscription(
+        log,
+        ad,
+        sub_id,
+        network_preference,
+        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+        voice_or_data=None):
+    rat_family_list = rat_families_for_network_preference(network_preference)
+    return _wait_for_droid_in_state_for_subscription(
+        log, ad, sub_id, max_wait_time,
+        is_droid_in_rat_family_list_for_subscription, rat_family_list,
+        voice_or_data)
+
+
+def wait_for_network_generation(log,
+                                ad,
+                                generation,
+                                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)
+
+
+def wait_for_network_generation_for_subscription(
+        log,
+        ad,
+        sub_id,
+        generation,
+        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
+        voice_or_data=None,
+        nr_type=None):
+
+    if generation == GEN_5G:
+        if is_current_network_5g(ad, sub_id=sub_id, nr_type=nr_type):
+            ad.log.info("Current network type is 5G.")
+            return True
+        else:
+            ad.log.error("Not in 5G coverage for Sub %s.", sub_id)
+            return False
+
+    return _wait_for_droid_in_state_for_subscription(
+        log, ad, sub_id, max_wait_time,
+        is_droid_in_network_generation_for_subscription, generation,
+        voice_or_data)
+
+
+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 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, retry=2):
+    """Ensure ad idle (not in call).
+    """
+    while ad.droid.telecomIsInCall() and retry > 0:
+        ad.droid.telecomEndCall()
+        time.sleep(3)
+        retry -= 1
+    if not wait_for_droid_not_in_call(log, ad, max_time=max_time):
+        ad.log.error("Failed to end call")
+        return False
+    return True
+
+
+def ensure_phone_subscription(log, ad):
+    """Ensure Phone Subscription.
+    """
+    #check for sim and service
+    duration = 0
+    while duration < MAX_WAIT_TIME_NW_SELECTION:
+        subInfo = ad.droid.subscriptionGetAllSubInfoList()
+        if subInfo and len(subInfo) >= 1:
+            ad.log.debug("Find valid subcription %s", subInfo)
+            break
+        else:
+            ad.log.info("Did not find any subscription")
+            time.sleep(5)
+            duration += 5
+    else:
+        ad.log.error("Unable to find a valid subscription!")
+        return False
+    while duration < MAX_WAIT_TIME_NW_SELECTION:
+        data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
+        voice_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
+        if data_sub_id > INVALID_SUB_ID or voice_sub_id > INVALID_SUB_ID:
+            ad.log.debug("Find valid voice or data sub id")
+            break
+        else:
+            ad.log.info("Did not find valid data or voice sub id")
+            time.sleep(5)
+            duration += 5
+    else:
+        ad.log.error("Unable to find valid data or voice sub id")
+        return False
+    while duration < MAX_WAIT_TIME_NW_SELECTION:
+        data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
+        if data_sub_id > INVALID_SUB_ID:
+            data_rat = get_network_rat_for_subscription(
+                log, ad, data_sub_id, NETWORK_SERVICE_DATA)
+        else:
+            data_rat = RAT_UNKNOWN
+        if voice_sub_id > INVALID_SUB_ID:
+            voice_rat = get_network_rat_for_subscription(
+                log, ad, voice_sub_id, NETWORK_SERVICE_VOICE)
+        else:
+            voice_rat = RAT_UNKNOWN
+        if data_rat != RAT_UNKNOWN or voice_rat != RAT_UNKNOWN:
+            ad.log.info("Data sub_id %s in %s, voice sub_id %s in %s",
+                        data_sub_id, data_rat, voice_sub_id, voice_rat)
+            return True
+        else:
+            ad.log.info("Did not attach for data or voice service")
+            time.sleep(5)
+            duration += 5
+    else:
+        ad.log.error("Did not attach for voice or data service")
+        return False
+
+
+def ensure_phone_default_state(log, ad, check_subscription=True, retry=2):
+    """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 not toggle_airplane_mode(log, ad, False, False):
+        ad.log.error("Fail to turn off airplane mode")
+        result = False
+    try:
+        set_wifi_to_default(log, ad)
+        while ad.droid.telecomIsInCall() and retry > 0:
+            ad.droid.telecomEndCall()
+            time.sleep(3)
+            retry -= 1
+        if not wait_for_droid_not_in_call(log, ad):
+            ad.log.error("Failed to end call")
+        #ad.droid.telephonyFactoryReset()
+        data_roaming = getattr(ad, 'roaming', False)
+        if get_cell_data_roaming_state_by_adb(ad) != data_roaming:
+            set_cell_data_roaming_state_by_adb(ad, data_roaming)
+        #remove_mobile_data_usage_limit(ad)
+        if not wait_for_not_network_rat(
+                log, ad, RAT_FAMILY_WLAN, voice_or_data=NETWORK_SERVICE_DATA):
+            ad.log.error("%s still in %s", NETWORK_SERVICE_DATA,
+                         RAT_FAMILY_WLAN)
+            result = False
+
+        if check_subscription and not ensure_phone_subscription(log, ad):
+            ad.log.error("Unable to find a valid subscription!")
+            result = False
+    except Exception as e:
+        ad.log.error("%s failure, toggle APM instead", e)
+        toggle_airplane_mode_by_adb(log, ad, True)
+        toggle_airplane_mode_by_adb(log, ad, False)
+        ad.send_keycode("ENDCALL")
+        ad.adb.shell("settings put global wfc_ims_enabled 0")
+        ad.adb.shell("settings put global mobile_data 1")
+
+    return result
+
+
+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.
+    Phone not in airplane mode.
+
+    Returns:
+        True if all steps of restoring default state succeed.
+        False if any of the steps to restore default state fails.
+    """
+    tasks = []
+    for ad in ads:
+        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 is_phone_not_in_call(log, ad):
+    """Return True if phone not in call.
+
+    Args:
+        log: log object.
+        ad:  android device.
+    """
+    in_call = ad.droid.telecomIsInCall()
+    call_state = ad.droid.telephonyGetCallState()
+    if in_call:
+        ad.log.info("Device is In Call")
+    if call_state != TELEPHONY_STATE_IDLE:
+        ad.log.info("Call_state is %s, not %s", call_state,
+                    TELEPHONY_STATE_IDLE)
+    return ((not in_call) and (call_state == TELEPHONY_STATE_IDLE))
+
+
+def wait_for_droid_not_in_call(log, ad, max_time=MAX_WAIT_TIME_CALL_DROP):
+    """Wait for android to be not in call state.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        If phone become not in call state within max_time, return True.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, is_phone_not_in_call)
+
+
+def wait_for_voice_attach(log, ad, max_time=MAX_WAIT_TIME_NW_SELECTION):
+    """Wait for android device to attach on voice.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device attach voice within max_time.
+        Return False if timeout.
+    """
+    return _wait_for_droid_in_state(log, ad, max_time, _is_attached,
+                                    NETWORK_SERVICE_VOICE)
+
+
+def wait_for_voice_attach_for_subscription(
+        log, ad, sub_id, max_time=MAX_WAIT_TIME_NW_SELECTION):
+    """Wait for android device to attach on voice in subscription id.
+
+    Args:
+        log: log object.
+        ad:  android device.
+        sub_id: subscription id.
+        max_time: maximal wait time.
+
+    Returns:
+        Return True if device attach voice within max_time.
+        Return False if timeout.
+    """
+    if not _wait_for_droid_in_state_for_subscription(
+            log, ad, sub_id, max_time, _is_attached_for_subscription,
+            NETWORK_SERVICE_VOICE):
+        return False
+
+    # TODO: b/26295983 if pone attach to 1xrtt from unknown, phone may not
+    # receive incoming call immediately.
+    if ad.droid.telephonyGetCurrentVoiceNetworkType() == RAT_1XRTT:
+        time.sleep(WAIT_TIME_1XRTT_VOICE_ATTACH)
+    return True
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_sms_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_sms_utils.py
index 408fee1..891e6b1 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_sms_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_sms_utils.py
@@ -18,13 +18,13 @@
 import time
 from acts.utils import rand_ascii_str
 from acts_contrib.test_utils.tel.tel_message_utils import sms_send_receive_verify
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
-
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
 
 message_lengths = (50, 160, 180)
 
+
 def _sms_test(log, ads):
     """Test SMS between two phones.
     Returns:
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_ss_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_ss_utils.py
index 702eadf..dafd078 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_ss_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_ss_utils.py
@@ -22,30 +22,32 @@
 from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_FOREGROUND
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WFC_ENABLED
 from acts_contrib.test_utils.tel.tel_defines import NOT_CHECK_MCALLFORWARDING_OPERATOR_LIST
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
 from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_slot_index_from_subid
 from acts_contrib.test_utils.tel.tel_test_utils import _phone_number_remove_prefix
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown_for_subscription
 from acts_contrib.test_utils.tel.tel_test_utils import check_call_state_ring_by_adb
 from acts_contrib.test_utils.tel.tel_test_utils import check_call_state_idle_by_adb
-from acts_contrib.test_utils.tel.tel_test_utils import dial_phone_number
-from acts_contrib.test_utils.tel.tel_test_utils import disconnect_call_by_id
 from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
-from acts_contrib.test_utils.tel.tel_test_utils import get_slot_index_from_subid
 from acts_contrib.test_utils.tel.tel_test_utils import get_user_config_profile
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
-from acts_contrib.test_utils.tel.tel_test_utils import is_phone_in_call
-from acts_contrib.test_utils.tel.tel_test_utils import last_call_drop_reason
 from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_msim
-from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_call_id_clearing
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_call_offhook_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_in_call_active
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_ringing_call_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import dial_phone_number
+from acts_contrib.test_utils.tel.tel_voice_utils import disconnect_call_by_id
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call
+from acts_contrib.test_utils.tel.tel_voice_utils import last_call_drop_reason
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_and_answer_call_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_for_call_id_clearing
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_for_call_offhook_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_for_in_call_active
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_for_ringing_call_for_subscription
 
 
 def call_setup_teardown_for_call_forwarding(
@@ -551,7 +553,7 @@
     if call_forwarding_type != "unconditional":
         return "unknown"
 
-    slot_index_of_default_voice_subid = get_slot_index_from_subid(log, ad,
+    slot_index_of_default_voice_subid = get_slot_index_from_subid(ad,
         get_incoming_voice_sub_id(ad))
     output = ad.adb.shell("dumpsys telephony.registry | grep mCallForwarding")
     if "mCallForwarding" in output:
@@ -1407,4 +1409,293 @@
         else:
             retry = retry + 1
 
-    return False
\ No newline at end of file
+    return False
+
+
+def three_phone_call_forwarding_short_seq(log,
+                             phone_a,
+                             phone_a_idle_func,
+                             phone_a_in_call_check_func,
+                             phone_b,
+                             phone_c,
+                             wait_time_in_call=WAIT_TIME_IN_CALL,
+                             call_forwarding_type="unconditional",
+                             retry=2):
+    """Short sequence of call process with call forwarding.
+    Test steps:
+        1. Ensure all phones are initially in idle state.
+        2. Enable call forwarding on Phone A.
+        3. Make a call from Phone B to Phone A, The call should be forwarded to
+           PhoneC. Accept the call on Phone C.
+        4. Ensure the call is connected and in correct phone state.
+        5. Hang up the call on Phone B.
+        6. Ensure all phones are in idle state.
+        7. Disable call forwarding on Phone A.
+        7. Make a call from Phone B to Phone A, The call should NOT be forwarded
+           to PhoneC. Accept the call on Phone A.
+        8. Ensure the call is connected and in correct phone state.
+        9. Hang up the call on Phone B.
+
+    Args:
+        phone_a: android object of Phone A
+        phone_a_idle_func: function to check idle state on Phone A
+        phone_a_in_call_check_func: function to check in-call state on Phone A
+        phone_b: android object of Phone B
+        phone_c: android object of Phone C
+        wait_time_in_call: time to wait in call.
+            This is optional, default is WAIT_TIME_IN_CALL
+        call_forwarding_type:
+            - "unconditional"
+            - "busy"
+            - "not_answered"
+            - "not_reachable"
+        retry: times of retry
+
+    Returns:
+        True: if call sequence succeed.
+        False: for errors
+    """
+    ads = [phone_a, phone_b, phone_c]
+
+    call_params = [
+        (ads[1], ads[0], ads[2], ads[1], phone_a_in_call_check_func, False)
+    ]
+
+    if call_forwarding_type != "unconditional":
+        call_params.append((
+            ads[1],
+            ads[0],
+            ads[2],
+            ads[1],
+            phone_a_in_call_check_func,
+            True))
+
+    for param in call_params:
+        ensure_phones_idle(log, ads)
+        if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+            phone_a.log.error("Phone A Failed to Reselect")
+            return False
+
+        time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+        log.info(
+            "---> Call forwarding %s (caller: %s, callee: %s, callee forwarded:"
+            " %s) <---",
+            call_forwarding_type,
+            param[0].serial,
+            param[1].serial,
+            param[2].serial)
+        while not call_setup_teardown_for_call_forwarding(
+                log,
+                *param,
+                wait_time_in_call=wait_time_in_call,
+                call_forwarding_type=call_forwarding_type) and retry >= 0:
+
+            if retry <= 0:
+                log.error("Call forwarding %s failed." % call_forwarding_type)
+                return False
+            else:
+                log.info(
+                    "RERUN the test case: 'Call forwarding %s'" %
+                    call_forwarding_type)
+
+            retry = retry - 1
+
+    return True
+
+def three_phone_call_waiting_short_seq(log,
+                             phone_a,
+                             phone_a_idle_func,
+                             phone_a_in_call_check_func,
+                             phone_b,
+                             phone_c,
+                             wait_time_in_call=WAIT_TIME_IN_CALL,
+                             call_waiting=True,
+                             scenario=None,
+                             retry=2):
+    """Short sequence of call process with call waiting.
+    Test steps:
+        1. Ensure all phones are initially in idle state.
+        2. Enable call waiting on Phone A.
+        3. Make the 1st call from Phone B to Phone A. Accept the call on Phone B.
+        4. Ensure the call is connected and in correct phone state.
+        5. Make the 2nd call from Phone C to Phone A. The call should be able to
+           income correctly. Whether or not the 2nd call should be answered by
+           Phone A depends on the scenario listed in the next step.
+        6. Following 8 scenarios will be tested:
+           - 1st call ended first by Phone B during 2nd call incoming. 2nd call
+             ended by Phone C
+           - 1st call ended first by Phone B during 2nd call incoming. 2nd call
+             ended by Phone A
+           - 1st call ended first by Phone A during 2nd call incoming. 2nd call
+             ended by Phone C
+           - 1st call ended first by Phone A during 2nd call incoming. 2nd call
+             ended by Phone A
+           - 1st call ended by Phone B. 2nd call ended by Phone C
+           - 1st call ended by Phone B. 2nd call ended by Phone A
+           - 1st call ended by Phone A. 2nd call ended by Phone C
+           - 1st call ended by Phone A. 2nd call ended by Phone A
+        7. Ensure all phones are in idle state.
+
+    Args:
+        phone_a: android object of Phone A
+        phone_a_idle_func: function to check idle state on Phone A
+        phone_a_in_call_check_func: function to check in-call state on Phone A
+        phone_b: android object of Phone B
+        phone_c: android object of Phone C
+        wait_time_in_call: time to wait in call.
+            This is optional, default is WAIT_TIME_IN_CALL
+        call_waiting: True for call waiting enabled and False for disabled
+        scenario: 1-8 for scenarios listed above
+        retry: times of retry
+
+    Returns:
+        True: if call sequence succeed.
+        False: for errors
+    """
+    ads = [phone_a, phone_b, phone_c]
+
+    sub_test_cases = [
+        {
+            "description": "1st call ended first by caller1 during 2nd call"
+                " incoming. 2nd call ended by caller2",
+            "params": (
+                ads[1],
+                ads[0],
+                ads[2],
+                ads[1],
+                ads[2],
+                phone_a_in_call_check_func,
+                True)},
+        {
+            "description": "1st call ended first by caller1 during 2nd call"
+                " incoming. 2nd call ended by callee",
+            "params": (
+                ads[1],
+                ads[0],
+                ads[2],
+                ads[1],
+                ads[0],
+                phone_a_in_call_check_func,
+                True)},
+        {
+            "description": "1st call ended first by callee during 2nd call"
+                " incoming. 2nd call ended by caller2",
+            "params": (
+                ads[1],
+                ads[0],
+                ads[2],
+                ads[0],
+                ads[2],
+                phone_a_in_call_check_func,
+                True)},
+        {
+            "description": "1st call ended first by callee during 2nd call"
+                " incoming. 2nd call ended by callee",
+            "params": (
+                ads[1],
+                ads[0],
+                ads[2],
+                ads[0],
+                ads[0],
+                phone_a_in_call_check_func,
+                True)},
+        {
+            "description": "1st call ended by caller1. 2nd call ended by"
+                " caller2",
+            "params": (
+                ads[1],
+                ads[0],
+                ads[2],
+                ads[1],
+                ads[2],
+                phone_a_in_call_check_func,
+                False)},
+        {
+            "description": "1st call ended by caller1. 2nd call ended by callee",
+            "params": (
+                ads[1],
+                ads[0],
+                ads[2],
+                ads[1],
+                ads[0],
+                phone_a_in_call_check_func,
+                False)},
+        {
+            "description": "1st call ended by callee. 2nd call ended by caller2",
+            "params": (
+                ads[1],
+                ads[0],
+                ads[2],
+                ads[0],
+                ads[2],
+                phone_a_in_call_check_func,
+                False)},
+        {
+            "description": "1st call ended by callee. 2nd call ended by callee",
+            "params": (
+                ads[1],
+                ads[0],
+                ads[2],
+                ads[0],
+                ads[0],
+                phone_a_in_call_check_func,
+                False)}
+    ]
+
+    if call_waiting:
+        if not scenario:
+            test_cases = sub_test_cases
+        else:
+            test_cases = [sub_test_cases[scenario-1]]
+    else:
+        test_cases = [
+            {
+                "description": "Call waiting deactivated",
+                "params": (
+                    ads[1],
+                    ads[0],
+                    ads[2],
+                    ads[0],
+                    ads[0],
+                    phone_a_in_call_check_func,
+                    False)}
+        ]
+
+    results = []
+
+    for test_case in test_cases:
+        ensure_phones_idle(log, ads)
+        if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
+            phone_a.log.error("Phone A Failed to Reselect")
+            return False
+
+        time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+        log.info(
+            "---> %s (caller1: %s, caller2: %s, callee: %s) <---",
+            test_case["description"],
+            test_case["params"][1].serial,
+            test_case["params"][2].serial,
+            test_case["params"][0].serial)
+
+        while not call_setup_teardown_for_call_waiting(
+            log,
+            *test_case["params"],
+            wait_time_in_call=wait_time_in_call,
+            call_waiting=call_waiting) and retry >= 0:
+
+            if retry <= 0:
+                log.error("Call waiting sub-case: '%s' failed." % test_case[
+                    "description"])
+                results.append(False)
+            else:
+                log.info("RERUN the sub-case: '%s'" % test_case["description"])
+
+            retry = retry - 1
+
+    for result in results:
+        if not result:
+            return False
+
+    return True
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_subscription_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_subscription_utils.py
index 7e936df..6fd9e90 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_subscription_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_subscription_utils.py
@@ -20,6 +20,7 @@
 import time
 
 from acts_contrib.test_utils.tel.tel_defines import CHIPSET_MODELS_LIST
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SIM_SLOT_INDEX
 from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID
 from future import standard_library
@@ -228,6 +229,7 @@
             return info['carrierId']
     return None
 
+
 def get_isopportunistic_from_slot_index(ad, sim_slot_index):
     """ Get the isOppotunistic field for a particular slot
 
@@ -244,6 +246,7 @@
             return info['isOpportunistic']
     return None
 
+
 def set_subid_for_data(ad, sub_id, time_to_sleep=WAIT_TIME_CHANGE_DATA_SUB_ID):
     """Set subId for data
 
@@ -339,6 +342,7 @@
     if hasattr(ad, "outgoing_voice_sub_id"):
         ad.outgoing_voice_sub_id = sub_id
 
+
 def set_default_sub_for_all_services(ad, slot_id=0):
     """Set subId for all services
 
@@ -559,6 +563,15 @@
 
     return host_sub_id, p1_sub_id, p2_sub_id
 
+
+def get_slot_index_from_subid(ad, sub_id):
+    try:
+        info = ad.droid.subscriptionGetSubInfoForSubscriber(sub_id)
+        return info['simSlotIndex']
+    except KeyError:
+        return INVALID_SIM_SLOT_INDEX
+
+
 def get_slot_index_from_data_sub_id(ad):
     """Get slot index from given sub ID for data
 
@@ -575,6 +588,7 @@
             return info['simSlotIndex']
     return INVALID_SUB_ID
 
+
 def get_slot_index_from_voice_sub_id(ad):
     """Get slot index from the current voice sub ID.
 
@@ -593,6 +607,7 @@
             return info['simSlotIndex']
     return INVALID_SUB_ID
 
+
 def get_all_sub_id(ad):
     """Return all valid subscription IDs.
 
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_test_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_test_utils.py
index 14e3209..3d7e935 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_test_utils.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-#   Copyright 2016 - Google
+#   Copyright 2022 - Google
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
@@ -27,18 +27,15 @@
 import struct
 
 from acts import signals
-from acts import utils
 from queue import Empty
 from acts.asserts import abort_all
-from acts.controllers.adb_lib.error import AdbError
+from acts.controllers.adb_lib.error import AdbCommandError, AdbError
 from acts.controllers.android_device import list_adb_devices
 from acts.controllers.android_device import list_fastboot_devices
 
-from acts.controllers.android_device import SL4A_APK_NAME
-from acts.libs.proc import job
+from acts.libs.proc.job import TimeoutError
 from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
-from acts_contrib.test_utils.tel.tel_defines import CarrierConfigs, CARRIER_NTT_DOCOMO, CARRIER_KDDI, CARRIER_RAKUTEN, \
-    CARRIER_SBM
+from acts_contrib.test_utils.tel.tel_defines import CarrierConfigs
 from acts_contrib.test_utils.tel.tel_defines import AOSP_PREFIX
 from acts_contrib.test_utils.tel.tel_defines import CARD_POWER_DOWN
 from acts_contrib.test_utils.tel.tel_defines import CARD_POWER_UP
@@ -51,50 +48,24 @@
 from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
 from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC_MODE_CHANGE
 from acts_contrib.test_utils.tel.tel_defines import CARRIER_UNKNOWN
-from acts_contrib.test_utils.tel.tel_defines import CARRIER_FRE
 from acts_contrib.test_utils.tel.tel_defines import COUNTRY_CODE_LIST
 from acts_contrib.test_utils.tel.tel_defines import DIALER_PACKAGE_NAME
-from acts_contrib.test_utils.tel.tel_defines import DATA_STATE_CONNECTED
-from acts_contrib.test_utils.tel.tel_defines import DATA_STATE_DISCONNECTED
 from acts_contrib.test_utils.tel.tel_defines import DATA_ROAMING_ENABLE
 from acts_contrib.test_utils.tel.tel_defines import DATA_ROAMING_DISABLE
 from acts_contrib.test_utils.tel.tel_defines import GEN_4G
-from acts_contrib.test_utils.tel.tel_defines import GEN_5G
 from acts_contrib.test_utils.tel.tel_defines import GEN_UNKNOWN
-from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_BACKGROUND
-from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_FOREGROUND
 from acts_contrib.test_utils.tel.tel_defines import INVALID_SIM_SLOT_INDEX
 from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
-from acts_contrib.test_utils.tel.tel_defines import MAX_SAVED_VOICE_MAIL
 from acts_contrib.test_utils.tel.tel_defines import MAX_SCREEN_ON_TIME
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_AIRPLANEMODE_EVENT
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_INITIATION
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALLEE_RINGING
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_DATA_SUB_CHANGE
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_IDLE_EVENT
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TELECOM_RINGING
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOICE_MAIL_COUNT
 from acts_contrib.test_utils.tel.tel_defines import MESSAGE_PACKAGE_NAME
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_DATA_STALL
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_NW_VALID_FAIL
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_FOR_DATA_STALL_RECOVERY
-from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_ONLY
-from acts_contrib.test_utils.tel.tel_defines import NETWORK_CONNECTION_TYPE_CELL
-from acts_contrib.test_utils.tel.tel_defines import NETWORK_CONNECTION_TYPE_WIFI
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
 from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_7_DIGIT
 from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_10_DIGIT
 from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_11_DIGIT
 from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_12_DIGIT
-from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
-from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
 from acts_contrib.test_utils.tel.tel_defines import RAT_UNKNOWN
-from acts_contrib.test_utils.tel.tel_defines import RAT_5G
 from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_EMERGENCY_ONLY
 from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_IN_SERVICE
 from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_MAPPING
@@ -106,78 +77,49 @@
 from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_PIN_REQUIRED
 from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_READY
 from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
-from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_IDLE
-from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_OFFHOOK
-from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_RINGING
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_1XRTT_VOICE_ATTACH
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_LEAVE_VOICE_MAIL
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_REJECT_CALL
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts_contrib.test_utils.tel.tel_defines import TYPE_MOBILE
-from acts_contrib.test_utils.tel.tel_defines import TYPE_WIFI
-from acts_contrib.test_utils.tel.tel_defines import EventCallStateChanged
 from acts_contrib.test_utils.tel.tel_defines import EventActiveDataSubIdChanged
 from acts_contrib.test_utils.tel.tel_defines import EventDisplayInfoChanged
-from acts_contrib.test_utils.tel.tel_defines import EventConnectivityChanged
-from acts_contrib.test_utils.tel.tel_defines import EventDataConnectionStateChanged
-from acts_contrib.test_utils.tel.tel_defines import EventMessageWaitingIndicatorChanged
 from acts_contrib.test_utils.tel.tel_defines import EventServiceStateChanged
-from acts_contrib.test_utils.tel.tel_defines import CallStateContainer
-from acts_contrib.test_utils.tel.tel_defines import DataConnectionStateContainer
-from acts_contrib.test_utils.tel.tel_defines import MessageWaitingIndicatorContainer
 from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackContainer
 from acts_contrib.test_utils.tel.tel_defines import ServiceStateContainer
 from acts_contrib.test_utils.tel.tel_defines import DisplayInfoContainer
 from acts_contrib.test_utils.tel.tel_defines import OverrideNetworkContainer
 from acts_contrib.test_utils.tel.tel_logging_utils import disable_qxdm_logger
+from acts_contrib.test_utils.tel.tel_logging_utils import get_screen_shot_log
+from acts_contrib.test_utils.tel.tel_logging_utils import log_screen_shot
 from acts_contrib.test_utils.tel.tel_logging_utils import start_qxdm_logger
 from acts_contrib.test_utils.tel.tel_lookup_tables import connection_type_from_type_string
 from acts_contrib.test_utils.tel.tel_lookup_tables import is_valid_rat
 from acts_contrib.test_utils.tel.tel_lookup_tables import get_allowable_network_preference
 from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_count_check_function
 from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_check_number
-from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_delete_digit
 from acts_contrib.test_utils.tel.tel_lookup_tables import network_preference_for_generation
 from acts_contrib.test_utils.tel.tel_lookup_tables import operator_name_from_network_name
 from acts_contrib.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
-from acts_contrib.test_utils.tel.tel_lookup_tables import rat_families_for_network_preference
-from acts_contrib.test_utils.tel.tel_lookup_tables import rat_family_for_generation
 from acts_contrib.test_utils.tel.tel_lookup_tables import rat_family_from_rat
 from acts_contrib.test_utils.tel.tel_lookup_tables import rat_generation_from_rat
-from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_slot_index_from_subid
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_by_adb
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_incoming_voice_sub_id
-from acts_contrib.test_utils.tel.tel_5g_utils import is_current_network_5g_for_subscription
-from acts_contrib.test_utils.tel.tel_5g_utils import is_current_network_5g
-from acts_contrib.test_utils.wifi import wifi_test_utils
 from acts.utils import adb_shell_ping
 from acts.utils import load_config
 from acts.logger import epoch_to_log_line_timestamp
 from acts.utils import get_current_epoch_time
 from acts.utils import exe_cmd
-from acts.libs.utils.multithread import multithread_func
 
-WIFI_SSID_KEY = wifi_test_utils.WifiEnums.SSID_KEY
-WIFI_PWD_KEY = wifi_test_utils.WifiEnums.PWD_KEY
-WIFI_CONFIG_APBAND_2G = 1
-WIFI_CONFIG_APBAND_5G = 2
-WIFI_CONFIG_APBAND_AUTO = wifi_test_utils.WifiEnums.WIFI_CONFIG_APBAND_AUTO
 log = logging
 STORY_LINE = "+19523521350"
 CallResult = TelephonyVoiceTestResult.CallResult.Value
-voice_call_type = {}
-result_dict ={}
 
 
 class TelResultWrapper(object):
@@ -492,14 +434,6 @@
     }
 
 
-def get_slot_index_from_subid(log, ad, sub_id):
-    try:
-        info = ad.droid.subscriptionGetSubInfoForSubscriber(sub_id)
-        return info['simSlotIndex']
-    except KeyError:
-        return INVALID_SIM_SLOT_INDEX
-
-
 def get_num_active_sims(log, ad):
     """ Get the number of active SIM cards by counting slots
 
@@ -588,12 +522,6 @@
     return signal_strength
 
 
-def get_wifi_signal_strength(ad):
-    signal_strength = ad.droid.wifiGetConnectionInfo()['rssi']
-    ad.log.info("WiFi Signal Strength is %s" % signal_strength)
-    return signal_strength
-
-
 def get_lte_rsrp(ad):
     try:
         if ad.adb.getprop("ro.build.version.release")[0] in ("9", "P"):
@@ -618,66 +546,6 @@
     return None
 
 
-def check_data_stall_detection(ad, wait_time=WAIT_TIME_FOR_DATA_STALL):
-    data_stall_detected = False
-    time_var = 1
-    try:
-        while (time_var < wait_time):
-            out = ad.adb.shell("dumpsys network_stack " \
-                              "| grep \"Suspecting data stall\"",
-                            ignore_status=True)
-            ad.log.debug("Output is %s", out)
-            if out:
-                ad.log.info("NetworkMonitor detected - %s", out)
-                data_stall_detected = True
-                break
-            time.sleep(30)
-            time_var += 30
-    except Exception as e:
-        ad.log.error(e)
-    return data_stall_detected
-
-
-def check_network_validation_fail(ad, begin_time=None,
-                                  wait_time=WAIT_TIME_FOR_NW_VALID_FAIL):
-    network_validation_fail = False
-    time_var = 1
-    try:
-        while (time_var < wait_time):
-            time_var += 30
-            nw_valid = ad.search_logcat("validation failed",
-                                         begin_time)
-            if nw_valid:
-                ad.log.info("Validation Failed received here - %s",
-                            nw_valid[0]["log_message"])
-                network_validation_fail = True
-                break
-            time.sleep(30)
-    except Exception as e:
-        ad.log.error(e)
-    return network_validation_fail
-
-
-def check_data_stall_recovery(ad, begin_time=None,
-                              wait_time=WAIT_TIME_FOR_DATA_STALL_RECOVERY):
-    data_stall_recovery = False
-    time_var = 1
-    try:
-        while (time_var < wait_time):
-            time_var += 30
-            recovery = ad.search_logcat("doRecovery() cleanup all connections",
-                                         begin_time)
-            if recovery:
-                ad.log.info("Recovery Performed here - %s",
-                            recovery[-1]["log_message"])
-                data_stall_recovery = True
-                break
-            time.sleep(30)
-    except Exception as e:
-        ad.log.error(e)
-    return data_stall_recovery
-
-
 def break_internet_except_sl4a_port(ad, sl4a_port):
     ad.log.info("Breaking internet using iptables rules")
     ad.adb.shell("iptables -I INPUT 1 -p tcp --dport %s -j ACCEPT" % sl4a_port,
@@ -970,434 +838,6 @@
     return True
 
 
-def wait_and_answer_call(log,
-                         ad,
-                         incoming_number=None,
-                         incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
-                         caller=None,
-                         video_state=None):
-    """Wait for an incoming call on default voice subscription and
-       accepts the call.
-
-    Args:
-        ad: android device object.
-        incoming_number: Expected incoming number.
-            Optional. Default is None
-        incall_ui_display: after answer the call, bring in-call UI to foreground or
-            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
-            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
-            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
-            else, do nothing.
-
-    Returns:
-        True: if incoming call is received and answered successfully.
-        False: for errors
-        """
-    return wait_and_answer_call_for_subscription(
-        log,
-        ad,
-        get_incoming_voice_sub_id(ad),
-        incoming_number,
-        incall_ui_display=incall_ui_display,
-        caller=caller,
-        video_state=video_state)
-
-
-def _wait_for_ringing_event(log, ad, wait_time):
-    """Wait for ringing event.
-
-    Args:
-        log: log object.
-        ad: android device object.
-        wait_time: max time to wait for ringing event.
-
-    Returns:
-        event_ringing if received ringing event.
-        otherwise return None.
-    """
-    event_ringing = None
-
-    try:
-        event_ringing = ad.ed.wait_for_event(
-            EventCallStateChanged,
-            is_event_match,
-            timeout=wait_time,
-            field=CallStateContainer.CALL_STATE,
-            value=TELEPHONY_STATE_RINGING)
-        ad.log.info("Receive ringing event")
-    except Empty:
-        ad.log.info("No Ringing Event")
-    finally:
-        return event_ringing
-
-
-def wait_for_ringing_call(log, ad, incoming_number=None):
-    """Wait for an incoming call on default voice subscription and
-       accepts the call.
-
-    Args:
-        log: log object.
-        ad: android device object.
-        incoming_number: Expected incoming number.
-            Optional. Default is None
-
-    Returns:
-        True: if incoming call is received and answered successfully.
-        False: for errors
-        """
-    return wait_for_ringing_call_for_subscription(
-        log, ad, get_incoming_voice_sub_id(ad), incoming_number)
-
-
-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,
-        interval=WAIT_TIME_BETWEEN_STATE_CHECK):
-    """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. Default is None
-        event_tracking_started: True if event tracking already state outside
-        timeout: time to wait for ring
-        interval: checking interval
-
-    Returns:
-        True: if incoming call is received and answered successfully.
-        False: for errors
-    """
-    if not event_tracking_started:
-        ad.ed.clear_events(EventCallStateChanged)
-        ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
-    ring_event_received = False
-    end_time = time.time() + timeout
-    try:
-        while time.time() < end_time:
-            if not ring_event_received:
-                event_ringing = _wait_for_ringing_event(log, ad, interval)
-                if event_ringing:
-                    if incoming_number and not check_phone_number_match(
-                            event_ringing['data']
-                        [CallStateContainer.INCOMING_NUMBER], incoming_number):
-                        ad.log.error(
-                            "Incoming Number not match. Expected number:%s, actual number:%s",
-                            incoming_number, event_ringing['data'][
-                                CallStateContainer.INCOMING_NUMBER])
-                        return False
-                    ring_event_received = True
-            telephony_state = ad.droid.telephonyGetCallStateForSubscription(
-                sub_id)
-            telecom_state = ad.droid.telecomGetCallState()
-            if telephony_state == TELEPHONY_STATE_RINGING and (
-                    telecom_state == TELEPHONY_STATE_RINGING):
-                ad.log.info("callee is in telephony and telecom RINGING state")
-                if caller:
-                    if caller.droid.telecomIsInCall():
-                        caller.log.info("Caller telecom is in call state")
-                        return True
-                    else:
-                        caller.log.info("Caller telecom is NOT in call state")
-                else:
-                    return True
-            else:
-                ad.log.info(
-                    "telephony in %s, telecom in %s, expecting RINGING state",
-                    telephony_state, telecom_state)
-            time.sleep(interval)
-    finally:
-        if not event_tracking_started:
-            ad.droid.telephonyStopTrackingCallStateChangeForSubscription(
-                sub_id)
-
-
-def wait_for_call_offhook_for_subscription(
-        log,
-        ad,
-        sub_id,
-        event_tracking_started=False,
-        timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
-        interval=WAIT_TIME_BETWEEN_STATE_CHECK):
-    """Wait for an incoming call on specified subscription.
-
-    Args:
-        log: log object.
-        ad: android device object.
-        sub_id: subscription ID
-        timeout: time to wait for ring
-        interval: checking interval
-
-    Returns:
-        True: if incoming call is received and answered successfully.
-        False: for errors
-    """
-    if not event_tracking_started:
-        ad.ed.clear_events(EventCallStateChanged)
-        ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
-    offhook_event_received = False
-    end_time = time.time() + timeout
-    try:
-        while time.time() < end_time:
-            if not offhook_event_received:
-                if wait_for_call_offhook_event(log, ad, sub_id, True,
-                                               interval):
-                    offhook_event_received = True
-            telephony_state = ad.droid.telephonyGetCallStateForSubscription(
-                sub_id)
-            telecom_state = ad.droid.telecomGetCallState()
-            if telephony_state == TELEPHONY_STATE_OFFHOOK and (
-                    telecom_state == TELEPHONY_STATE_OFFHOOK):
-                ad.log.info("telephony and telecom are in OFFHOOK state")
-                return True
-            else:
-                ad.log.info(
-                    "telephony in %s, telecom in %s, expecting OFFHOOK state",
-                    telephony_state, telecom_state)
-            if offhook_event_received:
-                time.sleep(interval)
-    finally:
-        if not event_tracking_started:
-            ad.droid.telephonyStopTrackingCallStateChangeForSubscription(
-                sub_id)
-
-
-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_events(EventCallStateChanged)
-        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)
-        ad.log.info("Got event %s", 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
-
-
-def wait_and_answer_call_for_subscription(
-        log,
-        ad,
-        sub_id,
-        incoming_number=None,
-        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
-        timeout=MAX_WAIT_TIME_CALLEE_RINGING,
-        caller=None,
-        video_state=None):
-    """Wait for an incoming call on specified subscription and
-       accepts the call.
-
-    Args:
-        log: log object.
-        ad: android device object.
-        sub_id: subscription ID
-        incoming_number: Expected incoming number.
-            Optional. Default is None
-        incall_ui_display: after answer the call, bring in-call UI to foreground or
-            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
-            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
-            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
-            else, do nothing.
-
-    Returns:
-        True: if incoming call is received and answered successfully.
-        False: for errors
-    """
-    ad.ed.clear_events(EventCallStateChanged)
-    ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
-    try:
-        if not wait_for_ringing_call_for_subscription(
-                log,
-                ad,
-                sub_id,
-                incoming_number=incoming_number,
-                caller=caller,
-                event_tracking_started=True,
-                timeout=timeout):
-            ad.log.info("Incoming call ringing check failed.")
-            return False
-        ad.log.info("Accept the ring call")
-        ad.droid.telecomAcceptRingingCall(video_state)
-
-        if wait_for_call_offhook_for_subscription(
-                log, ad, sub_id, event_tracking_started=True):
-            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()
-
-
-def wait_and_reject_call(log,
-                         ad,
-                         incoming_number=None,
-                         delay_reject=WAIT_TIME_REJECT_CALL,
-                         reject=True):
-    """Wait for an incoming call on default voice subscription and
-       reject the call.
-
-    Args:
-        log: log object.
-        ad: android device object.
-        incoming_number: Expected incoming number.
-            Optional. Default is None
-        delay_reject: time to wait before rejecting the call
-            Optional. Default is WAIT_TIME_REJECT_CALL
-
-    Returns:
-        True: if incoming call is received and reject successfully.
-        False: for errors
-    """
-    return wait_and_reject_call_for_subscription(log, ad,
-                                                 get_incoming_voice_sub_id(ad),
-                                                 incoming_number, delay_reject,
-                                                 reject)
-
-
-def wait_and_reject_call_for_subscription(log,
-                                          ad,
-                                          sub_id,
-                                          incoming_number=None,
-                                          delay_reject=WAIT_TIME_REJECT_CALL,
-                                          reject=True):
-    """Wait for an incoming call on specific subscription and
-       reject the call.
-
-    Args:
-        log: log object.
-        ad: android device object.
-        sub_id: subscription ID
-        incoming_number: Expected incoming number.
-            Optional. Default is None
-        delay_reject: time to wait before rejecting the call
-            Optional. Default is WAIT_TIME_REJECT_CALL
-
-    Returns:
-        True: if incoming call is received and reject successfully.
-        False: for errors
-    """
-
-    if not wait_for_ringing_call_for_subscription(log, ad, sub_id,
-                                                  incoming_number):
-        ad.log.error(
-            "Could not reject a call: incoming call in ringing check failed.")
-        return False
-
-    ad.ed.clear_events(EventCallStateChanged)
-    ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
-    if reject is True:
-        # Delay between ringing and reject.
-        time.sleep(delay_reject)
-        is_find = False
-        # Loop the call list and find the matched one to disconnect.
-        for call in ad.droid.telecomCallGetCallIds():
-            if check_phone_number_match(
-                    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:
-            ad.log.error("Callee did not find matching call to reject.")
-            return False
-    else:
-        # don't reject on callee. Just ignore the incoming call.
-        ad.log.info("Callee received incoming call. Ignore it.")
-    try:
-        ad.ed.wait_for_event(
-            EventCallStateChanged,
-            is_event_match_for_list,
-            timeout=MAX_WAIT_TIME_CALL_IDLE_EVENT,
-            field=CallStateContainer.CALL_STATE,
-            value_list=[TELEPHONY_STATE_IDLE, TELEPHONY_STATE_OFFHOOK])
-    except Empty:
-        ad.log.error("No onCallStateChangedIdle event received.")
-        return False
-    finally:
-        ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
-    return True
-
-
-def hangup_call(log, ad, is_emergency=False):
-    """Hang up ongoing active call.
-
-    Args:
-        log: log object.
-        ad: android device object.
-
-    Returns:
-        True: if all calls are cleared
-        False: for errors
-    """
-    # short circuit in case no calls are active
-    if not ad.droid.telecomIsInCall():
-        ad.log.warning("No active call exists.")
-        return True
-    ad.ed.clear_events(EventCallStateChanged)
-    ad.droid.telephonyStartTrackingCallState()
-    ad.log.info("Hangup call.")
-    if is_emergency:
-        for call in ad.droid.telecomCallGetCallIds():
-            ad.droid.telecomCallDisconnect(call)
-    else:
-        ad.droid.telecomEndCall()
-
-    try:
-        ad.ed.wait_for_event(
-            EventCallStateChanged,
-            is_event_match,
-            timeout=MAX_WAIT_TIME_CALL_IDLE_EVENT,
-            field=CallStateContainer.CALL_STATE,
-            value=TELEPHONY_STATE_IDLE)
-    except Empty:
-        ad.log.warning("Call state IDLE event is not received after hang up.")
-    finally:
-        ad.droid.telephonyStopTrackingCallStateChange()
-    if not wait_for_state(ad.droid.telecomIsInCall, False, 15, 1):
-        ad.log.error("Telecom is in call, hangup call failed.")
-        return False
-    return True
-
-
 def wait_for_cbrs_data_active_sub_change_event(
         ad,
         event_tracking_started=False,
@@ -1487,12 +927,6 @@
         ad.droid.telephonyStopTrackingDisplayInfoChange()
     return -1
 
-def disconnect_call_by_id(log, ad, call_id):
-    """Disconnect call by call id.
-    """
-    ad.droid.telecomCallDisconnect(call_id)
-    return True
-
 
 def _phone_number_remove_prefix(number):
     """Remove the country code and other prefix from the input phone number.
@@ -1563,89 +997,8 @@
         return False
 
 
-def initiate_call(log,
-                  ad,
-                  callee_number,
-                  emergency=False,
-                  timeout=MAX_WAIT_TIME_CALL_INITIATION,
-                  checking_interval=5,
-                  incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
-                  video=False,
-                  voice_type_init=None,
-                  call_stats_check=False,
-                  result_info=result_dict,
-                  nw_gen_5g=False,
-                  nr_type= None):
-    """Make phone call from caller to callee.
-
-    Args:
-        ad_caller: Caller android device object.
-        callee_number: Callee phone number.
-        emergency : specify the call is emergency.
-            Optional. Default value is False.
-        incall_ui_display: show the dialer UI foreground or backgroud
-        video: whether to initiate as video call
-
-    Returns:
-        result: if phone call is placed successfully.
-    """
-    ad.ed.clear_events(EventCallStateChanged)
-    sub_id = get_outgoing_voice_sub_id(ad)
-    begin_time = get_device_epoch_time(ad)
-    ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
-    try:
-        # Make a Call
-        ad.log.info("Make a phone call to %s", callee_number)
-        if emergency:
-            ad.droid.telecomCallEmergencyNumber(callee_number)
-        else:
-            ad.droid.telecomCallNumber(callee_number, video)
-
-        # Verify OFFHOOK state
-        if not wait_for_call_offhook_for_subscription(
-                log, ad, sub_id, event_tracking_started=True):
-            ad.log.info("sub_id %s not in call offhook state", sub_id)
-            last_call_drop_reason(ad, begin_time=begin_time)
-            return False
-        else:
-            return True
-
-        if call_stats_check:
-            voice_type_in_call = ad.droid.telephonyGetCurrentVoiceNetworkType()
-            phone_call_type = check_call_status(ad,
-                                                voice_type_init,
-                                                voice_type_in_call)
-            result_info["Call Stats"] = phone_call_type
-            ad.log.debug("Voice Call Type: %s", phone_call_type)
-
-    finally:
-        if hasattr(ad, "sdm_log") and getattr(ad, "sdm_log"):
-            ad.adb.shell("i2cset -fy 3 64 6 1 b", ignore_status=True)
-            ad.adb.shell("i2cset -fy 3 65 6 1 b", ignore_status=True)
-        ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
-
-        if nw_gen_5g:
-            if not is_current_network_5g(ad, nr_type= nr_type):
-                ad.log.error("Phone is not attached on 5G")
-
-        if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
-            ad.droid.telecomShowInCallScreen()
-        elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
-            ad.droid.showHomeScreen()
-
-
-def dial_phone_number(ad, callee_number):
-    for number in str(callee_number):
-        if number == "#":
-            ad.send_keycode("POUND")
-        elif number == "*":
-            ad.send_keycode("STAR")
-        elif number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]:
-            ad.send_keycode("%s" % number)
-
-
 def get_call_state_by_adb(ad):
-    slot_index_of_default_voice_subid = get_slot_index_from_subid(ad.log, ad,
+    slot_index_of_default_voice_subid = get_slot_index_from_subid(ad,
         get_incoming_voice_sub_id(ad))
     output = ad.adb.shell("dumpsys telephony.registry | grep mCallState")
     if "mCallState" in output:
@@ -1672,78 +1025,6 @@
     return re.search(r"mCallIncomingNumber=(.*)", output).group(1)
 
 
-def emergency_dialer_call_by_keyevent(ad, callee_number):
-    for i in range(3):
-        if "EmergencyDialer" in ad.get_my_current_focus_window():
-            ad.log.info("EmergencyDialer is the current focus window")
-            break
-        elif i <= 2:
-            ad.adb.shell("am start -a com.android.phone.EmergencyDialer.DIAL")
-            time.sleep(1)
-        else:
-            ad.log.error("Unable to bring up EmergencyDialer")
-            return False
-    ad.log.info("Make a phone call to %s", callee_number)
-    dial_phone_number(ad, callee_number)
-    ad.send_keycode("CALL")
-
-
-def initiate_emergency_dialer_call_by_adb(
-        log,
-        ad,
-        callee_number,
-        timeout=MAX_WAIT_TIME_CALL_INITIATION,
-        checking_interval=5):
-    """Make emergency call by EmergencyDialer.
-
-    Args:
-        ad: Caller android device object.
-        callee_number: Callee phone number.
-        emergency : specify the call is emergency.
-        Optional. Default value is False.
-
-    Returns:
-        result: if phone call is placed successfully.
-    """
-    try:
-        # Make a Call
-        ad.wakeup_screen()
-        ad.send_keycode("MENU")
-        ad.log.info("Call %s", callee_number)
-        ad.adb.shell("am start -a com.android.phone.EmergencyDialer.DIAL")
-        ad.adb.shell(
-            "am start -a android.intent.action.CALL_EMERGENCY -d tel:%s" %
-            callee_number)
-        if not timeout: return True
-        ad.log.info("Check call state")
-        # Verify Call State
-        elapsed_time = 0
-        while elapsed_time < timeout:
-            time.sleep(checking_interval)
-            elapsed_time += checking_interval
-            if check_call_state_connected_by_adb(ad):
-                ad.log.info("Call to %s is connected", callee_number)
-                return True
-            if check_call_state_idle_by_adb(ad):
-                ad.log.info("Call to %s failed", callee_number)
-                return False
-        ad.log.info("Make call to %s failed", callee_number)
-        return False
-    except Exception as e:
-        ad.log.error("initiate emergency call failed with error %s", e)
-
-
-def hangup_call_by_adb(ad):
-    """Make emergency call by EmergencyDialer.
-
-    Args:
-        ad: Caller android device object.
-        callee_number: Callee phone number.
-    """
-    ad.log.info("End call by adb")
-    ad.send_keycode("ENDCALL")
-
-
 def dumpsys_all_call_info(ad):
     """ Get call information by dumpsys telecom. """
     output = ad.adb.shell("dumpsys telecom")
@@ -1767,49 +1048,6 @@
     return calls_info
 
 
-def dumpsys_last_call_info(ad):
-    """ Get call information by dumpsys telecom. """
-    num = dumpsys_last_call_number(ad)
-    output = ad.adb.shell("dumpsys telecom")
-    result = re.search(r"Call TC@%s: {(.*?)}" % num, output, re.DOTALL)
-    call_info = {"TC": num}
-    if result:
-        result = result.group(1)
-        for attr in ("startTime", "endTime", "direction", "isInterrupted",
-                     "callTechnologies", "callTerminationsReason",
-                     "isVideoCall", "callProperties"):
-            match = re.search(r"%s: (.*)" % attr, result)
-            if match:
-                if attr in ("startTime", "endTime"):
-                    call_info[attr] = epoch_to_log_line_timestamp(
-                        int(match.group(1)))
-                else:
-                    call_info[attr] = match.group(1)
-    ad.log.debug("call_info = %s", call_info)
-    return call_info
-
-
-def dumpsys_last_call_number(ad):
-    output = ad.adb.shell("dumpsys telecom")
-    call_nums = re.findall("Call TC@(\d+):", output)
-    if not call_nums:
-        return 0
-    else:
-        return int(call_nums[-1])
-
-
-def dumpsys_new_call_info(ad, last_tc_number, retries=3, interval=5):
-    for i in range(retries):
-        if dumpsys_last_call_number(ad) > last_tc_number:
-            call_info = dumpsys_last_call_info(ad)
-            ad.log.info("New call info = %s", sorted(call_info.items()))
-            return call_info
-        else:
-            time.sleep(interval)
-    ad.log.error("New call is not in sysdump telecom")
-    return {}
-
-
 def dumpsys_carrier_config(ad):
     output = ad.adb.shell("dumpsys carrier_config").split("\n")
     output_phone_id_0 = []
@@ -1966,616 +1204,6 @@
         return False
 
 
-def call_reject(log, ad_caller, ad_callee, reject=True):
-    """Caller call Callee, then reject on callee.
-
-
-    """
-    subid_caller = ad_caller.droid.subscriptionGetDefaultVoiceSubId()
-    subid_callee = ad_callee.incoming_voice_sub_id
-    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)
-
-
-def call_reject_for_subscription(log,
-                                 ad_caller,
-                                 ad_callee,
-                                 subid_caller,
-                                 subid_callee,
-                                 reject=True):
-    """
-    """
-
-    caller_number = ad_caller.telephony['subscription'][subid_caller][
-        'phone_num']
-    callee_number = ad_callee.telephony['subscription'][subid_callee][
-        'phone_num']
-
-    ad_caller.log.info("Call from %s to %s", caller_number, callee_number)
-    if not initiate_call(log, ad_caller, callee_number):
-        ad_caller.log.error("Initiate call failed")
-        return False
-
-    if not wait_and_reject_call_for_subscription(
-            log, ad_callee, subid_callee, caller_number, WAIT_TIME_REJECT_CALL,
-            reject):
-        ad_callee.log.error("Reject call fail.")
-        return False
-    # Check if incoming call is cleared on callee or not.
-    if ad_callee.droid.telephonyGetCallStateForSubscription(
-            subid_callee) == TELEPHONY_STATE_RINGING:
-        ad_callee.log.error("Incoming call is not cleared")
-        return False
-    # Hangup on caller
-    hangup_call(log, ad_caller)
-    return True
-
-
-def call_reject_leave_message(log,
-                              ad_caller,
-                              ad_callee,
-                              verify_caller_func=None,
-                              wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
-    """On default voice subscription, Call from caller to callee,
-    reject on callee, caller leave a voice mail.
-
-    1. Caller call Callee.
-    2. Callee reject incoming call.
-    3. Caller leave a voice mail.
-    4. Verify callee received the voice mail notification.
-
-    Args:
-        ad_caller: caller android device object.
-        ad_callee: callee android device object.
-        verify_caller_func: function to verify caller is in correct state while in-call.
-            This is optional, default is None.
-        wait_time_in_call: time to wait when leaving a voice mail.
-            This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
-
-    Returns:
-        True: if voice message is received on callee successfully.
-        False: for errors
-    """
-    subid_caller = get_outgoing_voice_sub_id(ad_caller)
-    subid_callee = get_incoming_voice_sub_id(ad_callee)
-    return call_reject_leave_message_for_subscription(
-        log, ad_caller, ad_callee, subid_caller, subid_callee,
-        verify_caller_func, wait_time_in_call)
-
-
-def check_reject_needed_for_voice_mail(log, ad_callee):
-    """Check if the carrier requires reject call to receive voice mail or just keep ringing
-    Requested in b//155935290
-    Four Japan carriers do not need to reject
-    SBM, KDDI, Ntt Docomo, Rakuten
-    Args:
-        log: log object
-        ad_callee: android device object
-    Returns:
-        True if callee's carrier is not one of the four Japan carriers
-        False if callee's carrier is one of the four Japan carriers
-    """
-
-    operators_no_reject = [CARRIER_NTT_DOCOMO,
-                           CARRIER_KDDI,
-                           CARRIER_RAKUTEN,
-                           CARRIER_SBM]
-    operator_name = get_operator_name(log, ad_callee)
-
-    return operator_name not in operators_no_reject
-
-
-def call_reject_leave_message_for_subscription(
-        log,
-        ad_caller,
-        ad_callee,
-        subid_caller,
-        subid_callee,
-        verify_caller_func=None,
-        wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
-    """On specific voice subscription, Call from caller to callee,
-    reject on callee, caller leave a voice mail.
-
-    1. Caller call Callee.
-    2. Callee reject incoming call.
-    3. Caller leave a voice mail.
-    4. Verify callee received the voice mail notification.
-
-    Args:
-        ad_caller: caller android device object.
-        ad_callee: callee android device object.
-        subid_caller: caller's subscription id.
-        subid_callee: callee's subscription id.
-        verify_caller_func: function to verify caller is in correct state while in-call.
-            This is optional, default is None.
-        wait_time_in_call: time to wait when leaving a voice mail.
-            This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
-
-    Returns:
-        True: if voice message is received on callee successfully.
-        False: for errors
-    """
-
-    # Currently this test utility only works for TMO and ATT and SPT.
-    # It does not work for VZW (see b/21559800)
-    # "with VVM TelephonyManager APIs won't work for vm"
-
-    caller_number = ad_caller.telephony['subscription'][subid_caller][
-        'phone_num']
-    callee_number = ad_callee.telephony['subscription'][subid_callee][
-        'phone_num']
-
-    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):
-            ad_caller.log.error("Initiate call failed.")
-            return False
-        if check_reject_needed_for_voice_mail(log, ad_callee):
-            carrier_specific_delay_reject = 30
-        else:
-            carrier_specific_delay_reject = 2
-        carrier_reject_call = not check_reject_needed_for_voice_mail(log, ad_callee)
-
-        if not wait_and_reject_call_for_subscription(
-                log, ad_callee, subid_callee, incoming_number=caller_number, delay_reject=carrier_specific_delay_reject,
-                reject=carrier_reject_call):
-            ad_callee.log.error("Reject call fail.")
-            return False
-
-        ad_callee.droid.telephonyStartTrackingVoiceMailStateChangeForSubscription(
-            subid_callee)
-
-        # ensure that all internal states are updated in telecom
-        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-        ad_callee.ed.clear_events(EventCallStateChanged)
-
-        if verify_caller_func and not verify_caller_func(log, ad_caller):
-            ad_caller.log.error("Caller not in correct state!")
-            return False
-
-        # 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:
-            caller_state_result = ad_caller.droid.telecomIsInCall()
-        else:
-            caller_state_result = verify_caller_func(log, ad_caller)
-        if not caller_state_result:
-            ad_caller.log.error("Caller not in correct state after %s seconds",
-                                wait_time_in_call)
-
-        if not hangup_call(log, ad_caller):
-            ad_caller.log.error("Error in Hanging-Up Call")
-            return False
-
-        ad_callee.log.info("Wait for voice mail indicator on callee.")
-        try:
-            event = ad_callee.ed.wait_for_event(
-                EventMessageWaitingIndicatorChanged,
-                _is_on_message_waiting_event_true)
-            ad_callee.log.info("Got event %s", event)
-        except Empty:
-            ad_callee.log.warning("No expected event %s",
-                                  EventMessageWaitingIndicatorChanged)
-            return False
-        voice_mail_count_after = ad_callee.droid.telephonyGetVoiceMailCountForSubscription(
-            subid_callee)
-        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]
-        # or equals to -1 [For TMO]
-        # -1 means there are unread voice mail, but the count is unknown
-        if not check_voice_mail_count(log, ad_callee, voice_mail_count_before,
-                                      voice_mail_count_after):
-            log.error("before and after voice mail count is not incorrect.")
-            return False
-    finally:
-        ad_callee.droid.telephonyStopTrackingVoiceMailStateChangeForSubscription(
-            subid_callee)
-    return True
-
-
-def call_voicemail_erase_all_pending_voicemail(log, ad):
-    """Script for phone to erase all pending voice mail.
-    This script only works for TMO and ATT and SPT currently.
-    This script only works if phone have already set up voice mail options,
-    and phone should disable password protection for voice mail.
-
-    1. If phone don't have pending voice message, return True.
-    2. Dial voice mail number.
-        For TMO, the number is '123'
-        For ATT, the number is phone's number
-        For SPT, the number is phone's number
-    3. Wait for voice mail connection setup.
-    4. Wait for voice mail play pending voice message.
-    5. Send DTMF to delete one message.
-        The digit is '7'.
-    6. Repeat steps 4 and 5 until voice mail server drop this call.
-        (No pending message)
-    6. Check telephonyGetVoiceMailCount result. it should be 0.
-
-    Args:
-        log: log object
-        ad: android device object
-    Returns:
-        False if error happens. True is succeed.
-    """
-    log.info("Erase all pending voice mail.")
-    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 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)
-    while (is_phone_in_call(log, ad) and (count > 0)):
-        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
-    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 (ad.droid.telephonyGetVoiceMailCount() != 0)):
-        time.sleep(1)
-        remaining_time -= 1
-    current_voice_mail_count = ad.droid.telephonyGetVoiceMailCount()
-    ad.log.info("telephonyGetVoiceMailCount: %s", current_voice_mail_count)
-    return (current_voice_mail_count == 0)
-
-
-def _is_on_message_waiting_event_true(event):
-    """Private function to return if the received EventMessageWaitingIndicatorChanged
-    event MessageWaitingIndicatorContainer.IS_MESSAGE_WAITING field is True.
-    """
-    return event['data'][MessageWaitingIndicatorContainer.IS_MESSAGE_WAITING]
-
-
-def call_setup_teardown(log,
-                        ad_caller,
-                        ad_callee,
-                        ad_hangup=None,
-                        verify_caller_func=None,
-                        verify_callee_func=None,
-                        wait_time_in_call=WAIT_TIME_IN_CALL,
-                        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
-                        dialing_number_length=None,
-                        video_state=None,
-                        slot_id_callee=None,
-                        voice_type_init=None,
-                        call_stats_check=False,
-                        result_info=result_dict,
-                        nsa_5g_for_stress=False,
-                        nr_type= None):
-    """ Call process, including make a phone call from caller,
-    accept from callee, and hang up. The call is on default voice subscription
-
-    In call process, call from <droid_caller> to <droid_callee>,
-    accept the call, (optional)then hang up from <droid_hangup>.
-
-    Args:
-        ad_caller: Caller Android Device Object.
-        ad_callee: Callee Android Device Object.
-        ad_hangup: Android Device Object end the phone call.
-            Optional. Default value is None, and phone call will continue.
-        verify_call_mode_caller: func_ptr to verify caller in correct mode
-            Optional. Default is None
-        verify_call_mode_caller: func_ptr to verify caller in correct mode
-            Optional. Default is None
-        incall_ui_display: after answer the call, bring in-call UI to foreground or
-            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
-            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
-            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
-            else, do nothing.
-        dialing_number_length: the number of digits used for dialing
-        slot_id_callee : the slot if of the callee to call to
-
-    Returns:
-        True if call process without any error.
-        False if error happened.
-
-    """
-    subid_caller = get_outgoing_voice_sub_id(ad_caller)
-    if slot_id_callee is None:
-        subid_callee = get_incoming_voice_sub_id(ad_callee)
-    else:
-        subid_callee = get_subid_from_slot_index(log, ad_callee, slot_id_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,
-        incall_ui_display, dialing_number_length, video_state,
-        voice_type_init, call_stats_check, result_info, nsa_5g_for_stress,
-        nr_type)
-
-
-def call_setup_teardown_for_subscription(
-        log,
-        ad_caller,
-        ad_callee,
-        subid_caller,
-        subid_callee,
-        ad_hangup=None,
-        verify_caller_func=None,
-        verify_callee_func=None,
-        wait_time_in_call=WAIT_TIME_IN_CALL,
-        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
-        dialing_number_length=None,
-        video_state=None,
-        voice_type_init=None,
-        call_stats_check=False,
-        result_info=result_dict,
-        nsa_5g_for_stress=False,
-        nr_type= None):
-    """ 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.
-        verify_call_mode_caller: func_ptr to verify caller in correct mode
-            Optional. Default is None
-        verify_call_mode_caller: func_ptr to verify caller in correct mode
-            Optional. Default is None
-        incall_ui_display: after answer the call, bring in-call UI to foreground or
-            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
-            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
-            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
-            else, do nothing.
-
-    Returns:
-        TelResultWrapper which will evaluate as False if error.
-
-    """
-    CHECK_INTERVAL = 5
-    begin_time = get_current_epoch_time()
-    if not verify_caller_func:
-        verify_caller_func = is_phone_in_call
-    if not verify_callee_func:
-        verify_callee_func = is_phone_in_call
-
-    caller_number = ad_caller.telephony['subscription'][subid_caller][
-        'phone_num']
-    callee_number = ad_callee.telephony['subscription'][subid_callee][
-        'phone_num']
-    if dialing_number_length:
-        skip_test = False
-        trunc_position = 0 - int(dialing_number_length)
-        try:
-            caller_area_code = caller_number[:trunc_position]
-            callee_area_code = callee_number[:trunc_position]
-            callee_dial_number = callee_number[trunc_position:]
-        except:
-            skip_test = True
-        if caller_area_code != callee_area_code:
-            skip_test = True
-        if skip_test:
-            msg = "Cannot make call from %s to %s by %s digits" % (
-                caller_number, callee_number, dialing_number_length)
-            ad_caller.log.info(msg)
-            raise signals.TestSkip(msg)
-        else:
-            callee_number = callee_dial_number
-
-    tel_result_wrapper = TelResultWrapper(CallResult('SUCCESS'))
-    msg = "Call from %s to %s" % (caller_number, callee_number)
-    if video_state:
-        msg = "Video %s" % msg
-        video = True
-    else:
-        video = False
-    if ad_hangup:
-        msg = "%s for duration of %s seconds" % (msg, wait_time_in_call)
-    ad_caller.log.info(msg)
-
-    for ad in (ad_caller, ad_callee):
-        call_ids = ad.droid.telecomCallGetCallIds()
-        setattr(ad, "call_ids", call_ids)
-        if call_ids:
-            ad.log.info("Pre-exist CallId %s before making call", call_ids)
-    try:
-        if not initiate_call(
-                log,
-                ad_caller,
-                callee_number,
-                incall_ui_display=incall_ui_display,
-                video=video):
-            ad_caller.log.error("Initiate call failed.")
-            tel_result_wrapper.result_value = CallResult('INITIATE_FAILED')
-            return tel_result_wrapper
-        else:
-            ad_caller.log.info("Caller initate call successfully")
-        if not wait_and_answer_call_for_subscription(
-                log,
-                ad_callee,
-                subid_callee,
-                incoming_number=caller_number,
-                caller=ad_caller,
-                incall_ui_display=incall_ui_display,
-                video_state=video_state):
-            ad_callee.log.error("Answer call fail.")
-            tel_result_wrapper.result_value = CallResult(
-                'NO_RING_EVENT_OR_ANSWER_FAILED')
-            return tel_result_wrapper
-        else:
-            ad_callee.log.info("Callee answered the call successfully")
-
-        for ad, call_func in zip([ad_caller, ad_callee],
-                                 [verify_caller_func, verify_callee_func]):
-            call_ids = ad.droid.telecomCallGetCallIds()
-            new_call_ids = set(call_ids) - set(ad.call_ids)
-            if not new_call_ids:
-                ad.log.error(
-                    "No new call ids are found after call establishment")
-                ad.log.error("telecomCallGetCallIds returns %s",
-                             ad.droid.telecomCallGetCallIds())
-                tel_result_wrapper.result_value = CallResult('NO_CALL_ID_FOUND')
-            for new_call_id in new_call_ids:
-                if not wait_for_in_call_active(ad, call_id=new_call_id):
-                    tel_result_wrapper.result_value = CallResult(
-                        'CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT')
-                else:
-                    ad.log.info("callProperties = %s",
-                                ad.droid.telecomCallGetProperties(new_call_id))
-
-            if not ad.droid.telecomCallGetAudioState():
-                ad.log.error("Audio is not in call state")
-                tel_result_wrapper.result_value = CallResult(
-                    'AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT')
-
-            if call_func(log, ad):
-                ad.log.info("Call is in %s state", call_func.__name__)
-            else:
-                ad.log.error("Call is not in %s state, voice in RAT %s",
-                             call_func.__name__,
-                             ad.droid.telephonyGetCurrentVoiceNetworkType())
-                tel_result_wrapper.result_value = CallResult(
-                    'CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT')
-        if not tel_result_wrapper:
-            return tel_result_wrapper
-
-        if call_stats_check:
-            voice_type_in_call = check_voice_network_type([ad_caller, ad_callee], voice_init=False)
-            phone_a_call_type = check_call_status(ad_caller,
-                                                  voice_type_init[0],
-                                                  voice_type_in_call[0])
-            result_info["Call Stats"] = phone_a_call_type
-            ad_caller.log.debug("Voice Call Type: %s", phone_a_call_type)
-            phone_b_call_type = check_call_status(ad_callee,
-                                                  voice_type_init[1],
-                                                  voice_type_in_call[1])
-            result_info["Call Stats"] = phone_b_call_type
-            ad_callee.log.debug("Voice Call Type: %s", phone_b_call_type)
-
-        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
-            time_message = "at <%s>/<%s> second." % (elapsed_time,
-                                                     wait_time_in_call)
-            for ad, call_func in [(ad_caller, verify_caller_func),
-                                  (ad_callee, verify_callee_func)]:
-                if not call_func(log, ad):
-                    ad.log.error(
-                        "NOT in correct %s state at %s, voice in RAT %s",
-                        call_func.__name__, time_message,
-                        ad.droid.telephonyGetCurrentVoiceNetworkType())
-                    tel_result_wrapper.result_value = CallResult(
-                        'CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED')
-                else:
-                    ad.log.info("In correct %s state at %s",
-                                call_func.__name__, time_message)
-                if not ad.droid.telecomCallGetAudioState():
-                    ad.log.error("Audio is not in call state at %s",
-                                 time_message)
-                    tel_result_wrapper.result_value = CallResult(
-                        'AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED')
-            if not tel_result_wrapper:
-                return tel_result_wrapper
-
-        if ad_hangup:
-            if not hangup_call(log, ad_hangup):
-                ad_hangup.log.info("Failed to hang up the call")
-                tel_result_wrapper.result_value = CallResult('CALL_HANGUP_FAIL')
-                return tel_result_wrapper
-    finally:
-        if not tel_result_wrapper:
-            for ad in (ad_caller, ad_callee):
-                last_call_drop_reason(ad, begin_time)
-                try:
-                    if ad.droid.telecomIsInCall():
-                        ad.log.info("In call. End now.")
-                        ad.droid.telecomEndCall()
-                except Exception as e:
-                    log.error(str(e))
-
-        if nsa_5g_for_stress:
-            for ad in (ad_caller, ad_callee):
-                if not is_current_network_5g(ad, nr_type):
-                    ad.log.error("Phone not attached on 5G")
-
-        if ad_hangup or not tel_result_wrapper:
-            for ad in (ad_caller, ad_callee):
-                if not wait_for_call_id_clearing(
-                        ad, getattr(ad, "caller_ids", [])):
-                    tel_result_wrapper.result_value = CallResult(
-                        'CALL_ID_CLEANUP_FAIL')
-    return tel_result_wrapper
-
-
-def wait_for_call_id_clearing(ad,
-                              previous_ids,
-                              timeout=MAX_WAIT_TIME_CALL_DROP):
-    while timeout > 0:
-        new_call_ids = ad.droid.telecomCallGetCallIds()
-        if len(new_call_ids) <= len(previous_ids):
-            return True
-        time.sleep(5)
-        timeout = timeout - 5
-    ad.log.error("Call id clearing failed. Before: %s; After: %s",
-                 previous_ids, new_call_ids)
-    return False
-
-
-def last_call_drop_reason(ad, begin_time=None):
-    reasons = ad.search_logcat(
-        "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause", begin_time)
-    reason_string = ""
-    if reasons:
-        log_msg = "Logcat call drop reasons:"
-        for reason in reasons:
-            log_msg = "%s\n\t%s" % (log_msg, reason["log_message"])
-            if "ril reason str" in reason["log_message"]:
-                reason_string = reason["log_message"].split(":")[-1].strip()
-        ad.log.info(log_msg)
-    reasons = ad.search_logcat("ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION",
-                               begin_time)
-    if reasons:
-        ad.log.warning("ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION is seen")
-    ad.log.info("last call dumpsys: %s",
-                sorted(dumpsys_last_call_info(ad).items()))
-    return reason_string
-
-
 def phone_number_formatter(input_string, formatter=None):
     """Get expected format of input phone number string.
 
@@ -2708,7 +1336,7 @@
     return file_directory, file_name
 
 
-def _check_file_existance(ad, file_path, expected_file_size=None):
+def _check_file_existence(ad, file_path, expected_file_size=None):
     """Check file existance by file_path. If expected_file_size
        is provided, then also check if the file meet the file size requirement.
     """
@@ -2736,132 +1364,6 @@
         return False
 
 
-def check_curl_availability(ad):
-    if not hasattr(ad, "curl_capable"):
-        try:
-            out = ad.adb.shell("/data/curl --version")
-            if not out or "not found" in out:
-                setattr(ad, "curl_capable", False)
-                ad.log.info("curl is unavailable, use chrome to download file")
-            else:
-                setattr(ad, "curl_capable", True)
-        except Exception:
-            setattr(ad, "curl_capable", False)
-            ad.log.info("curl is unavailable, use chrome to download file")
-    return ad.curl_capable
-
-
-def start_youtube_video(ad, url="vnd.youtube:watch?v=pSJoP0LR8CQ"):
-    ad.log.info("Open an youtube video")
-    for _ in range(3):
-        ad.ensure_screen_on()
-        ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' % url)
-        if wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
-            ad.log.info("Started a video in youtube, audio is in MUSIC state")
-            return True
-        ad.log.info("Audio is not in MUSIC state. Quit Youtube.")
-        for _ in range(3):
-            ad.send_keycode("BACK")
-            time.sleep(1)
-        time.sleep(3)
-    return False
-
-
-def active_file_download_task(log, ad, file_name="5MB", method="curl"):
-    # 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
-    file_size_map = {
-        '1MB': 1000000,
-        '5MB': 5000000,
-        '10MB': 10000000,
-        '20MB': 20000000,
-        '50MB': 50000000,
-        '100MB': 100000000,
-        '200MB': 200000000,
-        '512MB': 512000000
-    }
-    url_map = {
-        "1MB": [
-            "http://146.148.91.8/download/1MB.zip",
-            "http://ipv4.download.thinkbroadband.com/1MB.zip"
-        ],
-        "5MB": [
-            "http://146.148.91.8/download/5MB.zip",
-            "http://212.183.159.230/5MB.zip",
-            "http://ipv4.download.thinkbroadband.com/5MB.zip"
-        ],
-        "10MB": [
-            "http://146.148.91.8/download/10MB.zip",
-            "http://212.183.159.230/10MB.zip",
-            "http://ipv4.download.thinkbroadband.com/10MB.zip",
-            "http://lax.futurehosting.com/test.zip",
-            "http://ovh.net/files/10Mio.dat"
-        ],
-        "20MB": [
-            "http://146.148.91.8/download/20MB.zip",
-            "http://212.183.159.230/20MB.zip",
-            "http://ipv4.download.thinkbroadband.com/20MB.zip"
-        ],
-        "50MB": [
-            "http://146.148.91.8/download/50MB.zip",
-            "http://212.183.159.230/50MB.zip",
-            "http://ipv4.download.thinkbroadband.com/50MB.zip"
-        ],
-        "100MB": [
-            "http://146.148.91.8/download/100MB.zip",
-            "http://212.183.159.230/100MB.zip",
-            "http://ipv4.download.thinkbroadband.com/100MB.zip",
-            "http://speedtest-ca.turnkeyinternet.net/100mb.bin",
-            "http://ovh.net/files/100Mio.dat",
-            "http://lax.futurehosting.com/test100.zip"
-        ],
-        "200MB": [
-            "http://146.148.91.8/download/200MB.zip",
-            "http://212.183.159.230/200MB.zip",
-            "http://ipv4.download.thinkbroadband.com/200MB.zip"
-        ],
-        "512MB": [
-            "http://146.148.91.8/download/512MB.zip",
-            "http://212.183.159.230/512MB.zip",
-            "http://ipv4.download.thinkbroadband.com/512MB.zip"
-        ]
-    }
-
-    file_size = file_size_map.get(file_name)
-    file_urls = url_map.get(file_name)
-    file_url = None
-    for url in file_urls:
-        url_splits = url.split("/")
-        if verify_http_connection(log, ad, url=url, retry=1):
-            output_path = "/sdcard/Download/%s" % url_splits[-1]
-            file_url = url
-            break
-    if not file_url:
-        ad.log.error("No url is available to download %s", file_name)
-        return False
-    timeout = min(max(file_size / 100000, 600), 3600)
-    if method == "sl4a":
-        return (http_file_download_by_sl4a, (ad, file_url, output_path,
-                                             file_size, True, timeout))
-    if method == "curl" and check_curl_availability(ad):
-        return (http_file_download_by_curl, (ad, file_url, output_path,
-                                             file_size, True, timeout))
-    elif method == "sl4a" or method == "curl":
-        return (http_file_download_by_sl4a, (ad, file_url, output_path,
-                                             file_size, True, timeout))
-    else:
-        return (http_file_download_by_chrome, (ad, file_url, file_size, True,
-                                               timeout))
-
-
-def active_file_download_test(log, ad, file_name="5MB", method="sl4a"):
-    task = active_file_download_task(log, ad, file_name, method=method)
-    if not task:
-        return False
-    return task[0](*task[1])
-
-
 def verify_internet_connection_by_ping(log,
                                        ad,
                                        retries=1,
@@ -2964,7 +1466,7 @@
                 log_file_path=log_file_path)
             return True
         result, data = ad.run_iperf_client(
-            iperf_server, iperf_option, timeout=timeout + 60)
+            iperf_server, iperf_option, timeout=timeout + 120)
         ad.log.info("iperf test result with server %s is %s", iperf_server,
                     result)
         if result:
@@ -3008,7 +1510,8 @@
                           ipv6=False,
                           rate_dict=None,
                           blocking=True,
-                          log_file_path=None):
+                          log_file_path=None,
+                          retry=5):
     """Iperf test by adb using UDP.
 
     Args:
@@ -3024,29 +1527,36 @@
         rate_dict: dictionary that can be passed in to save data
         blocking: run iperf in blocking mode if True
         log_file_path: location to save logs
+        retry: times of retry when the server is unavailable
     """
     iperf_option = "-u -i 1 -t %s -O %s -J" % (timeout, omit)
     if limit_rate:
         iperf_option += " -b %s" % limit_rate
     if pacing_timer:
         iperf_option += " --pacing-timer %s" % pacing_timer
-    if port_num:
-        iperf_option += " -p %s" % port_num
     if ipv6:
         iperf_option += " -6"
     if reverse:
         iperf_option += " -R"
-    try:
-        return iperf_test_with_options(log,
-                                        ad,
-                                        iperf_server,
-                                        iperf_option,
-                                        timeout,
-                                        rate_dict,
-                                        blocking,
-                                        log_file_path)
-    except AdbError:
-        return False
+    for _ in range(retry):
+        if port_num:
+            iperf_option_final = iperf_option + " -p %s" % port_num
+            port_num += 1
+        else:
+            iperf_option_final = iperf_option
+        try:
+            return iperf_test_with_options(log,
+                                           ad,
+                                           iperf_server,
+                                           iperf_option_final,
+                                           timeout,
+                                           rate_dict,
+                                           blocking,
+                                           log_file_path)
+        except (AdbCommandError, TimeoutError) as error:
+            continue
+        except AdbError:
+            return False
 
 
 def iperf_test_by_adb(log,
@@ -3060,7 +1570,8 @@
                       ipv6=False,
                       rate_dict=None,
                       blocking=True,
-                      log_file_path=None):
+                      log_file_path=None,
+                      retry=5):
     """Iperf test by adb using TCP.
 
     Args:
@@ -3076,368 +1587,34 @@
         rate_dict: dictionary that can be passed in to save data
         blocking: run iperf in blocking mode if True
         log_file_path: location to save logs
+        retry: times of retry when the server is unavailable
     """
     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:
-        return iperf_test_with_options(log,
-                                        ad,
-                                        iperf_server,
-                                        iperf_option,
-                                        timeout,
-                                        rate_dict,
-                                        blocking,
-                                        log_file_path)
-    except AdbError:
-        return False
-
-
-def http_file_download_by_curl(ad,
-                               url,
-                               out_path=None,
-                               expected_file_size=None,
-                               remove_file_after_check=True,
-                               timeout=3600,
-                               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_directory, file_name = _generate_file_directory_and_file_name(
-        url, out_path)
-    file_path = os.path.join(file_directory, file_name)
-    curl_cmd = "/data/curl"
-    if limit_rate:
-        curl_cmd += " --limit-rate %s" % limit_rate
-    if retry:
-        curl_cmd += " --retry %s" % retry
-    curl_cmd += " --url %s > %s" % (url, file_path)
-    try:
-        ad.log.info("Download %s to %s by adb shell command %s", url,
-                    file_path, curl_cmd)
-
-        ad.adb.shell(curl_cmd, timeout=timeout)
-        if _check_file_existance(ad, file_path, expected_file_size):
-            ad.log.info("%s is downloaded to %s successfully", url, file_path)
-            return True
+    for _ in range(retry):
+        if port_num:
+            iperf_option_final = iperf_option + " -p %s" % port_num
+            port_num += 1
         else:
-            ad.log.warning("Fail to download %s", url)
+            iperf_option_final = iperf_option
+        try:
+            return iperf_test_with_options(log,
+                                           ad,
+                                           iperf_server,
+                                           iperf_option_final,
+                                           timeout,
+                                           rate_dict=rate_dict,
+                                           blocking=blocking,
+                                           log_file_path=log_file_path)
+        except (AdbCommandError, TimeoutError) as error:
+            continue
+        except AdbError:
             return False
-    except Exception as e:
-        ad.log.warning("Download %s failed with exception %s", url, e)
-        return False
-    finally:
-        if remove_file_after_check:
-            ad.log.info("Remove the downloaded file %s", file_path)
-            ad.adb.shell("rm %s" % file_path, ignore_status=True)
-
-
-def open_url_by_adb(ad, url):
-    ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' % url)
-
-
-def http_file_download_by_chrome(ad,
-                                 url,
-                                 expected_file_size=None,
-                                 remove_file_after_check=True,
-                                 timeout=3600):
-    """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.
-    """
-    chrome_apk = "com.android.chrome"
-    file_directory, file_name = _generate_file_directory_and_file_name(
-        url, "/sdcard/Download/")
-    file_path = os.path.join(file_directory, file_name)
-    # Remove pre-existing file
-    ad.force_stop_apk(chrome_apk)
-    file_to_be_delete = os.path.join(file_directory, "*%s*" % file_name)
-    ad.adb.shell("rm -f %s" % file_to_be_delete)
-    ad.adb.shell("rm -rf /sdcard/Download/.*")
-    ad.adb.shell("rm -f /sdcard/Download/.*")
-    data_accounting = {
-        "total_rx_bytes": ad.droid.getTotalRxBytes(),
-        "mobile_rx_bytes": ad.droid.getMobileRxBytes(),
-        "subscriber_mobile_data_usage": get_mobile_data_usage(ad, None, None),
-        "chrome_mobile_data_usage": get_mobile_data_usage(
-            ad, None, chrome_apk)
-    }
-    ad.log.debug("Before downloading: %s", data_accounting)
-    ad.log.info("Download %s with timeout %s", url, timeout)
-    ad.ensure_screen_on()
-    open_url_by_adb(ad, url)
-    elapse_time = 0
-    result = True
-    while elapse_time < timeout:
-        time.sleep(30)
-        if _check_file_existance(ad, file_path, expected_file_size):
-            ad.log.info("%s is downloaded successfully", url)
-            if remove_file_after_check:
-                ad.log.info("Remove the downloaded file %s", file_path)
-                ad.adb.shell("rm -f %s" % file_to_be_delete)
-                ad.adb.shell("rm -rf /sdcard/Download/.*")
-                ad.adb.shell("rm -f /sdcard/Download/.*")
-            #time.sleep(30)
-            new_data_accounting = {
-                "mobile_rx_bytes":
-                ad.droid.getMobileRxBytes(),
-                "subscriber_mobile_data_usage":
-                get_mobile_data_usage(ad, None, None),
-                "chrome_mobile_data_usage":
-                get_mobile_data_usage(ad, None, chrome_apk)
-            }
-            ad.log.info("After downloading: %s", new_data_accounting)
-            accounting_diff = {
-                key: value - data_accounting[key]
-                for key, value in new_data_accounting.items()
-            }
-            ad.log.debug("Data accounting difference: %s", accounting_diff)
-            if getattr(ad, "on_mobile_data", False):
-                for key, value in accounting_diff.items():
-                    if value < expected_file_size:
-                        ad.log.warning("%s diff is %s less than %s", key,
-                                       value, expected_file_size)
-                        ad.data_accounting["%s_failure" % key] += 1
-            else:
-                for key, value in accounting_diff.items():
-                    if value >= expected_file_size:
-                        ad.log.error("%s diff is %s. File download is "
-                                     "consuming mobile data", key, value)
-                        result = False
-            return result
-        elif _check_file_existance(ad, "%s.crdownload" % file_path):
-            ad.log.info("Chrome is downloading %s", url)
-        elif elapse_time < 60:
-            # download not started, retry download wit chrome again
-            open_url_by_adb(ad, url)
-        else:
-            ad.log.error("Unable to download file from %s", url)
-            break
-        elapse_time += 30
-    ad.log.warning("Fail to download file from %s", url)
-    ad.force_stop_apk("com.android.chrome")
-    ad.adb.shell("rm -f %s" % file_to_be_delete)
-    ad.adb.shell("rm -rf /sdcard/Download/.*")
-    ad.adb.shell("rm -f /sdcard/Download/.*")
-    return False
-
-
-def http_file_download_by_sl4a(ad,
-                               url,
-                               out_path=None,
-                               expected_file_size=None,
-                               remove_file_after_check=True,
-                               timeout=300):
-    """Download http file by sl4a RPC call.
-
-    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.
-    """
-    file_folder, file_name = _generate_file_directory_and_file_name(
-        url, out_path)
-    file_path = os.path.join(file_folder, file_name)
-    ad.adb.shell("rm -f %s" % file_path)
-    accounting_apk = SL4A_APK_NAME
-    result = True
-    try:
-        if not getattr(ad, "data_droid", None):
-            ad.data_droid, ad.data_ed = ad.get_droid()
-            ad.data_ed.start()
-        else:
-            try:
-                if not ad.data_droid.is_live:
-                    ad.data_droid, ad.data_ed = ad.get_droid()
-                    ad.data_ed.start()
-            except Exception:
-                ad.log.info("Start new sl4a session for file download")
-                ad.data_droid, ad.data_ed = ad.get_droid()
-                ad.data_ed.start()
-        data_accounting = {
-            "mobile_rx_bytes":
-            ad.droid.getMobileRxBytes(),
-            "subscriber_mobile_data_usage":
-            get_mobile_data_usage(ad, None, None),
-            "sl4a_mobile_data_usage":
-            get_mobile_data_usage(ad, None, accounting_apk)
-        }
-        ad.log.debug("Before downloading: %s", data_accounting)
-        ad.log.info("Download file from %s to %s by sl4a RPC call", url,
-                    file_path)
-        try:
-            ad.data_droid.httpDownloadFile(url, file_path, timeout=timeout)
-        except Exception as e:
-            ad.log.warning("SL4A file download error: %s", e)
-            ad.data_droid.terminate()
-            return False
-        if _check_file_existance(ad, file_path, expected_file_size):
-            ad.log.info("%s is downloaded successfully", url)
-            new_data_accounting = {
-                "mobile_rx_bytes":
-                ad.droid.getMobileRxBytes(),
-                "subscriber_mobile_data_usage":
-                get_mobile_data_usage(ad, None, None),
-                "sl4a_mobile_data_usage":
-                get_mobile_data_usage(ad, None, accounting_apk)
-            }
-            ad.log.debug("After downloading: %s", new_data_accounting)
-            accounting_diff = {
-                key: value - data_accounting[key]
-                for key, value in new_data_accounting.items()
-            }
-            ad.log.debug("Data accounting difference: %s", accounting_diff)
-            if getattr(ad, "on_mobile_data", False):
-                for key, value in accounting_diff.items():
-                    if value < expected_file_size:
-                        ad.log.debug("%s diff is %s less than %s", key,
-                                       value, expected_file_size)
-                        ad.data_accounting["%s_failure"] += 1
-            else:
-                for key, value in accounting_diff.items():
-                    if value >= expected_file_size:
-                        ad.log.error("%s diff is %s. File download is "
-                                     "consuming mobile data", key, value)
-                        result = False
-            return result
-        else:
-            ad.log.warning("Fail to download %s", url)
-            return False
-    except Exception as e:
-        ad.log.error("Download %s failed with exception %s", url, e)
-        raise
-    finally:
-        if remove_file_after_check:
-            ad.log.info("Remove the downloaded file %s", file_path)
-            ad.adb.shell("rm %s" % file_path, ignore_status=True)
-
-
-def get_wifi_usage(ad, sid=None, apk=None):
-    if not sid:
-        sid = ad.droid.subscriptionGetDefaultDataSubId()
-    current_time = int(time.time() * 1000)
-    begin_time = current_time - 10 * 24 * 60 * 60 * 1000
-    end_time = current_time + 10 * 24 * 60 * 60 * 1000
-
-    if apk:
-        uid = ad.get_apk_uid(apk)
-        ad.log.debug("apk %s uid = %s", apk, uid)
-        try:
-            return ad.droid.connectivityQueryDetailsForUid(
-                TYPE_WIFI,
-                ad.droid.telephonyGetSubscriberIdForSubscription(sid),
-                begin_time, end_time, uid)
-        except:
-            return ad.droid.connectivityQueryDetailsForUid(
-                ad.droid.telephonyGetSubscriberIdForSubscription(sid),
-                begin_time, end_time, uid)
-    else:
-        try:
-            return ad.droid.connectivityQuerySummaryForDevice(
-                TYPE_WIFI,
-                ad.droid.telephonyGetSubscriberIdForSubscription(sid),
-                begin_time, end_time)
-        except:
-            return ad.droid.connectivityQuerySummaryForDevice(
-                ad.droid.telephonyGetSubscriberIdForSubscription(sid),
-                begin_time, end_time)
-
-
-def get_mobile_data_usage(ad, sid=None, apk=None):
-    if not sid:
-        sid = ad.droid.subscriptionGetDefaultDataSubId()
-    current_time = int(time.time() * 1000)
-    begin_time = current_time - 10 * 24 * 60 * 60 * 1000
-    end_time = current_time + 10 * 24 * 60 * 60 * 1000
-
-    if apk:
-        uid = ad.get_apk_uid(apk)
-        ad.log.debug("apk %s uid = %s", apk, uid)
-        try:
-            usage_info = ad.droid.getMobileDataUsageInfoForUid(uid, sid)
-            ad.log.debug("Mobile data usage info for uid %s = %s", uid,
-                        usage_info)
-            return usage_info["UsageLevel"]
-        except:
-            try:
-                return ad.droid.connectivityQueryDetailsForUid(
-                    TYPE_MOBILE,
-                    ad.droid.telephonyGetSubscriberIdForSubscription(sid),
-                    begin_time, end_time, uid)
-            except:
-                return ad.droid.connectivityQueryDetailsForUid(
-                    ad.droid.telephonyGetSubscriberIdForSubscription(sid),
-                    begin_time, end_time, uid)
-    else:
-        try:
-            usage_info = ad.droid.getMobileDataUsageInfo(sid)
-            ad.log.debug("Mobile data usage info = %s", usage_info)
-            return usage_info["UsageLevel"]
-        except:
-            try:
-                return ad.droid.connectivityQuerySummaryForDevice(
-                    TYPE_MOBILE,
-                    ad.droid.telephonyGetSubscriberIdForSubscription(sid),
-                    begin_time, end_time)
-            except:
-                return ad.droid.connectivityQuerySummaryForDevice(
-                    ad.droid.telephonyGetSubscriberIdForSubscription(sid),
-                    begin_time, end_time)
-
-
-def set_mobile_data_usage_limit(ad, limit, subscriber_id=None):
-    if not subscriber_id:
-        subscriber_id = ad.droid.telephonyGetSubscriberId()
-    ad.log.debug("Set subscriber mobile data usage limit to %s", limit)
-    ad.droid.logV("Setting subscriber mobile data usage limit to %s" % limit)
-    try:
-        ad.droid.connectivitySetDataUsageLimit(subscriber_id, str(limit))
-    except:
-        ad.droid.connectivitySetDataUsageLimit(subscriber_id, limit)
-
-
-def remove_mobile_data_usage_limit(ad, subscriber_id=None):
-    if not subscriber_id:
-        subscriber_id = ad.droid.telephonyGetSubscriberId()
-    ad.log.debug("Remove subscriber mobile data usage limit")
-    ad.droid.logV(
-        "Setting subscriber mobile data usage limit to -1, unlimited")
-    try:
-        ad.droid.connectivitySetDataUsageLimit(subscriber_id, "-1")
-    except:
-        ad.droid.connectivitySetDataUsageLimit(subscriber_id, -1)
 
 
 def trigger_modem_crash(ad, timeout=120):
@@ -3593,276 +1770,6 @@
     reboot_device(ad)
 
 
-def _connection_state_change(_event, target_state, connection_type):
-    if connection_type:
-        if 'TypeName' not in _event['data']:
-            return False
-        connection_type_string_in_event = _event['data']['TypeName']
-        cur_type = connection_type_from_type_string(
-            connection_type_string_in_event)
-        if cur_type != connection_type:
-            log.info(
-                "_connection_state_change expect: %s, received: %s <type %s>",
-                connection_type, connection_type_string_in_event, cur_type)
-            return False
-
-    if 'isConnected' in _event['data'] and _event['data']['isConnected'] == target_state:
-        return True
-    return False
-
-
-def wait_for_cell_data_connection(
-        log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
-    """Wait for data connection status to be expected value for default
-       data subscription.
-
-    Wait for the data connection status to be DATA_STATE_CONNECTED
-        or DATA_STATE_DISCONNECTED.
-
-    Args:
-        log: Log object.
-        ad: Android Device Object.
-        state: Expected status: True or False.
-            If True, it will wait for status to be DATA_STATE_CONNECTED.
-            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
-        timeout_value: wait for cell data timeout value.
-            This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
-
-    Returns:
-        True if success.
-        False if failed.
-    """
-    sub_id = get_default_data_sub_id(ad)
-    return wait_for_cell_data_connection_for_subscription(
-        log, ad, sub_id, state, timeout_value)
-
-
-def _is_data_connection_state_match(log, ad, expected_data_connection_state):
-    return (expected_data_connection_state ==
-            ad.droid.telephonyGetDataConnectionState())
-
-
-def _is_network_connected_state_match(log, ad,
-                                      expected_network_connected_state):
-    return (expected_network_connected_state ==
-            ad.droid.connectivityNetworkIsConnected())
-
-
-def wait_for_cell_data_connection_for_subscription(
-        log,
-        ad,
-        sub_id,
-        state,
-        timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
-    """Wait for data connection status to be expected value for specified
-       subscrption id.
-
-    Wait for the data connection status to be DATA_STATE_CONNECTED
-        or DATA_STATE_DISCONNECTED.
-
-    Args:
-        log: Log object.
-        ad: Android Device Object.
-        sub_id: subscription Id
-        state: Expected status: True or False.
-            If True, it will wait for status to be DATA_STATE_CONNECTED.
-            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
-        timeout_value: wait for cell data timeout value.
-            This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
-
-    Returns:
-        True if success.
-        False if failed.
-    """
-    state_str = {
-        True: DATA_STATE_CONNECTED,
-        False: DATA_STATE_DISCONNECTED
-    }[state]
-
-    data_state = ad.droid.telephonyGetDataConnectionState()
-    if not state and ad.droid.telephonyGetDataConnectionState() == state_str:
-        return True
-
-    ad.ed.clear_events(EventDataConnectionStateChanged)
-    ad.droid.telephonyStartTrackingDataConnectionStateChangeForSubscription(
-        sub_id)
-    ad.droid.connectivityStartTrackingConnectivityStateChange()
-    try:
-        ad.log.info("User data enabled for sub_id %s: %s", sub_id,
-                    ad.droid.telephonyIsDataEnabledForSubscription(sub_id))
-        data_state = ad.droid.telephonyGetDataConnectionState()
-        ad.log.info("Data connection state is %s", data_state)
-        ad.log.info("Network is connected: %s",
-                    ad.droid.connectivityNetworkIsConnected())
-        if data_state == state_str:
-            return _wait_for_nw_data_connection(
-                log, ad, state, NETWORK_CONNECTION_TYPE_CELL, timeout_value)
-
-        try:
-            ad.ed.wait_for_event(
-                EventDataConnectionStateChanged,
-                is_event_match,
-                timeout=timeout_value,
-                field=DataConnectionStateContainer.DATA_CONNECTION_STATE,
-                value=state_str)
-        except Empty:
-            ad.log.info("No expected event EventDataConnectionStateChanged %s",
-                        state_str)
-
-        # TODO: Wait for <MAX_WAIT_TIME_CONNECTION_STATE_UPDATE> seconds for
-        # data connection state.
-        # Otherwise, the network state will not be correct.
-        # The bug is tracked here: b/20921915
-
-        # Previously we use _is_data_connection_state_match,
-        # but telephonyGetDataConnectionState sometimes return wrong value.
-        # The bug is tracked here: b/22612607
-        # So we use _is_network_connected_state_match.
-
-        if _wait_for_droid_in_state(log, ad, timeout_value,
-                                    _is_network_connected_state_match, state):
-            return _wait_for_nw_data_connection(
-                log, ad, state, NETWORK_CONNECTION_TYPE_CELL, timeout_value)
-        else:
-            return False
-
-    finally:
-        ad.droid.telephonyStopTrackingDataConnectionStateChangeForSubscription(
-            sub_id)
-
-
-def wait_for_wifi_data_connection(
-        log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
-    """Wait for data connection status to be expected value and connection is by WiFi.
-
-    Args:
-        log: Log object.
-        ad: Android Device Object.
-        state: Expected status: True or False.
-            If True, it will wait for status to be DATA_STATE_CONNECTED.
-            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
-        timeout_value: wait for network data timeout value.
-            This is optional, default value is MAX_WAIT_TIME_NW_SELECTION
-
-    Returns:
-        True if success.
-        False if failed.
-    """
-    ad.log.info("wait_for_wifi_data_connection")
-    return _wait_for_nw_data_connection(
-        log, ad, state, NETWORK_CONNECTION_TYPE_WIFI, timeout_value)
-
-
-def wait_for_data_connection(
-        log, ad, state, timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
-    """Wait for data connection status to be expected value.
-
-    Wait for the data connection status to be DATA_STATE_CONNECTED
-        or DATA_STATE_DISCONNECTED.
-
-    Args:
-        log: Log object.
-        ad: Android Device Object.
-        state: Expected status: True or False.
-            If True, it will wait for status to be DATA_STATE_CONNECTED.
-            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
-        timeout_value: wait for network data timeout value.
-            This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
-
-    Returns:
-        True if success.
-        False if failed.
-    """
-    return _wait_for_nw_data_connection(log, ad, state, None, timeout_value)
-
-
-def _wait_for_nw_data_connection(
-        log,
-        ad,
-        is_connected,
-        connection_type=None,
-        timeout_value=MAX_WAIT_TIME_CONNECTION_STATE_UPDATE):
-    """Wait for data connection status to be expected value.
-
-    Wait for the data connection status to be DATA_STATE_CONNECTED
-        or DATA_STATE_DISCONNECTED.
-
-    Args:
-        log: Log object.
-        ad: Android Device Object.
-        is_connected: Expected connection status: True or False.
-            If True, it will wait for status to be DATA_STATE_CONNECTED.
-            If False, it will wait for status ti be DATA_STATE_DISCONNECTED.
-        connection_type: expected connection type.
-            This is optional, if it is None, then any connection type will return True.
-        timeout_value: wait for network data timeout value.
-            This is optional, default value is MAX_WAIT_TIME_CONNECTION_STATE_UPDATE
-
-    Returns:
-        True if success.
-        False if failed.
-    """
-    ad.ed.clear_events(EventConnectivityChanged)
-    ad.droid.connectivityStartTrackingConnectivityStateChange()
-    try:
-        cur_data_connection_state = ad.droid.connectivityNetworkIsConnected()
-        if is_connected == cur_data_connection_state:
-            current_type = get_internet_connection_type(log, ad)
-            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("data connection not on %s!", connection_type)
-                    return True
-                elif is_connected and current_type == connection_type:
-                    ad.log.info("data connection on %s as expected",
-                                connection_type)
-                    return True
-        else:
-            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)
-            ad.log.info("Got event: %s", event)
-        except Empty:
-            pass
-
-        log.info(
-            "_wait_for_nw_data_connection: check connection after wait event.")
-        # TODO: Wait for <MAX_WAIT_TIME_CONNECTION_STATE_UPDATE> seconds for
-        # data connection state.
-        # Otherwise, the network state will not be correct.
-        # The bug is tracked here: b/20921915
-        if _wait_for_droid_in_state(log, ad, timeout_value,
-                                    _is_network_connected_state_match,
-                                    is_connected):
-            current_type = get_internet_connection_type(log, ad)
-            ad.log.info("current data connection type: %s", current_type)
-            if not connection_type:
-                return True
-            else:
-                if not is_connected and current_type != 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("after event wait, data connection on %s",
-                                connection_type)
-                    return True
-                else:
-                    return False
-        else:
-            return False
-    except Exception as e:
-        ad.log.error("Exception error %s", str(e))
-        return False
-    finally:
-        ad.droid.connectivityStopTrackingConnectivityStateChange()
-
-
 def get_cell_data_roaming_state_by_adb(ad):
     """Get Cell Data Roaming state. True for enabled, False for disabled"""
     state_mapping = {"1": True, "0": False}
@@ -4028,125 +1935,6 @@
     return False
 
 
-def is_phone_in_call(log, ad):
-    """Return True if phone in call.
-
-    Args:
-        log: log object.
-        ad:  android device.
-    """
-    try:
-        return ad.droid.telecomIsInCall()
-    except:
-        return "mCallState=2" in ad.adb.shell(
-            "dumpsys telephony.registry | grep mCallState")
-
-
-def is_phone_not_in_call(log, ad):
-    """Return True if phone not in call.
-
-    Args:
-        log: log object.
-        ad:  android device.
-    """
-    in_call = ad.droid.telecomIsInCall()
-    call_state = ad.droid.telephonyGetCallState()
-    if in_call:
-        ad.log.info("Device is In Call")
-    if call_state != TELEPHONY_STATE_IDLE:
-        ad.log.info("Call_state is %s, not %s", call_state,
-                    TELEPHONY_STATE_IDLE)
-    return ((not in_call) and (call_state == TELEPHONY_STATE_IDLE))
-
-
-def wait_for_droid_in_call(log, ad, max_time):
-    """Wait for android to be in call state.
-
-    Args:
-        log: log object.
-        ad:  android device.
-        max_time: maximal wait time.
-
-    Returns:
-        If phone become in call state within max_time, return True.
-        Return False if timeout.
-    """
-    return _wait_for_droid_in_state(log, ad, max_time, is_phone_in_call)
-
-
-def is_phone_in_call_active(ad, call_id=None):
-    """Return True if phone in active call.
-
-    Args:
-        log: log object.
-        ad:  android device.
-        call_id: the call id
-    """
-    if ad.droid.telecomIsInCall():
-        if not call_id:
-            call_id = ad.droid.telecomCallGetCallIds()[0]
-        call_state = ad.droid.telecomCallGetCallState(call_id)
-        ad.log.info("%s state is %s", call_id, call_state)
-        return call_state == "ACTIVE"
-    else:
-        ad.log.info("Not in telecomIsInCall")
-        return False
-
-
-def wait_for_in_call_active(ad,
-                            timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
-                            interval=WAIT_TIME_BETWEEN_STATE_CHECK,
-                            call_id=None):
-    """Wait for call reach active state.
-
-    Args:
-        log: log object.
-        ad:  android device.
-        call_id: the call id
-    """
-    if not call_id:
-        call_id = ad.droid.telecomCallGetCallIds()[0]
-    args = [ad, call_id]
-    if not wait_for_state(is_phone_in_call_active, True, timeout, interval,
-                          *args):
-        ad.log.error("Call did not reach ACTIVE state")
-        return False
-    else:
-        return True
-
-
-def wait_for_telecom_ringing(log, ad, max_time=MAX_WAIT_TIME_TELECOM_RINGING):
-    """Wait for android to be in telecom ringing state.
-
-    Args:
-        log: log object.
-        ad:  android device.
-        max_time: maximal wait time. This is optional.
-            Default Value is MAX_WAIT_TIME_TELECOM_RINGING.
-
-    Returns:
-        If phone become in telecom ringing state within max_time, return True.
-        Return False if timeout.
-    """
-    return _wait_for_droid_in_state(
-        log, ad, max_time, lambda log, ad: ad.droid.telecomIsRinging())
-
-
-def wait_for_droid_not_in_call(log, ad, max_time=MAX_WAIT_TIME_CALL_DROP):
-    """Wait for android to be not in call state.
-
-    Args:
-        log: log object.
-        ad:  android device.
-        max_time: maximal wait time.
-
-    Returns:
-        If phone become not in call state within max_time, return True.
-        Return False if timeout.
-    """
-    return _wait_for_droid_in_state(log, ad, max_time, is_phone_not_in_call)
-
-
 def _is_attached(log, ad, voice_or_data):
     return _is_attached_for_subscription(
         log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
@@ -4164,48 +1952,6 @@
         log, ad, ad.droid.subscriptionGetDefaultSubId(), NETWORK_SERVICE_VOICE)
 
 
-def wait_for_voice_attach(log, ad, max_time=MAX_WAIT_TIME_NW_SELECTION):
-    """Wait for android device to attach on voice.
-
-    Args:
-        log: log object.
-        ad:  android device.
-        max_time: maximal wait time.
-
-    Returns:
-        Return True if device attach voice within max_time.
-        Return False if timeout.
-    """
-    return _wait_for_droid_in_state(log, ad, max_time, _is_attached,
-                                    NETWORK_SERVICE_VOICE)
-
-
-def wait_for_voice_attach_for_subscription(
-        log, ad, sub_id, max_time=MAX_WAIT_TIME_NW_SELECTION):
-    """Wait for android device to attach on voice in subscription id.
-
-    Args:
-        log: log object.
-        ad:  android device.
-        sub_id: subscription id.
-        max_time: maximal wait time.
-
-    Returns:
-        Return True if device attach voice within max_time.
-        Return False if timeout.
-    """
-    if not _wait_for_droid_in_state_for_subscription(
-            log, ad, sub_id, max_time, _is_attached_for_subscription,
-            NETWORK_SERVICE_VOICE):
-        return False
-
-    # TODO: b/26295983 if pone attach to 1xrtt from unknown, phone may not
-    # receive incoming call immediately.
-    if ad.droid.telephonyGetCurrentVoiceNetworkType() == RAT_1XRTT:
-        time.sleep(WAIT_TIME_1XRTT_VOICE_ATTACH)
-    return True
-
-
 def wait_for_data_attach(log, ad, max_time):
     """Wait for android device to attach on data.
 
@@ -4356,356 +2102,6 @@
     return model
 
 
-def ensure_preferred_network_type_for_subscription(
-        ad,
-        network_preference
-        ):
-    sub_id = ad.droid.subscriptionGetDefaultSubId()
-    if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
-            network_preference, sub_id):
-        ad.log.error("Set sub_id %s Preferred Networks Type %s failed.",
-                     sub_id, network_preference)
-    return True
-
-
-def ensure_network_rat(log,
-                       ad,
-                       network_preference,
-                       rat_family,
-                       voice_or_data=None,
-                       max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-                       toggle_apm_after_setting=False):
-    """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)
-
-
-def ensure_network_rat_for_subscription(
-        log,
-        ad,
-        sub_id,
-        network_preference,
-        rat_family,
-        voice_or_data=None,
-        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-        toggle_apm_after_setting=False):
-    """Ensure ad's current network is in expected rat_family.
-    """
-    if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
-            network_preference, sub_id):
-        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, new_state=True, strict_checking=False)
-        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-        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 %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)))
-    return result
-
-
-def ensure_network_preference(log,
-                              ad,
-                              network_preference,
-                              voice_or_data=None,
-                              max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-                              toggle_apm_after_setting=False):
-    """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,
-        voice_or_data, max_wait_time, toggle_apm_after_setting)
-
-
-def ensure_network_preference_for_subscription(
-        log,
-        ad,
-        sub_id,
-        network_preference,
-        voice_or_data=None,
-        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-        toggle_apm_after_setting=False):
-    """Ensure ad's network preference is <network_preference> for sub_id.
-    """
-    rat_family_list = rat_families_for_network_preference(network_preference)
-    if not ad.droid.telephonySetPreferredNetworkTypesForSubscription(
-            network_preference, sub_id):
-        log.error("Set Preferred Networks failed.")
-        return False
-    if is_droid_in_rat_family_list_for_subscription(
-            log, ad, sub_id, rat_family_list, voice_or_data):
-        return True
-
-    if toggle_apm_after_setting:
-        toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
-        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-        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)
-
-    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)))
-    return result
-
-
-def ensure_network_generation(log,
-                              ad,
-                              generation,
-                              max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-                              voice_or_data=None,
-                              toggle_apm_after_setting=False,
-                              nr_type=None):
-    """Ensure ad's network is <network generation> for default subscription ID.
-
-    Set preferred network generation to <generation>.
-    Toggle ON/OFF airplane mode if necessary.
-    Wait for ad in expected network type.
-    """
-    return ensure_network_generation_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId(), generation,
-        max_wait_time, voice_or_data, toggle_apm_after_setting, nr_type=nr_type)
-
-
-def ensure_network_generation_for_subscription(
-        log,
-        ad,
-        sub_id,
-        generation,
-        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-        voice_or_data=None,
-        toggle_apm_after_setting=False,
-        nr_type=None):
-    """Ensure ad's network is <network generation> for specified subscription ID.
-
-        Set preferred network generation to <generation>.
-        Toggle ON/OFF airplane mode if necessary.
-        Wait for ad in expected network type.
-
-    Args:
-        log: log object.
-        ad: android device object.
-        sub_id: subscription id.
-        generation: network generation, e.g. GEN_2G, GEN_3G, GEN_4G, GEN_5G.
-        max_wait_time: the time to wait for NW selection.
-        voice_or_data: check voice network generation or data network generation
-            This parameter is optional. If voice_or_data is None, then if
-            either voice or data in expected generation, function will return True.
-        toggle_apm_after_setting: Cycle airplane mode if True, otherwise do nothing.
-
-    Returns:
-        True if success, False if fail.
-    """
-    ad.log.info(
-        "RAT network type voice: %s, data: %s",
-        ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(sub_id),
-        ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(sub_id))
-
-    try:
-        ad.log.info("Finding the network preference for generation %s for "
-                    "operator %s phone type %s", generation,
-                    ad.telephony["subscription"][sub_id]["operator"],
-                    ad.telephony["subscription"][sub_id]["phone_type"])
-        network_preference = network_preference_for_generation(
-            generation, ad.telephony["subscription"][sub_id]["operator"],
-            ad.telephony["subscription"][sub_id]["phone_type"])
-        if ad.telephony["subscription"][sub_id]["operator"] == CARRIER_FRE \
-            and generation == GEN_4G:
-            network_preference = NETWORK_MODE_LTE_ONLY
-        ad.log.info("Network preference for %s is %s", generation,
-                    network_preference)
-        rat_family = rat_family_for_generation(
-            generation, ad.telephony["subscription"][sub_id]["operator"],
-            ad.telephony["subscription"][sub_id]["phone_type"])
-    except KeyError as e:
-        ad.log.error("Failed to find a rat_family entry for generation %s"
-                     " for subscriber id %s with error %s", generation,
-                     sub_id, e)
-        return False
-
-    if not set_preferred_network_mode_pref(log, ad, sub_id,
-                                           network_preference):
-        return False
-
-    if hasattr(ad, "dsds") and voice_or_data == "data" and sub_id != get_default_data_sub_id(ad):
-        ad.log.info("MSIM - Non DDS, ignore data RAT")
-        return True
-
-    if (generation == GEN_5G) or (generation == RAT_5G):
-        if is_current_network_5g_for_subscription(ad, sub_id=sub_id,
-                                        nr_type=nr_type):
-            ad.log.info("Current network type is 5G.")
-            return True
-        else:
-            ad.log.error("Not in 5G coverage for Sub %s.", sub_id)
-            return False
-
-    if is_droid_in_network_generation_for_subscription(
-            log, ad, sub_id, generation, voice_or_data):
-        return True
-
-    if toggle_apm_after_setting:
-        toggle_airplane_mode(log, ad, new_state=True, strict_checking=False)
-        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-        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)
-
-    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)))
-    if not result:
-        get_telephony_signal_strength(ad)
-    return result
-
-
-def wait_for_network_rat(log,
-                         ad,
-                         rat_family,
-                         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)
-
-
-def wait_for_network_rat_for_subscription(
-        log,
-        ad,
-        sub_id,
-        rat_family,
-        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-        voice_or_data=None):
-    return _wait_for_droid_in_state_for_subscription(
-        log, ad, sub_id, max_wait_time,
-        is_droid_in_rat_family_for_subscription, rat_family, voice_or_data)
-
-
-def wait_for_not_network_rat(log,
-                             ad,
-                             rat_family,
-                             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)
-
-
-def wait_for_not_network_rat_for_subscription(
-        log,
-        ad,
-        sub_id,
-        rat_family,
-        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-        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)
-    )
-
-
-def wait_for_preferred_network(log,
-                               ad,
-                               network_preference,
-                               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,
-        max_wait_time, voice_or_data)
-
-
-def wait_for_preferred_network_for_subscription(
-        log,
-        ad,
-        sub_id,
-        network_preference,
-        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-        voice_or_data=None):
-    rat_family_list = rat_families_for_network_preference(network_preference)
-    return _wait_for_droid_in_state_for_subscription(
-        log, ad, sub_id, max_wait_time,
-        is_droid_in_rat_family_list_for_subscription, rat_family_list,
-        voice_or_data)
-
-
-def wait_for_network_generation(log,
-                                ad,
-                                generation,
-                                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)
-
-
-def wait_for_network_generation_for_subscription(
-        log,
-        ad,
-        sub_id,
-        generation,
-        max_wait_time=MAX_WAIT_TIME_NW_SELECTION,
-        voice_or_data=None,
-        nr_type=None):
-
-    if generation == GEN_5G:
-        if is_current_network_5g_for_subscription(ad, sub_id=sub_id, nr_type=nr_type):
-            ad.log.info("Current network type is 5G.")
-            return True
-        else:
-            ad.log.error("Not in 5G coverage for Sub %s.", sub_id)
-            return False
-
-    return _wait_for_droid_in_state_for_subscription(
-        log, ad, sub_id, max_wait_time,
-        is_droid_in_network_generation_for_subscription, generation,
-        voice_or_data)
-
-
 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,
@@ -4898,324 +2294,6 @@
     return voice_mail_number
 
 
-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 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, retry=2):
-    """Ensure ad idle (not in call).
-    """
-    while ad.droid.telecomIsInCall() and retry > 0:
-        ad.droid.telecomEndCall()
-        time.sleep(3)
-        retry -= 1
-    if not wait_for_droid_not_in_call(log, ad, max_time=max_time):
-        ad.log.error("Failed to end call")
-        return False
-    return True
-
-
-def ensure_phone_subscription(log, ad):
-    """Ensure Phone Subscription.
-    """
-    #check for sim and service
-    duration = 0
-    while duration < MAX_WAIT_TIME_NW_SELECTION:
-        subInfo = ad.droid.subscriptionGetAllSubInfoList()
-        if subInfo and len(subInfo) >= 1:
-            ad.log.debug("Find valid subcription %s", subInfo)
-            break
-        else:
-            ad.log.info("Did not find any subscription")
-            time.sleep(5)
-            duration += 5
-    else:
-        ad.log.error("Unable to find a valid subscription!")
-        return False
-    while duration < MAX_WAIT_TIME_NW_SELECTION:
-        data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
-        voice_sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
-        if data_sub_id > INVALID_SUB_ID or voice_sub_id > INVALID_SUB_ID:
-            ad.log.debug("Find valid voice or data sub id")
-            break
-        else:
-            ad.log.info("Did not find valid data or voice sub id")
-            time.sleep(5)
-            duration += 5
-    else:
-        ad.log.error("Unable to find valid data or voice sub id")
-        return False
-    while duration < MAX_WAIT_TIME_NW_SELECTION:
-        data_sub_id = ad.droid.subscriptionGetDefaultDataSubId()
-        if data_sub_id > INVALID_SUB_ID:
-            data_rat = get_network_rat_for_subscription(
-                log, ad, data_sub_id, NETWORK_SERVICE_DATA)
-        else:
-            data_rat = RAT_UNKNOWN
-        if voice_sub_id > INVALID_SUB_ID:
-            voice_rat = get_network_rat_for_subscription(
-                log, ad, voice_sub_id, NETWORK_SERVICE_VOICE)
-        else:
-            voice_rat = RAT_UNKNOWN
-        if data_rat != RAT_UNKNOWN or voice_rat != RAT_UNKNOWN:
-            ad.log.info("Data sub_id %s in %s, voice sub_id %s in %s",
-                        data_sub_id, data_rat, voice_sub_id, voice_rat)
-            return True
-        else:
-            ad.log.info("Did not attach for data or voice service")
-            time.sleep(5)
-            duration += 5
-    else:
-        ad.log.error("Did not attach for voice or data service")
-        return False
-
-
-def ensure_phone_default_state(log, ad, check_subscription=True, retry=2):
-    """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 not toggle_airplane_mode(log, ad, False, False):
-        ad.log.error("Fail to turn off airplane mode")
-        result = False
-    try:
-        set_wifi_to_default(log, ad)
-        while ad.droid.telecomIsInCall() and retry > 0:
-            ad.droid.telecomEndCall()
-            time.sleep(3)
-            retry -= 1
-        if not wait_for_droid_not_in_call(log, ad):
-            ad.log.error("Failed to end call")
-        #ad.droid.telephonyFactoryReset()
-        data_roaming = getattr(ad, 'roaming', False)
-        if get_cell_data_roaming_state_by_adb(ad) != data_roaming:
-            set_cell_data_roaming_state_by_adb(ad, data_roaming)
-        #remove_mobile_data_usage_limit(ad)
-        if not wait_for_not_network_rat(
-                log, ad, RAT_FAMILY_WLAN, voice_or_data=NETWORK_SERVICE_DATA):
-            ad.log.error("%s still in %s", NETWORK_SERVICE_DATA,
-                         RAT_FAMILY_WLAN)
-            result = False
-
-        if check_subscription and not ensure_phone_subscription(log, ad):
-            ad.log.error("Unable to find a valid subscription!")
-            result = False
-    except Exception as e:
-        ad.log.error("%s failure, toggle APM instead", e)
-        toggle_airplane_mode_by_adb(log, ad, True)
-        toggle_airplane_mode_by_adb(log, ad, False)
-        ad.send_keycode("ENDCALL")
-        ad.adb.shell("settings put global wfc_ims_enabled 0")
-        ad.adb.shell("settings put global mobile_data 1")
-
-    return result
-
-
-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.
-    Phone not in airplane mode.
-
-    Returns:
-        True if all steps of restoring default state succeed.
-        False if any of the steps to restore default state fails.
-    """
-    tasks = []
-    for ad in ads:
-        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 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.
-
-    Returns:
-        True if wifi is connected to wifi_ssid
-        False if wifi is not connected to wifi_ssid
-    """
-    wifi_info = ad.droid.wifiGetConnectionInfo()
-    if wifi_info["supplicant_state"] == "completed" and wifi_info["SSID"] == wifi_ssid:
-        ad.log.info("Wifi is connected to %s", wifi_ssid)
-        ad.on_mobile_data = False
-        return True
-    else:
-        ad.log.info("Wifi is not connected to %s", wifi_ssid)
-        ad.log.debug("Wifi connection_info=%s", wifi_info)
-        ad.on_mobile_data = True
-        return False
-
-
-def ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd=None, retries=3, apm=False):
-    """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
-    """
-    if not toggle_airplane_mode(log, ad, apm, strict_checking=False):
-        return False
-
-    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 verify_internet_connection(log, ad, retries=3)
-        else:
-            ad.log.info("Connecting to wifi %s", wifi_ssid)
-            try:
-                ad.droid.wifiConnectByConfig(network)
-            except Exception:
-                ad.log.info("Connecting to wifi by wifiConnect instead")
-                ad.droid.wifiConnect(network)
-            time.sleep(20)
-            if check_is_wifi_connected(log, ad, wifi_ssid):
-                ad.log.info("Connected to Wifi %s", wifi_ssid)
-                return verify_internet_connection(log, ad, retries=3)
-    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():
-        ad.on_mobile_data = True
-        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
-    ad.on_mobile_data = True
-    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)
-    ad.on_mobile_data = True
-
-
-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):
-            ad.on_mobile_data = not state
-            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.
@@ -5318,61 +2396,6 @@
     return False
 
 
-def set_preferred_subid_for_sms(log, ad, sub_id):
-    """set subscription id for SMS
-
-    Args:
-        log: Log object.
-        ad: Android device object.
-        sub_id :Subscription ID.
-
-    """
-    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)
-    return sub_id == ad.droid.subscriptionGetDefaultSmsSubId()
-
-
-def set_preferred_subid_for_data(log, ad, sub_id):
-    """set subscription id for data
-
-    Args:
-        log: Log object.
-        ad: Android device object.
-        sub_id :Subscription ID.
-
-    """
-    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
-    # Data SIM change takes around 1 min
-    # Check whether data has changed to selected sim
-    if not wait_for_data_connection(log, ad, True,
-                                    MAX_WAIT_TIME_DATA_SUB_CHANGE):
-        log.error("Data Connection failed - Not able to switch Data SIM")
-        return False
-    return True
-
-
-def set_preferred_subid_for_voice(log, ad, sub_id):
-    """set subscription id for voice
-
-    Args:
-        log: Log object.
-        ad: Android device object.
-        sub_id :Subscription ID.
-
-    """
-    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
-    time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
-    return True
-
-
 def set_call_state_listen_level(log, ad, value, sub_id):
     """Set call state listen level for subscription id.
 
@@ -5397,34 +2420,6 @@
     return True
 
 
-def setup_sim(log, ad, sub_id, voice=False, sms=False, data=False):
-    """set subscription id for voice, sms and data
-
-    Args:
-        log: Log object.
-        ad: Android device object.
-        sub_id :Subscription ID.
-        voice: True if to set subscription as default voice subscription
-        sms: True if to set subscription as default sms subscription
-        data: True if to set subscription as default data subscription
-
-    """
-    if sub_id == INVALID_SUB_ID:
-        log.error("Invalid Subscription ID")
-        return False
-    else:
-        if voice:
-            if not set_preferred_subid_for_voice(log, ad, sub_id):
-                return False
-        if sms:
-            if not set_preferred_subid_for_sms(log, ad, sub_id):
-                return False
-        if data:
-            if not set_preferred_subid_for_data(log, ad, sub_id):
-                return False
-    return True
-
-
 def is_event_match(event, field, value):
     """Return if <field> in "event" match <value> or not.
 
@@ -6101,27 +3096,6 @@
         return False
 
 
-def extract_test_log(log, src_file, dst_file, test_tag):
-    os.makedirs(os.path.dirname(dst_file), exist_ok=True)
-    cmd = "grep -n '%s' %s" % (test_tag, src_file)
-    result = job.run(cmd, ignore_status=True)
-    if not result.stdout or result.exit_status == 1:
-        log.warning("Command %s returns %s", cmd, result)
-        return
-    line_nums = re.findall(r"(\d+).*", result.stdout)
-    if line_nums:
-        begin_line = int(line_nums[0])
-        end_line = int(line_nums[-1])
-        if end_line - begin_line <= 5:
-            result = job.run("wc -l < %s" % src_file)
-            if result.stdout:
-                end_line = int(result.stdout)
-        log.info("Extract %s from line %s to line %s to %s", src_file,
-                 begin_line, end_line, dst_file)
-        job.run("awk 'NR >= %s && NR <= %s' %s > %s" % (begin_line, end_line,
-                                                        src_file, dst_file))
-
-
 def get_device_epoch_time(ad):
     return int(1000 * float(ad.adb.shell("date +%s.%N")))
 
@@ -6196,32 +3170,6 @@
     return result
 
 
-def log_screen_shot(ad, test_name=""):
-    file_name = "/sdcard/Pictures/screencap"
-    if test_name:
-        file_name = "%s_%s" % (file_name, test_name)
-    file_name = "%s_%s.png" % (file_name, utils.get_current_epoch_time())
-    try:
-        ad.adb.shell("screencap -p %s" % file_name)
-    except:
-        ad.log.error("Fail to log screen shot to %s", file_name)
-
-
-def get_screen_shot_log(ad, test_name="", begin_time=None):
-    logs = ad.get_file_names("/sdcard/Pictures", begin_time=begin_time)
-    if logs:
-        ad.log.info("Pulling %s", logs)
-        log_path = os.path.join(ad.device_log_path, "Screenshot_%s" % ad.serial)
-        os.makedirs(log_path, exist_ok=True)
-        ad.pull_files(logs, log_path)
-    ad.adb.shell("rm -rf /sdcard/Pictures/screencap_*", ignore_status=True)
-
-
-def get_screen_shot_logs(ads, test_name="", begin_time=None):
-    for ad in ads:
-        get_screen_shot_log(ad, test_name=test_name, begin_time=begin_time)
-
-
 def get_carrier_id_version(ad):
     out = ad.adb.shell("dumpsys activity service TelephonyDebugService | " \
                        "grep -i carrier_list_version")
@@ -6757,12 +3705,6 @@
     return rx_power, tx_power
 
 
-
-
-
-
-
-
 def set_time_sync_from_network(ad, action):
     if (action == 'enable'):
         ad.log.info('Enabling sync time from network.')
@@ -6800,66 +3742,6 @@
     return get_value
 
 
-def wait_for_call_end(
-        log,
-        ad_caller,
-        ad_callee,
-        ad_hangup,
-        verify_caller_func,
-        verify_callee_func,
-        call_begin_time,
-        check_interval=5,
-        tel_result_wrapper=TelResultWrapper(CallResult('SUCCESS')),
-        wait_time_in_call=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
-        time_message = "at <%s>/<%s> second." % (elapsed_time, wait_time_in_call)
-        for ad, call_func in [(ad_caller, verify_caller_func),
-                              (ad_callee, verify_callee_func)]:
-            if not call_func(log, ad):
-                ad.log.error(
-                    "NOT in correct %s state at %s, voice in RAT %s",
-                    call_func.__name__,
-                    time_message,
-                    ad.droid.telephonyGetCurrentVoiceNetworkType())
-                tel_result_wrapper.result_value = CallResult(
-                    'CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED')
-            else:
-                ad.log.info("In correct %s state at %s",
-                    call_func.__name__, time_message)
-            if not ad.droid.telecomCallGetAudioState():
-                ad.log.error("Audio is not in call state at %s", time_message)
-                tel_result_wrapper.result_value = CallResult(
-                        'AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED')
-        if not tel_result_wrapper:
-            return tel_result_wrapper
-
-    if ad_hangup:
-        if not hangup_call(log, ad_hangup):
-            ad_hangup.log.info("Failed to hang up the call")
-            tel_result_wrapper.result_value = CallResult('CALL_HANGUP_FAIL')
-
-    if not tel_result_wrapper:
-        for ad in (ad_caller, ad_callee):
-            last_call_drop_reason(ad, call_begin_time)
-            try:
-                if ad.droid.telecomIsInCall():
-                    ad.log.info("In call. End now.")
-                    ad.droid.telecomEndCall()
-            except Exception as e:
-                log.error(str(e))
-    if ad_hangup or not tel_result_wrapper:
-        for ad in (ad_caller, ad_callee):
-            if not wait_for_call_id_clearing(ad, getattr(ad, "caller_ids", [])):
-                tel_result_wrapper.result_value = CallResult(
-                    'CALL_ID_CLEANUP_FAIL')
-
-    return tel_result_wrapper
-
-
 def change_voice_subid_temporarily(ad, sub_id, state_check_func, params=None):
     result = False
     voice_sub_id_changed = False
@@ -6901,47 +3783,6 @@
     return voice_network_list
 
 
-def check_call_status(ad, voice_type_init=None, voice_type_in_call=None):
-    """"
-    Args:
-        ad: Android device object
-        voice_type_init: Voice network type before initiate call
-        voice_type_in_call: Voice network type in call state
-
-    Return:
-         voice_call_type_dict: Voice call status
-    """
-    dut = str(ad.serial)
-    network_type = voice_type_init + "_" + voice_type_in_call
-    if network_type == "NR_NR":
-        voice_call_type_dict = update_voice_call_type_dict(dut, "VoNR")
-    elif network_type == "NR_LTE":
-        voice_call_type_dict = update_voice_call_type_dict(dut, "EPSFB")
-    elif network_type == "LTE_LTE":
-        voice_call_type_dict = update_voice_call_type_dict(dut, "VoLTE")
-    elif network_type == "LTE_WCDMA":
-        voice_call_type_dict = update_voice_call_type_dict(dut, "CSFB")
-    else:
-        voice_call_type_dict = update_voice_call_type_dict(dut, "UNKNOWN")
-    return voice_call_type_dict
-
-
-def update_voice_call_type_dict(dut, key):
-    """
-    Args:
-        dut: Serial Number of android device object
-        key: Network subscription parameter (VoNR or EPSFB or VoLTE or CSFB or UNKNOWN)
-    Return:
-        voice_call_type: Voice call status
-    """
-    if dut in voice_call_type.keys():
-        voice_call_type[dut][key] += 1
-    else:
-        voice_call_type[dut] = {key:0}
-        voice_call_type[dut][key] += 1
-    return voice_call_type
-
-
 def cycle_airplane_mode(ad):
     """Turn on APM and then off."""
     # APM toggle
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_video_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_video_utils.py
index 0d028f9..26751cf 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_video_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_video_utils.py
@@ -43,16 +43,16 @@
 from acts_contrib.test_utils.tel.tel_ims_utils import toggle_volte
 from acts_contrib.test_utils.tel.tel_ims_utils import set_wfc_mode_for_subscription
 from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_video_enabled
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_network_generation
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_iwlan_for_subscription
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_network_generation
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
-from acts_contrib.test_utils.tel.tel_test_utils import wait_and_answer_call_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_generation
 from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
 from acts_contrib.test_utils.tel.tel_voice_utils import is_call_hd
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_iwlan_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_and_answer_call_for_subscription
 
 
 def phone_setup_video(
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_voice_conf_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_voice_conf_utils.py
index da112d3..721e83e 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_voice_conf_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_voice_conf_utils.py
@@ -24,19 +24,20 @@
 from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_HOLDING
 from acts_contrib.test_utils.tel.tel_defines import PHONE_TYPE_GSM
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_voice_2g
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_voice_3g
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
 from acts_contrib.test_utils.tel.tel_test_utils import get_call_uri
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_test_utils import initiate_call
 from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
 from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
 from acts_contrib.test_utils.tel.tel_test_utils import is_uri_equivalent
-from acts_contrib.test_utils.tel.tel_test_utils import wait_and_reject_call_for_subscription
+from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
 from acts_contrib.test_utils.tel.tel_voice_utils import get_cep_conference_call_id
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
-from acts_contrib.test_utils.tel.tel_voice_utils import phone_setup_voice_3g
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
 from acts_contrib.test_utils.tel.tel_voice_utils import swap_calls
+from acts_contrib.test_utils.tel.tel_voice_utils import wait_and_reject_call_for_subscription
+
 
 def _three_phone_call_mo_add_mo(log, ads, phone_setups, verify_funcs):
     """Use 3 phones to make MO calls.
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_voice_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_voice_utils.py
index 84cdcf8..62e3d1e 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_voice_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_voice_utils.py
@@ -14,105 +14,934 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import re
 import time
+from queue import Empty
 from acts import signals
+from acts.logger import epoch_to_log_line_timestamp
+from acts.utils import get_current_epoch_time
 from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
 from acts_contrib.test_utils.tel.tel_defines import CarrierConfigs
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_NTT_DOCOMO, CARRIER_KDDI, CARRIER_RAKUTEN, CARRIER_SBM
 from acts_contrib.test_utils.tel.tel_defines import CALL_PROPERTY_HIGH_DEF_AUDIO
 from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
 from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_HOLDING
 from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
 from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
-from acts_contrib.test_utils.tel.tel_defines import CARRIER_TMO
+from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
 from acts_contrib.test_utils.tel.tel_defines import GEN_2G
 from acts_contrib.test_utils.tel.tel_defines import GEN_3G
-from acts_contrib.test_utils.tel.tel_defines import GEN_4G
-from acts_contrib.test_utils.tel.tel_defines import GEN_5G
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOLTE_ENABLED
-from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_WFC_ENABLED
+from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_BACKGROUND
+from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_FOREGROUND
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import MAX_SAVED_VOICE_MAIL
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_IDLE_EVENT
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_INITIATION
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALLEE_RINGING
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TELECOM_RINGING
+from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOICE_MAIL_COUNT
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
 from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
-from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_CDMA2000
-from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_LTE
-from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_GSM
-from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WCDMA
-from acts_contrib.test_utils.tel.tel_defines import RAT_FAMILY_WLAN
 from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
 from acts_contrib.test_utils.tel.tel_defines import RAT_IWLAN
 from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
 from acts_contrib.test_utils.tel.tel_defines import RAT_UMTS
 from acts_contrib.test_utils.tel.tel_defines import RAT_UNKNOWN
+from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_IDLE
+from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_OFFHOOK
+from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_RINGING
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_LEAVE_VOICE_MAIL
-from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_DISABLED
-from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_REJECT_CALL
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
-from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
-from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
-from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_GSM_UMTS
-from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_CDMA_EVDO
-from acts_contrib.test_utils.tel.tel_defines import NETWORK_MODE_LTE_GSM_WCDMA
-from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
-from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts_contrib.test_utils.tel.tel_defines import EventCallStateChanged
+from acts_contrib.test_utils.tel.tel_defines import EventMessageWaitingIndicatorChanged
+from acts_contrib.test_utils.tel.tel_defines import CallStateContainer
+from acts_contrib.test_utils.tel.tel_defines import MessageWaitingIndicatorContainer
 from acts_contrib.test_utils.tel.tel_ims_utils import is_wfc_enabled
 from acts_contrib.test_utils.tel.tel_ims_utils import toggle_volte
-from acts_contrib.test_utils.tel.tel_ims_utils import toggle_volte_for_subscription
 from acts_contrib.test_utils.tel.tel_ims_utils import toggle_wfc
 from acts_contrib.test_utils.tel.tel_ims_utils import set_wfc_mode
-from acts_contrib.test_utils.tel.tel_ims_utils import set_wfc_mode_for_subscription
-from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_enhanced_4g_lte_setting
 from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_volte_enabled
 from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_wfc_enabled
 from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_wfc_disabled
-from acts_contrib.test_utils.tel.tel_lookup_tables import network_preference_for_generation
-from acts_contrib.test_utils.tel.tel_5g_utils import is_current_network_5g_for_subscription
-from acts_contrib.test_utils.tel.tel_ss_utils import call_setup_teardown_for_call_forwarding
-from acts_contrib.test_utils.tel.tel_ss_utils import call_setup_teardown_for_call_waiting
-from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
+from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_delete_digit
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_network_rat
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phone_subscription
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_not_network_rat
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_voice_attach
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
-from acts_contrib.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
-from acts_contrib.test_utils.tel.tel_test_utils import call_reject_leave_message
-from acts_contrib.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_generation_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_network_rat_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_default_state
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phone_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_wifi_connected
-from acts_contrib.test_utils.tel.tel_test_utils import get_capability_for_subscription
+from acts_contrib.test_utils.tel.tel_test_utils import _wait_for_droid_in_state
+from acts_contrib.test_utils.tel.tel_test_utils import check_call_state_connected_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import check_call_state_idle_by_adb
+from acts_contrib.test_utils.tel.tel_test_utils import check_phone_number_match
+from acts_contrib.test_utils.tel.tel_test_utils import check_voice_mail_count
+from acts_contrib.test_utils.tel.tel_test_utils import check_voice_network_type
+from acts_contrib.test_utils.tel.tel_test_utils import get_call_uri
+from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
 from acts_contrib.test_utils.tel.tel_test_utils import get_network_gen_for_subscription
 from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat
 from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import get_telephony_signal_strength
-from acts_contrib.test_utils.tel.tel_test_utils import get_user_config_profile
+from acts_contrib.test_utils.tel.tel_test_utils import get_number_from_tel_uri
 from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
-from acts_contrib.test_utils.tel.tel_test_utils import reset_preferred_network_type_to_allowable_range
-from acts_contrib.test_utils.tel.tel_test_utils import set_preferred_network_mode_pref
-from acts_contrib.test_utils.tel.tel_test_utils import set_wifi_to_default
+from acts_contrib.test_utils.tel.tel_test_utils import get_user_config_profile
+from acts_contrib.test_utils.tel.tel_test_utils import get_voice_mail_number
+from acts_contrib.test_utils.tel.tel_test_utils import is_event_match
+from acts_contrib.test_utils.tel.tel_test_utils import is_event_match_for_list
+from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
 from acts_contrib.test_utils.tel.tel_test_utils import TelResultWrapper
-from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
 from acts_contrib.test_utils.tel.tel_test_utils import verify_incall_state
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_data_attach_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_generation
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_generation_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_rat
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_not_network_rat
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_rat_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_not_network_rat_for_subscription
 from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_voice_attach
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_voice_attach_for_subscription
-from acts_contrib.test_utils.tel.tel_test_utils import wifi_toggle_state
-from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
-from acts_contrib.test_utils.tel.tel_test_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected
+from acts_contrib.test_utils.tel.tel_wifi_utils import wifi_toggle_state
 
 CallResult = TelephonyVoiceTestResult.CallResult.Value
+result_dict ={}
+voice_call_type = {}
+
+
+def check_call_status(ad, voice_type_init=None, voice_type_in_call=None):
+    """"
+    Args:
+        ad: Android device object
+        voice_type_init: Voice network type before initiate call
+        voice_type_in_call: Voice network type in call state
+
+    Return:
+         voice_call_type_dict: Voice call status
+    """
+    dut = str(ad.serial)
+    network_type = voice_type_init + "_" + voice_type_in_call
+    if network_type == "NR_NR":
+        voice_call_type_dict = update_voice_call_type_dict(dut, "VoNR")
+    elif network_type == "NR_LTE":
+        voice_call_type_dict = update_voice_call_type_dict(dut, "EPSFB")
+    elif network_type == "LTE_LTE":
+        voice_call_type_dict = update_voice_call_type_dict(dut, "VoLTE")
+    elif network_type == "LTE_WCDMA":
+        voice_call_type_dict = update_voice_call_type_dict(dut, "CSFB")
+    else:
+        voice_call_type_dict = update_voice_call_type_dict(dut, "UNKNOWN")
+    return voice_call_type_dict
+
+
+def update_voice_call_type_dict(dut, key):
+    """
+    Args:
+        dut: Serial Number of android device object
+        key: Network subscription parameter (VoNR or EPSFB or VoLTE or CSFB or UNKNOWN)
+    Return:
+        voice_call_type: Voice call status
+    """
+    if dut in voice_call_type.keys():
+        voice_call_type[dut][key] += 1
+    else:
+        voice_call_type[dut] = {key:0}
+        voice_call_type[dut][key] += 1
+    return voice_call_type
+
+
+def dial_phone_number(ad, callee_number):
+    for number in str(callee_number):
+        if number == "#":
+            ad.send_keycode("POUND")
+        elif number == "*":
+            ad.send_keycode("STAR")
+        elif number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]:
+            ad.send_keycode("%s" % number)
+
+
+def disconnect_call_by_id(log, ad, call_id):
+    """Disconnect call by call id.
+    """
+    ad.droid.telecomCallDisconnect(call_id)
+    return True
+
+
+def dumpsys_last_call_info(ad):
+    """ Get call information by dumpsys telecom. """
+    num = dumpsys_last_call_number(ad)
+    output = ad.adb.shell("dumpsys telecom")
+    result = re.search(r"Call TC@%s: {(.*?)}" % num, output, re.DOTALL)
+    call_info = {"TC": num}
+    if result:
+        result = result.group(1)
+        for attr in ("startTime", "endTime", "direction", "isInterrupted",
+                     "callTechnologies", "callTerminationsReason",
+                     "isVideoCall", "callProperties"):
+            match = re.search(r"%s: (.*)" % attr, result)
+            if match:
+                if attr in ("startTime", "endTime"):
+                    call_info[attr] = epoch_to_log_line_timestamp(
+                        int(match.group(1)))
+                else:
+                    call_info[attr] = match.group(1)
+    ad.log.debug("call_info = %s", call_info)
+    return call_info
+
+
+def dumpsys_last_call_number(ad):
+    output = ad.adb.shell("dumpsys telecom")
+    call_nums = re.findall("Call TC@(\d+):", output)
+    if not call_nums:
+        return 0
+    else:
+        return int(call_nums[-1])
+
+
+def dumpsys_new_call_info(ad, last_tc_number, retries=3, interval=5):
+    for i in range(retries):
+        if dumpsys_last_call_number(ad) > last_tc_number:
+            call_info = dumpsys_last_call_info(ad)
+            ad.log.info("New call info = %s", sorted(call_info.items()))
+            return call_info
+        else:
+            time.sleep(interval)
+    ad.log.error("New call is not in sysdump telecom")
+    return {}
+
+
+def emergency_dialer_call_by_keyevent(ad, callee_number):
+    for i in range(3):
+        if "EmergencyDialer" in ad.get_my_current_focus_window():
+            ad.log.info("EmergencyDialer is the current focus window")
+            break
+        elif i <= 2:
+            ad.adb.shell("am start -a com.android.phone.EmergencyDialer.DIAL")
+            time.sleep(1)
+        else:
+            ad.log.error("Unable to bring up EmergencyDialer")
+            return False
+    ad.log.info("Make a phone call to %s", callee_number)
+    dial_phone_number(ad, callee_number)
+    ad.send_keycode("CALL")
+
+
+def get_current_voice_rat(log, ad):
+    """Return current Voice RAT
+
+    Args:
+        ad: Android device object.
+    """
+    return get_current_voice_rat_for_subscription(
+        log, ad, get_outgoing_voice_sub_id(ad))
+
+
+def get_current_voice_rat_for_subscription(log, ad, sub_id):
+    """Return current Voice RAT for subscription id.
+
+    Args:
+        ad: Android device object.
+        sub_id: subscription id.
+    """
+    return get_network_rat_for_subscription(log, ad, sub_id,
+                                            NETWORK_SERVICE_VOICE)
+
+
+def hangup_call_by_adb(ad):
+    """Make emergency call by EmergencyDialer.
+
+    Args:
+        ad: Caller android device object.
+        callee_number: Callee phone number.
+    """
+    ad.log.info("End call by adb")
+    ad.send_keycode("ENDCALL")
+
+
+def hangup_call(log, ad, is_emergency=False):
+    """Hang up ongoing active call.
+
+    Args:
+        log: log object.
+        ad: android device object.
+
+    Returns:
+        True: if all calls are cleared
+        False: for errors
+    """
+    # short circuit in case no calls are active
+    if not ad.droid.telecomIsInCall():
+        ad.log.warning("No active call exists.")
+        return True
+    ad.ed.clear_events(EventCallStateChanged)
+    ad.droid.telephonyStartTrackingCallState()
+    ad.log.info("Hangup call.")
+    if is_emergency:
+        for call in ad.droid.telecomCallGetCallIds():
+            ad.droid.telecomCallDisconnect(call)
+    else:
+        ad.droid.telecomEndCall()
+
+    try:
+        ad.ed.wait_for_event(
+            EventCallStateChanged,
+            is_event_match,
+            timeout=MAX_WAIT_TIME_CALL_IDLE_EVENT,
+            field=CallStateContainer.CALL_STATE,
+            value=TELEPHONY_STATE_IDLE)
+    except Empty:
+        ad.log.warning("Call state IDLE event is not received after hang up.")
+    finally:
+        ad.droid.telephonyStopTrackingCallStateChange()
+    if not wait_for_state(ad.droid.telecomIsInCall, False, 15, 1):
+        ad.log.error("Telecom is in call, hangup call failed.")
+        return False
+    return True
+
+
+def initiate_emergency_dialer_call_by_adb(
+        log,
+        ad,
+        callee_number,
+        timeout=MAX_WAIT_TIME_CALL_INITIATION,
+        checking_interval=5):
+    """Make emergency call by EmergencyDialer.
+
+    Args:
+        ad: Caller android device object.
+        callee_number: Callee phone number.
+        emergency : specify the call is emergency.
+        Optional. Default value is False.
+
+    Returns:
+        result: if phone call is placed successfully.
+    """
+    try:
+        # Make a Call
+        ad.wakeup_screen()
+        ad.send_keycode("MENU")
+        ad.log.info("Call %s", callee_number)
+        ad.adb.shell("am start -a com.android.phone.EmergencyDialer.DIAL")
+        ad.adb.shell(
+            "am start -a android.intent.action.CALL_EMERGENCY -d tel:%s" %
+            callee_number)
+        if not timeout: return True
+        ad.log.info("Check call state")
+        # Verify Call State
+        elapsed_time = 0
+        while elapsed_time < timeout:
+            time.sleep(checking_interval)
+            elapsed_time += checking_interval
+            if check_call_state_connected_by_adb(ad):
+                ad.log.info("Call to %s is connected", callee_number)
+                return True
+            if check_call_state_idle_by_adb(ad):
+                ad.log.info("Call to %s failed", callee_number)
+                return False
+        ad.log.info("Make call to %s failed", callee_number)
+        return False
+    except Exception as e:
+        ad.log.error("initiate emergency call failed with error %s", e)
+
+
+def initiate_call(log,
+                  ad,
+                  callee_number,
+                  emergency=False,
+                  incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+                  video=False):
+    """Make phone call from caller to callee.
+
+    Args:
+        ad_caller: Caller android device object.
+        callee_number: Callee phone number.
+        emergency : specify the call is emergency.
+            Optional. Default value is False.
+        incall_ui_display: show the dialer UI foreground or backgroud
+        video: whether to initiate as video call
+
+    Returns:
+        result: if phone call is placed successfully.
+    """
+    ad.ed.clear_events(EventCallStateChanged)
+    sub_id = get_outgoing_voice_sub_id(ad)
+    begin_time = get_device_epoch_time(ad)
+    ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
+    try:
+        # Make a Call
+        ad.log.info("Make a phone call to %s", callee_number)
+        if emergency:
+            ad.droid.telecomCallEmergencyNumber(callee_number)
+        else:
+            ad.droid.telecomCallNumber(callee_number, video)
+
+        # Verify OFFHOOK state
+        if not wait_for_call_offhook_for_subscription(
+                log, ad, sub_id, event_tracking_started=True):
+            ad.log.info("sub_id %s not in call offhook state", sub_id)
+            last_call_drop_reason(ad, begin_time=begin_time)
+            return False
+        else:
+            return True
+
+    finally:
+        if hasattr(ad, "sdm_log") and getattr(ad, "sdm_log"):
+            ad.adb.shell("i2cset -fy 3 64 6 1 b", ignore_status=True)
+            ad.adb.shell("i2cset -fy 3 65 6 1 b", ignore_status=True)
+        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()
+
+
+def last_call_drop_reason(ad, begin_time=None):
+    reasons = ad.search_logcat(
+        "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause", begin_time)
+    reason_string = ""
+    if reasons:
+        log_msg = "Logcat call drop reasons:"
+        for reason in reasons:
+            log_msg = "%s\n\t%s" % (log_msg, reason["log_message"])
+            if "ril reason str" in reason["log_message"]:
+                reason_string = reason["log_message"].split(":")[-1].strip()
+        ad.log.info(log_msg)
+    reasons = ad.search_logcat("ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION",
+                               begin_time)
+    if reasons:
+        ad.log.warning("ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION is seen")
+    ad.log.info("last call dumpsys: %s",
+                sorted(dumpsys_last_call_info(ad).items()))
+    return reason_string
+
+
+def call_reject(log, ad_caller, ad_callee, reject=True):
+    """Caller call Callee, then reject on callee.
+
+
+    """
+    subid_caller = ad_caller.droid.subscriptionGetDefaultVoiceSubId()
+    subid_callee = ad_callee.incoming_voice_sub_id
+    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)
+
+
+def call_reject_for_subscription(log,
+                                 ad_caller,
+                                 ad_callee,
+                                 subid_caller,
+                                 subid_callee,
+                                 reject=True):
+    """
+    """
+
+    caller_number = ad_caller.telephony['subscription'][subid_caller][
+        'phone_num']
+    callee_number = ad_callee.telephony['subscription'][subid_callee][
+        'phone_num']
+
+    ad_caller.log.info("Call from %s to %s", caller_number, callee_number)
+    if not initiate_call(log, ad_caller, callee_number):
+        ad_caller.log.error("Initiate call failed")
+        return False
+
+    if not wait_and_reject_call_for_subscription(
+            log, ad_callee, subid_callee, caller_number, WAIT_TIME_REJECT_CALL,
+            reject):
+        ad_callee.log.error("Reject call fail.")
+        return False
+    # Check if incoming call is cleared on callee or not.
+    if ad_callee.droid.telephonyGetCallStateForSubscription(
+            subid_callee) == TELEPHONY_STATE_RINGING:
+        ad_callee.log.error("Incoming call is not cleared")
+        return False
+    # Hangup on caller
+    hangup_call(log, ad_caller)
+    return True
+
+
+def call_reject_leave_message(log,
+                              ad_caller,
+                              ad_callee,
+                              verify_caller_func=None,
+                              wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
+    """On default voice subscription, Call from caller to callee,
+    reject on callee, caller leave a voice mail.
+
+    1. Caller call Callee.
+    2. Callee reject incoming call.
+    3. Caller leave a voice mail.
+    4. Verify callee received the voice mail notification.
+
+    Args:
+        ad_caller: caller android device object.
+        ad_callee: callee android device object.
+        verify_caller_func: function to verify caller is in correct state while in-call.
+            This is optional, default is None.
+        wait_time_in_call: time to wait when leaving a voice mail.
+            This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
+
+    Returns:
+        True: if voice message is received on callee successfully.
+        False: for errors
+    """
+    subid_caller = get_outgoing_voice_sub_id(ad_caller)
+    subid_callee = get_incoming_voice_sub_id(ad_callee)
+    return call_reject_leave_message_for_subscription(
+        log, ad_caller, ad_callee, subid_caller, subid_callee,
+        verify_caller_func, wait_time_in_call)
+
+
+def check_reject_needed_for_voice_mail(log, ad_callee):
+    """Check if the carrier requires reject call to receive voice mail or just keep ringing
+    Requested in b//155935290
+    Four Japan carriers do not need to reject
+    SBM, KDDI, Ntt Docomo, Rakuten
+    Args:
+        log: log object
+        ad_callee: android device object
+    Returns:
+        True if callee's carrier is not one of the four Japan carriers
+        False if callee's carrier is one of the four Japan carriers
+    """
+
+    operators_no_reject = [CARRIER_NTT_DOCOMO,
+                           CARRIER_KDDI,
+                           CARRIER_RAKUTEN,
+                           CARRIER_SBM]
+    operator_name = get_operator_name(log, ad_callee)
+
+    return operator_name not in operators_no_reject
+
+
+def _is_on_message_waiting_event_true(event):
+    """Private function to return if the received EventMessageWaitingIndicatorChanged
+    event MessageWaitingIndicatorContainer.IS_MESSAGE_WAITING field is True.
+    """
+    return event['data'][MessageWaitingIndicatorContainer.IS_MESSAGE_WAITING]
+
+
+def call_reject_leave_message_for_subscription(
+        log,
+        ad_caller,
+        ad_callee,
+        subid_caller,
+        subid_callee,
+        verify_caller_func=None,
+        wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
+    """On specific voice subscription, Call from caller to callee,
+    reject on callee, caller leave a voice mail.
+
+    1. Caller call Callee.
+    2. Callee reject incoming call.
+    3. Caller leave a voice mail.
+    4. Verify callee received the voice mail notification.
+
+    Args:
+        ad_caller: caller android device object.
+        ad_callee: callee android device object.
+        subid_caller: caller's subscription id.
+        subid_callee: callee's subscription id.
+        verify_caller_func: function to verify caller is in correct state while in-call.
+            This is optional, default is None.
+        wait_time_in_call: time to wait when leaving a voice mail.
+            This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
+
+    Returns:
+        True: if voice message is received on callee successfully.
+        False: for errors
+    """
+
+    # Currently this test utility only works for TMO and ATT and SPT.
+    # It does not work for VZW (see b/21559800)
+    # "with VVM TelephonyManager APIs won't work for vm"
+
+    caller_number = ad_caller.telephony['subscription'][subid_caller][
+        'phone_num']
+    callee_number = ad_callee.telephony['subscription'][subid_callee][
+        'phone_num']
+
+    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):
+            ad_caller.log.error("Initiate call failed.")
+            return False
+        if check_reject_needed_for_voice_mail(log, ad_callee):
+            carrier_specific_delay_reject = 30
+        else:
+            carrier_specific_delay_reject = 2
+        carrier_reject_call = not check_reject_needed_for_voice_mail(log, ad_callee)
+
+        if not wait_and_reject_call_for_subscription(
+                log, ad_callee, subid_callee, incoming_number=caller_number, delay_reject=carrier_specific_delay_reject,
+                reject=carrier_reject_call):
+            ad_callee.log.error("Reject call fail.")
+            return False
+
+        ad_callee.droid.telephonyStartTrackingVoiceMailStateChangeForSubscription(
+            subid_callee)
+
+        # ensure that all internal states are updated in telecom
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        ad_callee.ed.clear_events(EventCallStateChanged)
+
+        if verify_caller_func and not verify_caller_func(log, ad_caller):
+            ad_caller.log.error("Caller not in correct state!")
+            return False
+
+        # 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:
+            caller_state_result = ad_caller.droid.telecomIsInCall()
+        else:
+            caller_state_result = verify_caller_func(log, ad_caller)
+        if not caller_state_result:
+            ad_caller.log.error("Caller not in correct state after %s seconds",
+                                wait_time_in_call)
+
+        if not hangup_call(log, ad_caller):
+            ad_caller.log.error("Error in Hanging-Up Call")
+            return False
+
+        ad_callee.log.info("Wait for voice mail indicator on callee.")
+        try:
+            event = ad_callee.ed.wait_for_event(
+                EventMessageWaitingIndicatorChanged,
+                _is_on_message_waiting_event_true)
+            ad_callee.log.info("Got event %s", event)
+        except Empty:
+            ad_callee.log.warning("No expected event %s",
+                                  EventMessageWaitingIndicatorChanged)
+            return False
+        voice_mail_count_after = ad_callee.droid.telephonyGetVoiceMailCountForSubscription(
+            subid_callee)
+        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]
+        # or equals to -1 [For TMO]
+        # -1 means there are unread voice mail, but the count is unknown
+        if not check_voice_mail_count(log, ad_callee, voice_mail_count_before,
+                                      voice_mail_count_after):
+            log.error("before and after voice mail count is not incorrect.")
+            return False
+    finally:
+        ad_callee.droid.telephonyStopTrackingVoiceMailStateChangeForSubscription(
+            subid_callee)
+    return True
+
+
+def call_voicemail_erase_all_pending_voicemail(log, ad):
+    """Script for phone to erase all pending voice mail.
+    This script only works for TMO and ATT and SPT currently.
+    This script only works if phone have already set up voice mail options,
+    and phone should disable password protection for voice mail.
+
+    1. If phone don't have pending voice message, return True.
+    2. Dial voice mail number.
+        For TMO, the number is '123'
+        For ATT, the number is phone's number
+        For SPT, the number is phone's number
+    3. Wait for voice mail connection setup.
+    4. Wait for voice mail play pending voice message.
+    5. Send DTMF to delete one message.
+        The digit is '7'.
+    6. Repeat steps 4 and 5 until voice mail server drop this call.
+        (No pending message)
+    6. Check telephonyGetVoiceMailCount result. it should be 0.
+
+    Args:
+        log: log object
+        ad: android device object
+    Returns:
+        False if error happens. True is succeed.
+    """
+    log.info("Erase all pending voice mail.")
+    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 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)
+    while (is_phone_in_call(log, ad) and (count > 0)):
+        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
+    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 (ad.droid.telephonyGetVoiceMailCount() != 0)):
+        time.sleep(1)
+        remaining_time -= 1
+    current_voice_mail_count = ad.droid.telephonyGetVoiceMailCount()
+    ad.log.info("telephonyGetVoiceMailCount: %s", current_voice_mail_count)
+    return (current_voice_mail_count == 0)
+
+
+def call_setup_teardown(log,
+                        ad_caller,
+                        ad_callee,
+                        ad_hangup=None,
+                        verify_caller_func=None,
+                        verify_callee_func=None,
+                        wait_time_in_call=WAIT_TIME_IN_CALL,
+                        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+                        dialing_number_length=None,
+                        video_state=None,
+                        slot_id_callee=None,
+                        voice_type_init=None,
+                        call_stats_check=False,
+                        result_info=result_dict):
+    """ Call process, including make a phone call from caller,
+    accept from callee, and hang up. The call is on default voice subscription
+
+    In call process, call from <droid_caller> to <droid_callee>,
+    accept the call, (optional)then hang up from <droid_hangup>.
+
+    Args:
+        ad_caller: Caller Android Device Object.
+        ad_callee: Callee Android Device Object.
+        ad_hangup: Android Device Object end the phone call.
+            Optional. Default value is None, and phone call will continue.
+        verify_call_mode_caller: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        verify_call_mode_caller: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+        dialing_number_length: the number of digits used for dialing
+        slot_id_callee : the slot if of the callee to call to
+
+    Returns:
+        True if call process without any error.
+        False if error happened.
+
+    """
+    subid_caller = get_outgoing_voice_sub_id(ad_caller)
+    if slot_id_callee is None:
+        subid_callee = get_incoming_voice_sub_id(ad_callee)
+    else:
+        subid_callee = get_subid_from_slot_index(log, ad_callee, slot_id_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,
+        incall_ui_display, dialing_number_length, video_state,
+        voice_type_init, call_stats_check, result_info)
+
+
+def call_setup_teardown_for_subscription(
+        log,
+        ad_caller,
+        ad_callee,
+        subid_caller,
+        subid_callee,
+        ad_hangup=None,
+        verify_caller_func=None,
+        verify_callee_func=None,
+        wait_time_in_call=WAIT_TIME_IN_CALL,
+        incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
+        dialing_number_length=None,
+        video_state=None,
+        voice_type_init=None,
+        call_stats_check=False,
+        result_info=result_dict):
+    """ 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.
+        verify_call_mode_caller: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        verify_call_mode_caller: func_ptr to verify caller in correct mode
+            Optional. Default is None
+        incall_ui_display: after answer the call, bring in-call UI to foreground or
+            background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
+            if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
+            if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
+            else, do nothing.
+
+    Returns:
+        TelResultWrapper which will evaluate as False if error.
+
+    """
+    CHECK_INTERVAL = 5
+    begin_time = get_current_epoch_time()
+    if not verify_caller_func:
+        verify_caller_func = is_phone_in_call
+    if not verify_callee_func:
+        verify_callee_func = is_phone_in_call
+
+    caller_number = ad_caller.telephony['subscription'][subid_caller][
+        'phone_num']
+    callee_number = ad_callee.telephony['subscription'][subid_callee][
+        'phone_num']
+    if dialing_number_length:
+        skip_test = False
+        trunc_position = 0 - int(dialing_number_length)
+        try:
+            caller_area_code = caller_number[:trunc_position]
+            callee_area_code = callee_number[:trunc_position]
+            callee_dial_number = callee_number[trunc_position:]
+        except:
+            skip_test = True
+        if caller_area_code != callee_area_code:
+            skip_test = True
+        if skip_test:
+            msg = "Cannot make call from %s to %s by %s digits" % (
+                caller_number, callee_number, dialing_number_length)
+            ad_caller.log.info(msg)
+            raise signals.TestSkip(msg)
+        else:
+            callee_number = callee_dial_number
+
+    tel_result_wrapper = TelResultWrapper(CallResult('SUCCESS'))
+    msg = "Call from %s to %s" % (caller_number, callee_number)
+    if video_state:
+        msg = "Video %s" % msg
+        video = True
+    else:
+        video = False
+    if ad_hangup:
+        msg = "%s for duration of %s seconds" % (msg, wait_time_in_call)
+    ad_caller.log.info(msg)
+
+    for ad in (ad_caller, ad_callee):
+        call_ids = ad.droid.telecomCallGetCallIds()
+        setattr(ad, "call_ids", call_ids)
+        if call_ids:
+            ad.log.info("Pre-exist CallId %s before making call", call_ids)
+
+    if not initiate_call(
+            log,
+            ad_caller,
+            callee_number,
+            incall_ui_display=incall_ui_display,
+            video=video):
+        ad_caller.log.error("Initiate call failed.")
+        tel_result_wrapper.result_value = CallResult('INITIATE_FAILED')
+        return tel_result_wrapper
+    else:
+        ad_caller.log.info("Caller initate call successfully")
+    if not wait_and_answer_call_for_subscription(
+            log,
+            ad_callee,
+            subid_callee,
+            incoming_number=caller_number,
+            caller=ad_caller,
+            incall_ui_display=incall_ui_display,
+            video_state=video_state):
+        ad_callee.log.error("Answer call fail.")
+        tel_result_wrapper.result_value = CallResult(
+            'NO_RING_EVENT_OR_ANSWER_FAILED')
+        return tel_result_wrapper
+    else:
+        ad_callee.log.info("Callee answered the call successfully")
+
+    for ad, call_func in zip([ad_caller, ad_callee],
+                                [verify_caller_func, verify_callee_func]):
+        call_ids = ad.droid.telecomCallGetCallIds()
+        new_call_ids = set(call_ids) - set(ad.call_ids)
+        if not new_call_ids:
+            ad.log.error(
+                "No new call ids are found after call establishment")
+            ad.log.error("telecomCallGetCallIds returns %s",
+                            ad.droid.telecomCallGetCallIds())
+            tel_result_wrapper.result_value = CallResult('NO_CALL_ID_FOUND')
+        for new_call_id in new_call_ids:
+            if not wait_for_in_call_active(ad, call_id=new_call_id):
+                tel_result_wrapper.result_value = CallResult(
+                    'CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT')
+            else:
+                ad.log.info("callProperties = %s",
+                            ad.droid.telecomCallGetProperties(new_call_id))
+
+        if not ad.droid.telecomCallGetAudioState():
+            ad.log.error("Audio is not in call state")
+            tel_result_wrapper.result_value = CallResult(
+                'AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT')
+
+        if call_func(log, ad):
+            ad.log.info("Call is in %s state", call_func.__name__)
+        else:
+            ad.log.error("Call is not in %s state, voice in RAT %s",
+                            call_func.__name__,
+                            ad.droid.telephonyGetCurrentVoiceNetworkType())
+            tel_result_wrapper.result_value = CallResult(
+                'CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT')
+    if not tel_result_wrapper:
+        return tel_result_wrapper
+
+    if call_stats_check:
+        voice_type_in_call = check_voice_network_type([ad_caller, ad_callee], voice_init=False)
+        phone_a_call_type = check_call_status(ad_caller,
+                                                voice_type_init[0],
+                                                voice_type_in_call[0])
+        result_info["Call Stats"] = phone_a_call_type
+        ad_caller.log.debug("Voice Call Type: %s", phone_a_call_type)
+        phone_b_call_type = check_call_status(ad_callee,
+                                                voice_type_init[1],
+                                                voice_type_in_call[1])
+        result_info["Call Stats"] = phone_b_call_type
+        ad_callee.log.debug("Voice Call Type: %s", phone_b_call_type)
+
+    return wait_for_call_end(
+        log,
+        ad_caller,
+        ad_callee,
+        ad_hangup,
+        verify_caller_func,
+        verify_callee_func,
+        begin_time,
+        check_interval=CHECK_INTERVAL,
+        tel_result_wrapper=TelResultWrapper(CallResult('SUCCESS')),
+        wait_time_in_call=WAIT_TIME_IN_CALL)
 
 
 def two_phone_call_leave_voice_mail(
@@ -499,1113 +1328,38 @@
 
     return tel_result
 
-def three_phone_call_forwarding_short_seq(log,
-                             phone_a,
-                             phone_a_idle_func,
-                             phone_a_in_call_check_func,
-                             phone_b,
-                             phone_c,
-                             wait_time_in_call=WAIT_TIME_IN_CALL,
-                             call_forwarding_type="unconditional",
-                             retry=2):
-    """Short sequence of call process with call forwarding.
-    Test steps:
-        1. Ensure all phones are initially in idle state.
-        2. Enable call forwarding on Phone A.
-        3. Make a call from Phone B to Phone A, The call should be forwarded to
-           PhoneC. Accept the call on Phone C.
-        4. Ensure the call is connected and in correct phone state.
-        5. Hang up the call on Phone B.
-        6. Ensure all phones are in idle state.
-        7. Disable call forwarding on Phone A.
-        7. Make a call from Phone B to Phone A, The call should NOT be forwarded
-           to PhoneC. Accept the call on Phone A.
-        8. Ensure the call is connected and in correct phone state.
-        9. Hang up the call on Phone B.
+
+def is_phone_in_call(log, ad):
+    """Return True if phone in call.
 
     Args:
-        phone_a: android object of Phone A
-        phone_a_idle_func: function to check idle state on Phone A
-        phone_a_in_call_check_func: function to check in-call state on Phone A
-        phone_b: android object of Phone B
-        phone_c: android object of Phone C
-        wait_time_in_call: time to wait in call.
-            This is optional, default is WAIT_TIME_IN_CALL
-        call_forwarding_type:
-            - "unconditional"
-            - "busy"
-            - "not_answered"
-            - "not_reachable"
-        retry: times of retry
-
-    Returns:
-        True: if call sequence succeed.
-        False: for errors
+        log: log object.
+        ad:  android device.
     """
-    ads = [phone_a, phone_b, phone_c]
-
-    call_params = [
-        (ads[1], ads[0], ads[2], ads[1], phone_a_in_call_check_func, False)
-    ]
-
-    if call_forwarding_type != "unconditional":
-        call_params.append((
-            ads[1],
-            ads[0],
-            ads[2],
-            ads[1],
-            phone_a_in_call_check_func,
-            True))
-
-    for param in call_params:
-        ensure_phones_idle(log, ads)
-        if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
-            phone_a.log.error("Phone A Failed to Reselect")
-            return False
-
-        time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
-
-        log.info(
-            "---> Call forwarding %s (caller: %s, callee: %s, callee forwarded:"
-            " %s) <---",
-            call_forwarding_type,
-            param[0].serial,
-            param[1].serial,
-            param[2].serial)
-        while not call_setup_teardown_for_call_forwarding(
-                log,
-                *param,
-                wait_time_in_call=wait_time_in_call,
-                call_forwarding_type=call_forwarding_type) and retry >= 0:
-
-            if retry <= 0:
-                log.error("Call forwarding %s failed." % call_forwarding_type)
-                return False
-            else:
-                log.info(
-                    "RERUN the test case: 'Call forwarding %s'" %
-                    call_forwarding_type)
-
-            retry = retry - 1
-
-    return True
-
-def three_phone_call_waiting_short_seq(log,
-                             phone_a,
-                             phone_a_idle_func,
-                             phone_a_in_call_check_func,
-                             phone_b,
-                             phone_c,
-                             wait_time_in_call=WAIT_TIME_IN_CALL,
-                             call_waiting=True,
-                             scenario=None,
-                             retry=2):
-    """Short sequence of call process with call waiting.
-    Test steps:
-        1. Ensure all phones are initially in idle state.
-        2. Enable call waiting on Phone A.
-        3. Make the 1st call from Phone B to Phone A. Accept the call on Phone B.
-        4. Ensure the call is connected and in correct phone state.
-        5. Make the 2nd call from Phone C to Phone A. The call should be able to
-           income correctly. Whether or not the 2nd call should be answered by
-           Phone A depends on the scenario listed in the next step.
-        6. Following 8 scenarios will be tested:
-           - 1st call ended first by Phone B during 2nd call incoming. 2nd call
-             ended by Phone C
-           - 1st call ended first by Phone B during 2nd call incoming. 2nd call
-             ended by Phone A
-           - 1st call ended first by Phone A during 2nd call incoming. 2nd call
-             ended by Phone C
-           - 1st call ended first by Phone A during 2nd call incoming. 2nd call
-             ended by Phone A
-           - 1st call ended by Phone B. 2nd call ended by Phone C
-           - 1st call ended by Phone B. 2nd call ended by Phone A
-           - 1st call ended by Phone A. 2nd call ended by Phone C
-           - 1st call ended by Phone A. 2nd call ended by Phone A
-        7. Ensure all phones are in idle state.
-
-    Args:
-        phone_a: android object of Phone A
-        phone_a_idle_func: function to check idle state on Phone A
-        phone_a_in_call_check_func: function to check in-call state on Phone A
-        phone_b: android object of Phone B
-        phone_c: android object of Phone C
-        wait_time_in_call: time to wait in call.
-            This is optional, default is WAIT_TIME_IN_CALL
-        call_waiting: True for call waiting enabled and False for disabled
-        scenario: 1-8 for scenarios listed above
-        retry: times of retry
-
-    Returns:
-        True: if call sequence succeed.
-        False: for errors
-    """
-    ads = [phone_a, phone_b, phone_c]
-
-    sub_test_cases = [
-        {
-            "description": "1st call ended first by caller1 during 2nd call"
-                " incoming. 2nd call ended by caller2",
-            "params": (
-                ads[1],
-                ads[0],
-                ads[2],
-                ads[1],
-                ads[2],
-                phone_a_in_call_check_func,
-                True)},
-        {
-            "description": "1st call ended first by caller1 during 2nd call"
-                " incoming. 2nd call ended by callee",
-            "params": (
-                ads[1],
-                ads[0],
-                ads[2],
-                ads[1],
-                ads[0],
-                phone_a_in_call_check_func,
-                True)},
-        {
-            "description": "1st call ended first by callee during 2nd call"
-                " incoming. 2nd call ended by caller2",
-            "params": (
-                ads[1],
-                ads[0],
-                ads[2],
-                ads[0],
-                ads[2],
-                phone_a_in_call_check_func,
-                True)},
-        {
-            "description": "1st call ended first by callee during 2nd call"
-                " incoming. 2nd call ended by callee",
-            "params": (
-                ads[1],
-                ads[0],
-                ads[2],
-                ads[0],
-                ads[0],
-                phone_a_in_call_check_func,
-                True)},
-        {
-            "description": "1st call ended by caller1. 2nd call ended by"
-                " caller2",
-            "params": (
-                ads[1],
-                ads[0],
-                ads[2],
-                ads[1],
-                ads[2],
-                phone_a_in_call_check_func,
-                False)},
-        {
-            "description": "1st call ended by caller1. 2nd call ended by callee",
-            "params": (
-                ads[1],
-                ads[0],
-                ads[2],
-                ads[1],
-                ads[0],
-                phone_a_in_call_check_func,
-                False)},
-        {
-            "description": "1st call ended by callee. 2nd call ended by caller2",
-            "params": (
-                ads[1],
-                ads[0],
-                ads[2],
-                ads[0],
-                ads[2],
-                phone_a_in_call_check_func,
-                False)},
-        {
-            "description": "1st call ended by callee. 2nd call ended by callee",
-            "params": (
-                ads[1],
-                ads[0],
-                ads[2],
-                ads[0],
-                ads[0],
-                phone_a_in_call_check_func,
-                False)}
-    ]
-
-    if call_waiting:
-        if not scenario:
-            test_cases = sub_test_cases
-        else:
-            test_cases = [sub_test_cases[scenario-1]]
-    else:
-        test_cases = [
-            {
-                "description": "Call waiting deactivated",
-                "params": (
-                    ads[1],
-                    ads[0],
-                    ads[2],
-                    ads[0],
-                    ads[0],
-                    phone_a_in_call_check_func,
-                    False)}
-        ]
-
-    results = []
-
-    for test_case in test_cases:
-        ensure_phones_idle(log, ads)
-        if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
-            phone_a.log.error("Phone A Failed to Reselect")
-            return False
-
-        time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
-
-        log.info(
-            "---> %s (caller1: %s, caller2: %s, callee: %s) <---",
-            test_case["description"],
-            test_case["params"][1].serial,
-            test_case["params"][2].serial,
-            test_case["params"][0].serial)
-
-        while not call_setup_teardown_for_call_waiting(
-            log,
-            *test_case["params"],
-            wait_time_in_call=wait_time_in_call,
-            call_waiting=call_waiting) and retry >= 0:
-
-            if retry <= 0:
-                log.error("Call waiting sub-case: '%s' failed." % test_case[
-                    "description"])
-                results.append(False)
-            else:
-                log.info("RERUN the sub-case: '%s'" % test_case["description"])
-
-            retry = retry - 1
-
-    for result in results:
-        if not result:
-            return False
-
-    return True
-
-def phone_setup_iwlan(log,
-                      ad,
-                      is_airplane_mode,
-                      wfc_mode,
-                      wifi_ssid=None,
-                      wifi_pwd=None,
-                      nw_gen=None):
-    """Phone setup function for epdg call test.
-    Set WFC mode according to wfc_mode.
-    Set airplane mode according to is_airplane_mode.
-    Make sure phone connect to WiFi. (If wifi_ssid is not None.)
-    Wait for phone to be in iwlan data network type.
-    Wait for phone to report wfc enabled flag to be true.
-    Args:
-        log: Log object.
-        ad: Android device object.
-        is_airplane_mode: True to turn on airplane mode. False to turn off airplane mode.
-        wfc_mode: WFC mode to set to.
-        wifi_ssid: WiFi network SSID. This is optional.
-            If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
-        wifi_pwd: WiFi network password. This is optional.
-        nw_gen: network type selection. This is optional.
-            GEN_4G for 4G, GEN_5G for 5G or None for doing nothing.
-    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, nw_gen)
-
-
-def phone_setup_iwlan_for_subscription(log,
-                                       ad,
-                                       sub_id,
-                                       is_airplane_mode,
-                                       wfc_mode,
-                                       wifi_ssid=None,
-                                       wifi_pwd=None,
-                                       nw_gen=None,
-                                       nr_type=None):
-    """Phone setup function for epdg call test for subscription id.
-    Set WFC mode according to wfc_mode.
-    Set airplane mode according to is_airplane_mode.
-    Make sure phone connect to WiFi. (If wifi_ssid is not None.)
-    Wait for phone to be in iwlan data network type.
-    Wait for phone to report wfc enabled flag to be true.
-    Args:
-        log: Log object.
-        ad: Android device object.
-        sub_id: subscription id.
-        is_airplane_mode: True to turn on airplane mode. False to turn off airplane mode.
-        wfc_mode: WFC mode to set to.
-        wifi_ssid: WiFi network SSID. This is optional.
-            If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
-        wifi_pwd: WiFi network password. This is optional.
-        nw_gen: network type selection. This is optional.
-            GEN_4G for 4G, GEN_5G for 5G or None for doing nothing.
-        nr_type: NR network type
-    Returns:
-        True if success. False if fail.
-    """
-    if not get_capability_for_subscription(ad, CAPABILITY_WFC, sub_id):
-        ad.log.error("WFC is not supported, abort test.")
-        raise signals.TestSkip("WFC is not supported, abort test.")
-
-    if nw_gen:
-        if not ensure_network_generation_for_subscription(
-                log, ad, sub_id, nw_gen, voice_or_data=NETWORK_SERVICE_DATA,
-                nr_type=nr_type):
-            ad.log.error("Failed to set to %s data.", nw_gen)
-            return False
-    toggle_airplane_mode(log, ad, is_airplane_mode, strict_checking=False)
-
-    # Pause at least for 4 seconds is necessary after airplane mode was turned
-    # on due to the mechanism of deferring Wi-Fi (b/191481736)
-    if is_airplane_mode:
-        time.sleep(5)
-
-    # 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, apm=is_airplane_mode):
-            ad.log.error("Fail to bring up WiFi connection on %s.", wifi_ssid)
-            return False
-    else:
-        ad.log.info("WiFi network SSID not specified, available user "
-                    "parameters are: wifi_network_ssid, wifi_network_ssid_2g, "
-                    "wifi_network_ssid_5g")
-    if not set_wfc_mode_for_subscription(ad, wfc_mode, sub_id):
-        ad.log.error("Unable to set WFC mode to %s.", wfc_mode)
-        return False
-
-    if wfc_mode != WFC_MODE_DISABLED:
-        if not wait_for_wfc_enabled(log, ad, max_time=MAX_WAIT_TIME_WFC_ENABLED):
-            ad.log.error("WFC is not enabled")
-            return False
-
-    return True
-
-
-def phone_setup_iwlan_cellular_preferred(log,
-                                         ad,
-                                         wifi_ssid=None,
-                                         wifi_pwd=None):
-    """Phone setup function for iwlan Non-APM CELLULAR_PREFERRED test.
-    Set WFC mode according to CELLULAR_PREFERRED.
-    Set airplane mode according to False.
-    Make sure phone connect to WiFi. (If wifi_ssid is not None.)
-    Make sure phone don't report iwlan data network type.
-    Make sure phone don't report wfc enabled flag to be true.
-
-    Args:
-        log: Log object.
-        ad: Android device object.
-        wifi_ssid: WiFi network SSID. This is optional.
-            If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi.
-        wifi_pwd: WiFi network password. This is optional.
-
-    Returns:
-        True if success. False if fail.
-    """
-    toggle_airplane_mode(log, ad, False, 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 wifi_ssid is not None:
-        if not ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd):
-            ad.log.error("Connect to WiFi failed.")
-            return False
-    if not set_wfc_mode(log, ad, WFC_MODE_CELLULAR_PREFERRED):
-        ad.log.error("Set WFC mode failed.")
-        return False
-    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):
-        ad.log.error("Should report wifi calling disabled within %s.",
-                     MAX_WAIT_TIME_WFC_ENABLED)
-        return False
-    return True
+        return ad.droid.telecomIsInCall()
+    except:
+        return "mCallState=2" in ad.adb.shell(
+            "dumpsys telephony.registry | grep mCallState")
 
 
-def phone_setup_data_for_subscription(log, ad, sub_id, network_generation,
-                                        nr_type=None):
-    """Setup Phone <sub_id> Data to <network_generation>
+def is_phone_in_call_active(ad, call_id=None):
+    """Return True if phone in active call.
 
     Args:
-        log: log object
-        ad: android device object
-        sub_id: subscription id
-        network_generation: network generation, e.g. GEN_2G, GEN_3G, GEN_4G, GEN_5G
-        nr_type: NR network type e.g. NSA, SA, MMWAVE
-
-    Returns:
-        True if success, False if fail.
+        log: log object.
+        ad:  android device.
+        call_id: the call id
     """
-    toggle_airplane_mode(log, ad, False, strict_checking=False)
-    set_wifi_to_default(log, ad)
-    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
-        ad.log.error("Disable WFC failed.")
-        return False
-    if not ensure_network_generation_for_subscription(
-            log,
-            ad,
-            sub_id,
-            network_generation,
-            voice_or_data=NETWORK_SERVICE_DATA,
-            nr_type=nr_type):
-        get_telephony_signal_strength(ad)
-        return False
-    return True
-
-
-def phone_setup_5g(log, ad, nr_type=None):
-    """Setup Phone default data sub_id data to 5G.
-
-    Args:
-        log: log object
-        ad: android device object
-
-    Returns:
-        True if success, False if fail.
-    """
-    return phone_setup_5g_for_subscription(log, ad,
-                                           get_default_data_sub_id(ad), nr_type=nr_type)
-
-
-def phone_setup_5g_for_subscription(log, ad, sub_id, nr_type=None):
-    """Setup Phone <sub_id> Data to 5G.
-
-    Args:
-        log: log object
-        ad: android device object
-        sub_id: subscription id
-        nr_type: NR network type e.g. NSA, SA, MMWAVE
-
-    Returns:
-        True if success, False if fail.
-    """
-    return phone_setup_data_for_subscription(log, ad, sub_id, GEN_5G,
-                                        nr_type=nr_type)
-
-
-def phone_setup_4g(log, ad):
-    """Setup Phone default data sub_id data to 4G.
-
-    Args:
-        log: log object
-        ad: android device object
-
-    Returns:
-        True if success, False if fail.
-    """
-    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.
-
-    Args:
-        log: log object
-        ad: android device object
-        sub_id: subscription id
-
-    Returns:
-        True if success, False if fail.
-    """
-    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.
-
-    Args:
-        log: log object
-        ad: android device object
-
-    Returns:
-        True if success, False if fail.
-    """
-    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.
-
-    Args:
-        log: log object
-        ad: android device object
-        sub_id: subscription id
-
-    Returns:
-        True if success, False if fail.
-    """
-    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.
-
-    Args:
-        log: log object
-        ad: android device object
-
-    Returns:
-        True if success, False if fail.
-    """
-    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.
-
-    Args:
-        log: log object
-        ad: android device object
-        sub_id: subscription id
-
-    Returns:
-        True if success, False if fail.
-    """
-    return phone_setup_data_for_subscription(log, ad, sub_id, GEN_2G)
-
-
-def phone_setup_csfb(log, ad, nw_gen=GEN_4G, nr_type=None):
-    """Setup phone for CSFB call test.
-
-    Setup Phone to be in 4G mode.
-    Disabled VoLTE.
-
-    Args:
-        log: log object
-        ad: Android device object.
-        nw_gen: GEN_4G or GEN_5G
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    return phone_setup_csfb_for_subscription(log, ad,
-                                        get_outgoing_voice_sub_id(ad), nw_gen, nr_type=nr_type)
-
-
-def phone_setup_csfb_for_subscription(log, ad, sub_id, nw_gen=GEN_4G, nr_type=None):
-    """Setup phone for CSFB call test for subscription id.
-
-    Setup Phone to be in 4G mode.
-    Disabled VoLTE.
-
-    Args:
-        log: log object
-        ad: Android device object.
-        sub_id: subscription id.
-        nw_gen: GEN_4G or GEN_5G
-        nr_type: NR network type e.g. NSA, SA, MMWAVE
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    capabilities = ad.telephony["subscription"][sub_id].get("capabilities", [])
-    if capabilities:
-        if "hide_enhanced_4g_lte" in capabilities:
-            show_enhanced_4g_lte_mode = getattr(ad, "show_enhanced_4g_lte_mode", False)
-            if show_enhanced_4g_lte_mode in ["false", "False", False]:
-                ad.log.warning("'VoLTE' option is hidden. Test will be skipped.")
-                raise signals.TestSkip("'VoLTE' option is hidden. Test will be skipped.")
-
-    if nw_gen == GEN_4G:
-        if not phone_setup_4g_for_subscription(log, ad, sub_id):
-            ad.log.error("Failed to set to 4G data.")
-            return False
-    elif nw_gen == GEN_5G:
-        if not phone_setup_5g_for_subscription(log, ad, sub_id, nr_type=nr_type):
-            ad.log.error("Failed to set to 5G data.")
-            return False
-
-    if not toggle_volte_for_subscription(log, ad, sub_id, False):
-        return False
-
-    if not wait_for_voice_attach_for_subscription(log, ad, sub_id,
-                                                  MAX_WAIT_TIME_NW_SELECTION):
-        return False
-
-    return phone_idle_csfb_for_subscription(log, ad, sub_id, nw_gen)
-
-def phone_setup_volte(log, ad, nw_gen=GEN_4G, nr_type=None):
-    """Setup VoLTE enable.
-
-    Args:
-        log: log object
-        ad: android device object.
-        nw_gen: GEN_4G or GEN_5G
-
-    Returns:
-        True: if VoLTE is enabled successfully.
-        False: for errors
-    """
-    if not get_capability_for_subscription(ad, CAPABILITY_VOLTE,
-        get_outgoing_voice_sub_id(ad)):
-        ad.log.error("VoLTE is not supported, abort test.")
-        raise signals.TestSkip("VoLTE is not supported, abort test.")
-    return phone_setup_volte_for_subscription(log, ad,
-                        get_outgoing_voice_sub_id(ad), nw_gen, nr_type= nr_type)
-
-def phone_setup_volte_for_subscription(log, ad, sub_id, nw_gen=GEN_4G,
-                                        nr_type=None):
-    """Setup VoLTE enable for subscription id.
-    Args:
-        log: log object
-        ad: android device object.
-        sub_id: subscription id.
-        nw_gen: GEN_4G or GEN_5G.
-        nr_type: NR network type.
-
-    Returns:
-        True: if VoLTE is enabled successfully.
-        False: for errors
-    """
-    if not get_capability_for_subscription(ad, CAPABILITY_VOLTE,
-        get_outgoing_voice_sub_id(ad)):
-        ad.log.error("VoLTE is not supported, abort test.")
-        raise signals.TestSkip("VoLTE is not supported, abort test.")
-
-    if nw_gen == GEN_4G:
-        if not phone_setup_4g_for_subscription(log, ad, sub_id):
-            ad.log.error("Failed to set to 4G data.")
-            return False
-    elif nw_gen == GEN_5G:
-        if not phone_setup_5g_for_subscription(log, ad, sub_id,
-                                        nr_type=nr_type):
-            ad.log.error("Failed to set to 5G data.")
-            return False
-    operator_name = get_operator_name(log, ad, sub_id)
-    if operator_name == CARRIER_TMO:
-        return True
+    if ad.droid.telecomIsInCall():
+        if not call_id:
+            call_id = ad.droid.telecomCallGetCallIds()[0]
+        call_state = ad.droid.telecomCallGetCallState(call_id)
+        ad.log.info("%s state is %s", call_id, call_state)
+        return call_state == "ACTIVE"
     else:
-        if not wait_for_enhanced_4g_lte_setting(log, ad, sub_id):
-            ad.log.error("Enhanced 4G LTE setting is not available")
-            return False
-        toggle_volte_for_subscription(log, ad, sub_id, True)
-    return phone_idle_volte_for_subscription(log, ad, sub_id, nw_gen,
-                                        nr_type=nr_type)
-
-
-def phone_setup_voice_3g(log, ad):
-    """Setup phone voice to 3G.
-
-    Args:
-        log: log object
-        ad: Android device object.
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    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.
-
-    Args:
-        log: log object
-        ad: Android device object.
-        sub_id: subscription id.
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    if not phone_setup_3g_for_subscription(log, ad, sub_id):
-        ad.log.error("Failed to set to 3G data.")
+        ad.log.info("Not in telecomIsInCall")
         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.
-
-    Args:
-        log: log object
-        ad: Android device object.
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    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):
-    """Setup phone voice to 2G for subscription id.
-
-    Args:
-        log: log object
-        ad: Android device object.
-        sub_id: subscription id.
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    if not phone_setup_2g_for_subscription(log, ad, sub_id):
-        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.
-
-    Make sure phone attached to voice.
-    Make necessary delay.
-
-    Args:
-        ad: Android device object.
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    return phone_setup_voice_general_for_subscription(
-        log, ad, get_outgoing_voice_sub_id(ad))
-
-
-def phone_setup_voice_general_for_slot(log,ad,slot_id):
-    return phone_setup_voice_general_for_subscription(
-        log, ad, get_subid_from_slot_index(log,ad,slot_id))
-
-
-def phone_setup_voice_general_for_subscription(log, ad, sub_id):
-    """Setup phone for voice general call test for subscription id.
-
-    Make sure phone attached to voice.
-    Make necessary delay.
-
-    Args:
-        ad: Android device object.
-        sub_id: subscription id.
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    toggle_airplane_mode(log, ad, False, 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.
-
-    Make sure phone attached to data.
-    Make necessary delay.
-
-    Args:
-        ad: Android device object.
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    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.
-
-    Make sure phone attached to data.
-    Make necessary delay.
-
-    Args:
-        ad: Android device object.
-        sub_id: subscription id.
-
-    Returns:
-        True if setup successfully.
-        False for errors.
-    """
-    toggle_airplane_mode(log, ad, False, 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
-        reset_preferred_network_type_to_allowable_range(log, ad)
-
-    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, strict_checking=False)
-    set_wifi_to_default(log, ad)
-    if not set_wfc_mode(log, ad, WFC_MODE_DISABLED):
-        ad.log.error("Disable WFC failed.")
-        return False
-    return ensure_network_rat_for_subscription(log, ad, sub_id,
-                                               network_preference, rat_family)
-
-
-def phone_setup_lte_gsm_wcdma(log, ad):
-    return phone_setup_lte_gsm_wcdma_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId())
-
-
-def phone_setup_lte_gsm_wcdma_for_subscription(log, ad, sub_id):
-    return phone_setup_rat_for_subscription(
-        log, ad, sub_id, NETWORK_MODE_LTE_GSM_WCDMA, RAT_FAMILY_LTE)
-
-
-def phone_setup_gsm_umts(log, ad):
-    return phone_setup_gsm_umts_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId())
-
-
-def phone_setup_gsm_umts_for_subscription(log, ad, sub_id):
-    return phone_setup_rat_for_subscription(
-        log, ad, sub_id, NETWORK_MODE_GSM_UMTS, RAT_FAMILY_WCDMA)
-
-
-def phone_setup_gsm_only(log, ad):
-    return phone_setup_gsm_only_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId())
-
-
-def phone_setup_gsm_only_for_subscription(log, ad, sub_id):
-    return phone_setup_rat_for_subscription(
-        log, ad, sub_id, NETWORK_MODE_GSM_ONLY, RAT_FAMILY_GSM)
-
-
-def phone_setup_lte_cdma_evdo(log, ad):
-    return phone_setup_lte_cdma_evdo_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId())
-
-
-def phone_setup_lte_cdma_evdo_for_subscription(log, ad, sub_id):
-    return phone_setup_rat_for_subscription(
-        log, ad, sub_id, NETWORK_MODE_LTE_CDMA_EVDO, RAT_FAMILY_LTE)
-
-
-def phone_setup_cdma(log, ad):
-    return phone_setup_cdma_for_subscription(
-        log, ad, ad.droid.subscriptionGetDefaultSubId())
-
-
-def phone_setup_cdma_for_subscription(log, ad, sub_id):
-    return phone_setup_rat_for_subscription(log, ad, sub_id, NETWORK_MODE_CDMA,
-                                            RAT_FAMILY_CDMA2000)
-
-
-def phone_idle_volte(log, ad):
-    """Return if phone is idle for VoLTE call test.
-
-    Args:
-        ad: Android device object.
-    """
-    return phone_idle_volte_for_subscription(log, ad,
-                                             get_outgoing_voice_sub_id(ad))
-
-
-def phone_idle_volte_for_subscription(log, ad, sub_id, nw_gen=GEN_4G,
-                                    nr_type=None):
-    """Return if phone is idle for VoLTE call test for subscription id.
-    Args:
-        ad: Android device object.
-        sub_id: subscription id.
-        nw_gen: GEN_4G or GEN_5G.
-        nr_type: NR network type e.g. NSA, SA, MMWAVE
-    """
-    if nw_gen == GEN_5G:
-        if not is_current_network_5g_for_subscription(ad, sub_id=sub_id,
-                                            nr_type=nr_type):
-            ad.log.error("Not in 5G coverage.")
-            return False
-    else:
-        if not wait_for_network_rat_for_subscription(
-                log, ad, sub_id, RAT_FAMILY_LTE,
-                voice_or_data=NETWORK_SERVICE_VOICE):
-            ad.log.error("Voice rat not in LTE mode.")
-            return False
-    if not wait_for_volte_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED, sub_id):
-        ad.log.error(
-            "Failed to <report volte enabled true> within %s seconds.",
-            MAX_WAIT_TIME_VOLTE_ENABLED)
-        return False
-    return True
-
-
-def phone_idle_iwlan(log, ad):
-    """Return if phone is idle for WiFi calling call test.
-
-    Args:
-        ad: Android device object.
-    """
-    return phone_idle_iwlan_for_subscription(log, ad,
-                                             get_outgoing_voice_sub_id(ad))
-
-
-def phone_idle_iwlan_for_subscription(log, ad, sub_id):
-    """Return if phone is idle for WiFi calling call test for subscription id.
-
-    Args:
-        ad: Android device object.
-        sub_id: subscription id.
-    """
-    if not wait_for_wfc_enabled(log, ad, 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
-
-
-def phone_idle_csfb(log, ad):
-    """Return if phone is idle for CSFB call test.
-
-    Args:
-        ad: Android device object.
-    """
-    return phone_idle_csfb_for_subscription(log, ad,
-                                            get_outgoing_voice_sub_id(ad))
-
-
-def phone_idle_csfb_for_subscription(log, ad, sub_id, nw_gen=GEN_4G, nr_type=None):
-    """Return if phone is idle for CSFB call test for subscription id.
-
-    Args:
-        ad: Android device object.
-        sub_id: subscription id.
-        nw_gen: GEN_4G or GEN_5G
-    """
-    if nw_gen == GEN_5G:
-        if not is_current_network_5g_for_subscription(ad, sub_id=sub_id, nr_type=nr_type):
-            ad.log.error("Not in 5G coverage.")
-            return False
-    else:
-        if not wait_for_network_rat_for_subscription(